MartinSStewart/elm-uint64/1.1.0

NOTE: the latest version is 2.0.0

UInt64

type UInt64

64-bit unsigned integer.

UInt64 is represented internally as three unsigned integers:

  • high: 16-bit unsigned integer for bits 48 - 63
  • mid: 24-bit unsigned integer for bits 24 - 47
  • low: 24-bit unsigned integer for bits 0 - 23

Constants

Minimum possible UInt64 value.

Same as zero.

UInt64.minValue
    |> UInt64.toString
    --> "0"

Maximum possible UInt64 value.

2^64 - 1 = 18446744073709551615 = 0xFFFFFFFFFFFFFFFF

UInt64.maxValue
    |> UInt64.toString
    --> "18446744073709551615"

Maximum safe integer as UInt64.

2^53 - 1 = 9007199254740991 = 0x001FFFFFFFFFFFFF

Equal to Number.MAX_SAFE_INTEGER in JavaScript.

See also isSafe.

maxSafe as Float

Maximum UInt64 value that can be represented exactly as a Float.

2^64 - 2048 = 18446744073709549568 = 0xFFFFFFFFFFFFF800

Note: Float can't represent exactly all integers above maxSafe. For example integers maxFloat+ 1 <= x <=maxValue can't be represented exactly as Float, making this the maximum UInt64 value that can.

maxFloat as Float

Number 0

Number 1

Number 2

Argument handling

Every Int or Float argument of every UInt64 function is limited by one of the following three functions (or equivalent code):

  • If argument is Int and valid value is 0 <= x < 2^n, n <= 32
  • If argument is Int and valid value can be 2^32 or above
  • If argument is Float and valid value is 0 <= x <= max

Non-integer Int

Int arguments are expected to be integers and are not checked for non-integer values like NaN, -Infinity, +Infinity or 12.34.

Behavior of any UInt64 function is undefined if Int argument is not an integer.

Large Int

Behavior of Int for values above 2^31 - 1 is undefined in Elm. As of Elm 0.19.1, using Int as function parameter or return value works for full safe integer range up to maxSafe, but this could change in the future.

Note: This affects also limitSmallInt with n = 32.

limitSmallInt : Int -> Int -> Int

Limit Int to 0 <= value < 2 ^ bitSize.

  • bitSize: 1 <= bitSize <= 32
  • value: value to limit

See argument handling.

Algorithm:

  1. If value is negative, convert it to positive by two's complement.
  2. Apply unsigned bitwise AND with bitmask 2 ^ bitSize - 1.

Examples

-- `1234` limited to 6 bits, i.e. `0 <= x <= 63`
UInt64.limitSmallInt 6 1234
    --> 18


-- `-1` limited to 8 bits, i.e. `0 <= x <= 255`
UInt64.limitSmallInt 8 -1
    --> 0xFF


-- `-1` limited to 32 bits
UInt64.limitSmallInt 32 -1
    --> 0xFFFFFFFF
limitLargeInt : Int -> Int

Limit Int to 0 <= value <=maxSafe.

See argument handling.

Algorithm:

  • If value is negative, use 0 instead.
  • If value is above maxSafe, use maxSafe instead.

Examples

-- negative value is replaced with zero
UInt64.limitLargeInt -1
    --> 0


-- value above `maxSafe` is replaced with `maxSafe`
UInt64.limitLargeInt 9007199254740992
    --> 9007199254740991
limitFloat : Float -> Float -> Float

Limit Float to 0 <= value <= max.

  • max: maximum allowed value, max <=maxFloat
  • value: value to limit

See argument handling.

Algorithm:

  • If value is NaN, use 0 instead.
  • If value is negative, use 0 instead.
  • If value is above max, use max instead.
  • If value is not an integer, round down to integer.

Examples

-- `-1` limited to 12 decimal digits
UInt64.limitFloat 999999999999 -1
    --> 0


-- `1e20` limited to 12 decimal digits
UInt64.limitFloat 999999999999 1e20
    --> 999999999999


-- `1e20` limited to `0 <= value <= maxFloat`
UInt64.limitFloat UInt64.maxFloatAsFloat 1.0e20
    --> 18446744073709549568.0

Conversion - Int

