MartinSStewart / elm-safe-int / SafeInt

A safe 54-bit signed integer for use cases where normal Int isn't sufficient and 54-bit range will suffice.


type SafeInt

A safe 54-bit signed integer.

A SafeInt is either defined integer value from minValue to maxValue, or undefined.

Constants

minValue : SafeInt

Minimum possible defined value, - (2^53 - 1) = - 9 007 199 254 740 991.

Equal to Number.MIN_SAFE_INTEGER in JavaScript.

SafeInt.minValue
    |> SafeInt.toInt
    --> Just -9007199254740991

maxValue : SafeInt

Maximum possible defined value, 2^53 - 1 = 9 007 199 254 740 991.

Equal to Number.MAX_SAFE_INTEGER in JavaScript.

SafeInt.maxValue
    |> SafeInt.toInt
    --> Just 9007199254740991

undefined : SafeInt

Undefined value.

Functions return undefined when result can't be represented using an integer between minValue and maxValue. For example division by zero or when result is above maxValue.

-- `1 / 0` is undefined
SafeInt.div SafeInt.one SafeInt.zero
    |> SafeInt.toInt
    --> Nothing

-- `2 ^ 55` is undefined
SafeInt.pow SafeInt.two (SafeInt.new 55)
    |> SafeInt.toInt
    --> Nothing

Operators == and /= consider undefined to be equal to itself and unequal to any defined value, so to find out whether SafeInt is undefined or not, you can just compare it to undefined.

SafeInt.div SafeInt.one SafeInt.zero
    == SafeInt.undefined
    --> True

SafeInt.div SafeInt.one SafeInt.one
    == SafeInt.undefined
    --> False

zero : SafeInt

Number 0

one : SafeInt

Number 1

two : SafeInt

Number 2

Conversion from/to Int

new : Basics.Int -> SafeInt

Same as fromInt.

fromInt : Basics.Int -> SafeInt

Convert Int to SafeInt, rounding towards zero.

Return undefined if argument is NaN, below minValue or above maxValue.

Note: Strange cases where argument is NaN, -Infinity, +Infinity or non-integer like 1234.5 are supported.

toInt : SafeInt -> Maybe Basics.Int

Convert SafeInt to Maybe Int.

Return Just value if SafeInt is defined, and Nothing if SafeInt is undefined.

Conversion from/to Float

Comparison of Float to SafeInt conversion functions:

          -3.8 -3.5 -3.2   ~    3.2  3.5  3.8
          ---- ---- ----       ---- ---- ----
round     -4   -3   -3          3    4    4
ceiling   -3   -3   -3          4    4    4
truncate  -3   -3   -3          3    3    3
floor     -4   -4   -4          3    3    3

round : Basics.Float -> SafeInt

Convert Float to SafeInt, rounding to nearest integer and half towards positive infinity.

Return undefined if argument is NaN, below minValue or above maxValue.

[ 3.2, 3.5, 3.8 ]
    |> List.map (SafeInt.round >> SafeInt.toInt)
    --> [ Just 3, Just 4, Just 4 ]

[ -3.8, -3.5, -3.2 ]
    |> List.map (SafeInt.round >> SafeInt.toInt)
    --> [ Just -4, Just -3, Just -3 ]

ceiling : Basics.Float -> SafeInt

Convert Float to SafeInt, rounding towards positive infinity.

Return undefined if argument is NaN, below minValue or above maxValue.

SafeInt.ceiling 3.8
    |> SafeInt.toInt
    --> Just 4

SafeInt.ceiling -3.8
    |> SafeInt.toInt
    --> Just -3

truncate : Basics.Float -> SafeInt

Convert Float to SafeInt, rounding towards zero.

Return undefined if argument is NaN, below minValue or above maxValue.

SafeInt.truncate 3.8
    |> SafeInt.toInt
    --> Just 3

SafeInt.truncate -3.8
    |> SafeInt.toInt
    --> Just -3

floor : Basics.Float -> SafeInt

Convert Float to SafeInt, rounding towards negative infinity.

Return undefined if argument is NaN, below minValue or above maxValue.

SafeInt.floor 3.8
    |> SafeInt.toInt
    --> Just 3

SafeInt.floor -3.8
    |> SafeInt.toInt
    --> Just -4

toFloat : SafeInt -> Maybe Basics.Float

Convert SafeInt to Maybe Float.

Return Just value if SafeInt is defined, and Nothing if SafeInt is undefined.

Math

add : SafeInt -> SafeInt -> SafeInt

Addition.

Return undefined if

Example

