lue-bird/elm-bounded-nat/21.1.0

NOTE: the latest version is 35.0.0

N

Natural numbers within a typed range.

type alias N range =
N range

A bounded natural number >= 0

argument type

-- ≥ 0, else any limitations allowed
N range_


-- ≥ 4
N (In (Add4 minMinus4_) max_ difference_)


-- 4 ≤ n ≤ 15
N (In (Add4 minMinus4_) N15 difference_)

In (Add4 minMinus4_) N15 says:

  • the minimum-constraint can be 4|5|6|...
  • any maximum-constraint greater than 15 is forbidden
  • specific numbers like n0, n1, ... which carry difference_s are allowed

result type

-- ≥ 4
N (Min N4)


-- 2 ≤ n ≤ 12
N (In N2 (Add12 a_) {})

In N2 (Add12 a_) {} says:

  • the minimum-constraint can be 4|5|6|...
  • any maximum-constraint greater than 15 is forbidden
  • {}: no differences are attached. Only specific numbers like n0, n1, ... carry differencess

stored type

what to put in declared types like Model

-- ≥ 4
N (Min N4)


-- 2 ≤ n ≤ 12
N (In N2 N12 {})

They are like result types but without type variables.

specific number

Numbers n0, n1, ... supplied by this library.

The type Is (Diff x0 To nPlusX0) ... enables adding, subtracting N<x> types. Consider the type an implementation detail. You can come back to understand them later.

n3 :
    N
        (In
            N3
            (Add3 atLeast_)
            (Is
                (Diff x0 To (Add3 x0))
                (Diff x1 To (Add3 x1))
            )
        )


-- a number nTo15 away from 15
N (In n nAtLeast_ (Is (Diff nTo15 To N15) diff1_))

bounds

type alias In minimum maximum possibleRepresentationAsDifferences =
RecordWithoutConstructorFunction
{ min : minimum
, max : () -> maximum
, diff : possibleRepresentationAsDifferences
}

somewhere within a minimum & maximum

   ↓ minimum   ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...

In minimum maximum difference_

For arguments

percent : N (In min_ N100 difference_) -> Percent

→ min ≤ N100

If you want a number where you just care about the minimum, leave the max as a type variable.

   ↓ minimum    ↓ maximum or  →
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓...

Any natural number:

N (In min_ max_ difference_)

A number, at least 5:

N (In (Add5 minMinus5_) max_ difference_)
  • max_ could be a specific maximum or no maximum at all

  • difference_ could contain extra information if the argument is a specific number like n0, n1, ...

In minimum maximum {}

A number somewhere within a minimum & maximum. We don't know it exactly, though

   ↓ minimum   ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...

Do not use it as an argument type.

A number between 3 and 5

N (In N3 (Add5 a_) {})
type alias Min lowestPossibleValue =
In lowestPossibleValue NoMax {}

Only stored / result types should be Min.

Sometimes, you simply cannot compute a maximum.

abs : Int -> N (In N0 ?? {})
                ↓
abs : Int -> N (Min N0)

A number ≥ 5 for example:

N (Min N5)

Every Min min is of type In min ....

type alias NoMax =
NoMax

Flag "The number's maximum limit is unknown".

type alias Min min =
    In minimum NoMax {}

is the definition of Min.

An example where this is useful using typesafe-array:

type Tree childCountMax element
    = Tree
        element
        (Arr
            (In N0 childCountMax {})
            (Maybe (Tree childCountMax element))
        )


type alias TreeBinary element =
    Tree N2 element


type alias TreeMulti element =
    Tree NoMax element

Remember: ↑ and other Min/NoMax are result/stored types, not argument types. You can just use a variable Tree childCountMax_ element if you don't care about an enforced maximum.

type alias Exactly n =
In n n {}

Allow only a specific number.

Useful as a stored & argument type in combination with typesafe-arrays, not with Ns.

byte : Arr (Exactly N8) Bit -> Byte

→ A given Arr must have exactly 8 Bits.

type alias TicTacToeBoard =
    Arr
        (Exactly N3)
        (Arr (Exactly N3) TicTacToField)

→ A given Arr must have exactly 3 by 3 TicTacToeFields.

create

abs : Int -> N (Min N0)

