-module(dee@decimal). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dee/decimal.gleam"). -export([default_context/0, context/2, sign/1, coefficient/1, exponent/1, zero/0, one/0, infinity/0, neg_infinity/0, nan/0, from_int/1, from_parts/3, from_string/1, parse/1, from_float/1, absolute_value/1, negate/1, add/2, subtract/2, multiply/2, compare/2, eq/2, gt/2, lt/2, gte/2, lte/2, max/2, min/2, to_string/1, to_string_scientific/1, to_string_raw/1, to_string_xsd/1, to_int/1, to_float/1, scale/1, round/3, normalize/1, apply_context/2, sqrt_with_context/2, square_root/1, divide_with_context/3, divide/2, is_nan/1, is_inf/1, is_positive/1, is_negative/1, compare_with_threshold/3, eq_with_threshold/3, is_zero/1, is_integer/1, divide_integer/2, remainder/2, divide_remainder/2]). -export_type([sign/0, decimal/0, rounding_mode/0, context/0]). -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( " Arbitrary precision decimal arithmetic for Gleam.\n" "\n" " A pure Gleam implementation of\n" " [Elixir's Decimal](https://hexdocs.pm/decimal) library, providing the\n" " `Decimal` opaque type with arithmetic, comparison, conversion, and\n" " rounding operations. Works identically on both Erlang and JavaScript\n" " targets using a single codebase.\n" "\n" " A decimal number is represented as `sign * coefficient * 10 ^ exponent`\n" " where the coefficient is an arbitrary precision integer. This avoids the\n" " rounding errors inherent to floating-point arithmetic.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " // Floating point: 0.1 + 0.2 = 0.30000000000000004\n" " // Decimal: 0.1 + 0.2 = 0.3\n" " let assert Ok(a) = from_string(\"0.1\")\n" " let assert Ok(b) = from_string(\"0.2\")\n" " to_string(add(a, b))\n" " // -> \"0.3\"\n" " ```\n" "\n" " ```gleam\n" " let assert Ok(price) = from_string(\"19.99\")\n" " let assert Ok(tax_rate) = from_string(\"0.08\")\n" " let tax = multiply(price, tax_rate)\n" " to_string(tax)\n" " // -> \"1.5992\"\n" " ```\n" "\n" " ## Elixir Decimal Compatibility\n" "\n" " This library follows Elixir's Decimal as its reference implementation.\n" " The API, arithmetic behavior, rounding modes, and special value handling\n" " are designed to match. Code ported between the two libraries should\n" " produce identical results.\n" "\n" " Both libraries use the same mathematical representation internally\n" " (`sign * coefficient * 10^exponent`) with the same semantics for\n" " `NaN`, `Infinity`, and signed zero. The default context matches\n" " Elixir's: 28 digits of precision with `HalfUp` rounding.\n" "\n" " While the runtime representations differ (Gleam opaque type vs Elixir\n" " struct), converting between them on the BEAM is straightforward via\n" " `to_string`/`from_string` or by mapping the component fields through\n" " `from_parts`, `sign`, and `exponent`.\n" ). -type sign() :: positive | negative. -opaque decimal() :: {decimal, sign(), dee@internal:coefficient(), integer()}. -type rounding_mode() :: down | up | ceiling | floor | half_up | half_even | half_down. -type context() :: {context, integer(), rounding_mode()}. -file("src/dee/decimal.gleam", 108). ?DOC( " The default context with precision 28 and HalfUp rounding.\n" " Matches Elixir Decimal's default and IEEE decimal128.\n" ). -spec default_context() -> context(). default_context() -> {context, 28, half_up}. -file("src/dee/decimal.gleam", 120). ?DOC( " Creates a new context with the given precision and rounding mode.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " context(10, HalfEven)\n" " // -> Context(precision: 10, rounding: HalfEven)\n" " ```\n" ). -spec context(integer(), rounding_mode()) -> context(). context(Precision, Rounding) -> {context, Precision, Rounding}. -file("src/dee/decimal.gleam", 137). ?DOC( " Returns the sign of the decimal number.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " sign(from_int(42))\n" " // -> Positive\n" "\n" " sign(from_int(-7))\n" " // -> Negative\n" " ```\n" ). -spec sign(decimal()) -> sign(). sign(D) -> erlang:element(2, D). -file("src/dee/decimal.gleam", 145). ?DOC(false). -spec coefficient(decimal()) -> dee@internal:coefficient(). coefficient(D) -> erlang:element(3, D). -file("src/dee/decimal.gleam", 158). ?DOC( " Returns the exponent of the decimal number.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.23\")\n" " exponent(d)\n" " // -> -2\n" " ```\n" ). -spec exponent(decimal()) -> integer(). exponent(D) -> erlang:element(4, D). -file("src/dee/decimal.gleam", 165). ?DOC(" Decimal zero (0).\n"). -spec zero() -> decimal(). zero() -> {decimal, positive, {finite, bigi_ffi:zero()}, 0}. -file("src/dee/decimal.gleam", 170). ?DOC(" Decimal one (1).\n"). -spec one() -> decimal(). one() -> {decimal, positive, {finite, bigi_ffi:one()}, 0}. -file("src/dee/decimal.gleam", 175). ?DOC(" Positive infinity().\n"). -spec infinity() -> decimal(). infinity() -> {decimal, positive, infinity, 0}. -file("src/dee/decimal.gleam", 180). ?DOC(" Negative infinity().\n"). -spec neg_infinity() -> decimal(). neg_infinity() -> {decimal, negative, infinity, 0}. -file("src/dee/decimal.gleam", 185). ?DOC(" NaN (Not a Number).\n"). -spec nan() -> decimal(). nan() -> {decimal, positive, na_n, 0}. -file("src/dee/decimal.gleam", 194). -spec big_pow10(integer()) -> bigi:big_int(). big_pow10(N) -> case N of 0 -> bigi_ffi:one(); 1 -> bigi_ffi:ten(); 2 -> bigi_ffi:from(100); 3 -> bigi_ffi:from(1000); 4 -> bigi_ffi:from(10000); 5 -> bigi_ffi:from(100000); 6 -> bigi_ffi:from(1000000); 7 -> bigi_ffi:from(10000000); 8 -> bigi_ffi:from(100000000); 9 -> bigi_ffi:from(1000000000); _ when N > 0 -> Result@1 = case bigi_ffi:power(bigi_ffi:ten(), bigi_ffi:from(N)) of {ok, Result} -> Result; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"dee/decimal"/utf8>>, function => <<"big_pow10"/utf8>>, line => 207, value => _assert_fail, start => 5685, 'end' => 5749, pattern_start => 5696, pattern_end => 5706}) end, Result@1; _ -> bigi_ffi:one() end. -file("src/dee/decimal.gleam", 216). -spec big_is_zero(bigi:big_int()) -> boolean(). big_is_zero(N) -> bigi_ffi:compare(N, bigi_ffi:zero()) =:= eq. -file("src/dee/decimal.gleam", 221). -spec big_two() -> bigi:big_int(). big_two() -> bigi_ffi:from(2). -file("src/dee/decimal.gleam", 239). ?DOC( " Creates a new decimal number from an integer. The decimal number will\n" " be created exactly as specified with all digits kept.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " from_int(42) |> to_string\n" " // -> \"42\"\n" "\n" " from_int(-7) |> to_string\n" " // -> \"-7\"\n" " ```\n" ). -spec from_int(integer()) -> decimal(). from_int(Value) -> Big_value = bigi_ffi:from(Value), case Value < 0 of true -> {decimal, negative, {finite, bigi_ffi:negate(Big_value)}, 0}; false -> {decimal, positive, {finite, Big_value}, 0} end. -file("src/dee/decimal.gleam", 262). ?DOC( " Creates a new decimal number from the sign, coefficient, and exponent\n" " such that the number will be: `sign * coefficient * 10 ^ exponent`.\n" "\n" " The coefficient is stored as its absolute value regardless of input.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " from_parts(Positive, 42, 0) |> to_string\n" " // -> \"42\"\n" "\n" " from_parts(Negative, 12345, -2) |> to_string\n" " // -> \"-123.45\"\n" " ```\n" ). -spec from_parts(sign(), integer(), integer()) -> decimal(). from_parts(S, Coef, Exp) -> Abs_coef = case Coef < 0 of true -> bigi_ffi:from(- Coef); false -> bigi_ffi:from(Coef) end, {decimal, S, {finite, Abs_coef}, Exp}. -file("src/dee/decimal.gleam", 479). -spec is_digit(binary()) -> boolean(). is_digit(C) -> case C of <<"0"/utf8>> -> true; <<"1"/utf8>> -> true; <<"2"/utf8>> -> true; <<"3"/utf8>> -> true; <<"4"/utf8>> -> true; <<"5"/utf8>> -> true; <<"6"/utf8>> -> true; <<"7"/utf8>> -> true; <<"8"/utf8>> -> true; <<"9"/utf8>> -> true; _ -> false end. -file("src/dee/decimal.gleam", 463). -spec take_digits_acc(list(binary()), list(binary())) -> {list(binary()), list(binary())}. take_digits_acc(Chars, Acc) -> case Chars of [] -> {lists:reverse(Acc), []}; [C | Rest] -> case is_digit(C) of true -> take_digits_acc(Rest, [C | Acc]); false -> {lists:reverse(Acc), Chars} end end. -file("src/dee/decimal.gleam", 459). -spec take_digits(list(binary())) -> {list(binary()), list(binary())}. take_digits(Chars) -> take_digits_acc(Chars, []). -file("src/dee/decimal.gleam", 496). -spec trim_leading_zeros_list(list(binary())) -> list(binary()). trim_leading_zeros_list(Chars) -> case Chars of [] -> []; [<<"0"/utf8>>] -> [<<"0"/utf8>>]; [<<"0"/utf8>> | Rest] -> trim_leading_zeros_list(Rest); _ -> Chars end. -file("src/dee/decimal.gleam", 487). -spec trim_leading_zeros(binary()) -> binary(). trim_leading_zeros(S) -> Chars = gleam@string:to_graphemes(S), Trimmed = trim_leading_zeros_list(Chars), case Trimmed of [] -> <<"0"/utf8>>; _ -> erlang:list_to_binary(Trimmed) end. -file("src/dee/decimal.gleam", 441). -spec build_decimal(sign(), binary(), integer(), list(binary())) -> {ok, {decimal(), binary()}} | {error, nil}. build_decimal(S, Digits, Exp, Remaining) -> Trimmed_digits = trim_leading_zeros(Digits), case bigi_ffi:from_string(Trimmed_digits) of {ok, Coef} -> Decimal = {decimal, S, {finite, Coef}, Exp}, {ok, {Decimal, erlang:list_to_binary(Remaining)}}; {error, _} -> {error, nil} end. -file("src/dee/decimal.gleam", 407). -spec parse_with_exponent(sign(), binary(), integer(), list(binary())) -> {ok, {decimal(), binary()}} | {error, nil}. parse_with_exponent(S, Digits, Base_exp, Chars) -> case Chars of [<<"e"/utf8>> | Rest] -> {Exp_sign, After_sign} = case Rest of [<<"-"/utf8>> | R] -> {-1, R}; [<<"+"/utf8>> | R@1] -> {1, R@1}; _ -> {1, Rest} end, {Exp_digits, After_exp} = take_digits(After_sign), case Exp_digits of [] -> build_decimal(S, Digits, Base_exp, Chars); _ -> gleam@result:'try'( begin _pipe = gleam_stdlib:parse_int( erlang:list_to_binary(Exp_digits) ), gleam@result:replace_error(_pipe, nil) end, fun(Exp_val) -> Total_exp = Base_exp + (Exp_sign * Exp_val), build_decimal(S, Digits, Total_exp, After_exp) end ) end; [<<"E"/utf8>> | Rest] -> {Exp_sign, After_sign} = case Rest of [<<"-"/utf8>> | R] -> {-1, R}; [<<"+"/utf8>> | R@1] -> {1, R@1}; _ -> {1, Rest} end, {Exp_digits, After_exp} = take_digits(After_sign), case Exp_digits of [] -> build_decimal(S, Digits, Base_exp, Chars); _ -> gleam@result:'try'( begin _pipe = gleam_stdlib:parse_int( erlang:list_to_binary(Exp_digits) ), gleam@result:replace_error(_pipe, nil) end, fun(Exp_val) -> Total_exp = Base_exp + (Exp_sign * Exp_val), build_decimal(S, Digits, Total_exp, After_exp) end ) end; _ -> build_decimal(S, Digits, Base_exp, Chars) end. -file("src/dee/decimal.gleam", 361). -spec parse_number(sign(), list(binary())) -> {ok, {decimal(), binary()}} | {error, nil}. parse_number(S, Chars) -> {Int_digits, After_int} = take_digits(Chars), case {Int_digits, After_int} of {[], [<<"."/utf8>> | After_dot]} -> {Frac_digits, After_frac} = take_digits(After_dot), case Frac_digits of [] -> {error, nil}; _ -> All_digits = erlang:list_to_binary( [<<"0"/utf8>>, erlang:list_to_binary(Frac_digits)] ), Exp = - erlang:length(Frac_digits), parse_with_exponent(S, All_digits, Exp, After_frac) end; {[], _} -> {error, nil}; {_, [<<"."/utf8>> | After_dot@1]} -> {Frac_digits@1, After_frac@1} = take_digits(After_dot@1), case Frac_digits@1 of [] -> All_digits@1 = erlang:list_to_binary(Int_digits), parse_with_exponent(S, All_digits@1, 0, After_dot@1); _ -> All_digits@2 = erlang:list_to_binary( [erlang:list_to_binary(Int_digits), erlang:list_to_binary(Frac_digits@1)] ), Exp@1 = - erlang:length(Frac_digits@1), parse_with_exponent(S, All_digits@2, Exp@1, After_frac@1) end; {_, _} -> All_digits@3 = erlang:list_to_binary(Int_digits), parse_with_exponent(S, All_digits@3, 0, After_int) end. -file("src/dee/decimal.gleam", 343). -spec parse_numeric_string(binary()) -> {ok, {decimal(), binary()}} | {error, nil}. parse_numeric_string(Input) -> Chars = gleam@string:to_graphemes(Input), case Chars of [] -> {error, nil}; [First | Rest] -> {S, Remaining_chars} = case First of <<"-"/utf8>> -> {negative, Rest}; <<"+"/utf8>> -> {positive, Rest}; _ -> {positive, Chars} end, parse_number(S, Remaining_chars) end. -file("src/dee/decimal.gleam", 323). -spec parse_decimal_string(binary()) -> {ok, {decimal(), binary()}} | {error, nil}. parse_decimal_string(Input) -> case gleam@string:is_empty(Input) of true -> {error, nil}; false -> Lower = string:lowercase(Input), case Lower of <<"infinity"/utf8>> -> {ok, {infinity(), <<""/utf8>>}}; <<"inf"/utf8>> -> {ok, {infinity(), <<""/utf8>>}}; <<"-infinity"/utf8>> -> {ok, {neg_infinity(), <<""/utf8>>}}; <<"-inf"/utf8>> -> {ok, {neg_infinity(), <<""/utf8>>}}; <<"+infinity"/utf8>> -> {ok, {infinity(), <<""/utf8>>}}; <<"+inf"/utf8>> -> {ok, {infinity(), <<""/utf8>>}}; <<"nan"/utf8>> -> {ok, {nan(), <<""/utf8>>}}; <<"-nan"/utf8>> -> {ok, {{decimal, negative, na_n, 0}, <<""/utf8>>}}; <<"+nan"/utf8>> -> {ok, {nan(), <<""/utf8>>}}; _ -> parse_numeric_string(Input) end end. -file("src/dee/decimal.gleam", 289). ?DOC( " Parses a string into a decimal number. A decimal number will always be\n" " created exactly as specified with all digits kept.\n" "\n" " Accepts integers, decimals, scientific notation, `\"Infinity\"`, `\"-Infinity\"`,\n" " and `\"NaN\"`. Returns `Error` with the invalid input string on failure.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " from_string(\"3.14\")\n" " // -> Ok(...)\n" "\n" " from_string(\"-Infinity\")\n" " // -> Ok(...)\n" "\n" " from_string(\"bad\")\n" " // -> Error(\"bad\")\n" " ```\n" ). -spec from_string(binary()) -> {ok, decimal()} | {error, binary()}. from_string(Input) -> Trimmed = gleam@string:trim(Input), case parse_decimal_string(Trimmed) of {ok, {Decimal, Rest}} -> case gleam@string:is_empty(Rest) of true -> {ok, Decimal}; false -> {error, Input} end; {error, _} -> {error, Input} end. -file("src/dee/decimal.gleam", 315). ?DOC( " Parses a string into a decimal, returning the decimal and the remaining\n" " unparsed portion of the string. If parsing fails, returns `Error` with\n" " the original input string.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " parse(\"3.14rest\")\n" " // -> Ok(#(..., \"rest\"))\n" "\n" " parse(\"bad\")\n" " // -> Error(\"bad\")\n" " ```\n" ). -spec parse(binary()) -> {ok, {decimal(), binary()}} | {error, binary()}. parse(Input) -> case parse_decimal_string(Input) of {ok, Result} -> {ok, Result}; {error, _} -> {error, Input} end. -file("src/dee/decimal.gleam", 516). ?DOC( " Creates a new decimal number from a floating point number.\n" "\n" " Note that due to float representation, the result may not be exactly\n" " the value you expect. Use `from_string` for exact decimal values.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " from_float(3.14) |> to_string\n" " // -> \"3.14\"\n" " ```\n" ). -spec from_float(float()) -> decimal(). from_float(Value) -> Str = gleam_stdlib:float_to_string(Value), case from_string(Str) of {ok, D} -> D; {error, _} -> zero() end. -file("src/dee/decimal.gleam", 540). ?DOC( " Returns the absolute value of the given number. Sets the number's sign\n" " to positive.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " absolute_value(from_int(1)) |> to_string\n" " // -> \"1\"\n" "\n" " absolute_value(from_int(-1)) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec absolute_value(decimal()) -> decimal(). absolute_value(A) -> case erlang:element(3, A) of na_n -> nan(); _ -> {decimal, positive, erlang:element(3, A), erlang:element(4, A)} end. -file("src/dee/decimal.gleam", 558). ?DOC( " Negates the given number.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " negate(from_int(1)) |> to_string\n" " // -> \"-1\"\n" "\n" " negate(from_int(-1)) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec negate(decimal()) -> decimal(). negate(A) -> case erlang:element(3, A) of na_n -> nan(); _ -> New_sign = case erlang:element(2, A) of positive -> negative; negative -> positive end, {decimal, New_sign, erlang:element(3, A), erlang:element(4, A)} end. -file("src/dee/decimal.gleam", 603). -spec add_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer() ) -> decimal(). add_finite(Sa, Ca, Ea, Sb, Cb, Eb) -> Min_exp = gleam@int:min(Ea, Eb), Aligned_ca = bigi_ffi:multiply(Ca, big_pow10(Ea - Min_exp)), Aligned_cb = bigi_ffi:multiply(Cb, big_pow10(Eb - Min_exp)), Signed_a = case Sa of positive -> Aligned_ca; negative -> bigi_ffi:negate(Aligned_ca) end, Signed_b = case Sb of positive -> Aligned_cb; negative -> bigi_ffi:negate(Aligned_cb) end, Sum = bigi_ffi:add(Signed_a, Signed_b), case bigi_ffi:compare(Sum, bigi_ffi:zero()) of lt -> {decimal, negative, {finite, bigi_ffi:negate(Sum)}, Min_exp}; eq -> Result_sign = case Sa =:= Sb of true -> Sa; false -> positive end, {decimal, Result_sign, {finite, Sum}, Min_exp}; gt -> {decimal, positive, {finite, Sum}, Min_exp} end. -file("src/dee/decimal.gleam", 581). ?DOC( " Adds two numbers together.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"0.1\")\n" " let assert Ok(b) = from_string(\"0.2\")\n" " add(a, b) |> to_string\n" " // -> \"0.3\"\n" " ```\n" ). -spec add(decimal(), decimal()) -> decimal(). add(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> nan(); {_, na_n} -> nan(); {infinity, infinity} -> case erlang:element(2, A) =:= erlang:element(2, B) of true -> A; false -> nan() end; {infinity, _} -> A; {_, infinity} -> B; {{finite, Ca}, {finite, Cb}} -> add_finite( erlang:element(2, A), Ca, erlang:element(4, A), erlang:element(2, B), Cb, erlang:element(4, B) ) end. -file("src/dee/decimal.gleam", 653). ?DOC( " Subtracts the second number from the first. Equivalent to `add` when the\n" " second number's sign is negated.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"1\")\n" " let assert Ok(b) = from_string(\"0.1\")\n" " subtract(a, b) |> to_string\n" " // -> \"0.9\"\n" " ```\n" ). -spec subtract(decimal(), decimal()) -> decimal(). subtract(A, B) -> add(A, negate(B)). -file("src/dee/decimal.gleam", 702). -spec mult_signs(sign(), sign()) -> sign(). mult_signs(A, B) -> case {A, B} of {positive, positive} -> positive; {negative, negative} -> positive; {_, _} -> negative end. -file("src/dee/decimal.gleam", 666). ?DOC( " Multiplies two numbers.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"0.5\")\n" " multiply(a, from_int(3)) |> to_string\n" " // -> \"1.5\"\n" " ```\n" ). -spec multiply(decimal(), decimal()) -> decimal(). multiply(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> nan(); {_, na_n} -> nan(); {infinity, {finite, Cb}} -> case big_is_zero(Cb) of true -> nan(); false -> Result_sign = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign, infinity, 0} end; {{finite, Ca}, infinity} -> case big_is_zero(Ca) of true -> nan(); false -> Result_sign@1 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@1, infinity, 0} end; {infinity, infinity} -> Result_sign@2 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@2, infinity, 0}; {{finite, Ca@1}, {finite, Cb@1}} -> Result_sign@3 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), Result_coef = bigi_ffi:multiply(Ca@1, Cb@1), Result_exp = erlang:element(4, A) + erlang:element(4, B), {decimal, Result_sign@3, {finite, Result_coef}, Result_exp} end. -file("src/dee/decimal.gleam", 819). -spec sqrt_loop(bigi:big_int(), bigi:big_int()) -> bigi:big_int(). sqrt_loop(Guess, Coef) -> Quotient = bigi_ffi:divide(Coef, Guess), case bigi_ffi:compare(Guess, Quotient) of lt -> Guess; eq -> Guess; gt -> New_guess = bigi_ffi:divide( bigi_ffi:add(Guess, Quotient), big_two() ), sqrt_loop(New_guess, Coef) end. -file("src/dee/decimal.gleam", 906). -spec div_adjust(bigi:big_int(), bigi:big_int(), integer()) -> {bigi:big_int(), bigi:big_int(), integer()}. div_adjust(Ca, Cb, Adjust) -> case bigi_ffi:compare(Ca, Cb) of lt -> div_adjust(bigi_ffi:multiply(Ca, bigi_ffi:ten()), Cb, Adjust + 1); _ -> Cb_times_10 = bigi_ffi:multiply(Cb, bigi_ffi:ten()), case bigi_ffi:compare(Ca, Cb_times_10) of lt -> {Ca, Cb, Adjust}; eq -> {Ca, Cb, Adjust}; gt -> div_adjust(Ca, Cb_times_10, Adjust - 1) end end. -file("src/dee/decimal.gleam", 922). -spec div_calc( bigi:big_int(), bigi:big_int(), bigi:big_int(), integer(), bigi:big_int() ) -> {bigi:big_int(), integer(), bigi:big_int()}. div_calc(Ca, Cb, Coef, Adjust, Prec10) -> Digit = bigi_ffi:divide(Ca, Cb), Remainder = bigi_ffi:remainder(Ca, Cb), New_coef = bigi_ffi:add(Coef, Digit), case big_is_zero(Remainder) andalso (Adjust >= 0) of true -> {New_coef, Adjust, bigi_ffi:zero()}; false -> case bigi_ffi:compare(New_coef, Prec10) of gt -> {New_coef, Adjust, Remainder}; eq -> {New_coef, Adjust, Remainder}; lt -> div_calc( bigi_ffi:multiply(Remainder, bigi_ffi:ten()), Cb, bigi_ffi:multiply(New_coef, bigi_ffi:ten()), Adjust + 1, Prec10 ) end end. -file("src/dee/decimal.gleam", 964). -spec normalize_coef_exp_loop(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}. normalize_coef_exp_loop(Coef, Exp) -> case big_is_zero(bigi_ffi:remainder(Coef, bigi_ffi:ten())) of true -> normalize_coef_exp_loop( bigi_ffi:divide(Coef, bigi_ffi:ten()), Exp + 1 ); false -> {Coef, Exp} end. -file("src/dee/decimal.gleam", 957). -spec normalize_coef_exp(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}. normalize_coef_exp(Coef, Exp) -> case big_is_zero(Coef) of true -> {bigi_ffi:zero(), 0}; false -> normalize_coef_exp_loop(Coef, Exp) end. -file("src/dee/decimal.gleam", 1072). -spec compare_same_sign(bigi:big_int(), integer(), bigi:big_int(), integer()) -> gleam@order:order(). compare_same_sign(Ca, Ea, Cb, Eb) -> case Ea =:= Eb of true -> bigi_ffi:compare(Ca, Cb); false -> Min_exp = gleam@int:min(Ea, Eb), Aligned_a = bigi_ffi:multiply(Ca, big_pow10(Ea - Min_exp)), Aligned_b = bigi_ffi:multiply(Cb, big_pow10(Eb - Min_exp)), bigi_ffi:compare(Aligned_a, Aligned_b) end. -file("src/dee/decimal.gleam", 1088). -spec reverse_order(gleam@order:order()) -> gleam@order:order(). reverse_order(Ord) -> case Ord of lt -> gt; gt -> lt; eq -> eq end. -file("src/dee/decimal.gleam", 1027). -spec compare_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer() ) -> gleam@order:order(). compare_finite(Sa, Ca, Ea, Sb, Cb, Eb) -> Ca_zero = big_is_zero(Ca), Cb_zero = big_is_zero(Cb), case {Ca_zero, Cb_zero} of {true, true} -> eq; {true, false} -> case Sb of positive -> lt; negative -> gt end; {false, true} -> case Sa of positive -> gt; negative -> lt end; {false, false} -> case {Sa, Sb} of {positive, negative} -> gt; {negative, positive} -> lt; {positive, positive} -> compare_same_sign(Ca, Ea, Cb, Eb); {negative, negative} -> reverse_order(compare_same_sign(Ca, Ea, Cb, Eb)) end end. -file("src/dee/decimal.gleam", 996). -spec compare_ordered(decimal(), decimal()) -> gleam@order:order(). compare_ordered(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {infinity, infinity} -> case {erlang:element(2, A), erlang:element(2, B)} of {positive, positive} -> eq; {negative, negative} -> eq; {positive, negative} -> gt; {negative, positive} -> lt end; {infinity, _} -> case erlang:element(2, A) of positive -> gt; negative -> lt end; {_, infinity} -> case erlang:element(2, B) of positive -> lt; negative -> gt end; {{finite, Ca}, {finite, Cb}} -> compare_finite( erlang:element(2, A), Ca, erlang:element(4, A), erlang:element(2, B), Cb, erlang:element(4, B) ); {_, _} -> eq end. -file("src/dee/decimal.gleam", 986). ?DOC( " Compares two numbers numerically. Returns `Ok(order.Lt)`, `Ok(order.Eq)`,\n" " or `Ok(order.Gt)`. Returns `Error(Nil)` if either operand is `NaN` since\n" " `NaN` is not ordered.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " compare(from_int(1), from_int(2))\n" " // -> Ok(order.Lt)\n" "\n" " compare(nan(), from_int(1))\n" " // -> Error(Nil)\n" " ```\n" ). -spec compare(decimal(), decimal()) -> {ok, gleam@order:order()} | {error, nil}. compare(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> {error, nil}; {_, na_n} -> {error, nil}; {_, _} -> {ok, compare_ordered(A, B)} end. -file("src/dee/decimal.gleam", 1107). ?DOC( " Compares two numbers numerically and returns `True` if they are equal,\n" " otherwise `False`. Numerical equality means `1.0` equals `1.00`.\n" " `NaN` is not equal to itself.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"1.0\")\n" " eq(a, from_int(1))\n" " // -> True\n" " ```\n" ). -spec eq(decimal(), decimal()) -> boolean(). eq(A, B) -> case compare(A, B) of {ok, eq} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1116). ?DOC( " Compares two numbers numerically and returns `True` if the first\n" " argument is greater than the second, otherwise `False`.\n" ). -spec gt(decimal(), decimal()) -> boolean(). gt(A, B) -> case compare(A, B) of {ok, gt} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1125). ?DOC( " Compares two numbers numerically and returns `True` if the first\n" " argument is less than the second, otherwise `False`.\n" ). -spec lt(decimal(), decimal()) -> boolean(). lt(A, B) -> case compare(A, B) of {ok, lt} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1134). ?DOC( " Compares two numbers numerically and returns `True` if the first\n" " argument is greater than or equal to the second, otherwise `False`.\n" ). -spec gte(decimal(), decimal()) -> boolean(). gte(A, B) -> case compare(A, B) of {ok, gt} -> true; {ok, eq} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1143). ?DOC( " Compares two numbers numerically and returns `True` if the first\n" " argument is less than or equal to the second, otherwise `False`.\n" ). -spec lte(decimal(), decimal()) -> boolean(). lte(A, B) -> case compare(A, B) of {ok, lt} -> true; {ok, eq} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1178). -spec max_tiebreak(decimal(), decimal()) -> decimal(). max_tiebreak(A, B) -> gleam@bool:guard( erlang:element(2, A) /= erlang:element(2, B), case erlang:element(2, A) of positive -> A; negative -> B end, fun() -> case erlang:element(2, A) of positive -> case erlang:element(4, A) > erlang:element(4, B) of true -> A; false -> B end; negative -> case erlang:element(4, A) < erlang:element(4, B) of true -> A; false -> B end end end ). -file("src/dee/decimal.gleam", 1163). ?DOC( " Compares two values numerically and returns the maximum. Unlike most\n" " other functions, if a number is `NaN` the result will be the other number.\n" " If both are `NaN`, returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " max(from_int(1), from_int(2)) |> to_string\n" " // -> \"2\"\n" "\n" " max(from_int(1), nan()) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec max(decimal(), decimal()) -> decimal(). max(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, na_n} -> nan(); {na_n, _} -> B; {_, na_n} -> A; {_, _} -> case compare_ordered(A, B) of gt -> A; lt -> B; eq -> max_tiebreak(A, B) end end. -file("src/dee/decimal.gleam", 1227). -spec min_tiebreak(decimal(), decimal()) -> decimal(). min_tiebreak(A, B) -> gleam@bool:guard( erlang:element(2, A) /= erlang:element(2, B), case erlang:element(2, A) of negative -> A; positive -> B end, fun() -> case erlang:element(2, A) of positive -> case erlang:element(4, A) < erlang:element(4, B) of true -> A; false -> B end; negative -> case erlang:element(4, A) > erlang:element(4, B) of true -> A; false -> B end end end ). -file("src/dee/decimal.gleam", 1212). ?DOC( " Compares two values numerically and returns the minimum. Unlike most\n" " other functions, if a number is `NaN` the result will be the other number.\n" " If both are `NaN`, returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " min(from_int(1), from_int(2)) |> to_string\n" " // -> \"1\"\n" "\n" " min(from_int(1), nan()) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec min(decimal(), decimal()) -> decimal(). min(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, na_n} -> nan(); {na_n, _} -> B; {_, na_n} -> A; {_, _} -> case compare_ordered(A, B) of lt -> A; gt -> B; eq -> min_tiebreak(A, B) end end. -file("src/dee/decimal.gleam", 1352). -spec format_zero(binary(), integer()) -> binary(). format_zero(Sign_str, Exp) -> case Exp of 0 -> erlang:list_to_binary([Sign_str, <<"0"/utf8>>]); _ when Exp < 0 -> Zeros = gleam@string:repeat(<<"0"/utf8>>, - Exp), erlang:list_to_binary([Sign_str, <<"0."/utf8>>, Zeros]); _ -> erlang:list_to_binary( [Sign_str, <<"0E+"/utf8>>, erlang:integer_to_binary(Exp)] ) end. -file("src/dee/decimal.gleam", 1363). -spec format_decimal_notation(binary(), binary(), integer(), integer()) -> binary(). format_decimal_notation(Sign_str, Coef_str, Coef_len, Exp) -> Decimal_pos = Coef_len + Exp, case Decimal_pos =< 0 of true -> Leading_zeros = gleam@string:repeat(<<"0"/utf8>>, - Decimal_pos), erlang:list_to_binary( [Sign_str, <<"0."/utf8>>, Leading_zeros, Coef_str] ); false -> gleam@bool:guard( Exp =:= 0, erlang:list_to_binary([Sign_str, Coef_str]), fun() -> Int_part = gleam@string:slice(Coef_str, 0, Decimal_pos), Frac_part = gleam@string:slice( Coef_str, Decimal_pos, Coef_len - Decimal_pos ), erlang:list_to_binary( [Sign_str, Int_part, <<"."/utf8>>, Frac_part] ) end ) end. -file("src/dee/decimal.gleam", 1385). -spec format_e_notation(binary(), binary(), integer(), integer()) -> binary(). format_e_notation(Sign_str, Coef_str, Coef_len, Adjusted_exp) -> First_digit = gleam@string:slice(Coef_str, 0, 1), Rest_digits = gleam@string:slice(Coef_str, 1, Coef_len - 1), Exp_sign = case Adjusted_exp >= 0 of true -> <<"+"/utf8>>; false -> <<""/utf8>> end, case gleam@string:is_empty(Rest_digits) of true -> erlang:list_to_binary( [Sign_str, First_digit, <<"E"/utf8>>, Exp_sign, erlang:integer_to_binary(Adjusted_exp)] ); false -> erlang:list_to_binary( [Sign_str, First_digit, <<"."/utf8>>, Rest_digits, <<"E"/utf8>>, Exp_sign, erlang:integer_to_binary(Adjusted_exp)] ) end. -file("src/dee/decimal.gleam", 1334). -spec to_string_finite(sign(), bigi:big_int(), integer()) -> binary(). to_string_finite(S, Coef, Exp) -> Sign_str = case S of positive -> <<""/utf8>>; negative -> <<"-"/utf8>> end, gleam@bool:guard( big_is_zero(Coef), format_zero(Sign_str, Exp), fun() -> Coef_str = erlang:integer_to_binary(Coef), Coef_len = string:length(Coef_str), Adjusted_exp = (Exp + Coef_len) - 1, case (Exp =< 0) andalso (Adjusted_exp >= -6) of true -> format_decimal_notation(Sign_str, Coef_str, Coef_len, Exp); false -> format_e_notation( Sign_str, Coef_str, Coef_len, Adjusted_exp ) end end ). -file("src/dee/decimal.gleam", 1318). ?DOC( " Converts the given number to its string representation. Uses decimal\n" " notation when possible, scientific notation for very large or very small\n" " exponents.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.00\")\n" " to_string(d)\n" " // -> \"1.00\"\n" "\n" " to_string(from_int(42))\n" " // -> \"42\"\n" " ```\n" ). -spec to_string(decimal()) -> binary(). to_string(D) -> case erlang:element(3, D) of na_n -> <<"NaN"/utf8>>; infinity -> case erlang:element(2, D) of positive -> <<"Infinity"/utf8>>; negative -> <<"-Infinity"/utf8>> end; {finite, Coef} -> to_string_finite(erlang:element(2, D), Coef, erlang:element(4, D)) end. -file("src/dee/decimal.gleam", 1442). -spec to_string_scientific_finite(sign(), bigi:big_int(), integer()) -> binary(). to_string_scientific_finite(S, Coef, Exp) -> Sign_str = case S of positive -> <<""/utf8>>; negative -> <<"-"/utf8>> end, case big_is_zero(Coef) of true -> erlang:list_to_binary([Sign_str, <<"0E+0"/utf8>>]); false -> {Norm_coef, Norm_exp} = normalize_coef_exp(Coef, Exp), Coef_str = erlang:integer_to_binary(Norm_coef), Coef_len = string:length(Coef_str), First_digit = gleam@string:slice(Coef_str, 0, 1), Rest_digits = gleam@string:slice(Coef_str, 1, Coef_len - 1), Adjusted_exp = (Norm_exp + Coef_len) - 1, Exp_sign = case Adjusted_exp >= 0 of true -> <<"+"/utf8>>; false -> <<""/utf8>> end, case gleam@string:is_empty(Rest_digits) of true -> erlang:list_to_binary( [Sign_str, First_digit, <<"E"/utf8>>, Exp_sign, erlang:integer_to_binary(Adjusted_exp)] ); false -> erlang:list_to_binary( [Sign_str, First_digit, <<"."/utf8>>, Rest_digits, <<"E"/utf8>>, Exp_sign, erlang:integer_to_binary(Adjusted_exp)] ) end end. -file("src/dee/decimal.gleam", 1428). ?DOC( " Converts the given number to a string in scientific notation.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"123.45\")\n" " to_string_scientific(d)\n" " // -> \"1.2345E+2\"\n" " ```\n" ). -spec to_string_scientific(decimal()) -> binary(). to_string_scientific(D) -> case erlang:element(3, D) of na_n -> <<"NaN"/utf8>>; infinity -> case erlang:element(2, D) of positive -> <<"Infinity"/utf8>>; negative -> <<"-Infinity"/utf8>> end; {finite, Coef} -> to_string_scientific_finite( erlang:element(2, D), Coef, erlang:element(4, D) ) end. -file("src/dee/decimal.gleam", 1500). ?DOC( " Converts the given number to a string in raw format showing the\n" " coefficient and exponent directly: `\"12345E-2\"`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"123.45\")\n" " to_string_raw(d)\n" " // -> \"12345E-2\"\n" " ```\n" ). -spec to_string_raw(decimal()) -> binary(). to_string_raw(D) -> case erlang:element(3, D) of na_n -> <<"NaN"/utf8>>; infinity -> case erlang:element(2, D) of positive -> <<"Infinity"/utf8>>; negative -> <<"-Infinity"/utf8>> end; {finite, Coef} -> Sign_str = case erlang:element(2, D) of positive -> <<""/utf8>>; negative -> <<"-"/utf8>> end, Exp_sign = case erlang:element(4, D) >= 0 of true -> <<"+"/utf8>>; false -> <<""/utf8>> end, erlang:list_to_binary( [Sign_str, erlang:integer_to_binary(Coef), <<"E"/utf8>>, Exp_sign, erlang:integer_to_binary(erlang:element(4, D))] ) end. -file("src/dee/decimal.gleam", 1591). -spec expand_positive_exp(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}. expand_positive_exp(Coef, Exp) -> case Exp > 0 of true -> expand_positive_exp( bigi_ffi:multiply(Coef, bigi_ffi:ten()), Exp - 1 ); false -> {Coef, Exp} end. -file("src/dee/decimal.gleam", 1599). -spec remove_trailing_zeros_xsd(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}. remove_trailing_zeros_xsd(Coef, Exp) -> case Exp >= -1 of true -> {Coef, Exp}; false -> case big_is_zero(bigi_ffi:remainder(Coef, bigi_ffi:ten())) of true -> remove_trailing_zeros_xsd( bigi_ffi:divide(Coef, bigi_ffi:ten()), Exp + 1 ); false -> {Coef, Exp} end end. -file("src/dee/decimal.gleam", 1571). -spec canonical_xsd(bigi:big_int(), integer()) -> {bigi:big_int(), integer()}. canonical_xsd(Coef, Exp) -> case big_is_zero(Coef) of true -> {bigi_ffi:zero(), -1}; false -> {C, E} = expand_positive_exp(Coef, Exp), case E >= 0 of true -> {bigi_ffi:multiply(C, bigi_ffi:ten()), -1}; false -> remove_trailing_zeros_xsd(C, E) end end. -file("src/dee/decimal.gleam", 1543). ?DOC( " Converts the given number to a string in XSD canonical format.\n" " Integers always include `\".0\"`, trailing zeros are removed (keeping at\n" " least one decimal digit), and scientific notation is never used.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " to_string_xsd(from_int(42))\n" " // -> \"42.0\"\n" "\n" " let assert Ok(d) = from_string(\"1.00\")\n" " to_string_xsd(d)\n" " // -> \"1.0\"\n" " ```\n" ). -spec to_string_xsd(decimal()) -> binary(). to_string_xsd(D) -> case erlang:element(3, D) of na_n -> case erlang:element(2, D) of positive -> <<"NaN"/utf8>>; negative -> <<"-NaN"/utf8>> end; infinity -> case erlang:element(2, D) of positive -> <<"Infinity"/utf8>>; negative -> <<"-Infinity"/utf8>> end; {finite, Coef} -> {Xsd_coef, Xsd_exp} = canonical_xsd(Coef, erlang:element(4, D)), Sign_str = case erlang:element(2, D) of positive -> <<""/utf8>>; negative -> <<"-"/utf8>> end, gleam@bool:guard( big_is_zero(Xsd_coef), erlang:list_to_binary([Sign_str, <<"0.0"/utf8>>]), fun() -> to_string_finite(erlang:element(2, D), Xsd_coef, Xsd_exp) end ) end. -file("src/dee/decimal.gleam", 1636). -spec to_int_finite(sign(), bigi:big_int(), integer()) -> {ok, integer()} | {error, nil}. to_int_finite(S, Coef, Exp) -> gleam@result:'try'(case Exp >= 0 of true -> {ok, bigi_ffi:multiply(Coef, big_pow10(Exp))}; false -> Divisor = big_pow10(- Exp), case big_is_zero(bigi_ffi:remainder(Coef, Divisor)) of true -> {ok, bigi_ffi:divide(Coef, Divisor)}; false -> {error, nil} end end, fun(Value) -> Signed = case S of positive -> Value; negative -> bigi_ffi:negate(Value) end, _pipe = bigi_ffi:to(Signed), gleam@result:replace_error(_pipe, nil) end). -file("src/dee/decimal.gleam", 1629). ?DOC( " Returns the decimal converted to an integer. Fails when the decimal has\n" " a fractional part, is `NaN`, or is `Infinity`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " to_int(from_int(42))\n" " // -> Ok(42)\n" "\n" " let assert Ok(d) = from_string(\"1.00\")\n" " to_int(d)\n" " // -> Ok(1)\n" "\n" " let assert Ok(d) = from_string(\"1.5\")\n" " to_int(d)\n" " // -> Error(Nil)\n" " ```\n" ). -spec to_int(decimal()) -> {ok, integer()} | {error, nil}. to_int(D) -> case erlang:element(3, D) of na_n -> {error, nil}; infinity -> {error, nil}; {finite, Coef} -> to_int_finite(erlang:element(2, D), Coef, erlang:element(4, D)) end. -file("src/dee/decimal.gleam", 1665). ?DOC( " Returns the decimal converted to a float. The returned float may have\n" " lower precision than the decimal. Returns `Error(Nil)` for `NaN` and\n" " `Infinity` since these are not representable as floats on all targets.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.5\")\n" " to_float(d)\n" " // -> Ok(1.5)\n" " ```\n" ). -spec to_float(decimal()) -> {ok, float()} | {error, nil}. to_float(D) -> case erlang:element(3, D) of na_n -> {error, nil}; infinity -> {error, nil}; {finite, _} -> Str = to_string(D), case gleam_stdlib:parse_float(Str) of {ok, F} -> {ok, F}; {error, _} -> _pipe = gleam_stdlib:parse_int(Str), _pipe@1 = gleam@result:map(_pipe, fun erlang:float/1), gleam@result:replace_error(_pipe@1, nil) end end. -file("src/dee/decimal.gleam", 1694). ?DOC( " Returns the scale of the decimal. A decimal's scale is the number of\n" " digits after the decimal point.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " scale(from_int(42))\n" " // -> 0\n" "\n" " let assert Ok(d) = from_string(\"99.12345\")\n" " scale(d)\n" " // -> 5\n" " ```\n" ). -spec scale(decimal()) -> integer(). scale(D) -> case erlang:element(4, D) < 0 of true -> - erlang:element(4, D); false -> 0 end. -file("src/dee/decimal.gleam", 1725). -spec round_coef( bigi:big_int(), bigi:big_int(), bigi:big_int(), sign(), rounding_mode() ) -> bigi:big_int(). round_coef(Quotient, Remainder, Divisor, S, Mode) -> gleam@bool:guard( big_is_zero(Remainder), Quotient, fun() -> Half_divisor = bigi_ffi:divide(Divisor, big_two()), Divisor_even = big_is_zero(bigi_ffi:remainder(Divisor, big_two())), At_midpoint = (bigi_ffi:compare(Remainder, Half_divisor) =:= eq) andalso Divisor_even, Double_rem = bigi_ffi:multiply(Remainder, big_two()), Should_round_up = case Mode of down -> false; up -> true; ceiling -> S =:= positive; floor -> S =:= negative; half_up -> bigi_ffi:compare(Double_rem, Divisor) /= lt; half_down -> bigi_ffi:compare(Double_rem, Divisor) =:= gt; half_even -> case At_midpoint of true -> not big_is_zero( bigi_ffi:remainder(Quotient, big_two()) ); false -> bigi_ffi:compare(Double_rem, Divisor) =:= gt end end, case Should_round_up of true -> bigi_ffi:add(Quotient, bigi_ffi:one()); false -> Quotient end end ). -file("src/dee/decimal.gleam", 1761). -spec round_finite( sign(), bigi:big_int(), integer(), integer(), rounding_mode() ) -> decimal(). round_finite(S, Coef, Exp, Places, Mode) -> Target_exp = - Places, gleam@bool:guard( Exp >= Target_exp, begin Pad = Exp - Target_exp, {decimal, S, {finite, bigi_ffi:multiply(Coef, big_pow10(Pad))}, Target_exp} end, fun() -> Shift = Target_exp - Exp, Divisor = big_pow10(Shift), Quotient = bigi_ffi:divide(Coef, Divisor), Remainder = bigi_ffi:subtract( Coef, bigi_ffi:multiply(Quotient, Divisor) ), Final_coef = round_coef(Quotient, Remainder, Divisor, S, Mode), {decimal, S, {finite, Final_coef}, Target_exp} end ). -file("src/dee/decimal.gleam", 1716). ?DOC( " Rounds the given number to the specified number of decimal places with\n" " the given rounding mode.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.234\")\n" " round(d, 1, HalfUp) |> to_string\n" " // -> \"1.2\"\n" "\n" " round(d, 0, HalfUp) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec round(decimal(), integer(), rounding_mode()) -> decimal(). round(D, Places, Mode) -> case erlang:element(3, D) of na_n -> nan(); infinity -> D; {finite, Coef} -> round_finite( erlang:element(2, D), Coef, erlang:element(4, D), Places, Mode ) end. -file("src/dee/decimal.gleam", 1803). ?DOC( " Normalizes the given decimal by removing trailing zeros from the\n" " coefficient while keeping the number numerically equivalent by\n" " increasing the exponent.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.00\")\n" " normalize(d) |> to_string\n" " // -> \"1\"\n" "\n" " let assert Ok(d) = from_string(\"1.01\")\n" " normalize(d) |> to_string\n" " // -> \"1.01\"\n" " ```\n" ). -spec normalize(decimal()) -> decimal(). normalize(D) -> case erlang:element(3, D) of na_n -> nan(); infinity -> D; {finite, Coef} -> {Norm_coef, Norm_exp} = normalize_coef_exp( Coef, erlang:element(4, D) ), {decimal, erlang:element(2, D), {finite, Norm_coef}, Norm_exp} end. -file("src/dee/decimal.gleam", 1853). -spec count_digits_acc(bigi:big_int(), integer()) -> integer(). count_digits_acc(N, Acc) -> case bigi_ffi:compare(N, bigi_ffi:zero()) of lt -> case Acc =:= 0 of true -> 1; false -> Acc end; eq -> case Acc =:= 0 of true -> 1; false -> Acc end; gt -> count_digits_acc(bigi_ffi:divide(N, bigi_ffi:ten()), Acc + 1) end. -file("src/dee/decimal.gleam", 1849). -spec count_digits(bigi:big_int()) -> integer(). count_digits(N) -> count_digits_acc(N, 0). -file("src/dee/decimal.gleam", 1829). ?DOC( " Applies the given context to a decimal, rounding to the specified\n" " precision (significant digits) using the specified rounding mode.\n" " Special values (`NaN`, `Infinity`) pass through unchanged.\n" "\n" " Use `default_context()` for standard precision (28 digits, HalfUp).\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " apply_context(from_int(123), context(2, HalfUp)) |> to_string\n" " // -> \"1.2E+2\"\n" "\n" " apply_context(from_int(5), default_context()) |> to_string\n" " // -> \"5\"\n" " ```\n" ). -spec apply_context(decimal(), context()) -> decimal(). apply_context(D, Ctx) -> case erlang:element(3, D) of na_n -> D; infinity -> D; {finite, Coef} -> gleam@bool:guard( big_is_zero(Coef), D, fun() -> Digits = count_digits(Coef), gleam@bool:guard( Digits =< erlang:element(2, Ctx), D, fun() -> Shift = Digits - erlang:element(2, Ctx), Divisor = big_pow10(Shift), Quotient = bigi_ffi:divide(Coef, Divisor), Remainder = bigi_ffi:subtract( Coef, bigi_ffi:multiply(Quotient, Divisor) ), Final_coef = round_coef( Quotient, Remainder, Divisor, erlang:element(2, D), erlang:element(3, Ctx) ), {decimal, erlang:element(2, D), {finite, Final_coef}, erlang:element(4, D) + Shift} end ) end ) end. -file("src/dee/decimal.gleam", 749). -spec sqrt_finite(bigi:big_int(), integer(), context()) -> decimal(). sqrt_finite(Coef, Exp, Ctx) -> Precision = erlang:element(2, Ctx) + 1, Num_digits = count_digits(Coef), {Adjusted_coef, Shift} = case (Exp rem 2) =:= 0 of true -> Extra = Precision - ((Num_digits + 1) div 2), {Coef, Extra}; false -> Extra@1 = Precision - ((Num_digits div 2) + 1), {bigi_ffi:multiply(Coef, bigi_ffi:ten()), Extra@1} end, {Scaled_coef, Shift_exact} = case Shift >= 0 of true -> {bigi_ffi:multiply(Adjusted_coef, big_pow10(Shift * 2)), true}; false -> Divisor = big_pow10(- Shift * 2), {bigi_ffi:divide(Adjusted_coef, Divisor), big_is_zero(bigi_ffi:remainder(Adjusted_coef, Divisor))} end, Initial_guess = big_pow10(Precision + 1), Root = sqrt_loop(Initial_guess, Scaled_coef), Is_exact = Shift_exact andalso (bigi_ffi:compare( bigi_ffi:multiply(Root, Root), Scaled_coef ) =:= eq), Preferred_exp = case (Exp < 0) andalso ((Exp rem 2) /= 0) of true -> (Exp - 1) div 2; false -> Exp div 2 end, case Is_exact of true -> Result_coef = case Shift >= 0 of true -> bigi_ffi:divide(Root, big_pow10(Shift)); false -> bigi_ffi:multiply(Root, big_pow10(- Shift)) end, Result = {decimal, positive, {finite, Result_coef}, Preferred_exp}, apply_context(Result, Ctx); false -> Result_exp = Preferred_exp - Shift, Result@1 = {decimal, positive, {finite, Root}, Result_exp}, apply_context(Result@1, Ctx) end. -file("src/dee/decimal.gleam", 729). ?DOC( " Square root with explicit context.\n" " Uses Newton's (Babylonian) method with integer arithmetic.\n" " Returns NaN for negative inputs, zero for zero, infinity() for infinity().\n" ). -spec sqrt_with_context(decimal(), context()) -> decimal(). sqrt_with_context(D, Ctx) -> case erlang:element(3, D) of na_n -> nan(); infinity -> case erlang:element(2, D) of positive -> infinity(); negative -> nan() end; {finite, Coef} -> gleam@bool:guard( big_is_zero(Coef), D, fun() -> case erlang:element(2, D) of negative -> nan(); positive -> sqrt_finite(Coef, erlang:element(4, D), Ctx) end end ) end. -file("src/dee/decimal.gleam", 722). ?DOC( " Finds the square root of the given number using the default context\n" " (precision 28, HalfUp rounding).\n" "\n" " Returns `NaN` for negative inputs, zero for zero, and infinity() for infinity().\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"100\")\n" " square_root(d) |> to_string\n" " // -> \"10\"\n" " ```\n" ). -spec square_root(decimal()) -> decimal(). square_root(D) -> sqrt_with_context(D, default_context()). -file("src/dee/decimal.gleam", 880). -spec div_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer(), context() ) -> decimal(). div_finite(Sa, Ca, Ea, Sb, Cb, Eb, Ctx) -> Result_sign = mult_signs(Sa, Sb), {Adj_ca, Adj_cb, Adjust} = div_adjust(Ca, Cb, 0), Prec10 = big_pow10(erlang:element(2, Ctx)), {Coef, Final_adjust, _} = div_calc( Adj_ca, Adj_cb, bigi_ffi:zero(), Adjust, Prec10 ), Result_exp = (Ea - Eb) - Final_adjust, Result = {decimal, Result_sign, {finite, Coef}, Result_exp}, apply_context(Result, Ctx). -file("src/dee/decimal.gleam", 853). ?DOC( " Divides two numbers using the given context for precision and rounding.\n" " Division by zero returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let ctx = context(10, HalfEven)\n" " divide_with_context(from_int(1), from_int(3), ctx) |> to_string\n" " // -> \"0.3333333333\"\n" " ```\n" ). -spec divide_with_context(decimal(), decimal(), context()) -> decimal(). divide_with_context(A, B, Ctx) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> nan(); {_, na_n} -> nan(); {infinity, infinity} -> nan(); {_, {finite, Cb}} -> gleam@bool:guard( big_is_zero(Cb), nan(), fun() -> Result_sign = mult_signs( erlang:element(2, A), erlang:element(2, B) ), case erlang:element(3, A) of infinity -> {decimal, Result_sign, infinity, 0}; {finite, Ca} -> gleam@bool:guard( big_is_zero(Ca), {decimal, Result_sign, {finite, bigi_ffi:zero()}, 0}, fun() -> div_finite( erlang:element(2, A), Ca, erlang:element(4, A), erlang:element(2, B), Cb, erlang:element(4, B), Ctx ) end ); _ -> nan() end end ); {_, infinity} -> Result_sign@1 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@1, {finite, bigi_ffi:zero()}, 0} end. -file("src/dee/decimal.gleam", 839). ?DOC( " Divides two numbers using the default context (precision 28, HalfUp\n" " rounding). Division by zero returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " divide(from_int(3), from_int(4)) |> to_string\n" " // -> \"0.75\"\n" " ```\n" ). -spec divide(decimal(), decimal()) -> decimal(). divide(A, B) -> divide_with_context(A, B, default_context()). -file("src/dee/decimal.gleam", 1878). ?DOC( " Returns `True` if the number is `NaN`, otherwise `False`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " is_nan(nan())\n" " // -> True\n" "\n" " is_nan(from_int(42))\n" " // -> False\n" " ```\n" ). -spec is_nan(decimal()) -> boolean(). is_nan(D) -> case erlang:element(3, D) of na_n -> true; _ -> false end. -file("src/dee/decimal.gleam", 1897). ?DOC( " Returns `True` if the number is positive or negative infinity(),\n" " otherwise `False`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " is_inf(infinity())\n" " // -> True\n" "\n" " is_inf(from_int(1))\n" " // -> False\n" " ```\n" ). -spec is_inf(decimal()) -> boolean(). is_inf(D) -> case erlang:element(3, D) of infinity -> true; _ -> false end. -file("src/dee/decimal.gleam", 1916). ?DOC( " Returns `True` if the given number is positive, otherwise `False`.\n" " Returns `False` for zero, `NaN`, and `Infinity`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " is_positive(from_int(42))\n" " // -> True\n" "\n" " is_positive(from_int(0))\n" " // -> False\n" " ```\n" ). -spec is_positive(decimal()) -> boolean(). is_positive(D) -> case erlang:element(3, D) of na_n -> false; infinity -> false; {finite, Coef} -> (erlang:element(2, D) =:= positive) andalso (bigi_ffi:compare( Coef, bigi_ffi:zero() ) =:= gt) end. -file("src/dee/decimal.gleam", 1935). ?DOC( " Returns `True` if the given number is negative, otherwise `False`.\n" " Returns `False` for zero, `NaN`, and `Infinity`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " is_negative(from_int(-42))\n" " // -> True\n" "\n" " is_negative(from_int(0))\n" " // -> False\n" " ```\n" ). -spec is_negative(decimal()) -> boolean(). is_negative(D) -> case erlang:element(3, D) of na_n -> false; infinity -> false; {finite, Coef} -> (erlang:element(2, D) =:= negative) andalso (bigi_ffi:compare( Coef, bigi_ffi:zero() ) =:= gt) end. -file("src/dee/decimal.gleam", 1261). ?DOC( " Compares two numbers numerically with a threshold tolerance. If the\n" " second number is within the range of `a - threshold` to `a + threshold`,\n" " the numbers are considered equal. Returns `Error(Nil)` for negative\n" " thresholds or `NaN` operands.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"1.1\")\n" " let assert Ok(t) = from_string(\"0.2\")\n" " compare_with_threshold(a, from_int(1), t)\n" " // -> Ok(order.Eq)\n" " ```\n" ). -spec compare_with_threshold(decimal(), decimal(), decimal()) -> {ok, gleam@order:order()} | {error, nil}. compare_with_threshold(A, B, Threshold) -> gleam@bool:guard( is_negative(Threshold), {error, nil}, fun() -> Upper = add(A, Threshold), Lower = subtract(A, Threshold), case {compare(Upper, B), compare(Lower, B)} of {{ok, Upper_cmp}, {ok, Lower_cmp}} -> B_lte_upper = (Upper_cmp =:= gt) orelse (Upper_cmp =:= eq), B_gte_lower = (Lower_cmp =:= lt) orelse (Lower_cmp =:= eq), gleam@bool:guard( B_lte_upper andalso B_gte_lower, {ok, eq}, fun() -> case Upper_cmp of lt -> {ok, lt}; _ -> {ok, gt} end end ); {_, _} -> {error, nil} end end ). -file("src/dee/decimal.gleam", 1295). ?DOC( " Tests if two numbers are equal within a threshold tolerance. If the\n" " second number is within the range of `a - threshold` to `a + threshold`,\n" " returns `True`. Returns `False` for negative thresholds or `NaN` operands.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(a) = from_string(\"1.2\")\n" " let assert Ok(t) = from_string(\"0.2\")\n" " eq_with_threshold(a, from_int(1), t)\n" " // -> True\n" " ```\n" ). -spec eq_with_threshold(decimal(), decimal(), decimal()) -> boolean(). eq_with_threshold(A, B, Threshold) -> case compare_with_threshold(A, B, Threshold) of {ok, eq} -> true; _ -> false end. -file("src/dee/decimal.gleam", 1953). ?DOC( " Returns `True` if the given number is zero, otherwise `False`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " is_zero(zero())\n" " // -> True\n" "\n" " is_zero(from_int(1))\n" " // -> False\n" " ```\n" ). -spec is_zero(decimal()) -> boolean(). is_zero(D) -> case erlang:element(3, D) of {finite, Coef} -> big_is_zero(Coef); _ -> false end. -file("src/dee/decimal.gleam", 1974). ?DOC( " Returns `True` when the given decimal has no significant digits after\n" " the decimal point. Returns `False` for `NaN` and `Infinity`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let assert Ok(d) = from_string(\"1.00\")\n" " is_integer(d)\n" " // -> True\n" "\n" " let assert Ok(d) = from_string(\"1.10\")\n" " is_integer(d)\n" " // -> False\n" " ```\n" ). -spec is_integer(decimal()) -> boolean(). is_integer(D) -> case erlang:element(3, D) of na_n -> false; infinity -> false; {finite, Coef} -> case erlang:element(4, D) >= 0 of true -> true; false -> Divisor = big_pow10(- erlang:element(4, D)), big_is_zero(bigi_ffi:remainder(Coef, Divisor)) end end. -file("src/dee/decimal.gleam", 2028). -spec div_int_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer() ) -> decimal(). div_int_finite(Sa, Ca, Ea, Sb, Cb, Eb) -> Result_sign = mult_signs(Sa, Sb), Exp_diff = Ea - Eb, {Aligned_a, Aligned_b} = case Exp_diff >= 0 of true -> {bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb}; false -> {Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff))} end, Quotient = bigi_ffi:divide(Aligned_a, Aligned_b), {decimal, Result_sign, {finite, Quotient}, 0}. -file("src/dee/decimal.gleam", 2002). ?DOC( " Divides two numbers and returns the integer part (truncated toward zero).\n" " Division by zero returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " divide_integer(from_int(5), from_int(2)) |> to_string\n" " // -> \"2\"\n" " ```\n" ). -spec divide_integer(decimal(), decimal()) -> decimal(). divide_integer(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> nan(); {_, na_n} -> nan(); {infinity, infinity} -> nan(); {_, {finite, Cb}} -> gleam@bool:guard( big_is_zero(Cb), nan(), fun() -> case erlang:element(3, A) of infinity -> Result_sign = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign, infinity, 0}; {finite, Ca} -> gleam@bool:guard( big_is_zero(Ca), zero(), fun() -> div_int_finite( erlang:element(2, A), Ca, erlang:element(4, A), erlang:element(2, B), Cb, erlang:element(4, B) ) end ); _ -> nan() end end ); {_, infinity} -> Result_sign@1 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@1, {finite, bigi_ffi:zero()}, 0} end. -file("src/dee/decimal.gleam", 2080). -spec rem_finite(sign(), bigi:big_int(), integer(), bigi:big_int(), integer()) -> decimal(). rem_finite(Sa, Ca, Ea, Cb, Eb) -> Exp_diff = Ea - Eb, {Aligned_a, Aligned_b, Result_exp} = case Exp_diff >= 0 of true -> {bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb, Eb}; false -> {Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff)), Ea} end, Remainder = bigi_ffi:remainder(Aligned_a, Aligned_b), {decimal, Sa, {finite, Remainder}, Result_exp}. -file("src/dee/decimal.gleam", 2060). ?DOC( " Returns the remainder of integer division of two numbers. The result\n" " will have the sign of the first number. Division by zero returns `NaN`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " remainder(from_int(5), from_int(2)) |> to_string\n" " // -> \"1\"\n" " ```\n" ). -spec remainder(decimal(), decimal()) -> decimal(). remainder(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> nan(); {_, na_n} -> nan(); {_, {finite, Cb}} -> gleam@bool:guard( big_is_zero(Cb), nan(), fun() -> case erlang:element(3, A) of infinity -> nan(); {finite, Ca} -> gleam@bool:guard( big_is_zero(Ca), zero(), fun() -> rem_finite( erlang:element(2, A), Ca, erlang:element(4, A), Cb, erlang:element(4, B) ) end ); _ -> nan() end end ); {infinity, _} -> nan(); {_, infinity} -> A end. -file("src/dee/decimal.gleam", 2128). -spec div_rem_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer() ) -> {decimal(), decimal()}. div_rem_finite(Sa, Ca, Ea, Sb, Cb, Eb) -> Result_sign = mult_signs(Sa, Sb), Exp_diff = Ea - Eb, {Aligned_a, Aligned_b, Result_exp} = case Exp_diff >= 0 of true -> {bigi_ffi:multiply(Ca, big_pow10(Exp_diff)), Cb, Eb}; false -> {Ca, bigi_ffi:multiply(Cb, big_pow10(- Exp_diff)), Ea} end, Quotient = bigi_ffi:divide(Aligned_a, Aligned_b), Remainder = bigi_ffi:remainder(Aligned_a, Aligned_b), {{decimal, Result_sign, {finite, Quotient}, 0}, {decimal, Sa, {finite, Remainder}, Result_exp}}. -file("src/dee/decimal.gleam", 2108). ?DOC( " Integer division of two numbers and the remainder. Returns both the\n" " quotient and remainder as a tuple. Should be used when both\n" " `divide_integer` and `remainder` are needed. Division by zero returns\n" " `#(nan(), nan())`.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " let #(q, r) = divide_remainder(from_int(5), from_int(2))\n" " to_string(q)\n" " // -> \"2\"\n" " to_string(r)\n" " // -> \"1\"\n" " ```\n" ). -spec divide_remainder(decimal(), decimal()) -> {decimal(), decimal()}. divide_remainder(A, B) -> case {erlang:element(3, A), erlang:element(3, B)} of {na_n, _} -> {nan(), nan()}; {_, na_n} -> {nan(), nan()}; {_, {finite, Cb}} -> gleam@bool:guard( big_is_zero(Cb), {nan(), nan()}, fun() -> case erlang:element(3, A) of infinity -> {nan(), nan()}; {finite, Ca} -> gleam@bool:guard( big_is_zero(Ca), {zero(), zero()}, fun() -> div_rem_finite( erlang:element(2, A), Ca, erlang:element(4, A), erlang:element(2, B), Cb, erlang:element(4, B) ) end ); _ -> {nan(), nan()} end end ); {infinity, _} -> {nan(), nan()}; {_, infinity} -> {zero(), A} end.