-- |
-- Module:      Math.NumberTheory.Canon.Simple
-- Copyright:   (c) 2015-2018 Frederick Schneider
-- Licence:     MIT
-- Maintainer:  Frederick Schneider <frederick.schneider2011@gmail.com>
-- Stability:   Provisional
--
-- This a wrapper for the Canonical Representation type found in the Internals module.  
-- If you want to work with arbitrarily nested prime towers, you can use the Math.NumberTheory.Canon module.

{-# LANGUAGE MultiParamTypeClasses,FunctionalDependencies, FlexibleInstances, PatternSynonyms, ViewPatterns #-}

module Math.NumberTheory.Canon.Simple ( 
  SimpleCanon(..),  SC,
  toSimpleCanon,    toSC,   toSimpleCanonViaUserFunc,
  fromSimpleCanon,  fromSC,
  CanonConv,

  scGCD, scLCM,
  scLog, scLogDouble,
  scNegative, scPositive,
  scToInteger, scToI,
   
  RationalSimpleCanon(..), RC,
  toRationalSimpleCanon,   toRC,   toRationalSimpleCanonViaUserFunc,
  fromRationalSimpleCanon, fromRC, 
  rcNegative, rcPositive,              
  
  getNumer,     getDenom,     getNumerDenom,
  getNumerAsRC, getDenomAsRC, getNumerDenomAsRCs,  
  rcLog,        rcLogDouble,
                                
  (>^), (<^), (%)         
)
where

import GHC.Real (Ratio(..))
import Math.NumberTheory.Canon.Internals
import Math.NumberTheory.Canon.Additive
import Math.NumberTheory.Canon.AurifCyclo (crCycloInitMap)

-- | SimpleCanon is a new type wrapping a canonical representation.
newtype SimpleCanon = MakeSC CR_ deriving (Eq)

-- | This function allow you to specify a user function when converting a canon rep to an SC.
toSimpleCanonViaUserFunc :: CR_ -> (Integer -> Bool) -> SimpleCanon
toSimpleCanonViaUserFunc c f | crValidIntegralViaUserFunc c f == False = error $ invalidError 
                             | otherwise                               = MakeSC c
                             where invalidError = "toSimpleCanonViaUserFunc: Invalid integral canonical rep passed to constructor: " ++ (show c) 

-- | Grab the canon rep from a SimpleCanon.
fromSimpleCanon, fromSC :: SimpleCanon -> CR_
fromSimpleCanon (MakeSC i) = i
fromSC = fromSimpleCanon

-- | Shorthand type declaration
type SC = SimpleCanon

-- | Define various instances
instance Show SimpleCanon where 
  show c = crShow $ fromSC c
           
instance Enum SimpleCanon where
  toEnum   n = toSimpleCanon $ crFromI $ fromIntegral n
  fromEnum c = fromIntegral $ crToI $ fromSC c

instance Ord SimpleCanon where
  compare x y = crCmp (fromSC x) (fromSC y)

instance Real SimpleCanon where
  toRational c = scToI c :% 1

instance Integral SimpleCanon where
  toInteger c = scToI c
  quotRem n m = (MakeSC n', MakeSC m') 
                where (n', m') = fst $ crQuotRem (fromSC n) (fromSC m) crCycloInitMap
  mod n m     = MakeSC $ crMod (fromSC n) (fromSC m)
            
instance Fractional SimpleCanon where
  fromRational (n :% d) | m == 0    = MakeSC $ crFromI q
                        | otherwise = error "Modulus not zero.  Use Rational SimpleCanons for non-integers."
                        where (q, m) = quotRem n d
  (/) x y               = MakeSC $ crDivStrict (fromSC x) (fromSC y)

instance Num SimpleCanon where
  fromInteger n = MakeSC $ crFromI n    -- to do: check where called?
  x + y         = MakeSC $ fst $ crAdd      (fromSC x) (fromSC y) crCycloInitMap -- discard the map info
  x - y         = MakeSC $ fst $ crSubtract (fromSC x) (fromSC y) crCycloInitMap -- discard the map info
  x * y         = MakeSC $ crMult     (fromSC x) (fromSC y)
  
  negate x      = MakeSC $ crNegate $ fromSC x
  abs x         = MakeSC $ crAbs    $ fromSC x
  signum x      = MakeSC $ crSignum $ fromSC x

-- | Convert a SimpleCanon back to an Integer.
scToInteger, scToI :: SimpleCanon -> Integer
scToI c     = crToI $ fromSC c
scToInteger = scToI

-- | SimpleCanon GCD and LCM functions
scGCD, scLCM :: SimpleCanon -> SimpleCanon -> SimpleCanon
scGCD x y = MakeSC $ crGCD (fromSC x) (fromSC y)
scLCM x y = MakeSC $ crLCM (fromSC x) (fromSC y)

-- | Wrappers for underlying canon rep functions
scNegative, scPositive :: SimpleCanon -> Bool
scNegative c = crNegative $ fromSC c
scPositive c = crPositive $ fromSC c

-- | Wrapper for underlying CR function
scLog :: SimpleCanon -> Rational
scLog x = crLog $ fromSC x 

-- | Wrapper for underlying CR function
scLogDouble :: SimpleCanon -> Double
scLogDouble x = crLogDouble $ fromSC x 

-- | Newtype for RationalSimpleCanon.  The underlying canon rep is the same.
newtype RationalSimpleCanon = MakeRC CR_ deriving (Eq)

-- | Convert canon rep to RationalSimpleCanon via a user-supplied criterion function.
toRationalSimpleCanonViaUserFunc :: CR_ -> (Integer -> Bool) -> RationalSimpleCanon
toRationalSimpleCanonViaUserFunc c f | crValidRationalViaUserFunc c f == False = error $ invalidError 
                               | otherwise                               = MakeRC c
                               where invalidError = 
                                       "toRationalSimpleCanonViaUserFunc: Invalid rational canonical rep passed to constructor: " 
                                       ++ (show c) ++ " (user predicate supplied)" 

-- | Convert RC back to underlying canon rep.
fromRationalSimpleCanon, fromRC :: RationalSimpleCanon -> CR_
fromRC (MakeRC i)       = i
fromRationalSimpleCanon = fromRC

-- | Shorthand type name 
type RC = RationalSimpleCanon

-- | Define various instances for RationSimpleCanon.
instance Show RationalSimpleCanon where 
  show rc = crShowRational $ fromRC rc
  
instance Enum RationalSimpleCanon where
  toEnum   n = toRC $ crFromI $ fromIntegral n
  fromEnum c = fromIntegral $ toInteger c -- if not integral, this will fail

-- | Caveat: These functions will error out (in)directly if there are any negative exponents.
instance Integral RationalSimpleCanon where
  toInteger rc = crToI $ fromRC rc
  quotRem n m  | crIntegral $ fromRC n = (MakeRC n', MakeRC m') 
               | otherwise             = error "Can't perform 'quotRem' on non-integral RationalSimpleCanon"
               where (n', m') = fst $ crQuotRem (fromRC n) (fromRC m) crCycloInitMap
  mod n m      | crIntegral $ fromRC n = MakeRC $ crMod (fromRC n) (fromRC m) 
               | otherwise             = error "Can't perform 'mod' on non-integral RationalSimpleCanon"

instance Fractional RationalSimpleCanon where
  fromRational (n :% d) = MakeRC $ crDivRational (crFromI n) (crFromI d)
  (/) x y               = MakeRC $ crDivRational (fromRC x)  (fromRC y)

instance Ord RationalSimpleCanon where
  compare x y = crCmp (fromRC x) (fromRC y)
  
instance Real RationalSimpleCanon where
  toRational rc  = crToRational $ fromRC rc
                  
instance Num RationalSimpleCanon where
  fromInteger n = MakeRC $ crFromI n
  x + y         = MakeRC $ fst $ crAddR      (fromRC x) (fromRC y) crCycloInitMap
  x - y         = MakeRC $ fst $ crSubtractR (fromRC x) (fromRC y) crCycloInitMap
  x * y         = MakeRC $ crMult      (fromRC x) (fromRC y) 
  
  negate x      = MakeRC $ crNegate $ fromRC x
  abs x         = MakeRC $ crAbs    $ fromRC x 
  signum x      = MakeRC $ crSignum $ fromRC x

-- | Calls underlying canon rep function.
rcLog :: RationalSimpleCanon -> Rational
rcLog c = crLog $ fromRC c 

-- | Calls underlying canon rep function. 
rcLogDouble :: RationalSimpleCanon -> Double
rcLogDouble c = crLogDouble $ fromRC c

-- | Calls underlying canon rep function. 
getNumerAsRC :: RationalSimpleCanon -> RationalSimpleCanon
getNumerAsRC c = MakeRC $ crNumer $ fromRC c
          
-- | Calls underlying canon rep function. 
getDenomAsRC :: RationalSimpleCanon -> RationalSimpleCanon
getDenomAsRC c = MakeRC $ crDenom $ fromRC c

-- | Pulls numerator or denominator from RC and converts it to a SimpleCanon.
getNumer, getDenom :: RationalSimpleCanon -> SimpleCanon
getNumer c = MakeSC $ crNumer $ fromRC c
getDenom c = MakeSC $ crDenom $ fromRC c          

-- | Wraps crSplit function and returns a pair of SimpleCanons.
getNumerDenom :: RationalSimpleCanon -> (SimpleCanon, SimpleCanon)
getNumerDenom c = (MakeSC n, MakeSC d) 
                  where (n, d) = crSplit $ fromRC c
                 
-- | Wraps crSplit function and returns a pair of RationalSimpleCanons. 
getNumerDenomAsRCs :: RationalSimpleCanon -> (RationalSimpleCanon, RationalSimpleCanon)
getNumerDenomAsRCs c = (MakeRC n, MakeRC d) 
                        where (n, d) = crSplit $ fromRC c                                         

-- | Wraps underlying canon rep functions.
rcNegative, rcPositive :: RationalSimpleCanon -> Bool
rcNegative x = crNegative $ fromRC x
rcPositive x = crPositive $ fromRC x                         

-- | Modulus operator
infixl 7 %
(%) :: (Integral a) => a -> a -> a
n % m = mod n m 


-- | Multi-param typeclass for exponentiation
infixr 9 <^

class SimpleCanonExpnt a b c | a b -> c where 
  -- | Exponentiation Operator
  (<^) :: a -> b -> c

instance SimpleCanonExpnt Integer Integer SimpleCanon where
  p <^ e = MakeSC $ crExp (crFromI p) e False

instance SimpleCanonExpnt SimpleCanon Integer SimpleCanon where
  p <^ e = MakeSC $ crExp (fromSC p) e False

instance SimpleCanonExpnt RationalSimpleCanon Integer RationalSimpleCanon where
  p <^ e = MakeRC $ crExp (fromRC p) e True

instance SimpleCanonExpnt RationalSimpleCanon SimpleCanon RationalSimpleCanon where
  p <^ e = MakeRC $ crExp (fromRC p) (crToI $ fromSC e) True

-- | Multi-param typeclass for radical/root operator
infixr 9 >^ -- r >^ n means attempt to take the rth root of n

class SimpleCanonRoot a b c | a b -> c where
  -- | Root Operator
  (>^) :: a -> b -> c

instance SimpleCanonRoot SimpleCanon SimpleCanon SimpleCanon where
  r >^ n = MakeSC $ crRoot (fromSC n) (toInteger r)
  
instance SimpleCanonRoot Integer Integer SimpleCanon where
  r >^ n = MakeSC $ crRoot (crFromI n) r
  
instance SimpleCanonRoot Integer SimpleCanon SimpleCanon where
  r >^ n = MakeSC $ crRoot (fromSC n) r

instance SimpleCanonRoot SimpleCanon Integer SimpleCanon where
  r >^ n = MakeSC $ crRoot (crFromI n) (toInteger r)  
  
instance SimpleCanonRoot Integer RationalSimpleCanon RationalSimpleCanon where
  r >^ n = MakeRC $ crRoot (fromRC n) r

-- | Convert CanonConv types to SimpleCanon.
toSimpleCanon :: (CanonConv a) => a -> SimpleCanon
toSimpleCanon = toSC

-- | Convert CanonConv types to RationalSimpleCanon.
toRationalSimpleCanon :: (CanonConv a) => a -> RationalSimpleCanon
toRationalSimpleCanon = toRC

-- | Typeclass for converting to SimpleCanon and RationalSimpleCanon 
class CanonConv c where
  -- | Convert a type to a SimpleCanon.
  toSC          :: c -> SimpleCanon 

  -- | Convert a type to a RationalSimpleCanon.
  toRC                  :: c -> RationalSimpleCanon 
  
instance CanonConv SimpleCanon where  
  toSC c = c
  toRC c = MakeRC $ fromSC c

instance CanonConv CR_ where  
  toSC cr | crValidIntegral cr = MakeSC cr
          | otherwise          = error invalidError
          where invalidError = "Invalid integral canonical rep passed to constructor: " ++ (show cr) 
           
  toRC cr | crValidRational cr = MakeRC cr
          | otherwise          = error invalidRepRatioError 
          where invalidRepRatioError = "toRC: Invalid canonical rep passed to constructor: " ++ (show cr) 
  
instance CanonConv RationalSimpleCanon where  
  toSC rc | crValidIntegral frc = MakeSC frc
          | otherwise           = error invalidError
          where frc          = fromRC rc
                invalidError = "Invalid integral canonical rep passed to constructor: " ++ (show rc) 
  toRC rc = rc