-module(dysmal). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dysmal.gleam"). -export([to_string/1, add/2, subtract/2, multiply/2, is_zero/1, absolute_value/1, negate/1, divide/2, divide_with_opts/3, square_root/1, square_root_with_opts/2, from_float/1, from_float_with_opts/2, from_int/1, from_int_with_opts/2, round/2, compare/2, compare_with_opts/3, from_string/1, from_string_with_opts/2]). -export_type([decimal/0, opts/0, opts_key/0, rounding_algorithm/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. -type decimal() :: any(). -type opts() :: {opts, integer(), rounding_algorithm()}. -type opts_key() :: precision | rounding. -type rounding_algorithm() :: round_half_up | round_half_down | round_down | round_ceiling | round_floor. -file("src/dysmal.gleam", 109). ?DOC( " Convert a Decimal to a String.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.to_string\n" " // -> \"1234.56\"\n" " ```\n" ). -spec to_string(decimal()) -> binary(). to_string(Decimal) -> decimal:to_binary(Decimal). -file("src/dysmal.gleam", 124). ?DOC( " Add two Decimal numbers together.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.add(dysmal.from_int(100))\n" " |> dysmal.to_string\n" " // -> \"1334.56\"\n" " ```\n" ). -spec add(decimal(), decimal()) -> decimal(). add(X, Y) -> decimal:add(X, Y). -file("src/dysmal.gleam", 139). ?DOC( " Subtract one Decimal from another.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.subtract(dysmal.from_int(100))\n" " |> dysmal.to_string\n" " // -> \"1134.56\"\n" " ```\n" ). -spec subtract(decimal(), decimal()) -> decimal(). subtract(X, Y) -> decimal:sub(X, Y). -file("src/dysmal.gleam", 154). ?DOC( " Multiply one Decimal by another.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.multiply(dysmal.from_int(2))\n" " |> dysmal.to_string\n" " // -> \"2469.12\"\n" " ```\n" ). -spec multiply(decimal(), decimal()) -> decimal(). multiply(X, Y) -> decimal:mult(X, Y). -file("src/dysmal.gleam", 262). ?DOC( " Check if a Decimal is zero.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.is_zero\n" " // -> False\n" " ```\n" ). -spec is_zero(decimal()) -> boolean(). is_zero(X) -> decimal:is_zero(X). -file("src/dysmal.gleam", 369). ?DOC( " Returns the absolute value of a Decimal.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.absolute_value\n" " |> dysmal.to_string\n" " // -> \"1234.56\"\n" " ```\n" "\n" " ```gleam\n" " -1234.56\n" " |> dysmal.from_float\n" " |> dysmal.absolute_value\n" " |> dysmal.to_string\n" " // -> \"1234.56\"\n" " ```\n" ). -spec absolute_value(decimal()) -> decimal(). absolute_value(X) -> decimal:abs(X). -file("src/dysmal.gleam", 384). ?DOC( " Returns the negative of a Decimal.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.negate\n" " |> dysmal.to_string\n" " // -> \"-1234.56\"\n" " ```\n" ). -spec negate(decimal()) -> decimal(). negate(X) -> decimal:minus(X). -file("src/dysmal.gleam", 400). ?DOC(" Default options for precision and rounding.\n"). -spec default_opts() -> opts(). default_opts() -> {opts, 2, round_half_up}. -file("src/dysmal.gleam", 436). ?DOC(" Helper to convert rounding algorithm to atom\n"). -spec rounding_to_atom(rounding_algorithm()) -> any(). rounding_to_atom(Rounding) -> case Rounding of round_half_up -> erlang:binary_to_atom(<<"round_half_up"/utf8>>); round_half_down -> erlang:binary_to_atom(<<"round_half_down"/utf8>>); round_down -> erlang:binary_to_atom(<<"round_down"/utf8>>); round_ceiling -> erlang:binary_to_atom(<<"round_ceiling"/utf8>>); round_floor -> erlang:binary_to_atom(<<"round_floor"/utf8>>) end. -file("src/dysmal.gleam", 413). ?DOC(" Convert Opts type to a Dict.\n"). -spec opts_to_dict(opts()) -> gleam@dict:dict(opts_key(), integer()). opts_to_dict(Opts) -> {opts, Precision, Rounding} = Opts, Rounding_atom = rounding_to_atom(Rounding), _pipe = maps:new(), _pipe@1 = gleam@dict:insert(_pipe, precision, Precision), gleam@dict:insert(_pipe@1, rounding, Rounding_atom). -file("src/dysmal.gleam", 172). ?DOC( " Divide one Decimal by another.\n" "\n" " An Error is returned if the divisor is zero.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56\n" " |> dysmal.from_float\n" " |> dysmal.divide(dysmal.from_int(2))\n" " // -> Ok(#(61728, -2))\n" " ```\n" ). -spec divide(decimal(), decimal()) -> {ok, decimal()} | {error, nil}. divide(X, Y) -> case decimal:is_zero(Y) of true -> {error, nil}; false -> {ok, decimal:divide(X, Y, opts_to_dict(default_opts()))} end. -file("src/dysmal.gleam", 195). ?DOC( " Divide one Decimal by another with precision and rounding options.\n" "\n" " An Error is returned if the divisor is zero.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1000\n" " |> dysmal.from_int\n" " |> dysmal.divide_with_opts(\n" " dysmal.from_int(3),\n" " dysmal.Opts(3, dysmal.RoundCeiling),\n" " )\n" " // -> Ok(#(333334, -3))\n" " ```\n" ). -spec divide_with_opts(decimal(), decimal(), opts()) -> {ok, decimal()} | {error, nil}. divide_with_opts(X, Y, Opts) -> case decimal:is_zero(Y) of true -> {error, nil}; false -> {ok, decimal:divide(X, Y, opts_to_dict(Opts))} end. -file("src/dysmal.gleam", 222). ?DOC( " Returns the square root of a Decimal.\n" "\n" " An Error is returned if the number is less than zero.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1000\n" " |> dysmal.from_int\n" " |> dysmal.square_root\n" " // -> Ok(#(316, -1))\n" " ```\n" ). -spec square_root(decimal()) -> {ok, decimal()} | {error, nil}. square_root(X) -> X_string = decimal:to_binary(X), case gleam_stdlib:string_starts_with(X_string, <<"-"/utf8>>) of true -> {error, nil}; false -> {ok, decimal:sqrt(X, opts_to_dict(default_opts()))} end. -file("src/dysmal.gleam", 243). ?DOC( " Returns the square root of a Decimal with precision and rounding options.\n" "\n" " An Error is returned if the number is less than zero.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234\n" " |> dysmal.from_int\n" " |> dysmal.square_root_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n" " // -> Ok(#(35128, -3))\n" " ```\n" ). -spec square_root_with_opts(decimal(), opts()) -> {ok, decimal()} | {error, nil}. square_root_with_opts(X, Opts) -> case decimal:to_binary(X) of <<"0.0"/utf8>> -> {error, nil}; _ -> {ok, decimal:sqrt(X, opts_to_dict(Opts))} end. -file("src/dysmal.gleam", 52). ?DOC( " Create a new Decimal from a Float.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " dysmal.from_float(1234.56)\n" " // -> #(123456, -2)\n" " ```\n" ). -spec from_float(float()) -> decimal(). from_float(Value) -> decimal:to_decimal(Value, opts_to_dict(default_opts())). -file("src/dysmal.gleam", 66). ?DOC( " Create a new Decimal from a Float with precision and rounding options.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234.56781111\n" " |> dysmal.from_float_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n" " // -> #(12345679, -4)\n" " ```\n" ). -spec from_float_with_opts(float(), opts()) -> decimal(). from_float_with_opts(Value, Opts) -> decimal:to_decimal(Value, opts_to_dict(Opts)). -file("src/dysmal.gleam", 79). ?DOC( " Create a new Decimal from an Int.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " dysmal.from_int(1234)\n" " // -> #(1234, 0)\n" " ```\n" ). -spec from_int(integer()) -> decimal(). from_int(Value) -> decimal:to_decimal(Value, opts_to_dict(default_opts())). -file("src/dysmal.gleam", 93). ?DOC( " Create a new Decimal from an Int with precision and rounding options.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 1234\n" " |> dysmal.from_int_with_opts(dysmal.Opts(4, dysmal.RoundCeiling))\n" " // -> #(1234, 0)\n" " ```\n" ). -spec from_int_with_opts(integer(), opts()) -> decimal(). from_int_with_opts(Value, Opts) -> decimal:to_decimal(Value, opts_to_dict(Opts)). -file("src/dysmal.gleam", 275). ?DOC( " Round a Decimal with precision and rounding options.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " 333.33\n" " |> dysmal.from_float\n" " |> dysmal.round(dysmal.Opts(1, dysmal.RoundCeiling))\n" " // -> #(3334, -1)\n" " ```\n" ). -spec round(decimal(), opts()) -> decimal(). round(X, Opts) -> decimal:round(erlang:element(3, Opts), X, erlang:element(2, Opts)). -file("src/dysmal.gleam", 304). ?DOC( " Compares two Decimals, returning an order.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " let x = dysmal.from_float(333.33)\n" " let y = dysmal.from_float(555.55)\n" " dysmal.compare(x, y)\n" " // -> order.Gt\n" " ```\n" "\n" " ```gleam\n" " let x = dysmal.from_float(555.55)\n" " let y = dysmal.from_float(333.33)\n" " dysmal.compare(x, y)\n" " // -> order.Lt\n" " ```\n" "\n" " ```gleam\n" " let x = dysmal.from_float(333.33)\n" " let y = dysmal.from_float(333.33)\n" " dysmal.compare(x, y)\n" " // -> order.Eq\n" " ```\n" ). -spec compare(decimal(), decimal()) -> gleam@order:order(). compare(X, Y) -> case decimal:fast_cmp(X, Y) of 1 -> gt; -1 -> lt; _ -> eq end. -file("src/dysmal.gleam", 340). ?DOC( " Compares two Decimals with precision and rounding options, returning an order.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " let x = dysmal.from_float(333.3333)\n" " let y = dysmal.from_float(555.5555)\n" " let opts = dysmal.Opts(1, dysmal.RoundCeiling)\n" " dysmal.compare_with_opts(x, y, opts)\n" " // -> order.Gt\n" " ```\n" "\n" " ```gleam\n" " let x = dysmal.from_float(555.5555)\n" " let y = dysmal.from_float(333.3333)\n" " let opts = dysmal.Opts(1, dysmal.RoundCeiling)\n" " dysmal.compare_with_opts(x, y, opts)\n" " // -> order.Lt\n" " ```\n" "\n" " ```gleam\n" " let x = dysmal.from_float(333.3999)\n" " let y = dysmal.from_float(333.3888)\n" " let opts = dysmal.Opts(1, dysmal.RoundCeiling)\n" " dysmal.compare_with_opts(x, y, opts)\n" " // -> order.Eq\n" " ```\n" ). -spec compare_with_opts(decimal(), decimal(), opts()) -> gleam@order:order(). compare_with_opts(X, Y, Opts) -> case decimal:cmp(X, Y, opts_to_dict(Opts)) of 1 -> gt; -1 -> lt; _ -> eq end. -file("src/dysmal.gleam", 23). ?DOC( " Create a new Decimal from a String.\n" "\n" " An Error is returned if the string is not a valid decimal number.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " dysmal.from_string(\"1234.56\")\n" " // -> Ok(#(123456, -2))\n" " ```\n" "\n" " ```gleam\n" " dysmal.from_string(\"abc\")\n" " // -> Error(Nil)\n" " ```\n" ). -spec from_string(binary()) -> {ok, decimal()} | {error, nil}. from_string(Value) -> erlang_decimal_ffi:to_decimal_binary(Value, opts_to_dict(default_opts())). -file("src/dysmal.gleam", 39). ?DOC( " Create a new Decimal from a String with precision and rounding options.\n" "\n" " An Error is returned if the string is not a valid decimal number.\n" "\n" " # Examples\n" "\n" " ```gleam\n" " \"1234.56789999\"\n" " |> dysmal.from_string_with_opts(dysmal.Opts(6, dysmal.RoundFloor))\n" " // -> Ok(#(1234567899, -6))\n" " ```\n" ). -spec from_string_with_opts(binary(), opts()) -> {ok, decimal()} | {error, nil}. from_string_with_opts(Value, Opts) -> erlang_decimal_ffi:to_decimal_binary(Value, opts_to_dict(Opts)).