WIP: Moving formatters to HZ

This commit is contained in:
Craig Everett 2025-12-01 12:06:05 +09:00
parent c713053efd
commit 7393a02de2

278
src/hz_format.erl Normal file
View File

@ -0,0 +1,278 @@
%%% @doc
%%% GajuDesk Helper Functions
%%% @end
-module(hz_format).
-export([price/1, price/3, price/4, % read_price/1,
price_to_string/1, string_to_price/1]).
price(Pucks) ->
price(gaju, {west, $,, 3},Pucks).
price(puck, {west, Separator, Period}, Pucks) ->
western_format(Separator, Period, Pucks);
price(gaju, {west, Separator = $., Period}, Pucks) ->
western_format(Separator, $,, Period, all, Pucks);
price(gaju, {west, Separator, Period}, Pucks) ->
western_format(Separator, $., Period, all, Pucks);
price(Unit, jp, Pucks) ->
jp_format(Unit, all, Pucks).
-spec price(Unit, Style, Precision, Pucks) -> Serialized
when Unit :: gaju | puck,
Style :: jp | jp_pucks | {Separator, Period},
Precision :: all | 0..18,
Separator :: $, | $. | $_,
Period :: 3 | 4,
Pucks :: integer(),
Serialized :: string().
%% @doc
%% A formatting function that accepts a price value in pucks as an integer, and outputs a string
%% formatted in the desired way.
%%
%% Consider, for example, a few input and output pairs:
%% ```
%% price({$,, 3}, 3, 123456789123456789123456789) ->
%% "木123,456,789.123".
%%
%% price({$,, 3}, 6, 123456789123456789123456789) ->
%% "木123,456,789.123,456"
%%
%% price({$,, 3}, all, 123456789123456789123456789) ->
%% "木123.456.789,123.456.789.123.456.789"
%%
%% price({$_, 4}, 10, 123456789123456789123456789) ->
%% "木1_2345_6789.1234_5678_91"
%%
%% price(jp, 3, 123456789123456789123456789) ->
%% "1億2345万6789木12京3000兆本"
%%
%% price(jp, 6, 123456789123456789123456789) ->
%% "1億2345万6789木12京3456兆本"
%%
%% price(jp, 0, 123456789123456789123456789) ->
%% "1億2345万6789木"
%%
%% price(jp_pucks, all, 123456789123456789123456789) ->
%% "123秭4567垓8912京3456兆7891億2345万6789本"
%% '''
price(puck, {west, Separator, Period}, _, Pucks) ->
western_format(Separator, Period, Pucks);
price(gaju, {west, Separator = $., Period}, Precision, Pucks) ->
western_format(Separator, $,, Period, Precision, Pucks);
price(gaju, {west, Separator, Period}, Precision, Pucks) ->
western_format(Separator, $., Period, Precision, Pucks);
price(Unit, jp, Precision, Pucks) ->
jp_format(Unit, Precision, Pucks).
western_format(Separator, Period, Pucks) ->
P = lists:reverse(integer_to_list(Pucks)),
[puck_mark() | separate(Separator, Period, P)].
western_format(Separator, _, Period, 0, Pucks) ->
G = lists:reverse(integer_to_list(Pucks div one_gaju())),
[gaju_mark() | separate(Separator, Period, G)];
western_format(Separator, Break, Period, 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(Length - 18, SP)),
[gaju_mark() | separate(Separator, Period, Gs)];
{true, false} ->
{GChars, PChars} = lists:split(18, SP),
H = [gaju_mark() | separate(Separator, Period, lists:reverse(GChars))],
{P, E} = decimal_pucks(Precision, PChars),
T = lists:reverse(separate(Separator, Period, P)),
lists:flatten([H, Break, T, E]);
{false, true} ->
[gaju_mark(), $0];
{false, false} ->
{P, E} = decimal_pucks(Precision, PChars),
T = lists:reverse(separate(Separator, Period, P)),
lists:flatten([gaju_mark(), $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]).
jp_format(gaju, 0, Pucks) ->
G = lists:reverse(integer_to_list(Pucks div one_gaju())),
rank(gaju_mark(), G);
jp_format(gaju, all, Pucks) ->
H = jp_format(gaju, 0, Pucks),
T = jp_format(puck, all, Pucks rem one_gaju()),
H ++ T;
jp_format(gaju, Precision, Pucks) ->
H = jp_format(gaju, 0, Pucks),
T = jp_format(puck, Precision, Pucks rem one_gaju()),
H ++ T;
jp_format(puck, all, Pucks) ->
P = lists:reverse(integer_to_list(Pucks)),
case lists:all(fun(C) -> C =:= $0 end, P) of
false -> rank(puck_mark(), P);
true -> [$0, puck_mark()]
end;
jp_format(puck, Precision, Pucks) ->
Digits = min(Precision, 18),
P = lists:reverse(integer_to_list(Pucks)),
case length(P) > Digits of
true ->
[$0, puck_mark()];
false ->
Significant = lists:nthtail(Digits, P),
Filler = tuple_to_list(erlang:make_tuple(Digits, $0)),
PuckingString = Filler ++ Significant,
case lists:all(fun(C) -> C =:= $0 end, PuckingString) of
false -> rank(puck_mark(), PuckingString);
true -> [$0, puck_mark()]
end
end.
rank(Symbol, [$0, $0, $0, $0 | PT]) ->
rank(jp_ranks(), PT, [Symbol]);
rank(Symbol, [P4, $0, $0, $0 | PT]) ->
rank(jp_ranks(), PT, [P4, Symbol]);
rank(Symbol, [P4, P3, $0, $0 | PT]) ->
rank(jp_ranks(), PT, [P3, P4, Symbol]);
rank(Symbol, [P4, P3, P2, $0 | PT]) ->
rank(jp_ranks(), PT, [P2, P3, P4, Symbol]);
rank(Symbol, [P4, P3, P2, P1 | PT]) ->
rank(jp_ranks(), PT, [P1, P2, P3, P4, Symbol]);
rank(Symbol, [P4]) ->
[P4, Symbol];
rank(Symbol, [P4, P3]) ->
[P3, P4, Symbol];
rank(Symbol, [P4, P3, P2]) ->
[P2, P3, P4, Symbol].
jp_ranks() ->
"万億兆京垓秭穣溝澗正載極".
rank([_ | RT], [$0, $0, $0, $0 | PT], A) ->
rank(RT, PT, A);
rank([RH | RT], [P4, $0, $0, $0 | PT], A) ->
rank(RT, PT, [P4, RH | A]);
rank([RH | RT], [P4, P3, $0, $0 | PT], A) ->
rank(RT, PT, [P3, P4, RH | A]);
rank([RH | RT], [P4, P3, P2, $0 | PT], A) ->
rank(RT, PT, [P2, P3, P4, RH | A]);
rank([RH | RT], [P4, P3, P2, P1 | PT], A) ->
rank(RT, PT, [P1, P2, P3, P4, RH | A]);
rank(_, [$0, $0, $0, $0], A) ->
A;
rank(_, [$0, $0, $0], A) ->
A;
rank(_, [$0, $0], A) ->
A;
rank(_, [$0], A) ->
A;
rank(_, [], A) ->
A;
rank([RH | _], [P4, $0, $0, $0], A) ->
[P4, RH | A];
rank([RH | _], [P4, $0, $0], A) ->
[P4, RH | A];
rank([RH | _], [P4, $0], A) ->
[P4, RH | A];
rank([RH | _], [P4], A) ->
[P4, RH | A];
rank([RH | _], [P4, P3, $0, $0], A) ->
[P3, P4, RH | A];
rank([RH | _], [P4, P3, $0], A) ->
[P3, P4, RH | A];
rank([RH | _], [P4, P3], A) ->
[P3, P4, RH | A];
rank([RH | _], [P4, P3, P2, $0], A) ->
[P2, P3, P4, RH | A];
rank([RH | _], [P4, P3, P2], A) ->
[P2, P3, P4, RH | A];
rank([RH | _], [P4, P3, P2, P1], A) ->
[P1, P2, P3, P4, RH | A].
gaju_mark() -> $木.
puck_mark() -> $本.
one_gaju() -> 1_000_000_000_000_000_000.
-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 = 1_000_000_000_000_000_000,
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.