packages feed

brush-strokes-0.1.0.0: src/lib/Math/Ring.hs

{-# LANGUAGE ScopedTypeVariables #-}

module Math.Ring
  ( AbelianGroup(..), Signed(..), Ring(..), Field(..)
  , Floating(..), Transcendental(..)

  , ViaPrelude(..), ViaAbelianGroup(..)

  , ifThenElse
  )
  where

-- base
import Prelude ( Num, Fractional )
import Prelude hiding ( Num(..), Fractional(..), Floating(..) )
import qualified Prelude
import Data.Coerce
  ( coerce )
import Data.Monoid
  ( Ap(..) )

-- groups
import Data.Group
  ( Group(invert) )

--------------------------------------------------------------------------------

infixl 6 +, -
infixl 7 *, /
infixr 8 ^

class AbelianGroup a where
  {-# MINIMAL (+), fromInteger, ( (-) | negate ) #-}

  (+) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  fromInteger :: Integer -> a

  a - b = a + negate b
  negate b = fromInteger 0 - b

class AbelianGroup a => Signed a where
  abs    :: a -> a
  signum :: a -> a

class AbelianGroup a => Ring a where
  (*) :: a -> a -> a
  (^) :: a -> Word -> a
  (^) = defaultPow

class Ring a => Field a where
  {-# MINIMAL fromRational, ( recip | (/) ) #-}

  fromRational :: Rational -> a
  (/) :: a -> a -> a
  recip :: a -> a

  recip x = fromInteger 1 / x
  x / y = x * recip y

class Field a => Floating a where
  sqrt :: a -> a

class Floating a => Transcendental a where
  pi   :: a
  cos  :: a -> a
  sin  :: a -> a
  atan :: a -> a

--------------------------------------------------------------------------------

ifThenElse :: Bool -> a -> a -> a
ifThenElse b x y = if b then x else y

--------------------------------------------------------------------------------
-- A default power function, taken from base (GHC.Real).
--
-- DO NOT use with interval arithmetic!

{-# RULES
"defaultPow 2" forall x. defaultPow x 2 = x*x
"defaultPow 3" forall x. defaultPow x 3 = x*x*x
"defaultPow 4" forall x. defaultPow x 4 = let u = x*x in u*u
"defaultPow 5" forall x. defaultPow x 5 = let u = x*x in u*u*x
  #-}

{-# INLINE [1] defaultPow #-}
defaultPow :: Ring a => a -> Word -> a
defaultPow x0 y0
  | y0 < 0    = errorWithoutStackTrace "Negative exponent"
  | y0 == 0   = fromInteger 1
  | otherwise = powImpl x0 y0

powImpl :: Ring a => a -> Word -> a
-- powImpl : x0 ^ y0 = (x ^ y)
powImpl x y
  | even y    = powImpl (x * x) (y `quot` 2)
  | y == 1    = x
  | otherwise = powImplAcc (x * x) (y `quot` 2) x

{-# INLINABLE powImplAcc #-}
powImplAcc :: Ring a => a -> Word -> a -> a
-- powImplAcc : x0 ^ y0 = (x ^ y) * z
powImplAcc x y z
  | even y    = powImplAcc (x * x) (y `quot` 2) z
  | y == 1    = x * z
  | otherwise = powImplAcc (x * x) (y `quot` 2) (x * z)

--------------------------------------------------------------------------------

newtype ViaPrelude a = ViaPrelude { viaPrelude :: a }

instance Num a => AbelianGroup ( ViaPrelude a ) where
  (+) = coerce $ (Prelude.+) @a
  (-) = coerce $ (Prelude.-) @a
  fromInteger = coerce $ Prelude.fromInteger @a

instance Num a => Signed ( ViaPrelude a ) where
  abs    = coerce $ Prelude.abs    @a
  signum = coerce $ Prelude.signum @a

instance Num a => Ring ( ViaPrelude a ) where
  (*) = coerce $ (Prelude.*) @a
  (^) = coerce $ (Prelude.^) @a @Word

instance Fractional a => Field ( ViaPrelude a ) where
  fromRational = coerce $ Prelude.fromRational @a
  recip = coerce $ Prelude.recip @a
  (/) = coerce $ (Prelude./) @a

instance Prelude.Floating a => Floating ( ViaPrelude a ) where
  sqrt = coerce $ Prelude.sqrt @a

instance Prelude.Floating a => Transcendental ( ViaPrelude a ) where
  pi = coerce $ Prelude.pi @a
  sin = coerce $ Prelude.sin @a
  cos = coerce $ Prelude.cos @a
  atan = coerce $ Prelude.atan @a

--------------------------------------------------------------------------------

newtype ViaAbelianGroup a = ViaAbelianGroup { viaAbelianGroup :: a }

instance AbelianGroup a => Semigroup ( ViaAbelianGroup a ) where
  (<>) = coerce $ (+) @a
instance AbelianGroup a => Monoid    ( ViaAbelianGroup a ) where
  mempty = ViaAbelianGroup $ fromInteger 0
instance AbelianGroup a => Group     ( ViaAbelianGroup a ) where
  invert = coerce $ negate @a

--------------------------------------------------------------------------------

deriving via ViaPrelude Integer instance AbelianGroup   Integer
deriving via ViaPrelude Integer instance Ring           Integer
deriving via ViaPrelude Integer instance Signed         Integer

deriving via ViaPrelude Int     instance AbelianGroup   Int
deriving via ViaPrelude Int     instance Ring           Int
deriving via ViaPrelude Int     instance Signed         Int

deriving via ViaPrelude Word    instance AbelianGroup   Word
deriving via ViaPrelude Word    instance Ring           Word

deriving via ViaPrelude Float   instance AbelianGroup   Float
deriving via ViaPrelude Float   instance Ring           Float
deriving via ViaPrelude Float   instance Signed         Float
deriving via ViaPrelude Float   instance Field          Float
deriving via ViaPrelude Float   instance Floating       Float
deriving via ViaPrelude Float   instance Transcendental Float

deriving via ViaPrelude Double  instance AbelianGroup   Double
deriving via ViaPrelude Double  instance Ring           Double
deriving via ViaPrelude Double  instance Signed         Double
deriving via ViaPrelude Double  instance Field          Double
deriving via ViaPrelude Double  instance Floating       Double
deriving via ViaPrelude Double  instance Transcendental Double

--------------------------------------------------------------------------------

instance ( AbelianGroup r, Applicative f ) => AbelianGroup ( Ap f r ) where
  (+) = liftA2 $ (+) @r
  (-) = liftA2 $ (-) @r
  negate = fmap $ negate @r
  fromInteger = pure . fromInteger @r