-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, multiply/2, add/2, subtract/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", 105). ?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", 117). ?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", 134). ?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", 142). ?DOC(false). -spec coefficient(decimal()) -> dee@internal:coefficient(). coefficient(D) -> erlang:element(3, D). -file("src/dee/decimal.gleam", 155). ?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", 162). ?DOC(" Decimal zero (0).\n"). -spec zero() -> decimal(). zero() -> {decimal, positive, {finite, bigi_ffi:zero()}, 0}. -file("src/dee/decimal.gleam", 167). ?DOC(" Decimal one (1).\n"). -spec one() -> decimal(). one() -> {decimal, positive, {finite, bigi_ffi:one()}, 0}. -file("src/dee/decimal.gleam", 172). ?DOC(" Positive infinity().\n"). -spec infinity() -> decimal(). infinity() -> {decimal, positive, infinity, 0}. -file("src/dee/decimal.gleam", 177). ?DOC(" Negative infinity().\n"). -spec neg_infinity() -> decimal(). neg_infinity() -> {decimal, negative, infinity, 0}. -file("src/dee/decimal.gleam", 182). ?DOC(" NaN (Not a Number).\n"). -spec nan() -> decimal(). nan() -> {decimal, positive, na_n, 0}. -file("src/dee/decimal.gleam", 191). ?DOC( " Calculate 10^n as BigInt for non-negative n.\n" " Optimized with inline constants for common small values (0-9) to avoid\n" " bigi.power overhead. These cover most practical decimal operations.\n" ). -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); _ -> case N =< 0 of true -> bigi_ffi:one(); false -> 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 => 5691, 'end' => 5755, pattern_start => 5702, pattern_end => 5712}) end, Result@1 end end. -file("src/dee/decimal.gleam", 215). ?DOC(" Check if a BigInt is zero.\n"). -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", 220). ?DOC(" BigInt constant 2.\n"). -spec big_two() -> bigi:big_int(). big_two() -> bigi_ffi:from(2). -file("src/dee/decimal.gleam", 238). ?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", 261). ?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", 476). ?DOC(" Check if a character is a digit.\n"). -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", 493). -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", 484). ?DOC(" Trim leading zeros but keep at least one digit.\n"). -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", 438). ?DOC(" Build the final decimal from parsed components.\n"). -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", 507). -spec list_length_acc(list(any()), integer()) -> integer(). list_length_acc(List, Acc) -> case List of [] -> Acc; [_ | Rest] -> list_length_acc(Rest, Acc + 1) end. -file("src/dee/decimal.gleam", 503). ?DOC(" Get the length of a list.\n"). -spec list_length(list(any())) -> integer(). list_length(List) -> list_length_acc(List, 0). -file("src/dee/decimal.gleam", 519). -spec list_reverse_acc(list(HMQ), list(HMQ)) -> list(HMQ). list_reverse_acc(List, Acc) -> case List of [] -> Acc; [X | Rest] -> list_reverse_acc(Rest, [X | Acc]) end. -file("src/dee/decimal.gleam", 515). ?DOC(" Reverse a list.\n"). -spec list_reverse(list(HMN)) -> list(HMN). list_reverse(List) -> list_reverse_acc(List, []). -file("src/dee/decimal.gleam", 460). -spec take_digits_acc(list(binary()), list(binary())) -> {list(binary()), list(binary())}. take_digits_acc(Chars, Acc) -> case Chars of [] -> {list_reverse(Acc), []}; [C | Rest] -> case is_digit(C) of true -> take_digits_acc(Rest, [C | Acc]); false -> {list_reverse(Acc), Chars} end end. -file("src/dee/decimal.gleam", 456). ?DOC(" Take consecutive digit characters from the list.\n"). -spec take_digits(list(binary())) -> {list(binary()), list(binary())}. take_digits(Chars) -> take_digits_acc(Chars, []). -file("src/dee/decimal.gleam", 404). ?DOC(" Parse optional exponent part (e.g., e10, E-5).\n"). -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 [] -> {error, nil}; _ -> case gleam_stdlib:parse_int( erlang:list_to_binary(Exp_digits) ) of {ok, Exp_val} -> Total_exp = Base_exp + (Exp_sign * Exp_val), build_decimal(S, Digits, Total_exp, After_exp); {error, _} -> {error, nil} 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 [] -> {error, nil}; _ -> case gleam_stdlib:parse_int( erlang:list_to_binary(Exp_digits) ) of {ok, Exp_val} -> Total_exp = Base_exp + (Exp_sign * Exp_val), build_decimal(S, Digits, Total_exp, After_exp); {error, _} -> {error, nil} end end; _ -> build_decimal(S, Digits, Base_exp, Chars) end. -file("src/dee/decimal.gleam", 360). ?DOC(" Parse the numeric part (after optional sign).\n"). -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 = - list_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 = - list_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", 342). ?DOC(" Parse a numeric string (integer, decimal, or scientific notation).\n"). -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", 322). ?DOC(" Internal function to parse decimal string and return remaining input.\n"). -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", 288). ?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", 314). ?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", 537). ?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", 561). ?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", 579). ?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", 728). ?DOC(" Multiply two signs.\n"). -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", 689). ?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", 852). ?DOC( " Newton's iteration for integer square root.\n" " Converges when guess <= coef / guess.\n" ). -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", 947). ?DOC(" Adjust dividend/divisor so dividend >= divisor.\n"). -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", 963). ?DOC( " Division calculation using long division.\n" " After div_adjust, cb <= ca < cb * 10, so each digit is 1-9.\n" " This is O(precision) instead of O(quotient) with repeated subtraction.\n" ). -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", 1005). -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", 998). ?DOC(" Normalize coefficient and exponent by removing trailing zeros.\n"). -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", 1013). ?DOC(" Minimum of two integers.\n"). -spec int_min(integer(), integer()) -> integer(). int_min(A, B) -> case A < B of true -> A; false -> B end. -file("src/dee/decimal.gleam", 625). ?DOC(" Add two finite decimals.\n"). -spec add_finite( sign(), bigi:big_int(), integer(), sign(), bigi:big_int(), integer() ) -> decimal(). add_finite(Sa, Ca, Ea, Sb, Cb, Eb) -> Min_exp = 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", 602). ?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", 676). ?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", 1121). ?DOC( " Compare two positive finite decimals.\n" " Optimized with fast path for equal exponents (avoids BigInt multiplication).\n" ). -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 = 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", 1137). ?DOC(" Reverse the order.\n"). -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", 1076). ?DOC(" Compare two finite decimals.\n"). -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", 1045). ?DOC(" Compare two ordered (non-NaN) decimals.\n"). -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", 1035). ?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", 1156). ?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", 1165). ?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", 1174). ?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", 1183). ?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", 1192). ?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", 1212). ?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 -> case erlang:element(2, A) /= erlang:element(2, B) of true -> case erlang:element(2, A) of positive -> A; negative -> B end; false -> 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 end end. -file("src/dee/decimal.gleam", 1263). ?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 -> case erlang:element(2, A) /= erlang:element(2, B) of true -> case erlang:element(2, A) of negative -> A; positive -> B end; false -> 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 end end. -file("src/dee/decimal.gleam", 1402). ?DOC( " Convert a finite decimal to string using IEEE 754 to-scientific-string.\n" " Uses decimal notation when exp <= 0 and adjusted_exp >= -6.\n" " Uses E notation otherwise.\n" ). -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, case big_is_zero(Coef) of true -> case Exp of 0 -> erlang:list_to_binary([Sign_str, <<"0"/utf8>>]); _ -> case Exp < 0 of true -> Zeros = gleam@string:repeat(<<"0"/utf8>>, - Exp), erlang:list_to_binary( [Sign_str, <<"0."/utf8>>, Zeros] ); false -> erlang:list_to_binary( [Sign_str, <<"0E+"/utf8>>, erlang:integer_to_binary(Exp)] ) end end; false -> 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 -> 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 -> case Exp =:= 0 of true -> erlang:list_to_binary([Sign_str, Coef_str]); false -> 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; false -> 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 end end. -file("src/dee/decimal.gleam", 1386). ?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", 1514). ?DOC(" Convert a finite decimal to scientific notation.\n"). -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", 1500). ?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", 1572). ?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", 1667). ?DOC(" Expand positive exponent by multiplying coefficient.\n"). -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", 1675). ?DOC(" Remove trailing zeros from coefficient, but keep at least exp=-1.\n"). -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", 1647). ?DOC( " Transform a finite decimal to XSD canonical form.\n" " Ensures: at least one decimal digit, no trailing zeros beyond the mandatory one.\n" ). -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", 1615). ?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)), case big_is_zero(Xsd_coef) of true -> Sign_str = case erlang:element(2, D) of positive -> <<""/utf8>>; negative -> <<"-"/utf8>> end, erlang:list_to_binary([Sign_str, <<"0.0"/utf8>>]); false -> to_string_finite(erlang:element(2, D), Xsd_coef, Xsd_exp) end end. -file("src/dee/decimal.gleam", 1705). ?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} -> case erlang:element(4, D) >= 0 of true -> Value = bigi_ffi:multiply( Coef, big_pow10(erlang:element(4, D)) ), Signed = case erlang:element(2, D) of positive -> Value; negative -> bigi_ffi:negate(Value) end, case bigi_ffi:to(Signed) of {ok, N} -> {ok, N}; {error, _} -> {error, nil} end; false -> Divisor = big_pow10(- erlang:element(4, D)), case big_is_zero(bigi_ffi:remainder(Coef, Divisor)) of true -> Value@1 = bigi_ffi:divide(Coef, Divisor), Signed@1 = case erlang:element(2, D) of positive -> Value@1; negative -> bigi_ffi:negate(Value@1) end, case bigi_ffi:to(Signed@1) of {ok, N@1} -> {ok, N@1}; {error, _} -> {error, nil} end; false -> {error, nil} end end end. -file("src/dee/decimal.gleam", 1756). ?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, _} -> case gleam_stdlib:parse_int(Str) of {ok, I} -> {ok, erlang:float(I)}; {error, _} -> {error, nil} end end end. -file("src/dee/decimal.gleam", 1789). ?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", 1820). ?DOC(" Round a finite decimal to specified places.\n"). -spec round_finite( sign(), bigi:big_int(), integer(), integer(), rounding_mode() ) -> decimal(). round_finite(S, Coef, Exp, Places, Mode) -> Target_exp = - Places, case Exp >= Target_exp of true -> Pad = Exp - Target_exp, {decimal, S, {finite, bigi_ffi:multiply(Coef, big_pow10(Pad))}, Target_exp}; false -> Shift = Target_exp - Exp, Divisor = big_pow10(Shift), Quotient = bigi_ffi:divide(Coef, Divisor), Remainder = bigi_ffi:subtract( Coef, bigi_ffi:multiply(Quotient, Divisor) ), Should_round_up = case big_is_zero(Remainder) of true -> false; false -> 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, case Mode of down -> false; up -> true; ceiling -> S =:= positive; floor -> S =:= negative; half_up -> bigi_ffi:compare( bigi_ffi:multiply(Remainder, big_two()), Divisor ) /= lt; half_down -> bigi_ffi:compare( bigi_ffi:multiply(Remainder, big_two()), Divisor ) =:= gt; half_even -> case At_midpoint of true -> not big_is_zero( bigi_ffi:remainder(Quotient, big_two()) ); false -> bigi_ffi:compare( bigi_ffi:multiply(Remainder, big_two()), Divisor ) =:= gt end end end, Final_coef = case Should_round_up of true -> bigi_ffi:add(Quotient, bigi_ffi:one()); false -> Quotient end, {decimal, S, {finite, Final_coef}, Target_exp} end. -file("src/dee/decimal.gleam", 1811). ?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", 1906). ?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", 2010). -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", 2006). ?DOC(" Count the number of decimal digits in a non-negative BigInt.\n"). -spec count_digits(bigi:big_int()) -> integer(). count_digits(N) -> count_digits_acc(N, 0). -file("src/dee/decimal.gleam", 1932). ?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} -> case big_is_zero(Coef) of true -> D; false -> Digits = count_digits(Coef), case Digits > erlang:element(2, Ctx) of true -> 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) ), Should_round_up = case big_is_zero(Remainder) of true -> false; false -> 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, case erlang:element(3, Ctx) of down -> false; up -> true; ceiling -> erlang:element(2, D) =:= positive; floor -> erlang:element(2, D) =:= negative; half_up -> bigi_ffi:compare( bigi_ffi:multiply( Remainder, big_two() ), Divisor ) /= lt; half_down -> bigi_ffi:compare( bigi_ffi:multiply( Remainder, big_two() ), Divisor ) =:= gt; half_even -> case At_midpoint of true -> not big_is_zero( bigi_ffi:remainder( Quotient, big_two() ) ); false -> bigi_ffi:compare( bigi_ffi:multiply( Remainder, big_two() ), Divisor ) =:= gt end end end, Final_coef = case Should_round_up of true -> bigi_ffi:add(Quotient, bigi_ffi:one()); false -> Quotient end, {decimal, erlang:element(2, D), {finite, Final_coef}, erlang:element(4, D) + Shift}; false -> D end end end. -file("src/dee/decimal.gleam", 781). ?DOC(" Compute square_root of a positive finite decimal.\n"). -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 -> S = Precision - ((Num_digits + 1) div 2), {Coef, S}; false -> S@1 = Precision - ((Num_digits div 2) + 1), {bigi_ffi:multiply(Coef, bigi_ffi:ten()), S@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", 755). ?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} -> case big_is_zero(Coef) of true -> D; false -> 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", 748). ?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", 921). ?DOC(" Divide two finite decimals with precision limiting.\n"). -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", 886). ?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}} -> case big_is_zero(Cb) of true -> nan(); false -> 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} -> case big_is_zero(Ca) of true -> Result_sign@1 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@1, {finite, bigi_ffi:zero()}, 0}; false -> 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@2 = mult_signs( erlang:element(2, A), erlang:element(2, B) ), {decimal, Result_sign@2, {finite, bigi_ffi:zero()}, 0} end. -file("src/dee/decimal.gleam", 872). ?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", 2035). ?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", 2054). ?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", 2073). ?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", 2092). ?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", 1314). ?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) -> case is_negative(Threshold) of true -> {error, nil}; false -> Upper = add(A, Threshold), Lower = subtract(A, Threshold), case {compare(Upper, B), compare(Lower, B)} of {{ok, Upper_cmp}, {ok, Lower_cmp}} -> case {Upper_cmp, Lower_cmp} of {_, _} -> B_lte_upper = (Upper_cmp =:= gt) orelse (Upper_cmp =:= eq), B_gte_lower = (Lower_cmp =:= lt) orelse (Lower_cmp =:= eq), case B_lte_upper andalso B_gte_lower of true -> {ok, eq}; false -> case Upper_cmp of lt -> {ok, lt}; _ -> {ok, gt} end end end; {_, _} -> {error, nil} end end. -file("src/dee/decimal.gleam", 1363). ?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", 2110). ?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", 2131). ?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", 2189). ?DOC(" Integer division of two finite decimals.\n"). -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", 2159). ?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}} -> case big_is_zero(Cb) of true -> nan(); false -> 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} -> case big_is_zero(Ca) of true -> zero(); false -> 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", 2245). ?DOC(" Remainder of two finite decimals.\n"). -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", 2221). ?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}} -> case big_is_zero(Cb) of true -> nan(); false -> case erlang:element(3, A) of infinity -> nan(); {finite, Ca} -> case big_is_zero(Ca) of true -> zero(); false -> 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", 2297). ?DOC(" Div and remainder of two finite decimals.\n"). -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", 2273). ?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}} -> case big_is_zero(Cb) of true -> {nan(), nan()}; false -> case erlang:element(3, A) of infinity -> {nan(), nan()}; {finite, Ca} -> case big_is_zero(Ca) of true -> {zero(), zero()}; false -> 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.