Compare commits
No commits in common. "e456a96f526ba0155d5f4db0435c199555807a0b" and "6ba0c1f2aee594439c9ec9ca60d3d8f8d2c91ce4" have entirely different histories.
e456a96f52
...
6ba0c1f2ae
@ -1,717 +0,0 @@
|
|||||||
%%% @doc
|
|
||||||
%%% Formatting and reading functions for Gaju and Puck quantities
|
|
||||||
%%%
|
|
||||||
%%% The numbers involved in dealing with blockchain amounts are enormous
|
|
||||||
%%% by comparison to legacy forms of currency. It isn't so much that
|
|
||||||
%%% thousands of Gajus is hard to reason about, but rather that quadrillions
|
|
||||||
%%% of Pucks is quite hard to even lock on to visually.
|
|
||||||
%%%
|
|
||||||
%%% A normal commas and underscores method of decimal formatting is provided, as
|
|
||||||
%%% `us' formatting along with two additional approaches:
|
|
||||||
%%% - Japanese traditional myriad structure (`jp' style)
|
|
||||||
%%% - An internationalized variant inspired by the Japanese technique over periods
|
|
||||||
%%% (`metric' for SI prefixes, and `legacy' for Anglicized prefixes)
|
|
||||||
%%%
|
|
||||||
%%% These are all accessible via the `amount/N' functions.
|
|
||||||
%%%
|
|
||||||
%%% The `read/1' function can accept any of the output variants as a string and
|
|
||||||
%%% will return the number of pucks indicated by the provided string, allowing for
|
|
||||||
%%% simple copy/paste functionality as well as direct input using any of the
|
|
||||||
%%% supported notations.
|
|
||||||
%%% @end
|
|
||||||
|
|
||||||
-module(hz_format).
|
|
||||||
-vsn("0.7.0").
|
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
|
||||||
-license("GPL-3.0-or-later").
|
|
||||||
|
|
||||||
-export([amount/1, amount/2, amount/3,
|
|
||||||
approx_amount/2, approx_amount/3,
|
|
||||||
read/1,
|
|
||||||
one/1, mark/1,
|
|
||||||
price_to_string/1, string_to_price/1]).
|
|
||||||
|
|
||||||
-spec amount(Pucks) -> Formatted
|
|
||||||
when Pucks :: integer(),
|
|
||||||
Formatted :: string().
|
|
||||||
%% @doc
|
|
||||||
%% A convenience formatting function.
|
|
||||||
%% ```
|
|
||||||
%% hz_format:amount(1) ->
|
|
||||||
%% 木0.000,000,000,000,000,001
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(5000) ->
|
|
||||||
%% 木0.000,000,000,000,005
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(5000000000000000000) ->
|
|
||||||
%% 木5
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(500000123000000000000000) ->
|
|
||||||
%% 木500,000.123
|
|
||||||
%% '''
|
|
||||||
%% @equiv amount(us, Pucks).
|
|
||||||
|
|
||||||
amount(Pucks) ->
|
|
||||||
amount(us, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
-spec amount(Style, Pucks) -> Formatted
|
|
||||||
when Style :: us | jp | metric | legacy | {Separator, Span},
|
|
||||||
Separator :: $, | $_,
|
|
||||||
Span :: 3 | 4,
|
|
||||||
Pucks :: integer(),
|
|
||||||
Formatted :: string().
|
|
||||||
%% @doc
|
|
||||||
%% A money formatting function.
|
|
||||||
%% ```
|
|
||||||
%% hz_format:amount(us, 100500040123000000000000000) ->
|
|
||||||
%% 木100,500,040.123
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(jp, 100500040123000000000000000) ->
|
|
||||||
%% 1億50万40木 12京3000兆本
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(metric, 100500040123000000000000000) ->
|
|
||||||
%% 木100m 500k 40 G 123p P
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(legacy, 100500040123000000000000000) ->
|
|
||||||
%% 木100m 500k 40 G 123q P
|
|
||||||
%%
|
|
||||||
%% hz_format:amount({$_, 3}, 100500040123000000000000000) ->
|
|
||||||
%% 木100_500_040.123
|
|
||||||
%%
|
|
||||||
%% hz_format:amount({$_, 4}, 100500040123000000000000000) ->
|
|
||||||
%% 木1_0050_0040.123
|
|
||||||
%% '''
|
|
||||||
%% @equiv amount(gaju, Style, Pucks).
|
|
||||||
|
|
||||||
amount(Style, Pucks) ->
|
|
||||||
amount(gaju, Style, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
-spec amount(Unit, Style, Pucks) -> Formatted
|
|
||||||
when Unit :: gaju | puck,
|
|
||||||
Style :: us | jp | metric | legacy | {Separator, Span},
|
|
||||||
Separator :: $, | $_,
|
|
||||||
Span :: 3 | 4,
|
|
||||||
Pucks :: integer(),
|
|
||||||
Formatted :: string().
|
|
||||||
%% @doc
|
|
||||||
%% A simplified format function covering the most common formats desired.
|
|
||||||
%% ```
|
|
||||||
%% hz_format:amount(gaju, us, 100500040123000004500000000) ->
|
|
||||||
%% 木100,500,040.123,000,004,5
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(puck, us, 100500040123000004500000000) ->
|
|
||||||
%% 本100,500,040,123,000,004,500,000,000
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(gaju, jp, 100500040123000004500000000) ->
|
|
||||||
%% 1億50万40木 12京3000兆45億本
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(puck, jp, 100500040123000004500000000) ->
|
|
||||||
%% 100秭5000垓4012京3000兆45億本
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(gaju, metric, 100500040123000004500000000) ->
|
|
||||||
%% 木100m 500k 40 G 123p 4g 500m P
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(puck, metric, 100500040123000004500000000) ->
|
|
||||||
%% 本100y 500z 40e 123p 4g 500m P
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(gaju, legacy, 100500040123000004500000000) ->
|
|
||||||
%% 木100m 500k 40 G 123q 4b 500m P
|
|
||||||
%%
|
|
||||||
%% hz_format:amount(puck, legacy, 100500040123000004500000000) ->
|
|
||||||
%% 本100y 500z 40e 123q 4b 500m P
|
|
||||||
%% '''
|
|
||||||
|
|
||||||
amount(gaju, us, Pucks) ->
|
|
||||||
western($,, $., 3, all, Pucks);
|
|
||||||
amount(puck, us, Pucks) ->
|
|
||||||
western($,, 3, Pucks);
|
|
||||||
amount(Unit, jp, Pucks) ->
|
|
||||||
jp(Unit, all, Pucks);
|
|
||||||
amount(Unit, metric, Pucks) ->
|
|
||||||
bestern(Unit, ranks(metric), all, Pucks);
|
|
||||||
amount(Unit, legacy, Pucks) ->
|
|
||||||
bestern(Unit, ranks(heresy), all, Pucks);
|
|
||||||
amount(gaju, {Separator, Span}, Pucks) ->
|
|
||||||
western(Separator, $., Span, all, Pucks);
|
|
||||||
amount(puck, {Separator, Span}, Pucks) ->
|
|
||||||
western(Separator, Span, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-spec approx_amount(Precision, Pucks) -> Serialized
|
|
||||||
when Precision :: all | 0..18,
|
|
||||||
Pucks :: integer(),
|
|
||||||
Serialized :: string().
|
|
||||||
%% A formatter for decimal notation which permits a precision
|
|
||||||
%% value to be applied to the puck side of the format.
|
|
||||||
%% @equiv approx_amount(us, Precision, Pucks).
|
|
||||||
%% ```
|
|
||||||
%% hz_format:approx_amount(3, 100500040123000004500000001) ->
|
|
||||||
%% 木100,500,040.123...
|
|
||||||
%%
|
|
||||||
%% hz_format:approx_amount(13, 100500040123000004500000001) ->
|
|
||||||
%% 木100,500,040.123,000,004,5...
|
|
||||||
%%
|
|
||||||
%% hz_format:approx_amount(all, 100500040123000004500000001) ->
|
|
||||||
%% 木100,500,040.123,000,004,500,000,001
|
|
||||||
%% '''
|
|
||||||
|
|
||||||
approx_amount(Precision, Pucks) ->
|
|
||||||
approx_amount(us, Precision, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
-spec approx_amount(Style, Precision, Pucks) -> Serialized
|
|
||||||
when Style :: us | {Separator, Span},
|
|
||||||
Precision :: all | 0..18,
|
|
||||||
Separator :: $, | $_,
|
|
||||||
Span :: 3 | 4,
|
|
||||||
Pucks :: integer(),
|
|
||||||
Serialized :: string().
|
|
||||||
%% @doc
|
|
||||||
%% A formatter for decimal notation which permits a precision
|
|
||||||
%% value to be applied to the puck side of the format.
|
|
||||||
%% ```
|
|
||||||
%% hz_format:approx_amount({$_, 3}, 3, 100500040123000004500000001) ->
|
|
||||||
%% 木100_500_040.123...
|
|
||||||
%%
|
|
||||||
%% hz_format:approx_amount({$_, 4}, 12, 100500040123000004500000001) ->
|
|
||||||
%% 木1_0050_0040.1230_0000_45...
|
|
||||||
%% '''
|
|
||||||
|
|
||||||
approx_amount(us, Precision, Pucks) ->
|
|
||||||
western($,, $., 3, Precision, Pucks);
|
|
||||||
approx_amount({Separator, Span}, Precision, Pucks) ->
|
|
||||||
western(Separator, $., Span, Precision, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
western(Separator, Span, Pucks) when Pucks >= 0 ->
|
|
||||||
western2(Separator, Span, Pucks);
|
|
||||||
western(Separator, Span, Pucks) when Pucks < 0 ->
|
|
||||||
[$- | western2(Separator, Span, Pucks * -1)].
|
|
||||||
|
|
||||||
western2(Separator, Span, Pucks) ->
|
|
||||||
P = lists:reverse(integer_to_list(Pucks)),
|
|
||||||
[mark(puck) | separate(Separator, Span, P)].
|
|
||||||
|
|
||||||
|
|
||||||
western(Separator, Break, Span, Precision, Pucks) when Pucks >= 0 ->
|
|
||||||
western2(Separator, Break, Span, Precision, Pucks);
|
|
||||||
western(Separator, Break, Span, Precision, Pucks) when Pucks < 0 ->
|
|
||||||
[$- | western2(Separator, Break, Span, Precision, Pucks * -1)].
|
|
||||||
|
|
||||||
|
|
||||||
western2(Separator, _, Span, 0, Pucks) ->
|
|
||||||
G = lists:reverse(integer_to_list(Pucks div one(gaju))),
|
|
||||||
[mark(gaju) | separate(Separator, Span, G)];
|
|
||||||
western2(Separator, Break, Span, Precision, Pucks) ->
|
|
||||||
SP = integer_to_list(Pucks),
|
|
||||||
Length = length(SP),
|
|
||||||
Over18 = Length > 18,
|
|
||||||
NoPucks = (Pucks rem one(gaju)) =:= 0,
|
|
||||||
case {Over18, NoPucks} of
|
|
||||||
{true, true} ->
|
|
||||||
Gs = lists:reverse(lists:sublist(SP, Length - 18)),
|
|
||||||
[mark(gaju) | separate(Separator, Span, Gs)];
|
|
||||||
{true, false} ->
|
|
||||||
{PChars, GChars} = lists:split(18, lists:reverse(SP)),
|
|
||||||
H = [mark(gaju) | separate(Separator, Span, GChars)],
|
|
||||||
{P, E} = decimal_pucks(Precision, lists:reverse(PChars)),
|
|
||||||
T = lists:reverse(separate(Separator, Span, P)),
|
|
||||||
lists:flatten([H, Break, T, E]);
|
|
||||||
{false, true} ->
|
|
||||||
[mark(gaju), $0];
|
|
||||||
{false, false} ->
|
|
||||||
PChars = lists:flatten(string:pad(SP, 18, leading, $0)),
|
|
||||||
{P, E} = decimal_pucks(Precision, PChars),
|
|
||||||
T = lists:reverse(separate(Separator, Span, P)),
|
|
||||||
lists:flatten([mark(gaju), $0, Break, T, E])
|
|
||||||
end.
|
|
||||||
|
|
||||||
decimal_pucks(all, PChars) ->
|
|
||||||
RTrailing = lists:reverse(PChars),
|
|
||||||
{lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)), ""};
|
|
||||||
decimal_pucks(Precision, PChars) ->
|
|
||||||
{Significant, Rest} = lists:split(min(Precision, 18), PChars),
|
|
||||||
RTrailing = lists:reverse(Significant),
|
|
||||||
Trailing = lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)),
|
|
||||||
case lists:all(fun(C) -> C =:= $0 end, Rest) of
|
|
||||||
true -> {Trailing, ""};
|
|
||||||
false -> {Trailing, "..."}
|
|
||||||
end.
|
|
||||||
|
|
||||||
separate(_, _, "") ->
|
|
||||||
"";
|
|
||||||
separate(S, P, G) ->
|
|
||||||
separate(S, P, 1, G, []).
|
|
||||||
|
|
||||||
separate(_, _, _, [H], A) ->
|
|
||||||
[H | A];
|
|
||||||
separate(S, P, P, [H | T], A) ->
|
|
||||||
separate(S, P, 1, T, [S, H | A]);
|
|
||||||
separate(S, P, N, [H | T], A) ->
|
|
||||||
separate(S, P, N + 1, T, [H | A]).
|
|
||||||
|
|
||||||
|
|
||||||
bestern(gaju, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
|
||||||
[mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks)];
|
|
||||||
bestern(gaju, Ranks, Precision, Pucks) when Pucks < 0 ->
|
|
||||||
[$-, mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)];
|
|
||||||
bestern(puck, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
|
||||||
[mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks)];
|
|
||||||
bestern(puck, Ranks, Precision, Pucks) when Pucks < 0 ->
|
|
||||||
[$-, mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks * -1)].
|
|
||||||
|
|
||||||
jp(Unit, Precision, Pucks) when Pucks >= 0 ->
|
|
||||||
bestern2(Unit, ranks(jp), 4, Precision, Pucks);
|
|
||||||
jp(Unit, Precision, Pucks) when Pucks < 0 ->
|
|
||||||
[$-, bestern2(Unit, ranks(jp), 4, Precision, Pucks * -1)].
|
|
||||||
|
|
||||||
bestern2(gaju, Ranks, Span, 0, Pucks) ->
|
|
||||||
G = lists:reverse(integer_to_list(Pucks div one(gaju))),
|
|
||||||
case Span of
|
|
||||||
3 -> period("G", Ranks, G);
|
|
||||||
4 -> myriad(mark(gaju), Ranks, G)
|
|
||||||
end;
|
|
||||||
bestern2(gaju, Ranks, Span, all, Pucks) ->
|
|
||||||
P = lists:flatten(string:pad(integer_to_list(Pucks rem one(gaju)), 18, leading, $0)),
|
|
||||||
Zilch = lists:all(fun(C) -> C =:= $0 end, P),
|
|
||||||
{H, T} =
|
|
||||||
case {Span, Zilch} of
|
|
||||||
{3, false} -> {bestern2(gaju, Ranks, 3, 0, Pucks), period("P", Ranks, lists:reverse(P))};
|
|
||||||
{4, false} -> {jp(gaju, 0, Pucks), myriad(mark(puck), Ranks, lists:reverse(P))};
|
|
||||||
{3, true} -> {bestern2(gaju, Ranks, 3, 0, Pucks), ""};
|
|
||||||
{4, true} -> {jp(gaju, 0, Pucks), ""}
|
|
||||||
end,
|
|
||||||
lists:flatten([H, " ", T]);
|
|
||||||
bestern2(gaju, Ranks, Span, Precision, Pucks) ->
|
|
||||||
P = lists:flatten(string:pad(integer_to_list(Pucks rem one(gaju)), 18, leading, $0)),
|
|
||||||
H =
|
|
||||||
case Span of
|
|
||||||
3 -> bestern2(gaju, Ranks, 3, 0, Pucks);
|
|
||||||
4 -> jp(gaju, 0, Pucks)
|
|
||||||
end,
|
|
||||||
Digits = min(Precision, 18),
|
|
||||||
T =
|
|
||||||
case length(P) < Digits of
|
|
||||||
false ->
|
|
||||||
ReverseP = lists:reverse(lists:sublist(P, Digits)),
|
|
||||||
PuckingString = lists:flatten(string:pad(ReverseP, 18, leading, $0)),
|
|
||||||
case lists:all(fun(C) -> C =:= $0 end, PuckingString) of
|
|
||||||
false ->
|
|
||||||
case Span of
|
|
||||||
3 -> period("P", Ranks, PuckingString);
|
|
||||||
4 -> myriad(mark(puck), Ranks, PuckingString)
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
""
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
lists:flatten([H, " ", T]);
|
|
||||||
bestern2(puck, Ranks, Span, all, Pucks) ->
|
|
||||||
P = lists:reverse(integer_to_list(Pucks)),
|
|
||||||
case lists:all(fun(C) -> C =:= $0 end, P) of
|
|
||||||
false ->
|
|
||||||
case Span of
|
|
||||||
3 -> period("P", Ranks, P);
|
|
||||||
4 -> myriad(mark(puck), Ranks, P)
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
case Span of
|
|
||||||
3 -> [$0, " P"];
|
|
||||||
4 -> [$0, mark(puck)]
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
bestern2(puck, Ranks, Span, Precision, Pucks) ->
|
|
||||||
Digits = min(Precision, 18),
|
|
||||||
P = lists:flatten(string:pad(integer_to_list(Pucks), 18, leading, $0)),
|
|
||||||
case length(P) < Digits of
|
|
||||||
true ->
|
|
||||||
case Span of
|
|
||||||
3 -> [$0, " P"];
|
|
||||||
4 -> [$0, mark(puck)]
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
PucksToGive = lists:sublist(P, Digits),
|
|
||||||
PuckingString = lists:flatten(string:pad(lists:reverse(PucksToGive), 18, leading, $0)),
|
|
||||||
case lists:all(fun(C) -> C =:= $0 end, PuckingString) of
|
|
||||||
false ->
|
|
||||||
case Span of
|
|
||||||
3 -> period("P", Ranks, PuckingString);
|
|
||||||
4 -> myriad(mark(puck), Ranks, PuckingString)
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
case Span of
|
|
||||||
3 -> [$0, " P"];
|
|
||||||
4 -> [$0, mark(puck)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
period(Symbol, Ranks, [$0, $0, $0 | PT]) ->
|
|
||||||
rank3(Ranks, PT, [Symbol]);
|
|
||||||
period(Symbol, Ranks, [P3, $0, $0 | PT]) ->
|
|
||||||
rank3(Ranks, PT, [P3, 32, Symbol]);
|
|
||||||
period(Symbol, Ranks, [P3, P2, $0 | PT]) ->
|
|
||||||
rank3(Ranks, PT, [P2, P3, 32, Symbol]);
|
|
||||||
period(Symbol, Ranks, [P3, P2, P1 | PT]) ->
|
|
||||||
rank3(Ranks, PT, [P1, P2, P3, 32, Symbol]);
|
|
||||||
period(Symbol, _, [P3]) ->
|
|
||||||
[P3, 32, Symbol];
|
|
||||||
period(Symbol, _, [P3, P2]) ->
|
|
||||||
[P2, P3, 32, Symbol].
|
|
||||||
|
|
||||||
rank3([_ | RT], [$0, $0, $0 | PT], A) ->
|
|
||||||
rank3(RT, PT, A);
|
|
||||||
rank3([RH | RT], [P3, $0, $0 | PT], A) ->
|
|
||||||
rank3(RT, PT, [P3, RH | A]);
|
|
||||||
rank3([RH | RT], [P3, P2, $0 | PT], A) ->
|
|
||||||
rank3(RT, PT, [P2, P3, RH | A]);
|
|
||||||
rank3([RH | RT], [P3, P2, P1 | PT], A) ->
|
|
||||||
rank3(RT, PT, [P1, P2, P3, RH | A]);
|
|
||||||
rank3(_, [$0, $0, $0], A) ->
|
|
||||||
A;
|
|
||||||
rank3(_, [$0, $0], A) ->
|
|
||||||
A;
|
|
||||||
rank3(_, [$0], A) ->
|
|
||||||
A;
|
|
||||||
rank3(_, [], A) ->
|
|
||||||
A;
|
|
||||||
rank3([RH | _], [P3, $0, $0], A) ->
|
|
||||||
[P3, RH | A];
|
|
||||||
rank3([RH | _], [P3, $0], A) ->
|
|
||||||
[P3, RH | A];
|
|
||||||
rank3([RH | _], [P3], A) ->
|
|
||||||
[P3, RH | A];
|
|
||||||
rank3([RH | _], [P3, P2, $0], A) ->
|
|
||||||
[P2, P3, RH | A];
|
|
||||||
rank3([RH | _], [P3, P2], A) ->
|
|
||||||
[P2, P3, RH | A];
|
|
||||||
rank3([RH | _], [P3, P2, P1], A) ->
|
|
||||||
[P1, P2, P3, RH | A].
|
|
||||||
|
|
||||||
|
|
||||||
myriad(Symbol, Ranks, [$0, $0, $0, $0 | PT]) ->
|
|
||||||
rank4(Ranks, PT, [Symbol]);
|
|
||||||
myriad(Symbol, Ranks, [P4, $0, $0, $0 | PT]) ->
|
|
||||||
rank4(Ranks, PT, [P4, Symbol]);
|
|
||||||
myriad(Symbol, Ranks, [P4, P3, $0, $0 | PT]) ->
|
|
||||||
rank4(Ranks, PT, [P3, P4, Symbol]);
|
|
||||||
myriad(Symbol, Ranks, [P4, P3, P2, $0 | PT]) ->
|
|
||||||
rank4(Ranks, PT, [P2, P3, P4, Symbol]);
|
|
||||||
myriad(Symbol, Ranks, [P4, P3, P2, P1 | PT]) ->
|
|
||||||
rank4(Ranks, PT, [P1, P2, P3, P4, Symbol]);
|
|
||||||
myriad(Symbol, _, [P4]) ->
|
|
||||||
[P4, Symbol];
|
|
||||||
myriad(Symbol, _, [P4, P3]) ->
|
|
||||||
[P3, P4, Symbol];
|
|
||||||
myriad(Symbol, _, [P4, P3, P2]) ->
|
|
||||||
[P2, P3, P4, Symbol].
|
|
||||||
|
|
||||||
rank4([_ | RT], [$0, $0, $0, $0 | PT], A) ->
|
|
||||||
rank4(RT, PT, A);
|
|
||||||
rank4([RH | RT], [P4, $0, $0, $0 | PT], A) ->
|
|
||||||
rank4(RT, PT, [P4, RH | A]);
|
|
||||||
rank4([RH | RT], [P4, P3, $0, $0 | PT], A) ->
|
|
||||||
rank4(RT, PT, [P3, P4, RH | A]);
|
|
||||||
rank4([RH | RT], [P4, P3, P2, $0 | PT], A) ->
|
|
||||||
rank4(RT, PT, [P2, P3, P4, RH | A]);
|
|
||||||
rank4([RH | RT], [P4, P3, P2, P1 | PT], A) ->
|
|
||||||
rank4(RT, PT, [P1, P2, P3, P4, RH | A]);
|
|
||||||
rank4(_, [$0, $0, $0, $0], A) ->
|
|
||||||
A;
|
|
||||||
rank4(_, [$0, $0, $0], A) ->
|
|
||||||
A;
|
|
||||||
rank4(_, [$0, $0], A) ->
|
|
||||||
A;
|
|
||||||
rank4(_, [$0], A) ->
|
|
||||||
A;
|
|
||||||
rank4(_, [], A) ->
|
|
||||||
A;
|
|
||||||
rank4([RH | _], [P4, $0, $0, $0], A) ->
|
|
||||||
[P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, $0, $0], A) ->
|
|
||||||
[P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, $0], A) ->
|
|
||||||
[P4, RH | A];
|
|
||||||
rank4([RH | _], [P4], A) ->
|
|
||||||
[P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3, $0, $0], A) ->
|
|
||||||
[P3, P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3, $0], A) ->
|
|
||||||
[P3, P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3], A) ->
|
|
||||||
[P3, P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3, P2, $0], A) ->
|
|
||||||
[P2, P3, P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3, P2], A) ->
|
|
||||||
[P2, P3, P4, RH | A];
|
|
||||||
rank4([RH | _], [P4, P3, P2, P1], A) ->
|
|
||||||
[P1, P2, P3, P4, RH | A].
|
|
||||||
|
|
||||||
ranks(jp) ->
|
|
||||||
"万億兆京垓秭穣溝澗正載極";
|
|
||||||
ranks(metric) ->
|
|
||||||
["k ", "m ", "g ", "t ", "p ", "e ", "z ", "y ", "r ", "Q "];
|
|
||||||
ranks(heresy) ->
|
|
||||||
["k ", "m ", "b ", "t ", "q ", "e ", "z ", "y ", "r ", "Q "].
|
|
||||||
|
|
||||||
|
|
||||||
mark(gaju) -> $木;
|
|
||||||
mark(puck) -> $本.
|
|
||||||
|
|
||||||
one(gaju) -> 1_000_000_000_000_000_000;
|
|
||||||
one(puck) -> 1.
|
|
||||||
|
|
||||||
|
|
||||||
-spec read(Format) -> Result
|
|
||||||
when Format :: string(),
|
|
||||||
Result :: {ok, Pucks} | error,
|
|
||||||
Pucks :: integer().
|
|
||||||
%% @doc
|
|
||||||
%% Convert any valid string formatted representation and output a value in pucks.
|
|
||||||
%% NOTE: This function does not accept approximated values.
|
|
||||||
%% ```
|
|
||||||
%% 1> hz_format:read("木100,500,040.123,000,004,5").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 2> hz_format:read("本100,500,040,123,000,004,500,000,000").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 3> hz_format:read("1億50万40木 12京3000兆45億本").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 4> hz_format:read("100秭5000垓4012京3000兆45億本").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 5> hz_format:read("木100m 500k 40 G 123p 4g 500m P").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 6> hz_format:read("本100y 500z 40e 123p 4g 500m P").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 7> hz_format:read("木100m 500k 40 G 123q 4b 500m P").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% 8> hz_format:read("本100y 500z 40e 123q 4b 500m P").
|
|
||||||
%% {ok,100500040123000004500000000}
|
|
||||||
%% '''
|
|
||||||
|
|
||||||
read([$木 | Rest]) ->
|
|
||||||
read_w_gajus(Rest, []);
|
|
||||||
read([$本 | Rest]) ->
|
|
||||||
read_w_pucks(Rest, []);
|
|
||||||
read([C | Rest])
|
|
||||||
when C =:= $- orelse
|
|
||||||
C =:= $− orelse
|
|
||||||
C =:= $- ->
|
|
||||||
case read(Rest) of
|
|
||||||
{ok, Pucks} -> {ok, Pucks * -1};
|
|
||||||
Error -> Error
|
|
||||||
end;
|
|
||||||
read([C | Rest])
|
|
||||||
when C =:= 32 orelse % ASCII space
|
|
||||||
C =:= 12288 orelse % full-width space
|
|
||||||
C =:= $\t orelse
|
|
||||||
C =:= $\r orelse
|
|
||||||
C =:= $\n ->
|
|
||||||
read(Rest);
|
|
||||||
read([C | Rest]) when $0 =< C andalso C =< $9 ->
|
|
||||||
read(Rest, [C], []);
|
|
||||||
read([C | Rest]) when $0 =< C andalso C =< $9 ->
|
|
||||||
NumC = C - $0 + $0,
|
|
||||||
read(Rest, [NumC], []);
|
|
||||||
read(String) when is_binary(String) ->
|
|
||||||
read(binary_to_list(String));
|
|
||||||
read(_) ->
|
|
||||||
error.
|
|
||||||
|
|
||||||
read_w_gajus([C | Rest], A) when $0 =< C andalso C =< $9 ->
|
|
||||||
read_w_gajus(Rest, [C | A]);
|
|
||||||
read_w_gajus([C | Rest], A) when $0 =< C andalso C =< $9 ->
|
|
||||||
NumC = C - $0 + $0,
|
|
||||||
read_w_gajus(Rest, [NumC | A]);
|
|
||||||
read_w_gajus([$, | Rest], A) ->
|
|
||||||
read_w_gajus(Rest, A);
|
|
||||||
read_w_gajus([$_ | Rest], A) ->
|
|
||||||
read_w_gajus(Rest, A);
|
|
||||||
read_w_gajus([$. | Rest], A) ->
|
|
||||||
case read_w_pucks(Rest, []) of
|
|
||||||
{ok, P} ->
|
|
||||||
G = list_to_integer(lists:reverse(A)) * one(gaju),
|
|
||||||
{ok, G + P};
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end;
|
|
||||||
read_w_gajus([], A) ->
|
|
||||||
G = list_to_integer(lists:reverse(A)) * one(gaju),
|
|
||||||
{ok, G};
|
|
||||||
read_w_gajus([C, 32 | Rest], A) ->
|
|
||||||
read(Rest, [], [{C, A}]);
|
|
||||||
read_w_gajus([32, $G, 32 | Rest], A) ->
|
|
||||||
read(Rest, [], [{$G, A}], []);
|
|
||||||
read_w_gajus([32, $G], A) ->
|
|
||||||
calc([{$G, A}], []);
|
|
||||||
read_w_gajus([32, $P], A) ->
|
|
||||||
calc([], [{$P, A}]);
|
|
||||||
read_w_gajus(A, B) ->
|
|
||||||
io:format("A: ~ts, B: ~p~n", [A, B]),
|
|
||||||
error.
|
|
||||||
|
|
||||||
read_w_pucks([C | Rest], A) when $0 =< C andalso C =< $9 ->
|
|
||||||
read_w_pucks(Rest, [C | A]);
|
|
||||||
read_w_pucks([C | Rest], A) when $0 =< C andalso C =< $9 ->
|
|
||||||
NumC = C - $0 + $0,
|
|
||||||
read_w_pucks(Rest, [NumC | A]);
|
|
||||||
read_w_pucks([$, | Rest], A) ->
|
|
||||||
read_w_pucks(Rest, A);
|
|
||||||
read_w_pucks([$_ | Rest], A) ->
|
|
||||||
read_w_pucks(Rest, A);
|
|
||||||
read_w_pucks([C, 32 | Rest], A) ->
|
|
||||||
read(Rest, [], [], [{C, A}]);
|
|
||||||
read_w_pucks([32, $P], A) ->
|
|
||||||
calc([], [{$P, A}]);
|
|
||||||
read_w_pucks([], A) ->
|
|
||||||
Padded = lists:flatten(string:pad(lists:reverse(A), 18, trailing, $0)),
|
|
||||||
{ok, list_to_integer(Padded)}.
|
|
||||||
|
|
||||||
|
|
||||||
read([C | Rest], A, G) when $0 =< C andalso C =< $9 ->
|
|
||||||
read(Rest, [C | A], G);
|
|
||||||
read([C | Rest], A, G) when $0 =< C andalso C =< $9 ->
|
|
||||||
NumC = C - $0 + $0,
|
|
||||||
read(Rest, [NumC | A], G);
|
|
||||||
read([$木], A, G) ->
|
|
||||||
calc([{$G, A} | G], []);
|
|
||||||
read([$G], A, G) ->
|
|
||||||
calc([{$G, A} | G], []);
|
|
||||||
read([32, $G], A, G) ->
|
|
||||||
calc([{$G, A} | G], []);
|
|
||||||
read([32, $G, 32 | Rest], A, G) ->
|
|
||||||
read(Rest, [], [{$G, A} | G], []);
|
|
||||||
read([$木, 32 | Rest], A, G) ->
|
|
||||||
read(Rest, [], [{$G, A} | G], []);
|
|
||||||
read([$G, 32 | Rest], A, G) ->
|
|
||||||
read(Rest, [], [{$G, A} | G], []);
|
|
||||||
read([$本], A, P) ->
|
|
||||||
calc([], [{$P, A} | P]);
|
|
||||||
read([$P], A, P) ->
|
|
||||||
calc([], [{$P, A} | P]);
|
|
||||||
read([32, $P], A, P) ->
|
|
||||||
calc([], [{$P, A} | P]);
|
|
||||||
read([C, 32 | Rest], A, G) ->
|
|
||||||
read(Rest, [], [{C, A} | G]);
|
|
||||||
read([C | Rest], A, G) ->
|
|
||||||
read(Rest, [], [{C, A} | G]);
|
|
||||||
read(Rest, A, G) ->
|
|
||||||
io:format("read(\"~ts\", ~tp, ~tp) -> died!~n", [Rest, A, G]),
|
|
||||||
error.
|
|
||||||
|
|
||||||
|
|
||||||
read([C | Rest], A, G, P) when $0 =< C andalso C =< $9 ->
|
|
||||||
read(Rest, [C | A], G, P);
|
|
||||||
read([C | Rest], A, G, P) when $0 =< C andalso C =< $9 ->
|
|
||||||
NumC = C - $0 + $0,
|
|
||||||
read(Rest, [NumC | A], G, P);
|
|
||||||
read([$本], A, G, P) ->
|
|
||||||
calc(G, [{$P, A} | P]);
|
|
||||||
read([$P], A, G, P) ->
|
|
||||||
calc(G, [{$P, A} | P]);
|
|
||||||
read([32, $P], A, G, P) ->
|
|
||||||
calc(G, [{$P, A} | P]);
|
|
||||||
read([C, 32 | Rest], A, G, P) ->
|
|
||||||
read(Rest, [], G, [{C, A} | P]);
|
|
||||||
read([C | Rest], A, G, P) ->
|
|
||||||
read(Rest, [], G, [{C, A} | P]);
|
|
||||||
read(_, _, _, _) ->
|
|
||||||
io:format("read/4 died!~n"),
|
|
||||||
error.
|
|
||||||
|
|
||||||
calc(G, P) ->
|
|
||||||
case calc(gaju, G, 0) of
|
|
||||||
{ok, Gajus} ->
|
|
||||||
case calc(puck, P, 0) of
|
|
||||||
{ok, Pucks} -> {ok, Gajus + Pucks};
|
|
||||||
error -> error
|
|
||||||
end;
|
|
||||||
error ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
calc(U, [{_, []} | S], A) ->
|
|
||||||
calc(U, S, A);
|
|
||||||
calc(U, [{M, Cs} | S], A) ->
|
|
||||||
case magnitude(M) of
|
|
||||||
{ok, J} ->
|
|
||||||
N = list_to_integer(lists:reverse(Cs)) * J * one(U),
|
|
||||||
calc(U, S, A + N);
|
|
||||||
Error ->
|
|
||||||
Error
|
|
||||||
end;
|
|
||||||
calc(_, [], A) ->
|
|
||||||
{ok, A}.
|
|
||||||
|
|
||||||
|
|
||||||
magnitude($G) ->
|
|
||||||
{ok, 1};
|
|
||||||
magnitude($P) ->
|
|
||||||
{ok, 1};
|
|
||||||
magnitude(Mark) ->
|
|
||||||
case rank(Mark, ranks(jp), 1_0000, 1) of
|
|
||||||
{ok, J} ->
|
|
||||||
{ok, J};
|
|
||||||
error ->
|
|
||||||
case rank([Mark, 32], ranks(metric), 1_000, 1) of
|
|
||||||
{ok, J} -> {ok, J};
|
|
||||||
error -> rank([Mark, 32], ranks(heresy), 1_000, 1)
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
rank(Mark, [Mark | _], Magnitude, Sum) ->
|
|
||||||
{ok, Sum * Magnitude};
|
|
||||||
rank(Mark, [_ | Rest], Magnitude, Sum) ->
|
|
||||||
rank(Mark, Rest, Magnitude, Sum * Magnitude);
|
|
||||||
rank(_, [], _, _) ->
|
|
||||||
error.
|
|
||||||
|
|
||||||
|
|
||||||
-spec price_to_string(Pucks) -> Gajus
|
|
||||||
when Pucks :: integer(),
|
|
||||||
Gajus :: string().
|
|
||||||
%% @doc
|
|
||||||
%% A simplified formatting function that converts an integer value in Pucks to a string representation
|
|
||||||
%% in Gajus. Useful for formatting generic output for UI elements
|
|
||||||
|
|
||||||
price_to_string(Pucks) ->
|
|
||||||
Gaju = one(gaju),
|
|
||||||
H = integer_to_list(Pucks div Gaju),
|
|
||||||
R = Pucks rem Gaju,
|
|
||||||
case string:strip(lists:flatten(io_lib:format("~18..0w", [R])), right, $0) of
|
|
||||||
[] -> H;
|
|
||||||
T -> string:join([H, T], ".")
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
-spec string_to_price(Gajus) -> Pucks
|
|
||||||
when Gajus :: string(),
|
|
||||||
Pucks :: integer().
|
|
||||||
%% @doc
|
|
||||||
%% A simplified formatting function that converts a Gaju value represented as a string to an
|
|
||||||
%% integer value in Pucks.
|
|
||||||
|
|
||||||
string_to_price(String) ->
|
|
||||||
case string:split(String, ".") of
|
|
||||||
[H] -> join_price(H, "0");
|
|
||||||
[H, T] -> join_price(H, T);
|
|
||||||
_ -> {error, bad_price}
|
|
||||||
end.
|
|
||||||
|
|
||||||
join_price(H, T) ->
|
|
||||||
try
|
|
||||||
Parts = [H, string:pad(T, 18, trailing, $0)],
|
|
||||||
Price = list_to_integer(unicode:characters_to_list(Parts)),
|
|
||||||
case Price < 0 of
|
|
||||||
false -> {ok, Price};
|
|
||||||
true -> {error, negative_price}
|
|
||||||
end
|
|
||||||
catch
|
|
||||||
error:R -> {error, R}
|
|
||||||
end.
|
|
||||||
Loading…
x
Reference in New Issue
Block a user