-- | @since 0.1
module Numeric.Convert.Internal
  ( -- * Integers
    FromInteger (..),
    ToInteger (..),

    -- * Rationals
    FromRational (..),
    ToRational (..),

    -- * Reals
    FromReal (..),
    ToReal (..),
  )
where

import Data.Complex (Complex)
import Data.Fixed (Fixed, HasResolution)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Kind (Constraint, Type)
import Data.Ratio (Ratio)
import Data.Word (Word16, Word32, Word64, Word8)
import GHC.Natural (Natural)
import GHC.Stack.Types (HasCallStack)

-- NOTE: Internal module so that we can give our typeclasses mutual
-- constraints.
--
-- In general, conversions @a -> b@ are intended to be embeddings i.e.
-- well-behaved. This is why e.g. ToInteger requires a ToRational constraint
-- -- if you can embed in Z, you can embed in Q -- and why some instances are
-- missing e.g. fromReal :: Double -> Integer.
--
-- That said, there are still some instances of "bad behavior":
--
-- - Natural instances are partial.
-- - Bounded types may over/underflow.
-- - General floating point issues.

{- HLINT ignore FromInteger "Redundant bracket" -}

-------------------------------------------------------------------------------
----------------------------------- INTEGERS ----------------------------------
-------------------------------------------------------------------------------

-- | Replaces base's @fromInteger@ functionality for when we do not have a
-- 'Num' instance.
--
-- @
-- 1_000 :: Num a => a
--
-- -- becomes
--
-- fromZ 1_000 :: FromInteger a => a
-- @
--
-- Note that @fromInteger@'s deficiencies are inherited e.g. 'Natural' is
-- partial, bounded types have over/underflow issues.
--
-- @since 0.1
type FromInteger :: Type -> Constraint
class FromInteger a where
  -- | @since 0.1
  fromZ :: (HasCallStack) => Integer -> a

-- | @since 0.1
instance FromInteger Double where
  fromZ :: HasCallStack => Integer -> Double
fromZ = Integer -> Double
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Float where
  fromZ :: HasCallStack => Integer -> Float
fromZ = Integer -> Float
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Int where
  fromZ :: HasCallStack => Integer -> Int
fromZ = Integer -> Int
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Int8 where
  fromZ :: HasCallStack => Integer -> Int8
fromZ = Integer -> Int8
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Int16 where
  fromZ :: HasCallStack => Integer -> Int16
fromZ = Integer -> Int16
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Int32 where
  fromZ :: HasCallStack => Integer -> Int32
fromZ = Integer -> Int32
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Int64 where
  fromZ :: HasCallStack => Integer -> Int64
fromZ = Integer -> Int64
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Integer where
  fromZ :: HasCallStack => Integer -> Integer
