-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.