{-# OPTIONS_GHC -Wall #-}
module NumHask.Algebra.Integral
  ( Integral(..)
  , ToInteger(..)
  , FromInteger(..)
  , fromIntegral
  , even
  , odd
  , (^)
  , (^^)
  ) where
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word, Word8, Word16, Word32, Word64)
import GHC.Natural (Natural(..))
import NumHask.Algebra.Ring
import NumHask.Algebra.Additive
import NumHask.Algebra.Multiplicative
import qualified Prelude as P
import Prelude (Double, Float, Int, Integer, (.), fst, snd)
class (Semiring a) =>
      Integral a where
  infixl 7 `div`, `mod`
  div :: a -> a -> a
  div a1 a2 = fst (divMod a1 a2)
  mod :: a -> a -> a
  mod a1 a2 = snd (divMod a1 a2)
  divMod :: a -> a -> (a, a)
  quot :: a -> a -> a
  quot a1 a2 = fst (quotRem a1 a2)
  rem :: a -> a -> a
  rem a1 a2 = snd (quotRem a1 a2)
  quotRem :: a -> a -> (a, a)
instance Integral Int where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Integer where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Natural where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Int8 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Int16 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Int32 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Int64 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Word where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Word8 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Word16 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Word32 where
  divMod = P.divMod
  quotRem = P.quotRem
instance Integral Word64 where
  divMod = P.divMod
  quotRem = P.quotRem
class ToInteger a where
  toInteger :: a -> Integer
instance ToInteger Int where
  toInteger = P.toInteger
instance ToInteger Integer where
  toInteger = P.toInteger
instance ToInteger Natural where
  toInteger = P.toInteger
instance ToInteger Int8 where
  toInteger = P.toInteger
instance ToInteger Int16 where
  toInteger = P.toInteger
instance ToInteger Int32 where
  toInteger = P.toInteger
instance ToInteger Int64 where
  toInteger = P.toInteger
instance ToInteger Word where
  toInteger = P.toInteger
instance ToInteger Word8 where
  toInteger = P.toInteger
instance ToInteger Word16 where
  toInteger = P.toInteger
instance ToInteger Word32 where
  toInteger = P.toInteger
instance ToInteger Word64 where
  toInteger = P.toInteger
class FromInteger a where
  fromInteger :: Integer -> a
fromIntegral :: (ToInteger a, FromInteger b) => a -> b
fromIntegral = fromInteger . toInteger
instance FromInteger Double where
  fromInteger = P.fromInteger
instance FromInteger Float where
  fromInteger = P.fromInteger
instance FromInteger Int where
  fromInteger = P.fromInteger
instance FromInteger Integer where
  fromInteger = P.fromInteger
instance FromInteger Natural where
  fromInteger = P.fromInteger
instance FromInteger Int8 where
  fromInteger = P.fromInteger
instance FromInteger Int16 where
  fromInteger = P.fromInteger
instance FromInteger Int32 where
  fromInteger = P.fromInteger
instance FromInteger Int64 where
  fromInteger = P.fromInteger
instance FromInteger Word where
  fromInteger = P.fromInteger
instance FromInteger Word8 where
  fromInteger = P.fromInteger
instance FromInteger Word16 where
  fromInteger = P.fromInteger
instance FromInteger Word32 where
  fromInteger = P.fromInteger
instance FromInteger Word64 where
  fromInteger = P.fromInteger
even :: (P.Eq a, Integral a) => a -> P.Bool
even n = n `rem` (one + one) P.== zero
odd :: (P.Eq a, Integral a) => a -> P.Bool
odd =  P.not . even
(^) :: (P.Ord b, Integral b, Multiplicative a) => a -> b -> a
x0 ^ y0 | y0 P.< zero = P.undefined 
        | y0 P.== zero = one
        | P.otherwise = f x0 y0
    where
      two = one+one
      
      f x y | even y = f (x * x) (y `quot` two)
            | y P.== one = x
            | P.otherwise = g (x * x) (y `quot` two) x
              
      
      g x y z | even y = g (x * x) (y `quot` two) z
              | y P.== one = x * z
              | P.otherwise = g (x * x) (y `quot` two) (x * z)
                
(^^) :: (MultiplicativeGroup a) => a -> Integer -> a
(^^) x n = if n P.>= zero then x^n else recip (x ^ negate n)