The absolute value of an Int → ≥ 0

-4 |> N.abs |> N.toInt --> 4


16 |> N.abs |> N.toInt --> 16

Really only use this if you want the absolute value.

badLength =
    List.length >> N.abs
  • maybe, there's a solution that never even theoretically deals with unexpected values:

    mostCorrectLength =
        List.foldl
            (\_ -> N.minAdd n1 >> N.minDown n0)
            (n0 |> N.noMax)
  • other times, though, like with Array.length, which isn't O(n), you can escape with for example

    arrayLength =
        Array.length >> N.intAtLeast n0
( N (In lowerLimitMin upperLimitMin lowerLimitDifference_)
, N (In upperLimitMin upperLimitMax upperLimitDifference_)
)
-> Generator (N (In lowerLimitMin upperLimitMax {}))

Generate a random N in a range.

N.random ( n1, n10 )
--: Random.Generator (N (In N1 (Add10 a_) {}))
N (In min_ max difference_)
-> Emptiable (Stacked (N (In N0 max {}))) Never

Ns increasing from 0 to n In the end, there are n numbers.

N.until n6
    |> List.map (N.add n3)
    --: List (N (In N3 (Add9 a_)))
    |> List.map N.toInt
--> [ 3, 4, 5, 6, 7, 8, 9 ]


N.up atLeast7 |> List.map (N.add n3)
--: List (N (Min N10))

typesafe-array even knows the length! Try it.

up :
N (In min_ (Add1 maxMinus1) difference_)
-> List (N (In N0 maxMinus1 {}))

Ns increasing from 0 to n - 1. In the end, there are n numbers.

@deprecated in favor of until If you'd like to keep this → issue

N.up n7
    |> List.map (N.add n3)
    --: List (N (In N3 (Add9 a_)))
    |> List.map N.toInt
--> [ 3, 4, 5, 6, 7, 8, 9 ]

typesafe-array even knows the length! Try it.

specific numbers

If the package exposed every number 0 → 1000, tools can become unusably slow.

So only 0 → 16 are exposed, while larger numbers have to be generated locally.

Current method: generate them into a module exposing (n500, N500, Add500, ...) + import as N exposing (n500, ...)

In the future, elm-generate will allow auto-generating via elm-review.

type n

⏭ skip to last

type alias N0 =
N0able Never Possibly

Type for the exact natural number 0

type alias N1 =
N0able (N0able Never Possibly) Never

Type for the exact natural number 1

type alias N2 =
N0able (N0able (N0able Never Possibly) Never) Never

Type for the exact natural number 2

type alias N3 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never

Type for the exact natural number 3

type alias N4 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never

Type for the exact natural number 4

type alias N5 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never

Type for the exact natural number 5

type alias N6 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 6

type alias N7 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 7

type alias N8 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 8

type alias N9 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 9

type alias N10 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 10

type alias N11 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 11

type alias N12 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 12

type alias N13 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 13

type alias N14 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 14

type alias N15 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 15