fromInt : Int -> UInt64

Convert Int to UInt64.

See argument handling.

UInt64.fromInt 123
    |> UInt64.toString
    --> "123"
toInt31 : UInt64 -> Maybe Int

Convert UInt64 to 31-bit unsigned integer.

If UInt64 is above 2^31 - 1, return Nothing.

UInt64.fromInt 0x7FFFFFFF
    |> UInt64.toInt31
    --> Just 0x7FFFFFFF


UInt64.fromInt 0x80000000
    |> UInt64.toInt31
    --> Nothing
toInt53 : UInt64 -> Maybe Int

Convert UInt64 to 53-bit unsigned integer.

If UInt64 is above maxSafe, return Nothing.

See large Int note.

UInt64.maxSafe
    |> UInt64.toInt53
    --> Just 9007199254740991


UInt64.maxSafe
    |> UInt64.increment
    |> UInt64.toInt53
    --> Nothing

Conversion - Float

floor : Float -> UInt64

Convert Float to UInt64, rounding down.

*) If value is above maxValue, return maxValue.

See argument handling.

Values above maxSafe

Conversion is exact for all possible Float integer values from 0 to maxFloat. However because Float can't represent all integers above maxSafe, it can sometimes seem like there is an error.

In the following example value 11222333444555666777.0 can't be represented exactly as a Float. Nearest value that can be represented exactly as a Float is 11222333444555667456.0, and that is what UInt64.floor gets as its argument. This argument is then converted exactly to UInt64.

UInt64.floor 11222333444555666777.0
    |> UInt64.toString
    --> "11222333444555667456"

fromDecimal12s can be used instead to convert decimal literals above maxSafe to UInt64:

UInt64.fromDecimal12s 11222333 444555666777
    |> UInt64.toString
    --> "11222333444555666777"
toFloat : UInt64 -> Float

Convert UInt64 to Float.

Conversion is exact for any value from 0 to maxSafe, but above maxSafe value is rounded if it can't be represented exactly as Float.

Example

9007199254740993 can't be represented exactly as Float, so it's rounded to 9007199254740992.

UInt64.fromDecimal12s 9007 199254740993
    |> UInt64.toFloat
    --> 9007199254740992

Conversion - Parts

fromInt32s : Int -> Int -> UInt64

Convert 64-bit unsigned integer represented as two 32-bit unsigned integers to UInt64.

  • high: 32-bit unsigned integer for bits 32 - 63
  • low: 32-bit unsigned integer for bits 0 - 31

See argument handling.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.toHexString
    --> "11223344AABBCCDD"


UInt64.fromInt32s 1 2
    |> UInt64.toHexString
    --> "0000000100000002"
toInt32s : UInt64 -> ( Int, Int )

Convert UInt64 to 64-bit unsigned integer represented as two 32-bit unsigned integers.

  • high: 32-bit unsigned integer for bits 32 - 63
  • low: 32-bit unsigned integer for bits 0 - 31

See large Int note.

UInt64.floor 1e15
    |> UInt64.toInt32s
    --> ( 0x00038D7E, 0xA4C68000 )
fromInt24s : Int -> Int -> Int -> UInt64

Convert 64-bit unsigned integer represented as three 24-bit unsigned integers to UInt64.

This is the internal format of UInt64 and so fromInt24s is the fastest way to create UInt64 value.

  • high: 16-bit unsigned integer for bits 48 - 63
  • mid: 24-bit unsigned integer for bits 24 - 47
  • low: 24-bit unsigned integer for bits 0 - 23

See argument handling.

UInt64.fromInt24s 0x1122 0x334455 0x667788
    |> UInt64.toHexString
    --> "1122334455667788"


UInt64.fromInt24s 1 2 3
    |> UInt64.toHexString
    --> "0001000002000003"
toInt24s : UInt64 -> ( Int, Int, Int )

Convert UInt64 to 64-bit unsigned integer represented as three 24-bit unsigned integers.

This is the internal format of UInt64 and so toInt24s is the fastest way to extract value out of UInt64.

  • high: 16-bit unsigned integer for bits 48 - 63
  • mid: 24-bit unsigned integer for bits 24 - 47
  • low: 24-bit unsigned integer for bits 0 - 23