fromZ = Integer -> Integer
forall a. a -> a
id
  {-# INLINE fromZ #-}

-- | __WARNING: Partial__
--
-- @since 0.1
instance FromInteger Natural where
  fromZ :: HasCallStack => Integer -> Natural
fromZ = Integer -> Natural
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Word where
  fromZ :: HasCallStack => Integer -> Word
fromZ = Integer -> Word
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Word8 where
  fromZ :: HasCallStack => Integer -> Word8
fromZ = Integer -> Word8
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Word16 where
  fromZ :: HasCallStack => Integer -> Word16
fromZ = Integer -> Word16
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Word32 where
  fromZ :: HasCallStack => Integer -> Word32
fromZ = Integer -> Word32
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger Word64 where
  fromZ :: HasCallStack => Integer -> Word64
fromZ = Integer -> Word64
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance FromInteger (Ratio Integer) where
  fromZ :: HasCallStack => Integer -> Ratio Integer
fromZ = Integer -> Ratio Integer
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | __WARNING: Partial__
--
-- @since 0.1
instance FromInteger (Ratio Natural) where
  fromZ :: HasCallStack => Integer -> Ratio Natural
fromZ = Integer -> Ratio Natural
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance (RealFloat a) => FromInteger (Complex a) where
  fromZ :: HasCallStack => Integer -> Complex a
fromZ = Integer -> Complex a
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | @since 0.1
instance (HasResolution k) => FromInteger (Fixed k) where
  fromZ :: HasCallStack => Integer -> Fixed k
fromZ = Integer -> Fixed k
forall a. Num a => Integer -> a
fromInteger
  {-# INLINE fromZ #-}

-- | Integer embedding.
--
-- @since 0.1
type ToInteger :: Type -> Constraint
class (ToRational a) => ToInteger a where
  -- | @since 0.1
  toZ :: (HasCallStack) => a -> Integer

-- | @since 0.1
instance ToInteger Int where
  toZ :: HasCallStack => Int -> Integer
toZ = Int -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Int8 where
  toZ :: HasCallStack => Int8 -> Integer
toZ = Int8 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Int16 where
  toZ :: HasCallStack => Int16 -> Integer
toZ = Int16 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Int32 where
  toZ :: HasCallStack => Int32 -> Integer
toZ = Int32 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Int64 where
  toZ :: HasCallStack => Int64 -> Integer
toZ = Int64 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Integer where
  toZ :: HasCallStack => Integer -> Integer
toZ = Integer -> Integer
forall a. a -> a
id
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Natural where
  toZ :: HasCallStack => Natural -> Integer
toZ = Natural -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Word where
  toZ :: HasCallStack => Word -> Integer
toZ = Word -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Word8 where
  toZ :: HasCallStack => Word8 -> Integer
toZ = Word8 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Word16 where
  toZ :: HasCallStack => Word16 -> Integer
toZ = Word16 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Word32 where
  toZ :: HasCallStack => Word32 -> Integer
toZ = Word32 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-- | @since 0.1
instance ToInteger Word64 where
  toZ :: HasCallStack => Word64 -> Integer
toZ = Word64 -> Integer
forall a. Integral a => a -> Integer
toInteger
  {-# INLINE toZ #-}

-------------------------------------------------------------------------------
---------------------------------- RATIONALS ----------------------------------
-------------------------------------------------------------------------------

-- | Replaces base's @fromRational@ functionality for when we do not have a
-- 'Fractional' instance.
--
-- @
-- 5.5 :: Fractional a => a
--
-- -- becomes
--
-- fromQ 5.5 :: FromRational a => a
-- @
--
-- Note that @fromRational@'s deficiencies are inherited e.g. 'Natural' is
-- partial, bounded types have over/underflow issues.
--
-- @since 0.1
type FromRational :: Type -> Constraint
class (FromInteger a) => FromRational a where
  -- | @since 0.1
  fromQ :: (HasCallStack) => Rational -> a

-- | @since 0.1
instance FromRational Double where
  fromQ :: HasCallStack => Ratio Integer -> Double
fromQ = Ratio Integer -> Double
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | @since 0.1
instance FromRational Float where
  fromQ :: HasCallStack => Ratio Integer -> Float
fromQ = Ratio Integer -> Float
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | @since 0.1
instance FromRational (Ratio Integer) where
  fromQ :: HasCallStack => Ratio Integer -> Ratio Integer
fromQ = Ratio Integer -> Ratio Integer
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | __WARNING: Partial__
--
-- @since 0.1
instance FromRational (Ratio Natural) where
  fromQ :: HasCallStack => Ratio Integer -> Ratio Natural
fromQ = Ratio Integer -> Ratio Natural
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | @since 0.1
instance (RealFloat a) => FromRational (Complex a) where
  fromQ :: HasCallStack => Ratio Integer -> Complex a
fromQ = Ratio Integer -> Complex a
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | @since 0.1
instance (HasResolution k) => FromRational (Fixed k) where
  fromQ :: HasCallStack => Ratio Integer -> Fixed k
fromQ = Ratio Integer -> Fixed k
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE fromQ #-}

-- | Rational embedding.
--
-- @since 0.1
type ToRational :: Type -> Constraint
class (ToReal a) => ToRational a where
  -- | @since 0.1
  toQ :: (HasCallStack) => a -> Rational

-- | @since 0.1
instance ToRational Double where
  toQ :: HasCallStack => Double -> Ratio Integer
toQ = Double -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Float where
  toQ :: HasCallStack => Float -> Ratio Integer
toQ = Float -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Int where
  toQ :: HasCallStack => Int -> Ratio Integer
toQ = Int -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Int8 where
  toQ :: HasCallStack => Int8 -> Ratio Integer
toQ = Int8 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Int16 where
  toQ :: HasCallStack => Int16 -> Ratio Integer
toQ = Int16 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Int32 where
  toQ :: HasCallStack => Int32 -> Ratio Integer
toQ = Int32 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Int64 where
  toQ :: HasCallStack => Int64 -> Ratio Integer
toQ = Int64 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Integer where
  toQ :: HasCallStack => Integer -> Ratio Integer
toQ = Integer -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Natural where
  toQ :: HasCallStack => Natural -> Ratio Integer
toQ = Natural -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Word where
  toQ :: HasCallStack => Word -> Ratio Integer
toQ = Word -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Word8 where
  toQ :: HasCallStack => Word8 -> Ratio Integer
toQ = Word8 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Word16 where
  toQ :: HasCallStack => Word16 -> Ratio Integer
toQ = Word16 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Word32 where
  toQ :: HasCallStack => Word32 -> Ratio Integer
toQ = Word32 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational Word64 where
  toQ :: HasCallStack => Word64 -> Ratio Integer
toQ = Word64 -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational (Ratio Integer) where
  toQ :: HasCallStack => Ratio Integer -> Ratio Integer
toQ = Ratio Integer -> Ratio Integer
forall a. Fractional a => Ratio Integer -> a
fromRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance ToRational (Ratio Natural) where
  toQ :: HasCallStack => Ratio Natural -> Ratio Integer
toQ = Ratio Natural -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-- | @since 0.1
instance (HasResolution k) => ToRational (Fixed k) where
  toQ :: HasCallStack => Fixed k -> Ratio Integer
toQ = Fixed k -> Ratio Integer
forall a. Real a => a -> Ratio Integer
toRational
  {-# INLINE toQ #-}

-------------------------------------------------------------------------------
------------------------------------- REALS -----------------------------------
-------------------------------------------------------------------------------

-- | Conversion from 'Double'.
--
-- @since 0.1
type FromReal :: Type -> Constraint
class (FromRational a) => FromReal a where
  -- | @since 0.1
  fromR :: (HasCallStack) => Double -> a

-- | @since 0.1
instance FromReal Double where
  fromR :: HasCallStack => Double -> Double
fromR = Double -> Double
forall a. a -> a
id
  {-# INLINE fromR #-}

-- | @since 0.1
instance FromReal Float where
  fromR :: HasCallStack => Double -> Float
fromR = Double -> Float
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE fromR #-}

-- | @since 0.1
instance FromReal (Ratio Integer) where
  fromR :: HasCallStack => Double -> Ratio Integer
fromR = Double -> Ratio Integer
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE fromR #-}

-- | __WARNING: Partial__
--
-- @since 0.1
instance FromReal (Ratio Natural) where
  fromR :: HasCallStack => Double -> Ratio Natural
fromR = Double -> Ratio Natural
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE fromR #-}

-- | @since 0.1
instance (RealFloat a) => FromReal (Complex a) where
  fromR :: HasCallStack => Double -> Complex a
fromR = Double -> Complex a
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE fromR #-}

-- | @since 0.1
instance (HasResolution k) => FromReal (Fixed k) where
  fromR :: HasCallStack => Double -> Fixed k
fromR = Double -> Fixed k
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE fromR #-}

-- | Conversion to Double.
--
-- @since 0.1
type ToReal :: Type -> Constraint
class ToReal a where
  -- | @since 0.1
  toR :: (HasCallStack) => a -> Double

-- | @since 0.1
instance ToReal Double where
  toR :: HasCallStack => Double -> Double
toR = Double -> Double
forall a. a -> a
id
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Float where
  toR :: HasCallStack => Float -> Double
toR = Float -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Int where
  toR :: HasCallStack => Int -> Double
toR = Int -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Int8 where
  toR :: HasCallStack => Int8 -> Double
toR = Int8 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Int16 where
  toR :: HasCallStack => Int16 -> Double
toR = Int16 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Int32 where
  toR :: HasCallStack => Int32 -> Double
toR = Int32 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Int64 where
  toR :: HasCallStack => Int64 -> Double
toR = Int64 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Integer where
  toR :: HasCallStack => Integer -> Double
toR = Integer -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Natural where
  toR :: HasCallStack => Natural -> Double
toR = Natural -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Word where
  toR :: HasCallStack => Word -> Double
toR = Word -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Word8 where
  toR :: HasCallStack => Word8 -> Double
toR = Word8 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Word16 where
  toR :: HasCallStack => Word16 -> Double
toR = Word16 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Word32 where
  toR :: HasCallStack => Word32 -> Double
toR = Word32 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal Word64 where
  toR :: HasCallStack => Word64 -> Double
toR = Word64 -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal (Ratio Integer) where
  toR :: HasCallStack => Ratio Integer -> Double
toR = Ratio Integer -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance ToReal (Ratio Natural) where
  toR :: HasCallStack => Ratio Natural -> Double
toR = Ratio Natural -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}

-- | @since 0.1
instance (HasResolution k) => ToReal (Fixed k) where
  toR :: HasCallStack => Fixed k -> Double
toR = Fixed k -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac
  {-# INLINE toR #-}