Packages

Type safe ranged integer operations for Gleam.

Current section

Files

Jump to
ranged_int src ranged_int@interface.erl
Raw

src/ranged_int@interface.erl

-module(ranged_int@interface).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([overflowable_limits/2, max_limit/1, min_limit/1, from_bigint/2, compare/3, math_op/4, math_op_unary/3, fallible_op/4, eject/2, overflow/2]).
-export_type([overflowable/0, non_overflowable/0, limits/1, overflowable_limits/0, interface/2]).
-type overflowable() :: overflowable.
-type non_overflowable() :: non_overflowable.
-opaque limits(FQF) :: {limits,
ranged_int@internal@limit:limit(),
ranged_int@internal@limit:limit()} |
{gleam_phantom, FQF}.
-type overflowable_limits() :: {overflowable_limits,
bigi:big_int(),
bigi:big_int()}.
-type interface(FQG, FQH) :: {interface,
fun((FQG) -> bigi:big_int()),
fun((bigi:big_int()) -> FQG),
fun(() -> limits(FQH))}.
-spec overflowable_limits(bigi:big_int(), bigi:big_int()) -> limits(overflowable()).
overflowable_limits(Min, Max) ->
{limits, {bounded, Min}, {bounded, Max}}.
-spec max_limit(bigi:big_int()) -> limits(non_overflowable()).
max_limit(Max) ->
{limits, unbounded, {bounded, Max}}.
-spec min_limit(bigi:big_int()) -> limits(non_overflowable()).
min_limit(Min) ->
{limits, {bounded, Min}, unbounded}.
-spec from_bigint(bigi:big_int(), interface(FQO, any())) -> {ok, FQO} |
{error, ranged_int@utils:overflow()}.
from_bigint(Value, Interface) ->
Limits = (erlang:element(4, Interface))(),
case ranged_int@internal@limit:check_limits(
Value,
erlang:element(2, Limits),
erlang:element(3, Limits)
) of
no_overflow ->
{ok, (erlang:element(3, Interface))(Value)};
{overflow, Amount} ->
{error, {would_overflow, Amount}};
{underflow, Amount@1} ->
{error, {would_underflow, Amount@1}}
end.
-spec compare(FQT, FQT, interface(FQT, any())) -> gleam@order:order().
compare(Int1, Int2, Interface) ->
bigi_ffi:compare(
(erlang:element(2, Interface))(Int1),
(erlang:element(2, Interface))(Int2)
).
-spec math_op(
FQX,
bigi:big_int(),
interface(FQX, any()),
fun((bigi:big_int(), bigi:big_int()) -> bigi:big_int())
) -> {ok, FQX} | {error, ranged_int@utils:overflow()}.
math_op(Int1, Int2, Interface, Op) ->
Limits = (erlang:element(4, Interface))(),
Int1@1 = (erlang:element(2, Interface))(Int1),
Result = Op(Int1@1, Int2),
case ranged_int@internal@limit:check_limits(
Result,
erlang:element(2, Limits),
erlang:element(3, Limits)
) of
no_overflow ->
{ok, (erlang:element(3, Interface))(Result)};
{overflow, Amount} ->
{error, {would_overflow, Amount}};
{underflow, Amount@1} ->
{error, {would_underflow, Amount@1}}
end.
-spec math_op_unary(
FRC,
interface(FRC, any()),
fun((bigi:big_int()) -> bigi:big_int())
) -> {ok, FRC} | {error, ranged_int@utils:overflow()}.
math_op_unary(Int, Interface, Op) ->
Limits = (erlang:element(4, Interface))(),
Int@1 = (erlang:element(2, Interface))(Int),
Result = Op(Int@1),
case ranged_int@internal@limit:check_limits(
Result,
erlang:element(2, Limits),
erlang:element(3, Limits)
) of
no_overflow ->
{ok, (erlang:element(3, Interface))(Result)};
{overflow, Amount} ->
{error, {would_overflow, Amount}};
{underflow, Amount@1} ->
{error, {would_underflow, Amount@1}}
end.
-spec fallible_op(
FRH,
bigi:big_int(),
interface(FRH, any()),
fun((bigi:big_int(), bigi:big_int()) -> {ok, bigi:big_int()} | {error, FRL})
) -> {ok, {ok, FRH} | {error, ranged_int@utils:overflow()}} | {error, FRL}.
fallible_op(Int1, Int2, Interface, Op) ->
Limits = (erlang:element(4, Interface))(),
Int1@1 = (erlang:element(2, Interface))(Int1),
gleam@result:'try'(
Op(Int1@1, Int2),
fun(Result) ->
{ok,
case ranged_int@internal@limit:check_limits(
Result,
erlang:element(2, Limits),
erlang:element(3, Limits)
) of
no_overflow ->
{ok, (erlang:element(3, Interface))(Result)};
{overflow, Amount} ->
{error, {would_overflow, Amount}};
{underflow, Amount@1} ->
{error, {would_underflow, Amount@1}}
end}
end
).
-spec eject(
{ok, FRW} | {error, ranged_int@utils:overflow()},
interface(FRW, any())
) -> bigi:big_int().
eject(Value, Interface) ->
case Value of
{ok, New_value} ->
(erlang:element(2, Interface))(New_value);
{error, Overflow} ->
case {Overflow, (erlang:element(4, Interface))()} of
{{would_overflow, Amount}, {limits, _, {bounded, Max}}} ->
bigi_ffi:add(Max, Amount);
{{would_underflow, Amount@1}, {limits, {bounded, Min}, _}} ->
bigi_ffi:subtract(Min, Amount@1);
{_, _} ->
erlang:error(#{gleam_error => panic,
message => <<"Overflowed but there was no corresponding limit (o_O)"/utf8>>,
module => <<"ranged_int/interface"/utf8>>,
function => <<"eject"/utf8>>,
line => 185})
end
end.
-spec destructure_limits(limits(overflowable())) -> overflowable_limits().
destructure_limits(Limits) ->
_assert_subject = erlang:element(2, Limits),
{bounded, Min} = case _assert_subject of
{bounded, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"ranged_int/interface"/utf8>>,
function => <<"destructure_limits"/utf8>>,
line => 193})
end,
_assert_subject@1 = erlang:element(3, Limits),
{bounded, Max} = case _assert_subject@1 of
{bounded, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"ranged_int/interface"/utf8>>,
function => <<"destructure_limits"/utf8>>,
line => 194})
end,
{overflowable_limits, Min, Max}.
-spec overflow(
{ok, FRR} | {error, ranged_int@utils:overflow()},
interface(FRR, overflowable())
) -> FRR.
overflow(Value, Interface) ->
case Value of
{ok, New_value} ->
New_value;
{error, Overflow} ->
{overflowable_limits, Min, Max} = destructure_limits(
(erlang:element(4, Interface))()
),
_pipe = ranged_int@utils:overflow(Overflow, Min, Max),
(erlang:element(3, Interface))(_pipe)
end.