type alias N16 =
(N0able (N0able (N0able Never Possibly) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the exact natural number 16

type N0able successor possiblyOrNever
= N0 possiblyOrNever
| Add1 successor

Base type of N0, Add1 n following allowable-state:

Is the parameter possiblyOrNever set to Never like in

type alias Add1 n =
    N0able n Never

N0 is impossible to construct.

Is the parameter possiblyOrNever set to Possibly like in

type alias N0 =
    N0able Never Possibly

N0 is a possible value.

type alias Add1 n =
N0able n Never

Type for the natural number 1 + some natural number n

type alias Add2 n =
N0able (N0able n Never) Never

Type for the natural number 2 + some natural number n

type alias Add3 n =
N0able (N0able (N0able n Never) Never) Never

Type for the natural number 3 + some natural number n

type alias Add4 n =
N0able (N0able (N0able (N0able n Never) Never) Never) Never

Type for the natural number 4 + some natural number n

type alias Add5 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never

Type for the natural number 5 + some natural number n

type alias Add6 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never

Type for the natural number 6 + some natural number n

type alias Add7 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never

Type for the natural number 7 + some natural number n

type alias Add8 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never

Type for the natural number 8 + some natural number n

type alias Add9 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 9 + some natural number n

type alias Add10 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 10 + some natural number n

type alias Add11 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 11 + some natural number n

type alias Add12 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 12 + some natural number n

type alias Add13 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 13 + some natural number n

type alias Add14 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 14 + some natural number n

type alias Add15 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 15 + some natural number n

type alias Add16 n =
(N0able (N0able (N0able (N0able n Never) Never) Never) Never)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never
)
Never

Type for the natural number 16 + some natural number n

n0 : N (In N0 atLeast_ (Is (Diff x0 To x0) (Diff x1 To x1)))

The exact natural number 0

n1 :
N
(In
N1
(Add1 atLeast_)
(Is (Diff x0 To (Add1 x0)) (Diff x1 To (Add1 x1)))
)

The exact natural number 1

n2 :
N
(In
N2
(Add2 atLeast_)
(Is (Diff x0 To (Add2 x0)) (Diff x1 To (Add2 x1)))
)

The exact natural number 2

n3 :
N
(In
N3
(Add3 atLeast_)
(Is (Diff x0 To (Add3 x0)) (Diff x1 To (Add3 x1)))
)

The exact natural number 3

n4 :
N
(In
N4
(Add4 atLeast_)
(Is (Diff x0 To (Add4 x0)) (Diff x1 To (Add4 x1)))
)

The exact natural number 4

n5 :
N
(In
N5
(Add5 atLeast_)
(Is (Diff x0 To (Add5 x0)) (Diff x1 To (Add5 x1)))
)

The exact natural number 5

n6 :
N
(In
N6
(Add6 atLeast_)
(Is (Diff x0 To (Add6 x0)) (Diff x1 To (Add6 x1)))
)

The exact natural number 6

n7 :
N
(In
N7
(Add7 atLeast_)
(Is (Diff x0 To (Add7 x0)) (Diff x1 To (Add7 x1)))
)

The exact natural number 7

n8 :
N
(In
N8
(Add8 atLeast_)
(Is (Diff x0 To (Add8 x0)) (Diff x1 To (Add8 x1)))
)

The exact natural number 8

n9 :
N
(In
N9
(Add9 atLeast_)
(Is (Diff x0 To (Add9 x0)) (Diff x1 To (Add9 x1)))
)

The exact natural number 9

n10 :
N
(In
N10
(Add10 atLeast_)
(Is (Diff x0 To (Add10 x0)) (Diff x1 To (Add10 x1)))
)

The exact natural number 10

n11 :
N
(In
N11
(Add11 atLeast_)
(Is (Diff x0 To (Add11 x0)) (Diff x1 To (Add11 x1)))
)

The exact natural number 11

n12 :
N
(In
N12
(Add12 atLeast_)
(Is (Diff x0 To (Add12 x0)) (Diff x1 To (Add12 x1)))
)

The exact natural number 12

n13 :
N
(In
N13
(Add13 atLeast_)
(Is (Diff x0 To (Add13 x0)) (Diff x1 To (Add13 x1)))
)

The exact natural number 13

n14 :
N
(In
N14
(Add14 atLeast_)
(Is (Diff x0 To (Add14 x0)) (Diff x1 To (Add14 x1)))
)

The exact natural number 14

n15 :
N
(In
N15
(Add15 atLeast_)
(Is (Diff x0 To (Add15 x0)) (Diff x1 To (Add15 x1)))
)

The exact natural number 15

n16 :
N
(In
N16
(Add16 atLeast_)
(Is (Diff x0 To (Add16 x0)) (Diff x1 To (Add16 x1)))
)

The exact natural number 16

compare

type BelowOrAbove below above
= Below below
| Above above

The error result of comparing Ns.

  • Above: greater
  • Below: less

Values exist for each condition.

int clamp

intAtLeast : N (In min max_ lowerDifference_) -> Int -> N (Min min)

A N (Min ...) from an Int; if the Int < minimum, minimum is returned.

0
    |> N.intAtLeast n3
    --: N (Min N3)
    |> N.toInt
--> 3


9
    |> N.intAtLeast n3
    --: N (Min N3)
    |> N.toInt
--> 9

You can also use this if you know an Int must be at least minimum.

But avoid it if you can do better, like

goodLength =
    List.foldl
        (\_ -> N.minAdd n1 >> N.minDown n0)
        (n0 |> N.noMax)

If you want to handle the case < minimum yourself → intIsAtLeast

( N (In lowerLimit upperLimitMin lowerLimitDifference_)
, N (In upperLimitMin upperLimit upperLimitDifference_)
)
-> Int
-> N (In lowerLimit upperLimit {})

Create a N (In ...) by clamping an Int between a minimum & maximum.

  • if the Int < minimum, minimum is returned
  • if the Int > maximum, maximum is returned

If you want to handle the cases < minimum & > maximum explicitly, use intIsIn.

0
    |> N.intIn ( n3, n12 )
    --: N (In N3 (Add12 a_) {})
    |> N.toInt
--> 3


99
    |> N.intIn ( n3, n12 )
    --: N (In N3 (Add12 a_) {})
    |> N.toInt
--> 12


9
    |> N.intIn ( n3, n12 )
    --: N (In N3 (Add12 a_) {})
    |> N.toInt
--> 9




toDigit : Char -> Maybe (N (In N0 (Add9 atLeast_) {}))
toDigit char =
    ((char |> Char.toCode) - ('0' |> Char.toCode))
        |> N.intIsIn ( n0, n9 )
        |> Result.toMaybe

int compare

( N (In minDownLimit maxUpperLimit lowerLimitDifference_)
, N (In maxUpperLimit maxUpperLimitAtLeast upperLimitDifference_)
)
-> Int
-> Result
(BelowOrAbove Int (N (Min (Add1 maxUpperLimit))))
(N (In minDownLimit maxUpperLimitAtLeast {}))

Compared to a range from a lower to an upper bound, is the Int in range, BelowOrAbove?

inputIntJudge : Int -> Result String (N (In N1 (Add10 a_) {}))
inputIntJudge int =
    case int |> N.intIsIn ( n1, n10 ) of
        Ok inRange ->
            inRange |> N.maxOpen n10 |> Ok
        Err (N.Below _) ->
            Err "must be ≥ 1"
        Err (N.Above _) ->
            Err "must be ≤ 100"


0 |> inputIntJudge
--> Err "must be ≥ 1"
N (In min max_ minimumLimitDifference_)
-> Int
-> Result Int (N (Min min))

If the Int ≥ a given minimum, return Ok with the N (Min minimum), else Err with the input Int.

4 |> N.intIsAtLeast n5
--: Result Int (N (Min N5))
--> Err 4


1234 |> N.intIsAtLeast n5 |> Result.map N.toInt
--> Ok 1234

clamp

N (In minNewMin (Add1 maxMinus1) lowerDifference_)
-> N (In min_ (Add1 maxMinus1) difference_)
-> N (In minNewMin (Add1 maxMinus1) {})

Return the given number if the N is less.

between5And9 |> N.atLeast n10
--: N (In N10 (Add10 a_) {})


n15 |> N.atLeast n10 |> N.toInt
--> 15


n5AtLeast |> N.atLeast (n10 |> noMax)
--: N (Min N10)

(The type doesn't forbid that the lower limit you're comparing against is below the current minimum.)

N (In min maxCapped capDifference_)
-> N (In min max_ difference_)
-> N (In min maxCapped {})

Cap the N to at most a number.

between5And15
    |> N.atMost n10
--: N (In N5 (Add10 a_) {})


atLeast5 |> N.atMost n10
--: N (In N5 (Add10 a_) {})

(The type doesn't forbid that the upper limit you're comparing against is above the current maximum.)

compare maximum constrained

is :
N
(In
comparedAgainstMin
(Add1 comparedAgainstMaxMinus1)
comparedAgainstDifference
)
-> N (In min max difference_)
-> Result
(N (In min comparedAgainstMaxMinus1 {}))
(N (In (Add1 comparedAgainstMin) max {}))
)
(N
(In
comparedAgainstMin
(Add1 comparedAgainstMaxMinus1)
comparedAgainstDifference
)
)