Example

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.toInt24s
    --> ( 0x1122, 0x3344AA, 0xBBCCDD )
fromDecimal12s : Float -> Float -> UInt64

Convert 64-bit unsigned integer represented as two 12-decimal-digit unsigned integers to UInt64.

  • high: 8 highest digits, 0 <= high <= 18446745*
  • low: 12 lowest digits, 0 <= low <= 999999999999

*) If high is 18446745, return maxValue.

See argument handling.

UInt64.fromDecimal12s 111222 333444555666
    |> UInt64.toString
    --> "111222333444555666"


UInt64.fromDecimal12s 1 2
    |> UInt64.toString
    --> "1000000000002"

Convert list of bytes in big-endian order to UInt64.

  • each byte: 0 <= x <= 255
  • If list has less than 8 bytes, highest bytes will be zeroes.
  • If list has more than 8 bytes, highest extra bytes are ignored.

See argument handling.

UInt64.fromBigEndianBytes [ 0xAB, 0, 0xCD ]
    |> UInt64.toHexString
    --> "0000000000AB00CD"


List.range 0x01 0x0F
    |> UInt64.fromBigEndianBytes
    |> UInt64.toHexString
    --> "08090A0B0C0D0E0F"
toBigEndianBytes : UInt64 -> List Int

Convert UInt64 to list of 8 bytes in big-endian order.

UInt64.fromInt 0xABCDEF
    |> UInt64.toBigEndianBytes
    --> [ 0, 0, 0, 0, 0, 0xAB, 0xCD, 0xEF ]

Conversion - String

fromString : String -> Maybe UInt64

Convert String to UInt64.

String can be

  • decimal String of digits 0123456789
  • hexadecimal String with prefix 0x and digits 0123456789ABCDEFabcdef
  • octal String with prefix 0o and digits 01234567
  • binary String with prefix 0b and digits 01

Return Nothing if String isn't valid for any of the above formats, or if the value would be above maxValue.

Behavior is undefined if String contains invalid Unicode. Such String:s can't be fuzz-tested with elm-test currently, so I can't make this function robust against invalid Unicode.

UInt64.fromString "12345"
    |> Maybe.andThen UInt64.toInt31
    --> Just 12345


UInt64.fromString "0x11223344AABBCCDD"
    |> Maybe.map UInt64.toInt32s
    --> Just ( 0x11223344, 0xAABBCCDD )


UInt64.fromString "0o777"
    |> Maybe.map UInt64.toHexString
    --> Just "00000000000001FF"


UInt64.fromString "0b1111000011110000"
    |> Maybe.map UInt64.toHexString
    --> Just "000000000000F0F0"


-- `e` is not valid without `0x` prefix
UInt64.fromString "1e10"
    --> Nothing


-- value would be above `maxValue`
UInt64.fromString "111222333444555666777"
    --> Nothing
toString : UInt64 -> String

Convert UInt64 to decimal String.

UInt64.fromInt 0xFFFFFF
    |> UInt64.toString
    --> "16777215"
toHexString : UInt64 -> String

Convert UInt64 to uppercase hexadecimal String of 16 characters.

UInt64.floor 1e15
    |> UInt64.toHexString
    --> "00038D7EA4C68000"


UInt64.zero
    |> UInt64.toHexString
    --> "0000000000000000"

Math

Addition with wrapping overflow.

-- `123 + 456`
UInt64.add (UInt64.fromInt 123) (UInt64.fromInt 456)
    |> UInt64.toString
    --> "579"


-- `maxValue + 100`
UInt64.add UInt64.maxValue (UInt64.fromInt 100)
    |> UInt64.toString
    --> "99"

Subtraction with wrapping overflow.

-- `456 - 123`
UInt64.sub (UInt64.fromInt 456) (UInt64.fromInt 123)
    |> UInt64.toString
    --> "333"


-- `0 - 0xFF`
UInt64.sub UInt64.zero (UInt64.fromInt 0xFF)
    |> UInt64.toHexString
    --> "FFFFFFFFFFFFFF01"

Multiplication with wrapping overflow.

-- `1e9 * 1e9`
UInt64.mul (UInt64.floor 1e9) (UInt64.floor 1e9)
    |> UInt64.toString
    --> "1000000000000000000"


