├── README.md ├── .gitignore ├── CHANGELOG.md ├── tests ├── Main.elm ├── elm-package.json ├── Proto │ └── Expect.elm └── Tests.elm ├── elm-package.json └── src ├── Ratio └── Infix.elm └── Ratio.elm /README.md: -------------------------------------------------------------------------------- 1 | # Ratio 2 | 3 | A simple module for working with rational numbers. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | elm.js 3 | Main.html 4 | index.html 5 | documentation.json 6 | /tests/elm-stuff 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ratio changelog 2 | 3 | ## ratio 1.1.0 4 | 5 | * Upgraded to Elm 0.18 6 | * Added comparison and arithmetic functions 7 | * Added infix operators 8 | 9 | ## ratio 1.0.0 10 | 11 | Forked from https://github.com/imeckler/ratio 12 | -------------------------------------------------------------------------------- /tests/Main.elm: -------------------------------------------------------------------------------- 1 | port module Main exposing (..) 2 | 3 | import Tests 4 | import Test.Runner.Node exposing (run) 5 | import Json.Encode exposing (Value) 6 | 7 | 8 | main : Test.Runner.Node.TestProgram 9 | main = 10 | run emit Tests.all 11 | 12 | 13 | port emit : ( String, Value ) -> Cmd msg 14 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.0", 3 | "summary": "A ratio type for working with rational numbers", 4 | "repository": "https://github.com/elm-community/ratio.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "Ratio", 11 | "Ratio.Infix" 12 | ], 13 | "dependencies": { 14 | "elm-lang/core": "5.0.0 <= v < 6.0.0" 15 | }, 16 | "elm-version": "0.18.0 <= v < 0.19.0" 17 | } 18 | -------------------------------------------------------------------------------- /tests/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Rational tests", 4 | "repository": "https://github.com/elm-community/ratio.git", 5 | "license": "BSD-3-Clause", 6 | "source-directories": [ 7 | ".", 8 | "../src" 9 | ], 10 | "exposed-modules": [ ], 11 | "dependencies": { 12 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 13 | "elm-community/elm-test": "3.1.0 <= v < 4.0.0", 14 | "elm-community/shrink": "2.0.0 <= v < 3.0.0", 15 | "mgold/elm-random-pcg": "4.0.2 <= v < 5.0.0", 16 | "rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0" 17 | }, 18 | "elm-version": "0.18.0 <= v < 0.19.0" 19 | } 20 | -------------------------------------------------------------------------------- /tests/Proto/Expect.elm: -------------------------------------------------------------------------------- 1 | module Proto.Expect exposing (nearlyEqual) 2 | 3 | {-| 4 | 5 | Prototype of possible extensions to Test.Expectation 6 | 7 | @docs nearlyEqual 8 | 9 | -} 10 | 11 | import Test exposing (..) 12 | import Expect exposing (..) 13 | import String exposing (join) 14 | 15 | custom : (a -> b -> Result String ()) -> a -> b -> Expectation 16 | custom f expected actual = 17 | case f expected actual of 18 | Ok () -> 19 | Expect.pass 20 | 21 | Err str -> 22 | [ toString actual 23 | , "╷" 24 | , "│ " ++ str 25 | , "╵" 26 | , toString expected 27 | ] 28 | |> String.join "\n" 29 | |> Expect.fail 30 | 31 | {-| naive and rather arbitrary implementation of the requested Float expectation function almostEqual aka nearlyEqual 32 | 33 | (see https://github.com/elm-community/elm-test/issues/41) 34 | -} 35 | nearlyEqual : Float -> Float -> Expectation 36 | nearlyEqual = 37 | custom (\i j -> 38 | let 39 | tolerance = 40 | if (j > 1000000) then 41 | 0.1 42 | else if (j > 1) then 43 | 0.0001 44 | else 45 | 0.00000001 46 | in 47 | if (Basics.isInfinite i && Basics.isInfinite j) then 48 | Ok () 49 | else if (Basics.isNaN i && Basics.isNaN j) then 50 | Ok () 51 | else if abs (i - j) < tolerance then 52 | Ok () 53 | else 54 | Err <| "Expect to be nearly equal, tolerance: " ++ toString tolerance 55 | ) 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/Ratio/Infix.elm: -------------------------------------------------------------------------------- 1 | module Ratio.Infix 2 | exposing 3 | ( (|==|) 4 | , (|==) 5 | , (|/=|) 6 | , (|/=) 7 | , (|>|) 8 | , (|>=|) 9 | , (|<|) 10 | , (|<=|) 11 | , (+|) 12 | , (|+) 13 | , (|+|) 14 | , (-|) 15 | , (|-) 16 | , (|-|) 17 | , (*|) 18 | , (|*) 19 | , (|*|) 20 | , (/|) 21 | , (|/) 22 | , (|/|) 23 | ) 24 | 25 | {-| Infix operators for Rationals. 26 | 27 | Expressions may include both Rationals and Ints. Where both operands are Rationals, we surround the operator with the '|' symbol. 28 | Where one operand is a Rational and the other an Int, we place the '|' symbol at the side of the operator adjacent to the Rational. 29 | For example (where r is a Rational and i an Int): 30 | 31 | 32 | r |+| r 33 | 34 | i +| r 35 | 36 | r |+ i 37 | 38 | 39 | # Infix comparisons 40 | @docs (|==|), (|==), (|/=|), (|/=), (|>|), (|>=|), (|<|), (|<=|) 41 | 42 | # Infix arithmetic 43 | @docs (+|), (|+), (|+|), (-|), (|-), (|-|), 44 | (*|), (|*), (|*|), (/|), (|/), (|/|) 45 | 46 | -} 47 | 48 | import Ratio exposing (..) 49 | 50 | 51 | infixl 6 |+ 52 | 53 | 54 | infixl 6 +| 55 | 56 | 57 | infixl 6 |+| 58 | 59 | 60 | infixl 6 |- 61 | 62 | 63 | infixl 6 -| 64 | 65 | 66 | infixl 6 |-| 67 | 68 | 69 | infixl 7 |* 70 | 71 | 72 | infixl 7 *| 73 | 74 | 75 | infixl 7 |*| 76 | 77 | 78 | infixl 7 |/ 79 | 80 | 81 | infixl 7 /| 82 | 83 | 84 | infixl 7 |/| 85 | 86 | 87 | 88 | -- comparison ops 89 | {- we won't provide |> or <| for comparing Rationals with Ints because of 90 | confusion with the same infix functions in Basics 91 | -} 92 | 93 | 94 | {-| Synonym for Ratio.eq 95 | -} 96 | (|==|) : Rational -> Rational -> Bool 97 | (|==|) a b = 98 | eq a b 99 | 100 | 101 | {-| Test a Rational and an Int for equality 102 | -} 103 | (|==) : Rational -> Int -> Bool 104 | (|==) a b = 105 | eq a (fromInt b) 106 | 107 | 108 | {-| Synonym for Ratio.ne 109 | -} 110 | (|/=|) : Rational -> Rational -> Bool 111 | (|/=|) a b = 112 | ne a b 113 | 114 | 115 | {-| Test a Rational and an Int for inequality 116 | -} 117 | (|/=) : Rational -> Int -> Bool 118 | (|/=) a b = 119 | ne a (fromInt b) 120 | 121 | 122 | {-| Synonym for Ratio.gt 123 | -} 124 | (|>|) : Rational -> Rational -> Bool 125 | (|>|) a b = 126 | gt a b 127 | 128 | 129 | {-| Synonym for Ratio.ge 130 | -} 131 | (|>=|) : Rational -> Rational -> Bool 132 | (|>=|) a b = 133 | ge a b 134 | 135 | 136 | {-| Synonym for Ratio.lt 137 | -} 138 | (|<|) : Rational -> Rational -> Bool 139 | (|<|) a b = 140 | lt a b 141 | 142 | 143 | {-| Synonym for Ratio.le 144 | -} 145 | (|<=|) : Rational -> Rational -> Bool 146 | (|<=|) a b = 147 | le a b 148 | 149 | 150 | 151 | -- arithmetic ops 152 | 153 | 154 | {-| Add an Int to a Rational 155 | -} 156 | (+|) : Int -> Rational -> Rational 157 | (+|) i r = 158 | add (fromInt i) r 159 | 160 | 161 | {-| add a Rational to an Int 162 | -} 163 | (|+) : Rational -> Int -> Rational 164 | (|+) = 165 | flip (+|) 166 | 167 | 168 | {-| Synonym for Ratio.add 169 | -} 170 | (|+|) : Rational -> Rational -> Rational 171 | (|+|) = 172 | add 173 | 174 | 175 | {-| Subtract a Rational from an Iny 176 | -} 177 | (-|) : Int -> Rational -> Rational 178 | (-|) i r = 179 | subtract (fromInt i) r 180 | 181 | 182 | {-| Subtract an Int from a Rational 183 | -} 184 | (|-) : Rational -> Int -> Rational 185 | (|-) r i = 186 | subtract r (fromInt i) 187 | 188 | 189 | 190 | {- 191 | -- flip not used in arithmetic because it seems to give the wrong precedence in expressions 192 | -- not got to the cause of this yet 193 | (|-) : Rational -> Int -> Rational 194 | (|-) = 195 | flip (-|) 196 | -} 197 | 198 | 199 | {-| Synonym for Ratio.subtract 200 | -} 201 | (|-|) : Rational -> Rational -> Rational 202 | (|-|) = 203 | subtract 204 | 205 | 206 | {-| Multiply an Rational by an Int 207 | -} 208 | (|*) : Rational -> Int -> Rational 209 | (|*) = 210 | multiplyByInt 211 | 212 | 213 | {-| Multiply an Int by a Rational 214 | -} 215 | (*|) : Int -> Rational -> Rational 216 | (*|) = 217 | flip multiplyByInt 218 | 219 | 220 | {-| Synonym for Ratio.multiply 221 | -} 222 | (|*|) : Rational -> Rational -> Rational 223 | (|*|) = 224 | multiply 225 | 226 | 227 | {-| Divide a Rational by an Int 228 | -} 229 | (|/) : Rational -> Int -> Rational 230 | (|/) = 231 | divideByInt 232 | 233 | 234 | {-| Divide an Int by a Rational 235 | -} 236 | (/|) : Int -> Rational -> Rational 237 | (/|) = 238 | divideIntBy 239 | 240 | 241 | {-| Synonym for Ratio.divide 242 | -} 243 | (|/|) : Rational -> Rational -> Rational 244 | (|/|) = 245 | divide 246 | -------------------------------------------------------------------------------- /src/Ratio.elm: -------------------------------------------------------------------------------- 1 | module Ratio 2 | exposing 3 | ( gcd 4 | , add 5 | , subtract 6 | , multiply 7 | , multiplyByInt 8 | , divide 9 | , divideByInt 10 | , divideIntBy 11 | , negate 12 | , invert 13 | , Rational 14 | , over 15 | , denominator 16 | , numerator 17 | , split 18 | , toFloat 19 | , fromInt 20 | , eq 21 | , ne 22 | , gt 23 | , lt 24 | , ge 25 | , le 26 | , compare 27 | , max 28 | , min 29 | , isZero 30 | , isInfinite 31 | , round 32 | , floor 33 | , ceiling 34 | , truncate 35 | ) 36 | 37 | {-| A simple module providing a ratio type for rational numbers 38 | 39 | # Types 40 | @docs Rational 41 | 42 | # Construction 43 | @docs over, fromInt 44 | 45 | # Comparison 46 | @docs eq, ne, gt, lt, ge, le, max, min, compare 47 | 48 | # Mathematics 49 | @docs add, subtract, multiply, multiplyByInt 50 | @docs divide, divideByInt, divideIntBy, negate 51 | @docs isZero, isInfinite, round, floor, ceiling, truncate 52 | 53 | # Elimination 54 | @docs numerator, denominator, split 55 | 56 | # Utils 57 | @docs gcd, invert, toFloat 58 | 59 | -} 60 | 61 | import Basics exposing (..) 62 | 63 | 64 | {-| "Arbitrary" (up to `max_int` size) precision fractional numbers. Think of 65 | it as the length of a rigid bar that you've constructed from a bunch of 66 | initial bars of the same fixed length 67 | by the operations of gluing bars together and shrinking a 68 | given bar so that an integer number of copies of it glues together to 69 | make another given bar. 70 | 71 | -} 72 | type Rational 73 | = Rational Int Int 74 | 75 | 76 | {-| The biggest number that divides both arguments (the greatest common divisor). 77 | -} 78 | gcd : Int -> Int -> Int 79 | gcd a b = 80 | if b == 0 then 81 | a 82 | else 83 | gcd b (a % b) 84 | 85 | 86 | 87 | {- Normalisation of rationals with negative denominators 88 | 89 | Rational 1 (-3) becomes Rational (-1) 3 90 | 91 | Rational (-1) (-3) becomes Rational 1 3 92 | -} 93 | 94 | 95 | normalize (Rational p q) = 96 | let 97 | k = 98 | gcd p q 99 | * (if q < 0 then 100 | -1 101 | else 102 | 1 103 | ) 104 | in 105 | Rational (p // k) (q // k) 106 | 107 | 108 | 109 | {- Add or subtract two rationals :- 110 | f can be (+) or (-) 111 | -} 112 | 113 | 114 | addsub : (Int -> Int -> Int) -> Rational -> Rational -> Rational 115 | addsub f (Rational a b) (Rational c d) = 116 | normalize (Rational (f (a * d) (b * c)) (b * d)) 117 | 118 | 119 | {-| Addition. It's like gluing together two bars of the given lengths. 120 | -} 121 | add : Rational -> Rational -> Rational 122 | add = 123 | addsub (+) 124 | 125 | 126 | {-| subtraction. Is it like ungluing two bars of the given lengths? 127 | -} 128 | subtract : Rational -> Rational -> Rational 129 | subtract = 130 | addsub (-) 131 | 132 | 133 | {-| Mulitplication. `mulitply x (c / d)` is the length of the bar that you'd get 134 | if you glued `c` copies of a bar of length `x` end-to-end and then shrunk it 135 | down enough so that `d` copies of the shrunken bar would fit in the big 136 | glued bar. 137 | -} 138 | multiply : Rational -> Rational -> Rational 139 | multiply (Rational a b) (Rational c d) = 140 | normalize (Rational (a * c) (b * d)) 141 | 142 | 143 | {-| Multiply a rational by an Int 144 | -} 145 | multiplyByInt : Rational -> Int -> Rational 146 | multiplyByInt (Rational a b) i = 147 | normalize (Rational (a * i) b) 148 | 149 | 150 | {-| Divide two rationals 151 | -} 152 | divide : Rational -> Rational -> Rational 153 | divide r (Rational c d) = 154 | multiply r (Rational d c) 155 | 156 | 157 | {-| Divide a rational by an Int 158 | -} 159 | divideByInt : Rational -> Int -> Rational 160 | divideByInt r i = 161 | normalize (divide r (fromInt i)) 162 | 163 | 164 | {-| Divide an Int by a rational 165 | -} 166 | divideIntBy : Int -> Rational -> Rational 167 | divideIntBy i r = 168 | normalize (divide (fromInt i) r) 169 | 170 | 171 | 172 | {- This implementation gives the wrong precedence 173 | divideByInt r i = 174 | normalize (multiplyByInt (invert r) i) 175 | -} 176 | 177 | 178 | {-| multiplication by `-1`. 179 | -} 180 | negate : Rational -> Rational 181 | negate (Rational a b) = 182 | Rational (-a) b 183 | 184 | 185 | {-| invert the rational. r becomes 1/r. 186 | -} 187 | invert : Rational -> Rational 188 | invert (Rational a b) = 189 | normalize (Rational b a) 190 | 191 | 192 | {-| `over x y` is like `x / y`. 193 | -} 194 | over : Int -> Int -> Rational 195 | over x y = 196 | if (y < 0) then 197 | normalize (Rational -x -y) 198 | else 199 | normalize (Rational x y) 200 | 201 | 202 | {-| `fromInt x = over x 1` 203 | -} 204 | fromInt : Int -> Rational 205 | fromInt x = 206 | over x 1 207 | 208 | 209 | {-| -} 210 | numerator : Rational -> Int 211 | numerator (Rational a _) = 212 | a 213 | 214 | 215 | {-| -} 216 | denominator : Rational -> Int 217 | denominator (Rational _ b) = 218 | b 219 | 220 | 221 | {-| `split x = (numerator x, denominator x)` 222 | -} 223 | split : Rational -> ( Int, Int ) 224 | split (Rational a b) = 225 | ( a, b ) 226 | 227 | 228 | {-| -} 229 | toFloat : Rational -> Float 230 | toFloat (Rational a b) = 231 | Basics.toFloat a / Basics.toFloat b 232 | 233 | 234 | {-| -} 235 | eq : Rational -> Rational -> Bool 236 | eq a b = 237 | rel (==) a b 238 | 239 | 240 | {-| -} 241 | ne : Rational -> Rational -> Bool 242 | ne a b = 243 | rel (/=) a b 244 | 245 | 246 | {-| -} 247 | gt : Rational -> Rational -> Bool 248 | gt a b = 249 | rel (>) a b 250 | 251 | 252 | {-| -} 253 | lt : Rational -> Rational -> Bool 254 | lt a b = 255 | rel (<) a b 256 | 257 | 258 | {-| -} 259 | ge : Rational -> Rational -> Bool 260 | ge a b = 261 | rel (>=) a b 262 | 263 | 264 | {-| -} 265 | le : Rational -> Rational -> Bool 266 | le a b = 267 | rel (<=) a b 268 | 269 | 270 | {-| -} 271 | compare : Rational -> Rational -> Order 272 | compare a b = 273 | Basics.compare (toFloat a) (toFloat b) 274 | 275 | 276 | {-| -} 277 | max : Rational -> Rational -> Rational 278 | max a b = 279 | if gt a b then 280 | a 281 | else 282 | b 283 | 284 | 285 | {-| -} 286 | min : Rational -> Rational -> Rational 287 | min a b = 288 | if lt a b then 289 | a 290 | else 291 | b 292 | 293 | 294 | {-| -} 295 | isZero : Rational -> Bool 296 | isZero r = 297 | 0 == (numerator r) 298 | 299 | 300 | {-| -} 301 | isInfinite : Rational -> Bool 302 | isInfinite r = 303 | 0 == (denominator r) 304 | 305 | 306 | {-| -} 307 | round : Rational -> Int 308 | round = 309 | toFloat >> Basics.round 310 | 311 | 312 | {-| -} 313 | floor : Rational -> Int 314 | floor = 315 | toFloat >> Basics.floor 316 | 317 | 318 | {-| -} 319 | ceiling : Rational -> Int 320 | ceiling = 321 | toFloat >> Basics.ceiling 322 | 323 | 324 | {-| -} 325 | truncate : Rational -> Int 326 | truncate = 327 | toFloat >> Basics.truncate 328 | 329 | 330 | rel : (Int -> Int -> Bool) -> Rational -> Rational -> Bool 331 | rel relop a b = 332 | relop ((numerator a) * (denominator b)) ((numerator b) * (denominator a)) 333 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (..) 2 | 3 | import Test exposing (..) 4 | import Expect exposing (Expectation) 5 | import Proto.Expect as Expect 6 | import Fuzz exposing (Fuzzer, intRange, tuple, map, custom) 7 | import Shrink exposing (map, andMap) 8 | import Random.Pcg as Random 9 | import Ratio exposing (..) 10 | import Ratio.Infix exposing (..) 11 | 12 | 13 | fuzzRational : Fuzzer Rational 14 | fuzzRational = 15 | Fuzz.map2 over (intRange -100 100) (intRange 1 100) 16 | 17 | 18 | 19 | {- this attempts to shrink the Rational but I'm not sure if it's correct - 20 | (can you shrink both numerator and denomonator independently?) 21 | in any case it doesn't manage to shrink anything 22 | fuzzRational1 : Fuzzer Rational 23 | fuzzRational1 = 24 | Fuzz.custom 25 | (Random.map2 over (Random.int -100 100) (Random.int 1 100)) 26 | (\r -> Shrink.map over (Shrink.int (numerator r)) `Shrink.andMap` (Shrink.int (denominator r))) 27 | -} 28 | 29 | 30 | fuzzRationalPair : Fuzzer ( Rational, Rational ) 31 | fuzzRationalPair = 32 | Fuzz.tuple ( fuzzRational, fuzzRational ) 33 | 34 | 35 | fuzzRationalIntPair : Fuzzer ( Rational, Int ) 36 | fuzzRationalIntPair = 37 | Fuzz.tuple ( fuzzRational, intRange -100 100 ) 38 | 39 | 40 | fuzzRationalIntTrio : Fuzzer ( Rational, Int, Int ) 41 | fuzzRationalIntTrio = 42 | Fuzz.tuple3 ( fuzzRational, intRange -100 100, intRange -100 100 ) 43 | 44 | 45 | expectAlmostEqual : Float -> Float -> Expectation 46 | expectAlmostEqual = 47 | Expect.nearlyEqual 48 | 49 | 50 | all : Test 51 | all = 52 | concat 53 | [ basics 54 | , comparisons 55 | , infixComparisons 56 | , arithmetic 57 | ] 58 | 59 | 60 | basics : Test 61 | basics = 62 | describe "Basic Tests" 63 | [ test "numerator +n" <| 64 | \() -> 65 | Expect.equal 1 (numerator (over 1 4)) 66 | , test "numerator -n" <| 67 | \() -> 68 | Expect.equal -1 (numerator (over -1 4)) 69 | , test "numerator -d" <| 70 | \() -> 71 | Expect.equal -1 (numerator (over 1 -4)) 72 | , test "numerator -n -d" <| 73 | \() -> 74 | Expect.equal 1 (numerator (over -1 -4)) 75 | , test "denominator -d" <| 76 | \() -> 77 | Expect.equal 4 (denominator (over 1 -4)) 78 | , test "denominator -n" <| 79 | \() -> 80 | Expect.equal 4 (denominator (over -1 4)) 81 | , test "denominator -n -d" <| 82 | \() -> 83 | Expect.equal 4 (denominator (over -1 -4)) 84 | , test "denominator 0" <| 85 | \() -> 86 | Expect.equal 0 (denominator (over 1 0)) 87 | , test "eq" <| 88 | \() -> 89 | Expect.equal True (Ratio.eq (over 1 2) (over 1 2)) 90 | , test "eq vulgar" <| 91 | \() -> 92 | Expect.equal True (Ratio.eq (over 2 1) (fromInt 2)) 93 | , test "ne" <| 94 | \() -> 95 | Expect.equal True (Ratio.ne (over 1 2) (over 1 3)) 96 | , test "is zero" <| 97 | \() -> 98 | Expect.equal True (isZero (over 0 1)) 99 | , test "is not zero" <| 100 | \() -> 101 | Expect.equal False (isZero (over 1 1)) 102 | , test "infinity" <| 103 | \() -> 104 | Expect.equal True (Basics.isInfinite (Ratio.toFloat (over 1 0))) 105 | , test "infinity under mult by infinity" <| 106 | \() -> 107 | Expect.equal (over 1 0) (multiply (over 5 1) (over 1 0)) 108 | , test "subtraction of infinity" <| 109 | \() -> 110 | let 111 | minusInfinity = 112 | 100 -| Ratio.negate (over 1 0) 113 | in 114 | Expect.equal True (Basics.isInfinite (Ratio.toFloat (minusInfinity))) 115 | , test "production of infinity" <| 116 | \() -> 117 | let 118 | bigInt = 119 | 2 ^ 31 120 | in 121 | Expect.equal (over 1 0) (multiply (over bigInt 1) (over bigInt 0)) 122 | , test "infinity" <| 123 | \() -> 124 | Expect.equal True (Basics.isInfinite (Ratio.toFloat (over 1 0))) 125 | , -- test the expectAlmostEqual function 126 | test "expectAlmostEqual function 0" <| 127 | \() -> 128 | expectAlmostEqual 0.0 0.0 129 | , test "expectAlmostEqual function 10" <| 130 | \() -> 131 | expectAlmostEqual 10 10.00000001 132 | , test "expectAlmostEqual function 10^6" <| 133 | \() -> 134 | expectAlmostEqual 1000000 1000000.0001 135 | , test "expectAlmostEqual function 10^-6" <| 136 | \() -> 137 | expectAlmostEqual 0.000001 0.0000009999 138 | , -- basic subtraction 139 | test "8/7 -1 -1" <| 140 | \() -> 141 | Expect.equal (over -6 7) ((over 8 7) |- 1 |- 1) 142 | , test "7/3 -1/6 -1/6" <| 143 | \() -> 144 | Expect.equal (over 2 1) ((over 7 3) |-| (over 1 6) |-| (over 1 6)) 145 | , test "7/3 +1/6 -1/6" <| 146 | \() -> 147 | Expect.equal (over 7 3) ((over 7 3) |+| (over 1 6) |-| (over 1 6)) 148 | , test "7/3 -1/9 +4/9" <| 149 | \() -> 150 | Expect.equal (over 8 3) ((over 7 3) |-| (over 1 9) |+| (over 4 9)) 151 | , -- basic division 152 | test "8/7 / 1/2" <| 153 | \() -> 154 | Expect.equal (over 16 7) ((over 8 7) |/| (over 1 2)) 155 | , test "0 / 1/3" <| 156 | \() -> 157 | Expect.equal (over 0 1) (0 /| (over 1 3)) 158 | , test "8/7 / 2 / 3" <| 159 | \() -> 160 | Expect.equal (over 4 21) ((over 8 7) |/ 2 |/ 3) 161 | , test "round down pos" <| 162 | \() -> 163 | Expect.equal 3 (Ratio.round (over 10 3)) 164 | , test "round down neg" <| 165 | \() -> 166 | Expect.equal -3 (Ratio.round (over -10 3)) 167 | , test "round up pos" <| 168 | \() -> 169 | Expect.equal 5 (Ratio.round (over 19 4)) 170 | , test "round up neg" <| 171 | \() -> 172 | Expect.equal -5 (Ratio.round (over -19 4)) 173 | , test "floor pos" <| 174 | \() -> 175 | Expect.equal 4 (Ratio.floor (over 19 4)) 176 | , test "floor neg" <| 177 | \() -> 178 | Expect.equal -5 (Ratio.floor (over -19 4)) 179 | , test "ceiling pos" <| 180 | \() -> 181 | Expect.equal 5 (Ratio.ceiling (over 19 4)) 182 | , test "ceiling neg" <| 183 | \() -> 184 | Expect.equal -4 (Ratio.ceiling (over -19 4)) 185 | , test "truncate pos" <| 186 | \() -> 187 | Expect.equal 4 (Ratio.truncate (over 19 4)) 188 | , test "truncate neg" <| 189 | \() -> 190 | Expect.equal -4 (Ratio.truncate (over -19 4)) 191 | ] 192 | 193 | 194 | infixComparisons : Test 195 | infixComparisons = 196 | describe "Infix Comparison Tests" 197 | [ test "is |==|" <| 198 | \() -> 199 | Expect.equal True ((over 1 2) |==| (over 1 2)) 200 | , test "is |==" <| 201 | \() -> 202 | Expect.equal True ((over 3 1) |== 3) 203 | , test "not |==|" <| 204 | \() -> 205 | Expect.equal False ((over 1 2) |==| (over 1 3)) 206 | , test "is |/=|" <| 207 | \() -> 208 | Expect.equal True ((over 1 3) |/=| (over 1 2)) 209 | , test "is |/=" <| 210 | \() -> 211 | Expect.equal False ((over 3 1) |/= 3) 212 | , test "not |/=|" <| 213 | \() -> 214 | Expect.equal True ((over 1 2) |/=| (over 1 3)) 215 | , test "== in |>=|" <| 216 | \() -> 217 | Expect.equal True ((over 1 2) |>=| (over 1 2)) 218 | , test "== in |<=|" <| 219 | \() -> 220 | Expect.equal True ((over 1 2) |>=| (over 1 2)) 221 | ] 222 | 223 | 224 | comparisons : Test 225 | comparisons = 226 | describe "Comparison Checks" 227 | [ fuzz (fuzzRationalPair) ">" <| 228 | \( a, b ) -> 229 | gt a b 230 | |> Expect.equal ((Ratio.toFloat a) > (Ratio.toFloat b)) 231 | , fuzz (fuzzRationalPair) "<" <| 232 | \( a, b ) -> 233 | lt a b 234 | |> Expect.equal ((Ratio.toFloat a) < (Ratio.toFloat b)) 235 | , fuzz (fuzzRationalPair) ">=" <| 236 | \( a, b ) -> 237 | ge a b 238 | |> Expect.equal ((Ratio.toFloat a) >= (Ratio.toFloat b)) 239 | , fuzz (fuzzRationalPair) "<=" <| 240 | \( a, b ) -> 241 | le a b 242 | |> Expect.equal ((Ratio.toFloat a) <= (Ratio.toFloat b)) 243 | , fuzz (fuzzRationalPair) "max" <| 244 | \( a, b ) -> 245 | Ratio.max a b 246 | |> Ratio.toFloat 247 | |> Expect.equal (Basics.max (Ratio.toFloat a) (Ratio.toFloat b)) 248 | , fuzz (fuzzRationalPair) "min" <| 249 | \( a, b ) -> 250 | Ratio.min a b 251 | |> Ratio.toFloat 252 | |> Expect.equal (Basics.min (Ratio.toFloat a) (Ratio.toFloat b)) 253 | , fuzz (fuzzRationalPair) "compare" <| 254 | \( a, b ) -> 255 | let 256 | f x y = 257 | if (x |>| y) then 258 | GT 259 | else if (x |==| y) then 260 | EQ 261 | else 262 | LT 263 | in 264 | Ratio.compare a b 265 | |> Expect.equal (f a b) 266 | ] 267 | 268 | 269 | arithmetic : Test 270 | arithmetic = 271 | describe "Arithmetic Checks" 272 | [ -- addition 273 | fuzz (fuzzRationalIntPair) "|+" <| 274 | \( r, i ) -> 275 | r 276 | |+ i 277 | |> Ratio.toFloat 278 | |> expectAlmostEqual ((Ratio.toFloat r) + (Basics.toFloat i)) 279 | , fuzz (fuzzRationalIntPair) "+|" <| 280 | \( r, i ) -> 281 | i 282 | +| r 283 | |> Ratio.toFloat 284 | |> expectAlmostEqual ((Ratio.toFloat r) + (Basics.toFloat i)) 285 | , fuzz (fuzzRationalPair) "|+|" <| 286 | \( a, b ) -> 287 | a 288 | |+| b 289 | |> Ratio.toFloat 290 | |> expectAlmostEqual ((Ratio.toFloat a) + (Ratio.toFloat b)) 291 | , fuzz (fuzzRationalIntTrio) "transitivity of +" <| 292 | \( r, i, j ) -> 293 | r 294 | |+ i 295 | |+ j 296 | |> Ratio.toFloat 297 | |> expectAlmostEqual ((Ratio.toFloat r) + (Basics.toFloat i) + (Basics.toFloat j)) 298 | , -- subtraction 299 | fuzz (fuzzRationalIntPair) "|-" <| 300 | \( r, i ) -> 301 | r 302 | |- i 303 | |> Ratio.toFloat 304 | |> expectAlmostEqual ((Ratio.toFloat r) - (Basics.toFloat i)) 305 | , fuzz (fuzzRationalIntPair) "-|" <| 306 | \( r, i ) -> 307 | i 308 | -| r 309 | |> Ratio.toFloat 310 | |> expectAlmostEqual ((Basics.toFloat i) - (Ratio.toFloat r)) 311 | , fuzz (fuzzRationalPair) "|-|" <| 312 | \( a, b ) -> 313 | a 314 | |-| b 315 | |> Ratio.toFloat 316 | |> expectAlmostEqual ((Ratio.toFloat a) - (Ratio.toFloat b)) 317 | , fuzz (fuzzRationalIntTrio) "transitivity of -" <| 318 | \( r, i, j ) -> 319 | r 320 | |- i 321 | |- j 322 | |> Ratio.toFloat 323 | |> expectAlmostEqual ((Ratio.toFloat r) - (Basics.toFloat i) - (Basics.toFloat j)) 324 | , fuzz (fuzzRationalIntPair) "transitivity of - and +" <| 325 | \( r, i ) -> 326 | r 327 | |- i 328 | |+ i 329 | |> Ratio.toFloat 330 | |> expectAlmostEqual (Ratio.toFloat r) 331 | , fuzz (fuzzRationalIntPair) "transitivity of + abd -" <| 332 | \( r, i ) -> 333 | i 334 | +| r 335 | |-| r 336 | |> Ratio.toFloat 337 | |> expectAlmostEqual (Basics.toFloat i) 338 | , -- multiplication 339 | fuzz (fuzzRationalIntPair) "|*" <| 340 | \( r, i ) -> 341 | r 342 | |* i 343 | |> Ratio.toFloat 344 | |> expectAlmostEqual ((Ratio.toFloat r) * (Basics.toFloat i)) 345 | , fuzz (fuzzRationalIntPair) "*|" <| 346 | \( r, i ) -> 347 | i 348 | *| r 349 | |> Ratio.toFloat 350 | |> expectAlmostEqual ((Ratio.toFloat r) * (Basics.toFloat i)) 351 | , fuzz (fuzzRationalPair) "|*|" <| 352 | \( a, b ) -> 353 | a 354 | |*| b 355 | |> Ratio.toFloat 356 | |> expectAlmostEqual ((Ratio.toFloat a) * (Ratio.toFloat b)) 357 | , fuzz (fuzzRationalIntTrio) "transitivity of *" <| 358 | \( r, i, j ) -> 359 | r 360 | |* i 361 | |* j 362 | |> Ratio.toFloat 363 | |> expectAlmostEqual ((Ratio.toFloat r) * (Basics.toFloat i) * (Basics.toFloat j)) 364 | , -- division 365 | fuzz (fuzzRationalIntPair) "|/" <| 366 | \( r, i ) -> 367 | r 368 | |/ i 369 | |> Ratio.toFloat 370 | |> expectAlmostEqual ((Ratio.toFloat r) / (Basics.toFloat i)) 371 | , fuzz (fuzzRationalIntPair) "/|" <| 372 | \( r, i ) -> 373 | i 374 | /| r 375 | |> Ratio.toFloat 376 | |> expectAlmostEqual ((Basics.toFloat i) / (Ratio.toFloat r)) 377 | , fuzz (fuzzRationalPair) "|/|" <| 378 | \( a, b ) -> 379 | a 380 | |/| b 381 | |> Ratio.toFloat 382 | |> expectAlmostEqual ((Ratio.toFloat a) / (Ratio.toFloat b)) 383 | , fuzz (fuzzRationalIntTrio) "transitivity of /" <| 384 | \( r, i, j ) -> 385 | r 386 | |/ i 387 | |/ j 388 | |> Ratio.toFloat 389 | |> expectAlmostEqual ((Ratio.toFloat r) / (Basics.toFloat i) / (Basics.toFloat j)) 390 | ] 391 | --------------------------------------------------------------------------------