Skip to content

Commit

Permalink
Add modulo/remainder functions to Quantity module
Browse files Browse the repository at this point in the history
Resolves #46
  • Loading branch information
Ian Mackenzie committed Apr 25, 2020
1 parent a66c813 commit a7a2d31
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/Quantity.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Quantity exposing
, negate, abs, plus, minus, multiplyBy, divideBy, twice, half, squared, sqrt, cubed, cbrt
, times, over, over_
, per, at, at_, for, inverse
, modBy, fractionalModBy, remainderBy, fractionalRemainderBy
, ratio, clamp, interpolateFrom, midpoint, range, in_
, round, floor, ceiling, truncate, toFloatQuantity
, sum, minimum, maximum, minimumBy, maximumBy, sort, sortBy
Expand Down Expand Up @@ -50,6 +51,27 @@ and work with composite units in a fairly flexible way.
@docs per, at, at_, for, inverse
## Modular arithmetic
`modBy` and `remainderBy` behave just like the [`modBy`](https://package.elm-lang.org/packages/elm/core/latest/Basics#modBy)
and [`remainderBy`](https://package.elm-lang.org/packages/elm/core/latest/Basics#remainderBy)
functions from Elm's built-in `Basics` module, but work on `Quantity` values
instead of raw `Int`s. `fractionalModBy` and `fractionalRemainderBy` have the
same behaviour but extended to `Float`-valued quantities.
import Pixels exposing (pixels)
import Length exposing (meters, centimeters)
Quantity.modBy (pixels 4) (pixels 11)
--> pixels 3
Quantity.fractionalModBy (meters 0.5)
(centimeters 162.3)
--> centimeters 12.3
@docs modBy, fractionalModBy, remainderBy, fractionalRemainderBy
## Miscellaneous
@docs ratio, clamp, interpolateFrom, midpoint, range, in_
Expand Down Expand Up @@ -646,6 +668,30 @@ cbrt (Quantity value) =
Quantity -(-value ^ (1 / 3))


{-| -}
modBy : Quantity Int units -> Quantity Int units -> Quantity Int units
modBy (Quantity modulus) (Quantity value) =
Quantity (Basics.modBy modulus value)


{-| -}
fractionalModBy : Quantity Float units -> Quantity Float units -> Quantity Float units
fractionalModBy (Quantity modulus) (Quantity value) =
Quantity (value - modulus * Basics.toFloat (Basics.floor (value / modulus)))


{-| -}
remainderBy : Quantity Int units -> Quantity Int units -> Quantity Int units
remainderBy (Quantity modulus) (Quantity value) =
Quantity (Basics.remainderBy modulus value)


{-| -}
fractionalRemainderBy : Quantity Float units -> Quantity Float units -> Quantity Float units
fractionalRemainderBy (Quantity modulus) (Quantity value) =
Quantity (value - modulus * Basics.toFloat (Basics.truncate (value / modulus)))


{-| Interpolate from the first quantity to the second, based on a parameter that
ranges from zero to one. Passing a parameter value of zero will return the start
value and passing a parameter value of one will return the end value.
Expand Down
48 changes: 48 additions & 0 deletions tests/Tests.elm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module Tests exposing
, conversionsToQuantityAndBack
, densities
, durations
, fractionalModBy
, fractionalRemainderBy
, fromDmsNegative
, fromDmsPositive
, illuminances
Expand Down Expand Up @@ -54,6 +56,7 @@ import Pixels exposing (..)
import Power exposing (..)
import Pressure exposing (..)
import Quantity exposing (Quantity(..), at, at_, minus, per, plus, times)
import Random
import Resistance exposing (..)
import SolidAngle
import Speed exposing (..)
Expand Down Expand Up @@ -883,3 +886,48 @@ timeOffset =
|> Expect.within (Expect.Absolute 1) (Duration.inMilliseconds offset)
)
]


modulusFuzzer : Fuzzer Int
modulusFuzzer =
Fuzz.oneOf
[ Fuzz.intRange 1 Random.maxInt
, Fuzz.intRange (Random.minInt + 1) -1
]


valueFuzzer : Fuzzer Int
valueFuzzer =
Fuzz.intRange (Random.minInt + 1) Random.maxInt


fractionalModBy : Test
fractionalModBy =
Test.fuzz2
modulusFuzzer
valueFuzzer
"fractionalModBy"
(\modulus value ->
let
(Quantity result) =
Quantity.fractionalModBy (Quantity (toFloat modulus))
(Quantity (toFloat value))
in
result |> Expect.within (Expect.Absolute 0.0) (toFloat (modBy modulus value))
)


fractionalRemainderBy : Test
fractionalRemainderBy =
Test.fuzz2
modulusFuzzer
valueFuzzer
"fractionalRemainderBy"
(\modulus value ->
let
(Quantity result) =
Quantity.fractionalRemainderBy (Quantity (toFloat modulus))
(Quantity (toFloat value))
in
result |> Expect.within (Expect.Absolute 0.0) (toFloat (remainderBy modulus value))
)

0 comments on commit a7a2d31

Please sign in to comment.