-- `(1e10 * 1e10) % 2^64`
UInt64.mul (UInt64.floor 1e10) (UInt64.floor 1e10)
    |> UInt64.toString
    --> "7766279631452241920"

Power aka exponentiation. 0 ^ 0 = 1

-- `3 ^ 7`
UInt64.pow (UInt64.fromInt 3) (UInt64.fromInt 7)
    |> UInt64.toString
    --> "2187"


-- `10 ^ 19`
UInt64.pow (UInt64.fromInt 10) (UInt64.fromInt 19)
    |> UInt64.toString
    --> "10000000000000000000"


-- `(3 ^ 10000000000000000000) % 2^64`
UInt64.pow (UInt64.fromInt 10) (UInt64.fromInt 19)
    |> UInt64.pow (UInt64.fromInt 3)
    |> UInt64.toString
    --> "12038004833498693633"

Note: Uses fast algorithms: Bases 0-2 are special-cased, exponents 0-16 use addition-chain exponentiation🢅 and exponents over 16 use exponentiation by squaring🢅.

Increment by one with wrapping overflow.

-- `42 + 1`
UInt64.increment (UInt64.fromInt 42)
    |> UInt64.toString
    --> "43"


-- `maxValue + 1`
UInt64.increment UInt64.maxValue
    |> UInt64.toString
    --> "0"

Decrement by one with wrapping overflow.

-- `42 - 1`
UInt64.decrement (UInt64.fromInt 42)
    |> UInt64.toString
    --> "41"


-- `0 - 1`
UInt64.decrement UInt64.zero
    |> UInt64.toHexString
    --> "FFFFFFFFFFFFFFFF"

Squaring with wrapping overflow.

square a is same as mul a a or pow a two but faster.

-- `1e9 * 1e9`
UInt64.square (UInt64.floor 1e9)
    |> UInt64.toString
    --> "1000000000000000000"

Division

Note: I would prefer to cause runtime exception on division-by-zero, but that can't be tested, so I'll settle for returning zero which can be tested.

Integer division.

Examples

UInt64.div (UInt64.fromInt 1234) (UInt64.fromInt 100)
    |> UInt64.toFloat
    --> 12


-- 0xFFFFFFFFFFFFFFFF / 1e10
UInt64.div UInt64.maxValue (UInt64.floor 1e10)
    |> UInt64.toFloat
    --> 1844674407

Remainder after div.

Examples

UInt64.mod (UInt64.fromInt 1234) (UInt64.fromInt 100)
    |> UInt64.toFloat
    --> 34


-- 0xFFFFFFFFFFFFFFFF % 1e10
UInt64.mod UInt64.maxValue (UInt64.floor 1e10)
    |> UInt64.toFloat
    --> 3709551615

Integer division with modulo.

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

  • If divisor is zero, return ( zero, zero ).

Examples

UInt64.divMod (UInt64.fromInt 1234) (UInt64.fromInt 100)
    |> Tuple.mapBoth UInt64.toFloat UInt64.toFloat
    --> ( 12, 34 )


-- ( 0xFFFFFFFFFFFFFFFF / 1e10, 0xFFFFFFFFFFFFFFFF % 1e10 )
UInt64.divMod UInt64.maxValue (UInt64.floor 1e10)
    |> Tuple.mapBoth UInt64.toFloat UInt64.toFloat
    --> ( 1844674407, 3709551615 )

Bitwise

Bitwise AND.

UInt64.and
    (UInt64.fromInt32s 0x11223344 0xAABBCCDD)
    (UInt64.fromInt32s 0x0000FFFF 0xFFFF0000)
    |> UInt64.toHexString
    --> "00003344AABB0000"

Bitwise OR.

UInt64.or
    (UInt64.fromInt32s 0x11223344 0xAABBCCDD)
    (UInt64.fromInt32s 0x0000FFFF 0xFFFF0000)
    |> UInt64.toHexString
    --> "1122FFFFFFFFCCDD"

Bitwise XOR.

UInt64.xor
    (UInt64.fromInt32s 0x11223344 0xAABBCCDD)
    (UInt64.fromInt32s 0x0000FFFF 0xFFFF0000)
    |> UInt64.toHexString
    --> "1122CCBB5544CCDD"

