File: DynamicSpec.hs

package info (click to toggle)
haskell-dimensional 1.5-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 348 kB
  • sloc: haskell: 2,370; makefile: 2
file content (157 lines) | stat: -rw-r--r-- 9,887 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
module Numeric.Units.Dimensional.DynamicSpec where

import Numeric.Units.Dimensional.Prelude
import Numeric.Units.Dimensional.Dynamic hiding ((*),(/),(^),(*~),(/~), recip)
import Numeric.Units.Dimensional.Dimensions.TermLevel (hasSomeDimension)
import qualified Numeric.Units.Dimensional.Dynamic as Dyn
import qualified Prelude as P
import Test.Hspec
import Test.QuickCheck

spec :: Spec
spec = do
         describe "Dynamic quantity promotion and demotion" $ do
           it "round-trips through AnyQuantity" $ property $
             \x -> let x' = x *~ kilo newton
                       x'' = demoteQuantity x' :: AnyQuantity Double
                    in Just x' == promoteQuantity x''
           it "round-trips through DynQuantity" $ property $
             \x -> let x' = x *~ micro watt
                       x'' = demoteQuantity x' :: DynQuantity Rational
                    in Just x' == promoteQuantity x''
           it "round-trips through AnyQuantity then DynQuantity" $ property $
             \x -> let x' = x *~ gram
                       x'' = demoteQuantity x' :: AnyQuantity Double
                       x''' = demoteQuantity x'' :: DynQuantity Double
                    in Just x' == promoteQuantity x'''
           it "doesn't promote invalid quantities" $ do
             (promoteQuantity invalidQuantity :: Maybe (Length Double)) `shouldBe` Nothing
           it "doesn't promote AnyQuantity to the wrong dimension" $ do
             let x = 12.3 *~ meter
                 x' = demoteQuantity x :: AnyQuantity Double
             (promoteQuantity x' :: Maybe (Mass Double)) `shouldBe` Nothing
           it "doesn't promote DynQuantity to the wrong dimension" $ do
             let x = 12.3 *~ mole
                 x' = demoteQuantity x :: DynQuantity Double
             (promoteQuantity x' :: Maybe (Time Double)) `shouldBe` Nothing
           it "properly combines with dynamic units" $ do
             let meter' = demoteUnit' meter
             (promoteQuantity (139.4 Dyn.*~ meter' :: AnyQuantity Double)) `shouldBe` Just (139.4 *~ meter)
           it "properly eliminates dynamic units" $ do
             let ampere' = demoteUnit' ampere
                 i = demoteQuantity $ 47 *~ ampere :: AnyQuantity Double
             i Dyn./~ ampere' `shouldBe` Just 47
           it "doesn't eliminate dynamic units of the wrong dimension" $ do
             let ampere' = demoteUnit' ampere
                 i = demoteQuantity $ 47 *~ joule :: AnyQuantity Double
             i Dyn./~ ampere' `shouldBe` Nothing
         describe "DynQuantity arithmetic" $ do
           -- declare some static quantities and their dynamic counterparts for arithmetic tests
           let x1 = 12.3 *~ meter
               x2 = (-7.9) *~ meter
               a = 93 *~ square (kilo meter)
               m = 147 *~ kilo gram
               t = 14.9 *~ second
               f = 87.2 *~ milli newton
               phi = 1.61803398875 *~ one
               x1' = demoteQuantity x1 :: DynQuantity Double
               x2' = demoteQuantity x2 :: DynQuantity Double
               a' = demoteQuantity a :: DynQuantity Double
               m' = demoteQuantity m :: DynQuantity Double
               t' = demoteQuantity t :: DynQuantity Double
               f' = demoteQuantity f :: DynQuantity Double
               phi' = demoteQuantity phi :: DynQuantity Double
           context "Num instance" $ do
             it "matches static addition" $ do
               (x1' P.+ x2') `shouldBe` demoteQuantity (x1 + x2)
             it "allows addition with polydimensional zero" $ do
               (t' P.+ polydimensionalZero) `shouldBe` t'
               (polydimensionalZero P.+ t') `shouldBe` t'
               (polydimensionalZero P.+ polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)               
             it "propagates witnesses to zero during addition" $ do
               -- We want to test that the witness for polymorphic zero was actually added to the other addend.
               -- The reason for this property is that if the other addend is some element of the underlying type
               -- which can't act as a divisor (such as a propagating nAn), then we want that information to still
               -- be around when we go to promote the result.
               let nan = 0 P./ 0 :: Double
                   x = demoteQuantity $ nan *~ meter
                   Just y = promoteQuantity (polydimensionalZero P.+ x) :: Maybe (Length Double)
               (y /~ meter) `shouldSatisfy` P.isNaN
             it "matches static subtraction" $ do
               (x2' P.- x1') `shouldBe` demoteQuantity (x2 - x1)
             it "allows subtraction with polydimensional zero" $ do
               (m' P.- polydimensionalZero) `shouldBe` m'
               (polydimensionalZero P.- m') `shouldBe` (P.negate m')
               (polydimensionalZero P.- polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)               
             it "matches static multiplication" $ do
               promoteQuantity (x1' P.* f') `shouldBe` Just (x1 * f)
             it "allows multiplication with polydimensional zero" $ do
               (f' P.* polydimensionalZero) `shouldBe` polydimensionalZero
               (polydimensionalZero P.* m') `shouldBe` polydimensionalZero
               (polydimensionalZero P.* polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)
             it "matches static negation" $ do
               (P.negate m') `shouldBe` demoteQuantity (negate m)
             it "negates polydimensional zero" $ do
               (P.negate polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)
             it "matches static absolute value" $ do
               (P.abs x2') `shouldBe` demoteQuantity (abs x2)
             it "takes absolute value of polydimensional zero" $ do
               (P.abs polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)
             it "matches static signum" $ do
               (P.signum x1') `shouldBe` demoteQuantity (signum x1)
               (P.signum x2') `shouldBe` demoteQuantity (signum x2)
             it "takes signum of polydimensional zero" $ do
               (P.signum polydimensionalZero) `shouldBe` demoteQuantity (_0 :: Dimensionless Double)
             it "implements fromInteger with dimensionless result" $ do
               (P.fromInteger 7 :: DynQuantity Double) `shouldBe` demoteQuantity _7
           context "Fractional instance" $ do
             it "matches static division" $ do
               ((f' P.* x1') P./ t') `shouldBe` demoteQuantity ((f * x1) / t)
             it "matches static reciprocal" $ do
               (P.recip m') `shouldBe` demoteQuantity (recip m)
             it "implements fromRational with dimensionless result" $ do
               let pi' = 22 P./ 7 :: Rational
               (P.fromRational pi' :: DynQuantity Rational) `shouldBe` demoteQuantity (pi' *~ one)
             it "permits polydimensional zero as a dividend" $ do
               (polydimensionalZero P./ m') `shouldBe` polydimensionalZero
             it "propagates witnesses to zero during division" $ do
               -- We want to test that the witness for polymorphic zero was actually divided by the divisor.
               -- The reason for this property is that if the divisor is itself zero (but not polydimensionalZero),
               -- or some other element of the underlying type which can't act as a divisor (such as a propagating nAn),
               -- then we want that information to still be around when we go to promote the result.
               let nan = 0 P./ 0 :: Double
                   x = demoteQuantity $ nan *~ meter
                   y = polydimensionalZero P./ x
                   Just y' = promoteQuantity y :: Maybe (Length Double)
               (y' /~ meter) `shouldSatisfy` P.isNaN
           context "Floating instance" $ do
             it "implements dimensionless pi" $ do
               (P.pi :: DynQuantity Double) `shouldBe` demoteQuantity pi
             it "implements dimensionless sin" $ do
               -- this will serve as a test for all the single-argument dimensionless functions
               (P.sin phi') `shouldBe` demoteQuantity (sin phi)
             it "rejects non-dimensionless arguments to sin" $ do
               (P.sin m') `shouldBe` invalidQuantity
             it "implements dimensionless sin of polydimensional zero" $ do
               (P.sin polydimensionalZero) `shouldBe` (0 :: DynQuantity Double)
             it "matches static square root" $ do
               (P.sqrt a') `shouldBe` demoteQuantity (sqrt a)
             it "rejects arguments to square root with non-square dimensions" $ do
               (P.sqrt f') `shouldNotSatisfy` hasSomeDimension
             it "takes the square root of polydimensional zero" $ do
               (P.sqrt polydimensionalZero) `shouldBe` (polydimensionalZero :: DynQuantity Double)
             it "matches static dimensionless exponentiation" $ do
               (phi' P.** phi') `shouldBe` demoteQuantity (phi ** phi)
             it "rejects non-dimensionless arguments to dimensionless exponentiation" $ do
               (phi' P.** m') `shouldNotSatisfy` hasSomeDimension
               (x1' P.** phi') `shouldNotSatisfy` hasSomeDimension
             it "matches static logBase" $ do
               (P.logBase 10 phi') `shouldBe` demoteQuantity (logBase (10 *~ one) phi)
             it "rejects non-dimensionless arguments to logBase" $ do
               (P.logBase 10 x1') `shouldNotSatisfy` hasSomeDimension
               (P.logBase x1' 10) `shouldNotSatisfy` hasSomeDimension
         describe "Dynamic units" $ do
           describe "Promotion and demotion" $ do
             return ()
           describe "Arithmetic" $ do
             return ()