Is the N equal to, BelowOrAbove a given number?

giveAPresent { age } =
    case age |> N.is n18 of
        Err (N.Below younger) ->
            toy { age = younger }


        Err (N.Above older) ->
            book { age = older }


        Ok _ ->
            bigPresent


toy : { age : N (In min_ N17 difference_) } -> Toy


book :
    { age : N (In (N19 minMinus19_) max_ difference_) }
    -> Book

(The type doesn't forbid that the number you're comparing against is below the current minimum or above the current maximum. → Err or Ok values don't necessarily follow min <= max for N (In min max ...) Luckily that's not a problem, since the values won't be produced anyway.)

( N
(In
lowerLimit
(Add1 lowerLimitMaxMinus1)
lowerLimitDifference_
)
, N (In upperLimitMin upperLimitMax upperLimitDifference_)
)
-> N (In min max difference_)
-> Result
(N (In min lowerLimitMaxMinus1 {}))
(N (In (Add1 upperLimitMin) max {}))
)
(N (In lowerLimit upperLimitMax {}))

Compared to a range from a lower to an upper bound, is the N in range or BelowOrAbove?

isIn3To10 : N (In min_ (Add10 maxMinus10_) d_) -> Maybe (N (In N3 (Add10 a_) {}))
isIn3To10 =
    N.isIn ( n3, n10 )
        >> Result.toMaybe


