{-# OPTIONS_HADDOCK not-home, show-extensions #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
module Numeric.Units.Dimensional.Dimensions.TermLevel
(
  
  Dimension'(..),
  
  HasDimension(..), HasDynamicDimension(..), DynamicDimension(..),
  
  (*), (/), (^), recip, nroot, sqrt, cbrt,
  
  dOne,
  dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity,
  
  asList,
  
  matchDimensions, isCompatibleWith, hasSomeDimension
)
where
import Control.DeepSeq
import Data.Data
import Data.Semigroup (Semigroup(..))
import Data.Monoid (Monoid(..))
import GHC.Generics
import Prelude (id, all, fst, snd, fmap, otherwise, divMod, ($), (+), (-), (.), (&&), Int, Show, Eq(..), Ord(..), Maybe(..), Bool(..))
import qualified Prelude as P
data Dimension' = Dim' !Int !Int !Int !Int !Int !Int !Int
  deriving (Show, Eq, Ord, Data, Generic, Typeable)
instance NFData Dimension' where
  rnf !_ = () 
instance Semigroup Dimension' where
  (<>) = (*)
instance Monoid Dimension' where
  mempty = dOne
  mappend = (Data.Semigroup.<>)
data DynamicDimension = NoDimension 
                      | SomeDimension Dimension' 
                      | AnyDimension 
  deriving (Eq, Ord, Show, Data, Generic, Typeable)
instance NFData DynamicDimension where
class HasDynamicDimension a where
  
  
  
  
  dynamicDimension :: a -> DynamicDimension
  default dynamicDimension :: (HasDimension a) => a -> DynamicDimension
  dynamicDimension = SomeDimension . dimension
class HasDynamicDimension a => HasDimension a where
  
  dimension :: a -> Dimension'
instance HasDynamicDimension DynamicDimension where
  dynamicDimension = id
instance HasDynamicDimension Dimension' where
instance HasDimension Dimension' where
  dimension = id
matchDimensions :: DynamicDimension -> DynamicDimension -> DynamicDimension
matchDimensions AnyDimension        AnyDimension                   = AnyDimension
matchDimensions d@(SomeDimension _) AnyDimension                   = d
matchDimensions AnyDimension        d@(SomeDimension _)            = d
matchDimensions (SomeDimension d1)  (SomeDimension d2) | d1 == d2  = SomeDimension d1
matchDimensions _                   _                              = NoDimension
isCompatibleWith :: (HasDynamicDimension a) => a -> Dimension' -> Bool
isCompatibleWith = f . dynamicDimension
  where
    f AnyDimension       _             = True
    f (SomeDimension d1) d2 | d1 == d2 = True
    f _                  _             = False
hasSomeDimension :: (HasDynamicDimension a) => a -> Bool
hasSomeDimension = (/= NoDimension) . dynamicDimension
dOne :: Dimension'
dOne = Dim' 0 0 0 0 0 0 0
dLength, dMass, dTime, dElectricCurrent, dThermodynamicTemperature, dAmountOfSubstance, dLuminousIntensity :: Dimension'
dLength                   = Dim' 1 0 0 0 0 0 0
dMass                     = Dim' 0 1 0 0 0 0 0
dTime                     = Dim' 0 0 1 0 0 0 0
dElectricCurrent          = Dim' 0 0 0 1 0 0 0
dThermodynamicTemperature = Dim' 0 0 0 0 1 0 0
dAmountOfSubstance        = Dim' 0 0 0 0 0 1 0
dLuminousIntensity        = Dim' 0 0 0 0 0 0 1
infixr 8  ^
infixl 7  *, /
(*) :: Dimension' -> Dimension' -> Dimension'
(Dim' l m t i th n j) * (Dim' l' m' t' i' th' n' j') = Dim' (l + l') (m + m') (t + t') (i + i') (th + th') (n + n') (j + j')
(/) :: Dimension' -> Dimension' -> Dimension'
(Dim' l m t i th n j) / (Dim' l' m' t' i' th' n' j') = Dim' (l - l') (m - m') (t - t') (i - i') (th - th') (n - n') (j - j')
(^) :: Dimension' -> Int -> Dimension'
(Dim' l m t i th n j) ^ x = Dim' (x P.* l) (x P.* m) (x P.* t) (x P.* i) (x P.* th) (x P.* n) (x P.* j)
recip :: Dimension' -> Dimension'
recip = (dOne /)
nroot :: Int -> Dimension' -> Maybe Dimension'
nroot n d | n /= 0 && all ((== 0) . snd) ds = fromList . fmap fst $ ds
          | otherwise                      = Nothing
  where
    ds = fmap (`divMod` n) . asList $ d
sqrt :: Dimension' -> Maybe Dimension'
sqrt = nroot 2
cbrt :: Dimension' -> Maybe Dimension'
cbrt = nroot 3
asList :: Dimension' -> [Int]
asList (Dim' l m t i th n j) = [l, m, t, i, th, n, j]
fromList :: [Int] -> Maybe Dimension'
fromList [l, m, t, i, th, n, j] = Just $ Dim' l m t i th n j
fromList _ = Nothing