-- `123 + 456`
SafeInt.add (SafeInt.new 123) (SafeInt.new 456)
    |> SafeInt.toInt
    --> Just 579

sub : SafeInt -> SafeInt -> SafeInt

Subtraction.

Return undefined if

Example

-- `456 - 123`
SafeInt.sub (SafeInt.new 456) (SafeInt.new 123)
    |> SafeInt.toInt
    --> Just 333

mul : SafeInt -> SafeInt -> SafeInt

Multiplication.

Return undefined if

Example

-- `123 * 456`
SafeInt.mul (SafeInt.new 123) (SafeInt.new 456)
    |> SafeInt.toInt
    --> Just 56088

pow : SafeInt -> SafeInt -> SafeInt

Power aka exponentiation, rounding towards zero.

Return undefined if

The table below shows the return values of pow a b near zero. U denotes undefined and *0 denotes non-integer result rounded towards zero.

  b: -2 -1  0  1  2
 a   -- -- -- -- --
--
-2   *0 *0  1 -2  4
-1    1 -1  1 -1  1
 0    U  U  U  0  0
 1    1  1  1  1  1
 2   *0 *0  1  2  4

Example

-- `2 ^ 40`
SafeInt.pow SafeInt.two (SafeInt.new 40)
    |> SafeInt.toInt
    --> Just 1099511627776

Note: Opinions differ on what the result of 0 ^ 0, NaN ^ 0 and 1 ^ NaN should be. SafeInt takes the stance to return undefined when uncertain, so all of these (using undefined instead of NaN) return undefined. For more information see e.g. Zero to the power of zero๐Ÿข… and NaN ยง Function definition๐Ÿข….

Division Basics

SafeInt has four basic division functions: div, mod, quotient and remainder.

Both div and quotient calculate integer division, the difference is in rounding: div rounds the result towards negative infinity while quotient rounds towards zero.

The // operator is similar to quotient.

-- -10 divided by 3 is -3.333..., rounded to -4
SafeInt.div (SafeInt.new -10) (SafeInt.new 3)
    |> SafeInt.toInt
    --> Just -4

-- -10 divided by 3 is -3.333..., rounded to -3
SafeInt.quotient (SafeInt.new -10) (SafeInt.new 3)
    |> SafeInt.toInt
    --> Just -3

Likewise both mod and remainder calculate remainder after integer division: mod calculates remainder after div and remainder calculates remainder after quotient.

-- -10 divided by 3 is -3.333..., rounded to -4
-- then remainder is -10 - (3 * -4) = 2
SafeInt.mod (SafeInt.new -10) (SafeInt.new 3)
    |> SafeInt.toInt
    --> Just 2

-- -10 divided by 3 is -3.333..., rounded to -3
-- then remainder is -10 - (3 * -3) = -1
SafeInt.remainder (SafeInt.new -10) (SafeInt.new 3)
    |> SafeInt.toInt
    --> Just -1

Division compared

The table below shows a comparison of div, mod, quotient and remainder, with dividend from -7 to 7 and divisor either 3 or -3.


dividend:   -7 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7
            -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
divisor = 3
div         -3 -2 -2 -2 -1 -1 -1  0  0  0  1  1  1  2  2
mod          2  0  1  2  0  1  2  0  1  2  0  1  2  0  1

quotient    -2 -2 -1 -1 -1  0  0  0  0  0  1  1  1  2  2
remainder   -1  0 -2 -1  0 -2 -1  0  1  2  0  1  2  0  1

divisor = -3
div          2  2  1  1  1  0  0  0 -1 -1 -1 -2 -2 -2 -3
mod         -1  0 -2 -1  0 -2 -1  0 -2 -1  0 -2 -1  0 -2

quotient     2  2  1  1  1  0  0  0  0  0 -1 -1 -1 -2 -2
remainder   -1  0 -2 -1  0 -2 -1  0  1  2  0  1  2  0  1

Division undefined

All division functions return undefined if divisor is 0 or either argument is undefined.

-- `1 / 0` is undefined
SafeInt.div SafeInt.one SafeInt.zero
    |> SafeInt.toInt
    --> Nothing

div : SafeInt -> SafeInt -> SafeInt

Integer division, rounding towards negative infinity.

See Division Basics for more information about division functions.