n9 |> isIn3To10 |> Maybe.map N.toInt
--> Just 9


n12 |> isIn3To10
--> Nothing

(The type doesn't forbid that the limits you're comparing against are below the current minimum, above the current maximum or in the wrong order. → Err or Ok values don't necessarily follow min <= max for N (In min max ...) Luckily that's not a problem, since the values won't be produced anyway.)

Here's some example-cases:

  • hi < lo < min < max → Below | Above
  • hi < min < lo < max → Below | Above
  • hi < min < max < lo → Below | Above
  • lo < hi < min < max → Above
  • max < hi < min < lo → Below
  • min < hi < lo < max → Below | Above
  • lo < min < hi < max → Ok | Above
  • max < min < hi < lo → Below
  • max < lo < hi < min → Below | Ok | Above ← the default case
  • lo < min < max < hi → Ok
  • min < lo < max < hi → Ok | Below
  • min < max < lo < hi → Below

.

  • did we miss something? → issue
  • feel motivated to add the other cases? → PR
N
(In
lowerLimitMin
(Add1 lowerLimitMaxMinus1)
lowerLimitDifference_
)
-> N (In min max difference_)
-> Result
(N (In min lowerLimitMaxMinus1 {}))
(N (In lowerLimitMin max {}))

Is the N below than or at least as big as a given number?

vote :
    { age : N (In (Add18 minMinus18_) max_ difference_) }
    -> Vote


tryToVote { age } =
    case age |> N.isAtLeast n18 of
        Err _ ->
            --😓
            Nothing


        Ok oldEnough ->
            vote { age = oldEnough } |> Just


factorial : N (In min_ max_ difference_) -> N (Min N1)
factorial =
    factorialBody


factorialBody : N (In min_ max_ difference_) -> N (Min N1)
factorialBody x =
    case x |> N.isAtLeast n1 of
        Err _ ->
            n1 |> N.noMax


        Ok atLeast1 ->
            factorial (atLeast1 |> N.minSub n1)
                |> N.mul atLeast1

(The type doesn't forbid that the lower limit you're comparing against is below the current minimum or above the current maximum. → Err or Ok values don't necessarily follow min <= max for N (In min max ...) Luckily that's not a problem, since the values won't be produced anyway.)

N (In upperLimitMin upperLimitMax upperLimitDifference_)
-> N (In min max difference_)
-> Result
(N (In (Add1 upperLimitMin) max {}))
(N (In min upperLimitMax {}))

Is the N at most (Ok) or greater than (Err) a given number?

goToBelow18Party : { age : N (In min_ N17 difference_) } -> Snack


tryToGoToBelow18Party { age } =
    case age |> N.isAtMost n17 of
        Ok below18 ->
            goToBelow18Party { age = below18 } |> Just


        Err _ ->
            Nothing

(The type doesn't forbid that the upper limit you're comparing against is below the current minimum or above the current maximum. → Err or Ok values don't necessarily follow min <= max for N (In min max ...) Luckily that's not a problem, since the values won't be produced anyway.)

alter

add :
N
(In
added_
addedAtLeast_
(Is (Diff min To sumMin) (Diff max To sumMax))
)
-> N (In min max difference_)
-> N (In sumMin sumMax {})

Add a given specific N (In ... (Is ...)).

between70And100 |> N.add n7
--: N (In N77 (Add107 a_) {})

Use addIn if you want to add an N that isn't a N (In ... (Is ...)).

( N
(In
addedMin
addedMinAtLeast_
(Is (Diff min To sumMin) addedMinDiff1_)
)
, N
(In
addedMax
addedMaxAtLeast_
(Is (Diff max To sumMax) addedMaxDiff1_)
)
)
-> N (In addedMin addedMax addedDifference_)
-> N (In min max difference_)
-> N (In sumMin sumMax {})

Add an N that can be in a range.

The tuple argument should contain

  1. the minimum added value
  2. the maximum added value
between3And10 |> N.addIn ( n1, n12 ) between1And12
--: N (In N4 (N22Plus a_) {})
sub :
N
(In
subtractedMin_
subtractedMax_
(Is (Diff differenceMin To min) (Diff differenceMax To max))
)
-> N (In min max diff_)
-> N (In differenceMin differenceMax {})

Subtract a given specific N (In ... (Is ...)).

between7And10 |> N.sub n7
--: N (In N0 (Add3 a_) {})

Use subIn if you want to subtract an N that isn't a N (In ... (Is ...)).

( N
(In
subtractedMin
subtractedMinAtLeast_
(Is (Diff differenceMax To max) subtractedMinDiff1_)
)
, N
(In
subtractedMax
subtractedMaxAtLeast_
(Is (Diff differenceMin To min) subtractedMaxDiff1_)
)
)
-> N (In subtractedMin subtractedMax subtractedDifference_)
-> N (In min max difference_)
-> N (In differenceMin differenceMax {})

Subtract an N that can be in a range.

The tuple argument should contain

  1. the minimum subtracted value
  2. the maximum subtracted value
between6And12 |> N.subIn ( n1, n5 ) between1And5
--: N (In N1 (Add5 a_) {})
N
(In
(Add1 exponentMinMinus1_)
(Add1 exponentMax_)
exponentDifference_
)
-> N (In min max_ difference_)
-> N (Min min)

N Raised to a given power p ≥ 1 → x ^ p ≥ x

atLeast5 |> N.toPower n2
--: N (Min N5)


atLeast2 |> N.toPower n5
--: N (Min N2)
N
(In
(Add1 divisorMinMinus1_)
(Add1 divisorMaxMinus1)
divisorDifference_
)
-> N (In min_ max_ difference_)
-> N (In N0 divisorMaxMinus1 {})

The remainder after dividing by a N d ≥ 1. We know x % d ≤ d - 1

atMost7 |> N.remainderBy n3
--: N (In N0 (Add3 a_) {})
mul :
N
(In
(Add1 multiplicandMinMinus1_)
multiplicandMax_
multiplicandDifference_
)
-> N (In min max_ difference_)
-> N (Min min)

Multiply by N n ≥ 1. we know that if n ≥ 1, x * n ≥ x.

atLeast5 |> N.mul n2
--: N (Min N5)


atLeast2 |> N.mul n5
--: N (Min N2)
div :
N (In (Add1 divisorMinMinus1_) divisorMax_ divisorDifference_)
-> N (In min_ max difference_)
-> N (In N0 max {})

Divide (//) by an N d ≥ 1.

  • / 0 is impossible
  • x / d is at most x
atMost7
    |> N.div n3
    --: N (In N0 (Add7 a_) {})
    |> N.toInt
--> 2

alter maximum unconstrained

N
(In
subtracted_
subtractedAtLeast_
(Is (Diff differenceMin To min) subtractedDiff1_)
)
-> N (In min max difference_)
-> N (In differenceMin max {})

From an N with an unknown maximum constraint, subtract a specific number

atLeast7 |> N.minSub n2
--: N (Min N5)

Use minSubAtMost if you want to subtract an N that can be in a range.

N
(In added_ addedAtLeast_ (Is (Diff min To sumMin) addedDiff1_))
-> N (In min max_ difference_)
-> N (Min sumMin)

To the N without a known maximum-constraint, add a specific N (In ... (Is ...)) value.

atLeast70 |> N.add n7
--: N (Min N77)

Use addAtLeast if you want to add an N that can be in a range.

N
(In
subtractedMax
subtractedMaxAtLeast_
(Is (Diff differenceMin To min) subtractedMaxDiff1_)
)
-> N
(In
(N0able subtractedMinPlus1_ Possibly)
subtractedMax
subtractedDifference_
)
-> N (In min max difference_)
-> N (In differenceMin max {})

From an N without an unknown maximum constraint, subtract a number in a range.

The first argument is the maximum of the subtracted number.

atLeast6 |> N.minSubAtMost n5 between0And5
--: N (Min N1)
N
(In
addedMin
addedMinAtLeast_
(Is (Diff min To sumMin) addedDiff1_)
)
-> N (In addedMin addedMax_ addedDifference_)
-> N (In min max_ difference_)
-> N (Min sumMin)

To the N without a known maximum-constraint,, add an N. The second argument is the minimum added value.

atLeast5 |> N.addAtLeast n2 atLeast2
--: N (Min N7)

Use minAdd to add exact numbers.

broaden

toInt : N range_ -> Int

Drop the range constraints to feed another library with its Int representation.

toFloat : N range_ -> Float

Drop the range constraints to feed another library with its Float representation.

type information

N (In newMin min lowerDifference_)
-> N (In min max difference_)
-> N (In newMin max {})

Set the minimum lower.

[ atLeast3, atLeast4 ]

Elm complains:

But all the previous elements in the list are: N (Min N3)

[ atLeast3
, atLeast4 |> N.minDown n3
]
noDiff : N (In min max difference_) -> N (In min max {})

Convert it to a N (In min max {}).

n4 |> N.noDiff
--: N (In N4 (Add4 a_) {})

Example

[ in3To10, n3 ]

all the previous elements in the list are: N (In N3 N10 {})

[ in3To10
, n3 |> N.noDiff
]
noMax : N (In min max_ difference_) -> N (Min min)

Convert a N (In min ...) to a N (Min min).

between3And10 |> N.noMax
--: N (Min N4)

Use it to unify different types of number minimum constraints like

[ atLeast1, between1And10 ]

Elm complains:

But all the previous elements in the list are: N (Min N1)

[ atLeast1
, between1And10 |> N.noMax
]
N (In max maxOpen maxOpenDifference_)
-> N (In min max difference_)
-> N (In min maxOpen {})

Make it fit into functions with require a higher maximum.

You should type arguments and stored types as broad as possible.

onlyAtMost18 : N (In min_ N18 difference_) -> ...


onlyAtMost18 between3And8 -- works

But once you implement onlyAtMost18, you might use the n in onlyAtMost19:

onlyAtMost18 n =
    -- onlyAtMost19 n → error
    onlyAtMost19 (n |> N.maxOpen n18)
N
(In
increase_
increaseAtLeast_
(Is (Diff max To maxIncreased) diff1_)
)
-> N (In min max difference_)
-> N (In min maxIncreased {})

Have a specific maximum in mind? → maxOpen

Want to increase the upper bound by a fixed amount? ↓

maxUp4 : N (In min max difference_) -> N (In min (Add4 max) {})
maxUp4 =
    N.maxUp n4

When is this useful? Very rarely. This is how up reverse could be implemented

down :
    N (In min (Add1 maxMinus1) difference_)
    -> List (N (In N0 maxMinus1 {}))
down length =
    case length |> isAtLeast n1 of
        Err _ ->
            []


        Ok lengthAtLeast1 ->
            let
                lengthMinus1 =
                    lengthAtLeast1 |> sub n1
            in
            lengthMinus1
                :: (lengthMinus1
                        -- → In N0 maxMinus1
                        |> maxUp n1
                        |> downRecursive
                   )

(to be fair: here, you could also use lengthAtLeast1 |> subMin n1 instead of lengthMinus1 |> maxUp n1)

miss operation x?

Anything that can't be expressed with the available operations? → issue/PR

internals

Useful for fancy things – building structures like typesafe-array

While the internally stored Int isn't directly guaranteed to be in bounds by elm, minimum, maximum, differences must be built as actual values checked by the compiler.

type alias Is difference0 difference1 =
RecordWithoutConstructorFunction
{ diff0 : difference0
, diff1 : difference1
}

Associated differences.

type Diff low toTag high
= Difference
{ add : low -> high
, sub : high -> low
}

Diff low To high: an exact number as the difference high - low.

In
    N5
    (Add5 a_)
    (Is
        (Diff myAge To sistersAge)
        (Diff mothersAge To fathersAge)
    )
  • myAge + 5 = sistersAge
  • mothersAge + 5 = fathersAge
type To

Just a word in the type Diff.

Diff low To high

→ distance high - low.

( N
(In
added
added0AtLeast_
(Is (Diff n To sum) (Diff nAtLeast To sumAtLeast))
)
, N
(In
added
added1AtLeast_
(Is (Diff x0PlusN To aPlusSum) (Diff x1PlusN To bPlusSum))
)
)
-> N
(In n nAtLeast (Is (Diff x0 To x0PlusN) (Diff x1 To x1PlusN)))
-> N
(In
sum
sumAtLeast
(Is (Diff x0 To aPlusSum) (Diff x1 To bPlusSum))
)

The N (In ... (Is ...)) plus another N (In ... (Is ...)). Give the added value twice as a tuple.

n6 |> N.diffAdd ( n5, n5 )
--→ n11
--: N
--:     (In
--:         N11
--:         (Add11 a_)
--:         (Is
--:             (Diff x0 To (Add11 x0))
--:             (Diff x1 To (Add11 x1))
--:         )
--:     )

This is only rarely useful, as for example

isInRangeXToXPlus10 x =
    isIn ( x, x |> N.diffAdd ( n10, n10 ) )

would force x to be of type N (In ... (Is ...)). Instead,

isInXToXPlus10 x =
    isIn ( x, x |> N.add n10 )
( N
(In
subtracted
subtracted0AtLeast_
(Is
(Diff difference To n)
(Diff differenceAtLeast To nAtLeast)
)
)
, N
(In
subtracted
subtracted1AtLeast_
(Is
(Diff x0PlusDifference To x0PlusN)
(Diff x1PlusDifference To x1PlusN)
)
)
)
-> N
(In n nAtLeast (Is (Diff x0 To x0PlusN) (Diff x1 To x1PlusN)))
-> N
(In
difference
differenceAtLeast
(Is
(Diff x0 To x0PlusDifference)
(Diff x1 To x1PlusDifference)
)
)

The N (In ... (Is ...) minus another N (In ... (Is ...)). Give the subtracted value twice as a tuple.

n6 |> N.diffSub ( n5, n5 )
--→ n1
--: N
--:     (In
--:         N1
--:         (Add1 a_)
--:         (Is
--:             (Diff x0 To (Add1 x0))
--:             (Diff x1 To (Add1 x1))
--:         )
--:     )

This is only rarely useful, as for example

isInXMinus10ToX x =
    isIn ( x |> N.diffSub ( n10, n10 ), x )

would force x to be of type N (In ... (Is ...)). Instead,

isInXMinus10ToX x =
    isIn ( x |> N.sub n10, x )

Constructor for NoMax

minimum : N (In minimum maximum_ difference_) -> minimum

The smallest allowed number promised by the range type.

N (In min_ max_ { differences_ | diff0 : difference0 })
-> difference0

The number representation as a difference promised by its type Is (Diff low To high) ....

N (In min_ max_ { differences_ | diff1 : difference1 })
-> difference1

The number representation as a difference promised by its type Is ... (Diff low To high).

differences : N (In min_ max_ differences) -> differences

The number representation as all differences promised by its type Is (Diff low0 To high0) (Diff low1 To high1).

For the individual differences: difference0, difference1

addDifference : Diff low To high -> low -> high

To the number, add a specific other one by supplying one of its differences promised by its type Is (Diff low0 To high0) (Diff low1 To high1)

subDifference : Diff low To high -> high -> low

To the number, subtract a specific other one by supplying one of its differences promised by its type Is (Diff low0 To high0) (Diff low1 To high1)

differenceAdd : Diff middle To high -> Diff low To middle -> Diff low To high

Chain the difference further to a higher number.

differenceSub : Diff middle To high -> Diff low To high -> Diff low To middle

Chain the difference back to a lower number.