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