SafeInt.div (SafeInt.new 1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just 12

SafeInt.div (SafeInt.new -1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just -13

mod : SafeInt -> SafeInt -> SafeInt

Remainder after div. This is also used for modular arithmetic๐Ÿข….

See Division Basics for more information about division functions.

SafeInt.mod (SafeInt.new 1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just 34

SafeInt.mod (SafeInt.new -1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just 66

quotient : SafeInt -> SafeInt -> SafeInt

Integer division, rounding towards zero.

This is similar to // operator.

See Division Basics for more information about division functions.

SafeInt.quotient (SafeInt.new 1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just 12

SafeInt.quotient (SafeInt.new -1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just -12

remainder : SafeInt -> SafeInt -> SafeInt

Remainder after quotient.

See Division Basics for more information about division functions.

SafeInt.remainder (SafeInt.new 1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just 34

SafeInt.remainder (SafeInt.new -1234) (SafeInt.new 100)
    |> SafeInt.toInt
    --> Just -34

Division Reversed

Functions divBy, modBy, quotientBy and remainderBy are same as basic division functions, except with reversed arguments.

modBy is similar to Basics.modBy and remainderBy to Basics.remainderBy.

-- `2^40 / 10`
SafeInt.pow SafeInt.two (SafeInt.new 40)
    |> SafeInt.divBy (SafeInt.new 10)
    |> SafeInt.toInt
    --> Just 109951162777

divBy : SafeInt -> SafeInt -> SafeInt

Same as div except with reversed arguments.

modBy : SafeInt -> SafeInt -> SafeInt

Same as mod except with reversed arguments.

This is similar to Basics.modBy.

quotientBy : SafeInt -> SafeInt -> SafeInt

Same as quotient except with reversed arguments.

remainderBy : SafeInt -> SafeInt -> SafeInt

Same as remainder except with reversed arguments.

This is similar to Basics.remainderBy.

Division Combined

divMod : SafeInt -> SafeInt -> ( SafeInt, SafeInt )

Combines div and mod into a single function.

divMod a b is same as ( div a b, mod a b ) except faster.

SafeInt.divMod (SafeInt.new 1234) (SafeInt.new 100)
    |> Tuple.mapBoth SafeInt.toInt SafeInt.toInt
    --> ( Just 12, Just 34 )

SafeInt.divMod (SafeInt.new -1234) (SafeInt.new 100)
    |> Tuple.mapBoth SafeInt.toInt SafeInt.toInt
    --> ( Just -13, Just 66 )

quotRem : SafeInt -> SafeInt -> ( SafeInt, SafeInt )

Combines quotient and remainder into a single function.

quotRem a b is same as ( quotient a b, remainder a b ) except faster.

SafeInt.quotRem (SafeInt.new 1234) (SafeInt.new 100)
    |> Tuple.mapBoth SafeInt.toInt SafeInt.toInt
    --> ( Just 12, Just 34 )

SafeInt.quotRem (SafeInt.new -1234) (SafeInt.new 100)
    |> Tuple.mapBoth SafeInt.toInt SafeInt.toInt
    --> ( Just -12, Just -34 )

divModBy : SafeInt -> SafeInt -> ( SafeInt, SafeInt )

Same as divMod except with reversed arguments.

quotRemBy : SafeInt -> SafeInt -> ( SafeInt, SafeInt )

Same as quotRem except with reversed arguments.

Comparison

Operators == and /= consider undefined to be equal to itself and unequal to any defined value.

compare : Basics.Bool -> SafeInt -> SafeInt -> Basics.Order

Compare two SafeInt:s.

First argument defines how undefined is handled:

Example

[ SafeInt.new 34
, SafeInt.new 12
, SafeInt.undefined
, SafeInt.new 56
, SafeInt.undefined
]
    |> List.sortWith (SafeInt.compare True)
    |> List.map SafeInt.toInt
    --> [ Nothing, Nothing, Just 12, Just 34, Just 56 ]

Signs

abs : SafeInt -> SafeInt

Absolute value.

Return undefined if argument is undefined.

SafeInt.abs (SafeInt.new 123)
    |> SafeInt.toInt
    --> Just 123

SafeInt.abs (SafeInt.new -123)
    |> SafeInt.toInt
    --> Just 123

negate : SafeInt -> SafeInt

Negation.

Return undefined if argument is undefined.

SafeInt.negate (SafeInt.new 123)
    |> SafeInt.toInt
    --> Just -123

SafeInt.negate (SafeInt.new -123)
    |> SafeInt.toInt
    --> Just 123

sign : SafeInt -> SafeInt

Sign.

Examples

SafeInt.sign (SafeInt.new 123)
    |> SafeInt.toInt
    --> Just 1

SafeInt.sign (SafeInt.new -123)
    |> SafeInt.toInt
    --> Just -1