Bitwise complement, aka bitwise NOT, aka one's complement.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.complement
    |> UInt64.toHexString
    --> "EEDDCCBB55443322"

Bitwise shift left, filling with zeroes from right.

  • shift: 0 <= x <= 63

See argument handling.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.shiftLeftBy 20
    |> UInt64.toHexString
    --> "344AABBCCDD00000"

Bitwise shift right, filling with zeroes from left.

  • shift: 0 <= x <= 63

See argument handling.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.shiftRightZfBy 20
    |> UInt64.toHexString
    --> "0000011223344AAB"

Bitwise rotate left.

  • shift: 0 <= x <= 63

See argument handling.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.rotateLeftBy 20
    |> UInt64.toHexString
    --> "344AABBCCDD11223"

Bitwise rotate right.

  • shift: 0 <= x <= 63

See argument handling.

UInt64.fromInt32s 0x11223344 0xAABBCCDD
    |> UInt64.rotateRightBy 20
    |> UInt64.toHexString
    --> "BCCDD11223344AAB"

Bitwise shift right by one bit, filling with zero from left.

shiftRightZfBy1 a is same as shiftRightZfBy 1 a but faster.

UInt64.fromInt32s 0xEECCAA88 0x66442200
    |> UInt64.shiftRightZfBy1
    |> UInt64.toHexString
    --> "7766554433221100"
getBit : Int -> UInt64 -> Int

Return a bit.

  • bitNumber: 0 <= x <= 63, least significant bit is 0

See argument handling.

UInt64.one
    |> UInt64.getBit 0
    --> 1
setBit : Int -> Int -> UInt64 -> UInt64

Set a bit to given value.

  • bitNumber: 0 <= x <= 63, least significant bit is 0
  • bitValue: new value, either 0 or 1

See argument handling.

UInt64.zero
    |> UInt64.setBit 30 1
    |> UInt64.toHexString
    --> "0000000040000000"

Comparison

compare : UInt64 -> UInt64 -> Order

Compare two UInt64:s.

UInt64.compare UInt64.zero UInt64.one
    --> LT

Check

isSafe : UInt64 -> Bool

Return True if argument is safe integer.

A safe integer is an integer that

  • can be represented exacly as Float and
  • no other integer is rounded to as Float.

Unsigned integers from 0 to maxSafe are safe integers.

Example

For example 2^53 is not a safe integer. While it can be represented exactly as Float, there exists another integer 2^53 + 1 which is rounded to 2^53:

-- 2^53 + 1 (9007199254740993) is rounded to
-- 2^53     (9007199254740992) when converted to Float
UInt64.fromDecimal12s 9007 199254740993
    |> UInt64.toFloat
    --> 9007199254740992


UInt64.fromDecimal12s 9007 199254740993
    |> UInt64.isSafe
    --> False

This happens because 2^53 + 1 can't be represented exactly as Float, so it is rounded to another integer.

isZero : UInt64 -> Bool

Return True if argument is zero.

This is same as (==)zero but much faster.

Note: See Performance Optimization🢅 for discussion about speed of == in Elm 0.19.1.

isEven : UInt64 -> Bool

Return True if argument is even.

isOdd : UInt64 -> Bool

Return True if argument is odd.

Extra

Extra functions which can be useful for some use cases, e.g. benchmarking and testing.

These are not intended to be useful generally.

divModFast : UInt64 -> UInt64 -> Result String ( UInt64, UInt64 )

Fast but complex algorithm for integer division with modulo.

  • If divisor is zero, return Ok ( zero, zero ).
  • If impossible happens, return Err error.

You should usually use divMod instead because it will use even faster algorithms when dividend < 2^53 or divisor < 2^29, and will fall back to divModFast otherwise.

So divModFast is faster than divMod only when dividend >= 2^53 && divisor >= 2^29.

Simple but slow long division🢅 algorithm for integer division with modulo.

  • If divisor is zero, return ( zero, zero ).

Intended use cases:

  • Benchmarking against other algorithms.
  • Used as fallback algorithm with divModFast, in case it returns Err. Which should never happen.