Current section
Files
Jump to
Current section
Files
src/ranged_int@interface.erl
-module(ranged_int@interface).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/ranged_int/interface.gleam").
-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]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" The ranged integer \"interface\".\n"
"\n"
" This module contains types and functions to be implemented and used to\n"
" create custom ranged integer types.\n"
"\n"
" See the README for instructions on how to create your own type with this\n"
" module.\n"
).
-type overflowable() :: overflowable.
-type non_overflowable() :: non_overflowable.
-opaque limits(DYE) :: {limits,
ranged_int@internal@limit:limit(),
ranged_int@internal@limit:limit()} |
{gleam_phantom, DYE}.
-type overflowable_limits() :: {overflowable_limits,
bigi:big_int(),
bigi:big_int()}.
-type interface(DYF, DYG) :: {interface,
fun((DYF) -> bigi:big_int()),
fun((bigi:big_int()) -> DYF),
fun(() -> limits(DYG))}.
-file("src/ranged_int/interface.gleam", 64).
?DOC(
" Construct limits for an overflowable ranged integer. The given limits are\n"
" inclusive.\n"
).
-spec overflowable_limits(bigi:big_int(), bigi:big_int()) -> limits(overflowable()).
overflowable_limits(Min, Max) ->
{limits, {bounded, Min}, {bounded, Max}}.
-file("src/ranged_int/interface.gleam", 70).
?DOC(
" Construct limits for a non-overflowable ranged integer that only has a\n"
" maximum limit. The given limit is inclusive.\n"
).
-spec max_limit(bigi:big_int()) -> limits(non_overflowable()).
max_limit(Max) ->
{limits, unbounded, {bounded, Max}}.
-file("src/ranged_int/interface.gleam", 76).
?DOC(
" Construct limits for a non-overflowable ranged integer that only has a\n"
" minimum limit. The given limit is inclusive.\n"
).
-spec min_limit(bigi:big_int()) -> limits(non_overflowable()).
min_limit(Min) ->
{limits, {bounded, Min}, unbounded}.
-file("src/ranged_int/interface.gleam", 81).
?DOC(" Create a ranged integer from a big integer.\n").
-spec from_bigint(bigi:big_int(), interface(DYN, any())) -> {ok, DYN} |
{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.
-file("src/ranged_int/interface.gleam", 95).
?DOC(
" Compare two ranged integers, returning an order that denotes if `int1` is\n"
" lower, bigger than, or equal to `int2`.\n"
).
-spec compare(DYS, DYS, interface(DYS, any())) -> gleam@order:order().
compare(Int1, Int2, Interface) ->
bigi_ffi:compare(
(erlang:element(2, Interface))(Int1),
(erlang:element(2, Interface))(Int2)
).
-file("src/ranged_int/interface.gleam", 104).
?DOC(" Run a math operation on a ranged integer and an unranged big integer.\n").
-spec math_op(
DYW,
bigi:big_int(),
interface(DYW, any()),
fun((bigi:big_int(), bigi:big_int()) -> bigi:big_int())
) -> {ok, DYW} | {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.
-file("src/ranged_int/interface.gleam", 121).
?DOC(" Run a unary math operation on a ranged integer.\n").
-spec math_op_unary(
DZB,
interface(DZB, any()),
fun((bigi:big_int()) -> bigi:big_int())
) -> {ok, DZB} | {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.
-file("src/ranged_int/interface.gleam", 141).
?DOC(
" Run a math operation that may fail on a ranged integer and an unranged big\n"
" integer.\n"
"\n"
" Some functions such as `bigi.divide_no_zero` will return an error if the\n"
" arguments are invalid. This error is passed upwards by this function.\n"
).
-spec fallible_op(
DZG,
bigi:big_int(),
interface(DZG, any()),
fun((bigi:big_int(), bigi:big_int()) -> {ok, bigi:big_int()} | {error, DZK})
) -> {ok, {ok, DZG} | {error, ranged_int@utils:overflow()}} | {error, DZK}.
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
).
-file("src/ranged_int/interface.gleam", 177).
?DOC(
" Eject the result of an operation to big integer. The big integer may be\n"
" outside the range of the original ranged integers.\n"
).
-spec eject(
{ok, DZV} | {error, ranged_int@utils:overflow()},
interface(DZV, 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>>,
file => <<?FILEPATH/utf8>>,
module => <<"ranged_int/interface"/utf8>>,
function => <<"eject"/utf8>>,
line => 186})
end
end.
-file("src/ranged_int/interface.gleam", 192).
-spec destructure_limits(limits(overflowable())) -> overflowable_limits().
destructure_limits(Limits) ->
Min@1 = case erlang:element(2, Limits) of
{bounded, Min} -> Min;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ranged_int/interface"/utf8>>,
function => <<"destructure_limits"/utf8>>,
line => 194,
value => _assert_fail,
start => 6907,
'end' => 6949,
pattern_start => 6918,
pattern_end => 6936})
end,
Max@1 = case erlang:element(3, Limits) of
{bounded, Max} -> Max;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ranged_int/interface"/utf8>>,
function => <<"destructure_limits"/utf8>>,
line => 195,
value => _assert_fail@1,
start => 6952,
'end' => 6994,
pattern_start => 6963,
pattern_end => 6981})
end,
{overflowable_limits, Min@1, Max@1}.
-file("src/ranged_int/interface.gleam", 164).
?DOC(
" Map the overflow result of an operation to the integer's allowed range.\n"
"\n"
" The overflow is calculated using a modulo against the amount of integers in\n"
" the allowed range. This matches the unsigned integer overflow logic of C/C++\n"
" but is also defined for signed (negative) values.\n"
"\n"
" This can only be called for overflowable integers.\n"
).
-spec overflow(
{ok, DZQ} | {error, ranged_int@utils:overflow()},
interface(DZQ, overflowable())
) -> DZQ.
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.