diff --git a/src/hz_format.erl b/src/hz_format.erl new file mode 100644 index 0000000..cb723f2 --- /dev/null +++ b/src/hz_format.erl @@ -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.