packages feed

units-1.0.0: src/Data/Dimensions/Dim.hs

{- Data/Dimensions.hs

   The units Package
   Copyright (c) 2013 Richard Eisenberg
   eir@cis.upenn.edu

   This file defines the Dim type and operations on that type.
-}

{-# LANGUAGE TypeFamilies, TypeOperators, DataKinds, UndecidableInstances,
             ConstraintKinds, StandaloneDeriving, GeneralizedNewtypeDeriving,
             FlexibleInstances #-}

module Data.Dimensions.Dim where

import GHC.TypeLits ( Sing )

import Data.Dimensions.DimSpec
import Data.Dimensions.Z

-------------------------------------------------------------
--- Internal ------------------------------------------------
-------------------------------------------------------------

-- | Dim adds a dimensional annotation to a Double. This is the
-- representation for all dimensioned quantities.
newtype Dim (a :: [DimSpec *]) = Dim Double

-------------------------------------------------------------
--- User-facing ---------------------------------------------
-------------------------------------------------------------

infixl 6 .+
-- | Add two compatible dimensioned quantities
(.+) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Dim (ChooseFrom d1 d2)
(Dim a) .+ (Dim b) = Dim (a + b)

infixl 6 .-
-- | Subtract two compatible dimensioned quantities
(.-) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Dim (ChooseFrom d1 d2)
(Dim a) .- (Dim b) = Dim (a - b)

infixl 7 .*
-- | Multiply two dimensioned quantities
(.*) :: Dim a -> Dim b -> Dim (Normalize (a @+ b))
(Dim a) .* (Dim b) = Dim (a * b)

infixl 7 ./
-- | Divide two dimensioned quantities
(./) :: Dim a -> Dim b -> Dim (Normalize (a @- b))
(Dim a) ./ (Dim b) = Dim (a / b)

infixr 8 .^
-- | Raise a dimensioned quantity to a power known at compile time
(.^) :: Dim a -> Sing z -> Dim (a @* z)
(Dim a) .^ sz = Dim (a ^^ szToInt sz)

-- | Take the n'th root of a dimensioned quantity, where n is known at compile
-- time
nthRoot :: (Zero < z) ~ True => Sing z -> Dim a -> Dim (a @/ z)
nthRoot sz (Dim a) = Dim (a ** (1.0 / (fromIntegral $ szToInt sz)))

infix 4 .<
-- | Check if one dimensioned quantity is less than a compatible one
(.<) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Bool
(Dim a) .< (Dim b) = a < b

infix 4 .>
-- | Check if one dimensioned quantity is greater than a compatible one
(.>) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Bool
(Dim a) .> (Dim b) = a > b

infix 4 .<=
-- | Check if one dimensioned quantity is less than or equal to a compatible one
(.<=) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Bool
(Dim a) .<= (Dim b) = a <= b

infix 4 .>=
-- | Check if one dimensioned quantity is greater than or equal to a compatible one
(.>=) :: (d1 @~ d2) => Dim d1 -> Dim d2 -> Bool
(Dim a) .>= (Dim b) = a >= b

-- | Compare two compatible dimensioned quantities for equality
dimEq :: (d0 @~ d1, d0 @~ d2) => Dim d0  -- ^ If the difference between the next
                                         -- two arguments are less  than this 
                                         -- amount, they are considered equal
      -> Dim d1 -> Dim d2 -> Bool
dimEq (Dim epsilon) (Dim a) (Dim b) = abs(a-b) < epsilon

-- | Compare two compatible dimensioned quantities for inequality
dimNeq :: (d0 @~ d1, d0 @~ d2) => Dim d0 -- ^ If the difference between the next
                                         -- two arguments are less  than this 
                                         -- amount, they are considered equal
       -> Dim d1 -> Dim d2 -> Bool
dimNeq (Dim epsilon) (Dim a) (Dim b) = abs(a-b) >= epsilon

-- | Square a dimensioned quantity
dimSqr :: Dim a -> Dim (Normalize (a @+ a))
dimSqr x = x .* x

-- | Take the square root of a dimensioned quantity
dimSqrt :: Dim a -> Dim (a @/ Two)
dimSqrt = nthRoot pTwo

-- | Take the cube root of a dimensioned quantity
dimCubeRoot :: Dim a -> Dim (a @/ Three)
dimCubeRoot = nthRoot pThree

infixl 7 *.
-- | Multiply a dimensioned quantity by a scalar @Double@
(*.) :: Double -> Dim a -> Dim a
a *. (Dim b) = Dim (a * b)

-------------------------------------------------------------
--- Instances -----------------------------------------------
-------------------------------------------------------------

deriving instance Eq (Dim '[])
deriving instance Ord (Dim '[])
deriving instance Num (Dim '[])
deriving instance Real (Dim '[])
deriving instance Fractional (Dim '[])
deriving instance Floating (Dim '[])
deriving instance RealFrac (Dim '[])
deriving instance RealFloat (Dim '[])

-------------------------------------------------------------
--- Combinators ---------------------------------------------
-------------------------------------------------------------

infixl 7 %*
-- | Multiply two dimension types to produce a new one. For example:
--
-- > type Velocity = Length %/ Time
type family (d1 :: *) %* (d2 :: *) :: *
type instance (Dim d1) %* (Dim d2) = Dim (d1 @+ d2)

infixl 7 %/
-- | Divide two dimension types to produce a new one
type family (d1 :: *) %/ (d2 :: *) :: *
type instance (Dim d1) %/ (Dim d2) = Dim (d1 @- d2)

infixr 8 %^
-- | Exponentiate a dimension type to an integer
type family (d :: *) %^ (z :: Z) :: *
type instance (Dim d) %^ z = Dim (d @* z)