{-# LANGUAGE TypeOperators, FlexibleContexts, DataKinds, TypeFamilies, ScopedTypeVariables, ConstraintKinds, GeneralizedNewtypeDeriving, MultiParamTypeClasses, FlexibleInstances, InstanceSigs, CPP #-} #if __GLASGOW_HASKELL__ >= 711 {-# OPTIONS_GHC -Wno-redundant-constraints #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.Metrology.Linear -- Copyright : (C) 2014 Richard Eisenberg, (C) 2015 Tobias Markus -- 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 -- @linear@ library. ------------------------------------------------------------------------------ module Data.Metrology.Linear ( -- * 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 zeroV, (|^+^|), (|^-^|), qNegateV, qSumV, -- ** Multiplicative operations (|*^|), (|^*|), (|^/|), (*^|), (|^*), (|^/), (|.|), -- ** Vector-space operations qBasis, qBasisFor, qScaled, qOuter, qUnit, qQuadrance, qNorm, qSignorm, qProject, qCross, -- ** Affine operations (|.-.|), (|.+^|), (|.-^|), qQd, qDistance, qQdA, qDistanceA, -- * Nondimensional units, conversion between quantities and numeric values numInV, (^#), quOfV, (^%), showInV, convertV, constantV, ) 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 Linear import Linear.Affine hiding (P) import qualified Control.Lens as Lens import Data.Proxy import Data.Foldable as F #if __GLASGOW_HASKELL__ < 709 import Data.Traversable ( Traversable ) #endif --------------------------------------- -- Additive operations --------------------------------------- -- | The number 0, polymorphic in its dimension. Use of this will -- often require a type annotation. zeroV :: (Additive f, Num a) => Qu d l (f a) zeroV = Qu Linear.zero infixl 6 |^+^| -- | Add two compatible vector quantities (|^+^|) :: (d1 @~ d2, Additive f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (f a) (Qu a) |^+^| (Qu b) = Qu (a ^+^ b) -- | Negate a vector quantity qNegateV :: (Additive f, Num a) => Qu d l (f a) -> Qu d l (f a) qNegateV (Qu x) = Qu (negated x) infixl 6 |^-^| -- | Subtract two compatible quantities (|^-^|) :: (d1 @~ d2, Additive f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (f a) (Qu a) |^-^| (Qu b) = Qu (a ^-^ b) -- | Take the sum of a list of quantities qSumV :: (Foldable t, Additive f, Num a) => t (Qu d l (f a)) -> Qu d l (f a) qSumV = F.foldr (|^+^|) zeroV --------------------------------------- -- Multiplicative operations --------------------------------------- infixl 7 |*^|, |^*|, |^/| -- | Multiply a scalar quantity by a vector quantity (|*^|) :: (Functor f, Num a) => Qu d1 l a -> Qu d2 l (f a) -> Qu (Normalize (d1 @+ d2)) l (f a) (Qu a) |*^| (Qu b) = Qu (a *^ b) -- | Multiply a vector quantity by a scalar quantity (|^*|) :: (Functor f, Num a) => Qu d1 l (f a) -> Qu d2 l a -> Qu (Normalize (d1 @+ d2)) l (f a) (Qu a) |^*| (Qu b) = Qu (a ^* b) -- | Divide a vector quantity by a scalar quantity (|^/|) :: (Functor f, Fractional a) => Qu d1 l (f a) -> Qu d2 l a -> Qu (Normalize (d1 @- d2)) l (f a) (Qu a) |^/| (Qu b) = Qu (a ^/ b) infixl 7 |^/ -- | Divide a quantity by a plain old number (|^/) :: (Functor f, Fractional a) => Qu d l (f a) -> a -> Qu d l (f a) (Qu a) |^/ b = Qu (a ^/ b) infixl 7 *^| , |^* -- | Multiply a quantity by a plain old number from the left (*^|) :: (Functor f, Num a) => a -> Qu b l (f a) -> Qu b l (f a) a *^| (Qu b) = Qu (a *^ b) -- | Multiply a quantity by a plain old number from the right (|^*) :: (Functor f, Num a) => Qu b l (f a) -> a -> Qu b l (f a) (Qu a) |^* b = Qu (a ^* b) --------------------------------------- -- Vector-space operations --------------------------------------- -- | Return a default basis, where each basis element measures 1 of the -- unit provided. qBasis :: ( ValidDLU dim lcsu unit , Additive f , Traversable f , Fractional a ) => unit -> [Qu dim lcsu (f a)] qBasis u = map (^% u) basis -- | Return a default basis for the vector space provided. Each basis -- element measures 1 of the unit provided. qBasisFor :: ( ValidDLU dim lcsu unit , Additive f , Traversable f , Fractional a ) => unit -> Qu dim lcsu (f b) -> [Qu dim lcsu (f a)] qBasisFor u (Qu vec) = map (^% u) (basisFor vec) -- | Produce a diagonal (scale) matrix from a vector qScaled :: (Traversable f, Num a) => Qu dim lcsu (f a) -> Qu dim lcsu (f (f a)) qScaled (Qu vec) = Qu (scaled vec) -- | Outer (tensor) product of two quantity vectors qOuter :: (Functor f, Functor g, Num a) => Qu d1 l (f a) -> Qu d2 l (g a) -> Qu (Normalize (d1 @+ d2)) l (f (g a)) qOuter (Qu a) (Qu b) = Qu (a `outer` b) -- | Create a unit vector from a setter and a choice of unit. qUnit :: (ValidDLU dim lcsu unit, Additive t, Fractional a) => Lens.ASetter' (t a) a -> unit -> Qu dim lcsu (t a) qUnit setter u = unit setter ^% u infixl 7 |.| -- | Take a inner (dot) product between two quantities. (|.|) :: (Metric f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (Normalize (d1 @+ d2)) l a (Qu a) |.| (Qu b) = Qu (a `dot` b) -- | Square the length of a vector. qQuadrance :: (Metric f, Num a) => Qu d l (f a) -> Qu (d @* Z.Two) l a qQuadrance (Qu x) = Qu (quadrance x) -- | Length of a vector. qNorm :: (Metric f, Floating a) => Qu d l (f a) -> Qu d l a qNorm (Qu x) = Qu (norm 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. qSignorm :: (Metric f, Floating a) => Qu d l (f a) -> Qu '[] l (f a) qSignorm (Qu x) = Qu (signorm x) -- | @qProject u v@ computes the projection of @v@ onto @u@. qProject :: (Metric f, Fractional a) => Qu d2 l (f a) -> Qu d1 l (f a) -> Qu d1 l (f a) qProject (Qu u) (Qu v) = Qu (u `project` v) -- | Cross product of 3D vectors. qCross :: Num a => Qu d1 l (V3 a) -> Qu d2 l (V3 a) -> Qu (Normalize (d1 @+ d2)) l (V3 a) qCross (Qu x) (Qu y) = Qu (x `cross` y) -- | Square of the distance between two vectors. qQd :: (d1 @~ d2, Metric f, Metric (Diff f), Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (d1 @* Z.Two) l a qQd (Qu a) (Qu b) = Qu (a `qd` b) -- | Distance between two vectors. qDistance :: (d1 @~ d2, Metric f, Metric (Diff f), Floating a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l a qDistance (Qu a) (Qu b) = Qu (a `distance` b) --------------------------------------- -- Affine space operations --------------------------------------- -- | Subtract point quantities. (|.-.|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l (Diff f a) (Qu a) |.-.| (Qu b) = Qu (a .-. b) -- | Add a point to a vector. (|.+^|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (Diff f a) -> Qu d1 l (f a) (Qu a) |.+^| (Qu b) = Qu (a .+^ b) -- | Subract a vector from a point. (|.-^|) :: (d1 @~ d2, Affine f, Num a) => Qu d1 l (f a) -> Qu d2 l (Diff f a) -> Qu d1 l (f a) (Qu a) |.-^| (Qu b) = Qu (a .-^ b) -- | Square of the distance between two points. qQdA :: (d1 @~ d2, Affine f, Foldable (Diff f), Num a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu (d1 @* Z.Two) l a qQdA (Qu a) (Qu b) = Qu (a `qdA` b) -- | Distance between two points. qDistanceA :: (d1 @~ d2, Affine f, Foldable (Diff f), Floating a) => Qu d1 l (f a) -> Qu d2 l (f a) -> Qu d1 l a qDistanceA (Qu a) (Qu b) = Qu (a `distanceA` 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 numInV :: forall unit dim lcsu f a. ( ValidDLU dim lcsu unit , Functor f , Fractional a ) => Qu dim lcsu (f a) -> unit -> (f a) numInV (Qu val) u = val ^* fromRational (canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu)) / canonicalConvRatio u) infix 5 ^# -- | Infix synonym for 'numIn' (^#) :: ( ValidDLU dim lcsu unit , Functor f , Fractional a ) => Qu dim lcsu (f a) -> unit -> (f a) (^#) = numInV -- | Creates a dimensioned quantity in the given unit. For example: -- -- > height :: Length -- > height = quOf 2.0 Meter -- -- or -- -- > height = 2.0 % Meter quOfV :: forall unit dim lcsu f a. ( ValidDLU dim lcsu unit , Functor f , Fractional a ) => (f a) -> unit -> Qu dim lcsu (f a) quOfV d u = Qu (d ^* fromRational (canonicalConvRatio u / canonicalConvRatioSpec (Proxy :: Proxy (LookupList dim lcsu)))) infixr 9 ^% -- | Infix synonym for 'quOf' (^%) :: ( ValidDLU dim lcsu unit , Functor f , Fractional a ) => (f a) -> unit -> Qu dim lcsu (f a) (^%) = quOfV -- | Dimension-keeping cast between different CSUs. convertV :: forall d l1 l2 f a. ( ConvertibleLCSUs d l1 l2 , Functor f , Fractional a ) => Qu d l1 (f a) -> Qu d l2 (f a) convertV (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. constantV :: ( d @~ e , ConvertibleLCSUs e DefaultLCSU l , Functor f , Fractional a ) => Qu d DefaultLCSU (f a) -> Qu e l (f a) constantV = convertV . redim infix 1 `showInV` -- | Show a dimensioned quantity in a given unit. (The default @Show@ -- instance always uses units as specified in the LCSU.) showInV :: ( ValidDLU dim lcsu unit , Functor f , Fractional a , Show unit , Show a , Show (f a) ) => Qu dim lcsu (f a) -> unit -> String showInV x u = show (x ^# u) ++ " " ++ show u