{-# LANGUAGE TypeOperators, FlexibleContexts, DataKinds, TypeFamilies, CPP, ScopedTypeVariables, ConstraintKinds, GeneralizedNewtypeDeriving #-} #if __GLASGOW_HASKELL__ >= 711 {-# OPTIONS_GHC -Wno-redundant-constraints #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.Metrology.Vector -- Copyright : (C) 2014 Richard Eisenberg -- License : BSD-style (see LICENSE) -- Maintainer : Richard Eisenberg (rae@cs.brynmawr.edu) -- Stability : experimental -- Portability : non-portable -- -- Exports combinators for building quantities out of vectors, from the -- vector-space library. ------------------------------------------------------------------------------ module Data.Metrology.Vector ( -- * Term-level combinators -- | The term-level arithmetic operators are defined by -- applying vertical bar(s) to the sides the dimensioned -- quantities acts on. -- ** Additive operations zero, (|+|), (|-|), qSum, qNegate, -- ** Multiplicative operations between non-vector quantities (|*|), (|/|), (/|), -- ** Multiplicative operations between a vector and a scalar (*|), (|*), (|/), -- ** Multiplicative operations on vectors (|*^|), (|^*|), (|^/|), (|.|), -- ** Exponentiation (|^), (|^^), qNthRoot, qSq, qCube, qSqrt, qCubeRoot, -- ** Other vector operations qMagnitudeSq, qMagnitude, qNormalized, qProject, qCross2, qCross3, -- ** Affine operations Point(..), QPoint, (|.-.|), (|.+^|), (|.-^|), qDistanceSq, qDistance, pointNumIn, (.#), quOfPoint, (%.), -- ** Comparison qCompare, (|<|), (|>|), (|<=|), (|>=|), (|==|), (|/=|), qApprox, qNapprox, -- * Nondimensional units, conversion between quantities and numeric values numIn, (#), quOf, (%), showIn, unity, redim, convert, defaultLCSU, constant, -- * Type-level unit combinators (:*)(..), (:/)(..), (:^)(..), (:@)(..), UnitPrefix(..), -- * Type-level quantity combinators type (%*), type (%/), type (%^), -- * Creating quantity types Qu, MkQu_D, MkQu_DLN, MkQu_U, MkQu_ULN, -- * Creating new dimensions Dimension, -- * Creating new units Unit(type BaseUnit, type DimOfUnit, conversionRatio), Canonical, -- * Numbers, the only built-in unit Dimensionless(..), Number(..), Count, quantity, -- * LCSUs (locally coherent system of units) MkLCSU, LCSU(DefaultLCSU), DefaultUnitOfDim, -- * Validity checks and assertions CompatibleUnit, CompatibleDim, ConvertibleLCSUs_D, DefaultConvertibleLCSU_D, DefaultConvertibleLCSU_U, MultDimFactors, MultUnitFactors, UnitOfDimFactors, -- * Type-level integers Z(..), Succ, Pred, type (#+), type (#-), type (#*), type (#/), Negate, -- ** Synonyms for small numbers One, Two, Three, Four, Five, MOne, MTwo, MThree, MFour, MFive, -- ** Term-level singletons sZero, sOne, sTwo, sThree, sFour, sFive, sMOne, sMTwo, sMThree, sMFour, sMFive, sSucc, sPred, sNegate, -- * Internal definitions -- | The following module is re-exported solely to prevent noise in error messages; -- we do not recommend trying to use these definitions in user code. module Data.Metrology.Internal ) where import Data.Metrology.Qu import Data.Metrology.LCSU import Data.Metrology.Validity import Data.Metrology.Factor import Data.Metrology.Z as Z import Data.Metrology.Units import Data.Metrology.Combinators import Data.Metrology.Dimensions import Data.Metrology.Internal import Data.AffineSpace import Data.VectorSpace import Data.Cross hiding ( One, Two, Three ) import Data.Proxy import Data.Coerce import Data.Foldable as F --------------------------------------- -- Additive operations --------------------------------------- -- | The number 0, polymorphic in its dimension. Use of this will -- often require a type annotation. zero :: AdditiveGroup n => Qu dimspec l n zero = Qu zeroV infixl 6 |+| -- | Add two compatible quantities (|+|) :: (d1 @~ d2, AdditiveGroup n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n (Qu a) |+| (Qu b) = Qu (a ^+^ b) -- | Negate a quantity qNegate :: AdditiveGroup n => Qu d l n -> Qu d l n qNegate (Qu x) = Qu (negateV x) infixl 6 |-| -- | Subtract two compatible quantities (|-|) :: (d1 @~ d2, AdditiveGroup n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l n a |-| b = a |+| qNegate b -- | Take the sum of a list of quantities qSum :: (Foldable f, AdditiveGroup n) => f (Qu d l n) -> Qu d l n qSum = F.foldr (|+|) zero --------------------------------------- -- Vector multiplicative operations --------------------------------------- infixl 7 |*^|, |^*|, |^/| -- | Multiply a scalar quantity by a vector quantity (|*^|) :: VectorSpace n => Qu d1 l (Scalar n) -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l n (Qu a) |*^| (Qu b) = Qu (a *^ b) -- | Multiply a vector quantity by a scalar quantity (|^*|) :: VectorSpace n => Qu d1 l n -> Qu d2 l (Scalar n) -> Qu (Normalize (d1 @+ d2)) l n (Qu a) |^*| (Qu b) = Qu (a ^* b) -- | Divide a vector quantity by a scalar quantity (|^/|) :: (VectorSpace n, Fractional (Scalar n)) => Qu d1 l n -> Qu d2 l (Scalar n) -> Qu (Normalize (d1 @- d2)) l n (Qu a) |^/| (Qu b) = Qu (a ^/ b) infixl 7 |/ -- | Divide a quantity by a scalar (|/) :: (VectorSpace n, Fractional (Scalar n)) => Qu a l n -> Scalar n -> Qu a l n (Qu a) |/ b = Qu (a ^/ b) -- The above function should *not* need to be privileged. But, GHC can't figure -- out that a @@- '[] ~ a. Urgh. infixl 7 *| , |* -- | Multiply a quantity by a scalar from the left (*|) :: VectorSpace n => Scalar n -> Qu b l n -> Qu b l n a *| (Qu b) = Qu (a *^ b) -- | Multiply a quantity by a scalar from the right (|*) :: VectorSpace n => Qu a l n -> Scalar n -> Qu a l n (Qu a) |* b = Qu (a ^* b) --------------------------------------- -- Multiplicative operations --------------------------------------- infixl 7 |.| -- | Take a inner (dot) product between two quantities. (|.|) :: InnerSpace n => Qu d1 l n -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l (Scalar n) (Qu a) |.| (Qu b) = Qu (a <.> b) -- | Square the length of a vector. qMagnitudeSq :: InnerSpace n => Qu d l n -> Qu (d @* Z.Two) l (Scalar n) qMagnitudeSq (Qu x) = Qu (magnitudeSq x) -- | Length of a vector. qMagnitude :: (InnerSpace n, Floating (Scalar n)) => Qu d l n -> Qu d l (Scalar n) qMagnitude (Qu x) = Qu (magnitude x) -- | Vector in same direction as given one but with length of one. If given the zero -- vector, then return it. The returned vector is dimensionless. qNormalized :: (InnerSpace n, Floating (Scalar n)) => Qu d l n -> Qu '[] l n qNormalized (Qu x) = Qu (normalized x) -- | @qProject u v@ computes the projection of @v@ onto @u@. qProject :: (InnerSpace n, Floating (Scalar n)) => Qu d2 l n -> Qu d1 l n -> Qu d1 l n qProject (Qu u) (Qu v) = Qu (u `project` v) -- | Cross product of 2D vectors. qCross2 :: HasCross2 n => Qu d l n -> Qu d l n qCross2 (Qu x) = Qu (cross2 x) -- | Cross product of 3D vectors. qCross3 :: HasCross3 n => Qu d1 l n -> Qu d2 l n -> Qu (Normalize (d1 @+ d2)) l n qCross3 (Qu x) (Qu y) = Qu (x `cross3` y) --------------------------------------- -- Affine space operations --------------------------------------- -- | A @Point n@ is an affine space built over @n@. Two @Point@s cannot be added, -- but they can be subtracted to yield a difference of type @n@. newtype Point n = Point n deriving (Show, Eq, Enum, Bounded) -- | Make a point quantity from a non-point quantity. type family QPoint n where QPoint (Qu d l n) = Qu d l (Point n) instance AdditiveGroup n => AffineSpace (Point n) where type Diff (Point n) = n (.-.) = coerce ((^-^) :: n -> n -> n) (.+^) = coerce ((^+^) :: n -> n -> n) -- | Make a point quantity at the given unit. Like 'quOf'. quOfPoint :: forall dim lcsu unit n. ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => n -> unit -> Qu dim lcsu (Point n) quOfPoint n unit = Qu (Point x) where Qu x = quOf n unit :: Qu dim lcsu n infix 5 %. -- | Infix synonym of 'quOfPoint' (%.) :: ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => n -> unit -> Qu dim lcsu (Point n) (%.) = quOfPoint -- | Extract the numerical value from a point quantity. Like 'numIn'. pointNumIn :: forall unit dim lcsu n. ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => Qu dim lcsu (Point n) -> unit -> n pointNumIn (Qu (Point n)) unit = numIn (Qu n :: Qu dim lcsu n) unit infix 5 .# -- | Infix synonym for 'pointNumIn'. (.#) :: (ValidDLU dim lcsu unit, VectorSpace n, Fractional (Scalar n)) => Qu dim lcsu (Point n) -> unit -> n (.#) = pointNumIn infixl 6 |.-.|, |.+^|, |.-^| -- | Subtract point quantities. (|.-.|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l n -> Qu d1 l (Diff n) (Qu a) |.-.| (Qu b) = Qu (a .-. b) -- | Add a point to a vector. (|.+^|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l (Diff n) -> Qu d1 l n (Qu a) |.+^| (Qu b) = Qu (a .+^ b) -- | Subract a vector from a point. (|.-^|) :: (d1 @~ d2, AffineSpace n) => Qu d1 l n -> Qu d2 l (Diff n) -> Qu d1 l n (Qu a) |.-^| (Qu b) = Qu (a .-^ b) -- | Square of the distance between two points. qDistanceSq :: (d1 @~ d2, AffineSpace n, InnerSpace (Diff n)) => Qu d1 l n -> Qu d2 l n -> Qu (d1 @* Z.Two) l (Scalar (Diff n)) qDistanceSq (Qu a) (Qu b) = Qu (a `distanceSq` b) -- | Distance between two points. qDistance :: (d1 @~ d2, AffineSpace n, InnerSpace (Diff n), Floating (Scalar (Diff n))) => Qu d1 l n -> Qu d2 l n -> Qu d1 l (Scalar (Diff n)) qDistance (Qu a) (Qu b) = Qu (a `distance` b) --------------------------------------- -- Top-level operations --------------------------------------- -- | Extracts a numerical value from a dimensioned quantity, expressed in -- the given unit. For example: -- -- > inMeters :: Length -> Double -- > inMeters x = numIn x Meter -- -- or -- -- > inMeters x = x # Meter numIn :: forall unit dim lcsu n. ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => Qu dim lcsu n -> unit -> n numIn (Qu val) u = val ^* fromRational (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu)) / canonicalConvRatio u) infix 5 # -- | Infix synonym for 'numIn' (#) :: ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => Qu dim lcsu n -> unit -> n (#) = numIn -- | Creates a dimensioned quantity in the given unit. For example: -- -- > height :: Length -- > height = quOf 2.0 Meter -- -- or -- -- > height = 2.0 % Meter quOf :: forall unit dim lcsu n. ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => n -> unit -> Qu dim lcsu n quOf d u = Qu (d ^* fromRational (canonicalConvRatio u / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu)))) infix 5 % -- | Infix synonym for 'quOf' (%) :: ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) ) => n -> unit -> Qu dim lcsu n (%) = quOf -- | Dimension-keeping cast between different CSUs. convert :: forall d l1 l2 n. ( ConvertibleLCSUs d l1 l2 , VectorSpace n , Fractional (Scalar n) ) => Qu d l1 n -> Qu d l2 n convert (Qu x) = Qu $ x ^* fromRational ( canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l1)) / canonicalConvRatioSpec (Proxy :: Proxy (LookupList d l2))) -- | Compute the argument in the @DefaultLCSU@, and present the result as -- lcsu-polymorphic dimension-polymorphic value. Named 'constant' because one -- of its dominant usecase is to inject constant quantities into -- dimension-polymorphic expressions. constant :: ( d @~ e , ConvertibleLCSUs e DefaultLCSU l , VectorSpace n , Fractional (Scalar n) ) => Qu d DefaultLCSU n -> Qu e l n constant = convert . redim infix 1 `showIn` -- | Show a dimensioned quantity in a given unit. (The default @Show@ -- instance always uses units as specified in the LCSU.) showIn :: ( ValidDLU dim lcsu unit , VectorSpace n , Fractional (Scalar n) , Show unit , Show n ) => Qu dim lcsu n -> unit -> String showIn x u = show (x # u) ++ " " ++ show u