{-# LANGUAGE NoImplicitPrelude
           , PackageImports
           , TypeSynonymInstances
           , UnicodeSyntax
  #-}

module Text.Numeral.Exp.Classes
    ( Unknown(unknown, isUnknown)
    , Lit(lit)
    , Neg(neg)
    , Add(add)
    , Mul(mul)
    , Sub(sub)
    , Scale(scale)
    ) where

-------------------------------------------------------------------------------
-- Imports
-------------------------------------------------------------------------------

import "base" Data.Bool ( Bool(False) )
import "base" Data.Function ( const )
import "base" Prelude ( (+), (*), (^), subtract, negate, fromInteger, error )
import "base-unicode-symbols" Prelude.Unicode ( , () )


-------------------------------------------------------------------------------
-- Exp classes
-------------------------------------------------------------------------------

-- | An unknown value. This is used to signal that a value can not be
-- represented in the expression language.
--
-- Law: isUnknown unknown == True
class Unknown α where
    unknown  α
    isUnknown  α  Bool

-- | A literal value.
--
-- Example in English:
--
-- > "three" = lit 3
class Lit α where lit    α

-- | Negation of a value.
--
-- Example in English:
--
-- > "minus two" = neg (lit 2)
class Neg α where neg  α  α

-- | Addition of two values.
--
-- Example in English:
--
-- > "fifteen" = lit 5 `add` lit 10
class Add α where add  α  α  α

-- | Multiplication of two values.
--
-- Example in English:
--
-- > "thirty" = lit 3 `mul` lit 10
class Mul α where mul  α  α  α

-- | One value subtracted from another value.
--
-- Example in Latin:
--
-- > "duodēvīgintī" = lit 2 `sub` (lit 2 `mul` lit 10)
class Sub α where sub  α  α  α

-- | A step in a scale of large values.
--
-- Should be interpreted as @10 ^ (rank * base + offset)@.
--
-- Example in English:
--
-- > "quadrillion" = scale 3 3 4
class Scale α where
    scale   -- ^ Base.
            -- ^ Offset.
           α -- ^ Rank.
           α

infixl 6 `add`
infixl 6 `sub`
infixl 7 `mul`


-------------------------------------------------------------------------------
-- Integer instances
-------------------------------------------------------------------------------

instance Unknown  where
    unknown   = error "unknown"
    isUnknown = const False
instance Lit  where lit = fromInteger
instance Neg  where neg = negate
instance Add  where add = (+)
instance Mul  where mul = (*)
instance Sub  where sub = subtract
instance Scale  where scale b o r = 10 ^ (rb + o)