From 7393a02de2b7aae3aa90bb2752ccdf785ccc149a Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 1 Dec 2025 12:06:05 +0900 Subject: [PATCH 01/14] WIP: Moving formatters to HZ --- src/hz_format.erl | 278 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 src/hz_format.erl 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. -- 2.30.2 From e4e6e35bf8d7a832240860dc526ae546d5b23ad7 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 1 Dec 2025 18:36:50 +0900 Subject: [PATCH 02/14] WIP: Fix remaining cases --- src/hz_format.erl | 258 ++++++++++++++++++++++++++++------------------ 1 file changed, 157 insertions(+), 101 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index cb723f2..a516fb2 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -4,109 +4,167 @@ -module(hz_format). --export([price/1, price/3, price/4, % read_price/1, +-export([price/1, price/2, price/3, price/4, % read_price/1, price_to_string/1, string_to_price/1]). +-spec price(Pucks) -> Formatted + when Pucks :: integer(), + Formatted :: string(). +%% @doc +%% A convenience formatting function. +%% @equiv price(us, Pucks). price(Pucks) -> - price(gaju, {west, $,, 3},Pucks). + price(us, 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); +-spec price(Style, Pucks) -> Formatted + when Style :: us | ch | jp | {Separator, Period}, + Separator :: $, | $. | $_, + Period :: 3 | 4, + Pucks :: integer(), + Formatted :: string(). +%% @doc +%% A convenient formatting function. +%% @equiv price(gaju, Style, Pucks). + +price(Style, Pucks) -> + price(gaju, Style, Pucks). + + +-spec price(Unit, Style, Pucks) -> Formatted + when Unit :: gaju | puck, + Style :: us | ch | jp | {Separator, Period}, + Separator :: $, | $. | $_, + Period :: 3 | 4, + Pucks :: integer(), + Formatted :: string(). +%% @doc +%% A simplified format function covering the most common formats desired. +%% +%% ``` + +price(gaju, us, Pucks) -> + wetsern($,, $., 3, all, Pucks); +price(puck, us, Pucks) -> + wetsern($,, 3, Pucks); +price(gaju, ch, Pucks) -> + wetsern($., $,, 3, all, Pucks); +price(puck, ch, Pucks) -> + wetsern($,, 3, Pucks); price(Unit, jp, Pucks) -> - jp_format(Unit, all, Pucks). + jp(Unit, all, Pucks); +price(gaju, {$., Period}, Pucks) -> + wetsern($., $,, Period, all, Pucks); +price(gaju, {Separator, Period}, Pucks) -> + wetsern(Separator, $., Period, all, Pucks); +price(puck, {Separator, Period}, Pucks) -> + wetsern(Separator, Period, Pucks). -spec price(Unit, Style, Precision, Pucks) -> Serialized when Unit :: gaju | puck, - Style :: jp | jp_pucks | {Separator, Period}, + Style :: us | ch | jp | {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. +%% A flexible, if annoyingly complex, formatting function. %% -%% Consider, for example, a few input and output pairs: %% ``` -%% price({$,, 3}, 3, 123456789123456789123456789) -> +%% price(gaju, us, 3, 123456789123456789123456789) -> +%% "木123,456,789.123...". +%% +%% price(gaju, ch, 3, 123456789123456789123456789) -> +%% "木123.456.789,123...". +%% +%% price(gaju, us, 3, 123456789123000000000000000) -> %% "木123,456,789.123". %% -%% price({$,, 3}, 6, 123456789123456789123456789) -> -%% "木123,456,789.123,456" +%% price(gaju, {$,, 3}, 3, 123456789123456789123456789) -> +%% "木123,456,789.123...". %% -%% price({$,, 3}, all, 123456789123456789123456789) -> +%% price(gaju, {$,, 3}, 6, 123456789123000000000000000) -> +%% "木123,456,789.123" +%% +%% price(gaju, {$., 3}, all, 123456789123456789123456789) -> %% "木123.456.789,123.456.789.123.456.789" %% -%% price({$_, 4}, 10, 123456789123456789123456789) -> -%% "木1_2345_6789.1234_5678_91" +%% price(gaju, {$_, 4}, 10, 123456789123456789123456789) -> +%% "木1_2345_6789.1234_5678_91..." %% -%% price(jp, 3, 123456789123456789123456789) -> -%% "1億2345万6789木12京3000兆本" +%% price(gaju, jp, 3, 123456789123456789123456789) -> +%% "1億2345万6789木 12京3000兆本" %% -%% price(jp, 6, 123456789123456789123456789) -> -%% "1億2345万6789木12京3456兆本" +%% price(gaju, jp, 6, 123456789123456789123456789) -> +%% "1億2345万6789木 12京3456兆本" %% -%% price(jp, 0, 123456789123456789123456789) -> +%% price(gaju, jp, 0, 123456789123456789123456789) -> %% "1億2345万6789木" %% -%% price(jp_pucks, all, 123456789123456789123456789) -> +%% price(puck, jp, 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). +price(gaju, us, Precision, Pucks) -> + wetsern($,, $., 3, Precision, Pucks); +price(gaju, ch, Precision, Pucks) -> + wetsern($., $,, 3, Precision, Pucks); +price(gaju, jp, Precision, Pucks) -> + jp(gaju, Precision, Pucks); +price(gaju, {$., Period}, Precision, Pucks) -> + wetsern($., $,, Period, Precision, Pucks); +price(gaju, {Separator, Period}, Precision, Pucks) -> + wetsern(Separator, $., Period, Precision, Pucks); +price(puck, us, _, Pucks) -> + wetsern($,, 3, Pucks); +price(puck, ch, _, Pucks) -> + wetsern($., 3, Pucks); +price(puck, jp, _, Pucks) -> + jp(puck, all, Pucks); +price(puck, {Separator, Period}, _, Pucks) -> + wetsern(Separator, Period, Pucks). -western_format(Separator, Period, Pucks) -> +wetsern(Separator, Period, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), [puck_mark() | separate(Separator, Period, P)]. -western_format(Separator, _, Period, 0, Pucks) -> +wetsern(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) -> +wetsern(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)), + Gs = lists:reverse(lists:sublist(SP, Length - 18)), [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), + {PChars, GChars} = lists:split(18, lists:reverse(SP)), + H = [gaju_mark() | separate(Separator, Period, GChars)], + {P, E} = decimal_pucks(Precision, lists:reverse(PChars)), T = lists:reverse(separate(Separator, Period, P)), lists:flatten([H, Break, T, E]); {false, true} -> [gaju_mark(), $0]; {false, false} -> + PChars = lists:flatten(string:pad(SP, 18, leading, $0)), {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) -> + io:format("decimal_pucks: PChars: ~p~n", [PChars]), RTrailing = lists:reverse(PChars), {lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)), ""}; decimal_pucks(Precision, PChars) -> @@ -131,101 +189,99 @@ separate(S, P, N, [H | T], A) -> separate(S, P, N + 1, T, [H | A]). -jp_format(gaju, 0, Pucks) -> +jp(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) -> + rank4(gaju_mark(), G); +jp(gaju, all, Pucks) -> + H = jp(gaju, 0, Pucks), + T = jp(puck, all, Pucks rem one_gaju()), + lists:flatten([H, " ", T]); +jp(gaju, Precision, Pucks) -> + H = jp(gaju, 0, Pucks), + T = jp(puck, Precision, Pucks rem one_gaju()), + lists:flatten([H, " ", T]); +jp(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); + false -> rank4(puck_mark(), P); true -> [$0, puck_mark()] end; -jp_format(puck, Precision, Pucks) -> +jp(puck, Precision, Pucks) -> Digits = min(Precision, 18), - P = lists:reverse(integer_to_list(Pucks)), - case length(P) > Digits of + P = lists:flatten(string:pad(integer_to_list(Pucks), 18, leading, $0)), + 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, + PuckingString = lists:flatten(string:pad(lists:reverse(lists:sublist(P, Digits)), 18, leading, $0)), case lists:all(fun(C) -> C =:= $0 end, PuckingString) of - false -> rank(puck_mark(), PuckingString); + false -> rank4(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]) -> +rank4(Symbol, [$0, $0, $0, $0 | PT]) -> + rank4(ranks(jp), PT, [Symbol]); +rank4(Symbol, [P4, $0, $0, $0 | PT]) -> + rank4(ranks(jp), PT, [P4, Symbol]); +rank4(Symbol, [P4, P3, $0, $0 | PT]) -> + rank4(ranks(jp), PT, [P3, P4, Symbol]); +rank4(Symbol, [P4, P3, P2, $0 | PT]) -> + rank4(ranks(jp), PT, [P2, P3, P4, Symbol]); +rank4(Symbol, [P4, P3, P2, P1 | PT]) -> + rank4(ranks(jp), PT, [P1, P2, P3, P4, Symbol]); +rank4(Symbol, [P4]) -> [P4, Symbol]; -rank(Symbol, [P4, P3]) -> +rank4(Symbol, [P4, P3]) -> [P3, P4, Symbol]; -rank(Symbol, [P4, P3, P2]) -> +rank4(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) -> +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; -rank(_, [$0, $0, $0], A) -> +rank4(_, [$0, $0, $0], A) -> A; -rank(_, [$0, $0], A) -> +rank4(_, [$0, $0], A) -> A; -rank(_, [$0], A) -> +rank4(_, [$0], A) -> A; -rank(_, [], A) -> +rank4(_, [], A) -> A; -rank([RH | _], [P4, $0, $0, $0], A) -> +rank4([RH | _], [P4, $0, $0, $0], A) -> [P4, RH | A]; -rank([RH | _], [P4, $0, $0], A) -> +rank4([RH | _], [P4, $0, $0], A) -> [P4, RH | A]; -rank([RH | _], [P4, $0], A) -> +rank4([RH | _], [P4, $0], A) -> [P4, RH | A]; -rank([RH | _], [P4], A) -> +rank4([RH | _], [P4], A) -> [P4, RH | A]; -rank([RH | _], [P4, P3, $0, $0], A) -> +rank4([RH | _], [P4, P3, $0, $0], A) -> [P3, P4, RH | A]; -rank([RH | _], [P4, P3, $0], A) -> +rank4([RH | _], [P4, P3, $0], A) -> [P3, P4, RH | A]; -rank([RH | _], [P4, P3], A) -> +rank4([RH | _], [P4, P3], A) -> [P3, P4, RH | A]; -rank([RH | _], [P4, P3, P2, $0], A) -> +rank4([RH | _], [P4, P3, P2, $0], A) -> [P2, P3, P4, RH | A]; -rank([RH | _], [P4, P3, P2], A) -> +rank4([RH | _], [P4, P3, P2], A) -> [P2, P3, P4, RH | A]; -rank([RH | _], [P4, P3, P2, P1], A) -> +rank4([RH | _], [P4, P3, P2, P1], A) -> [P1, P2, P3, P4, RH | A]. +ranks(jp) -> + "万億兆京垓秭穣溝澗正載極". + gaju_mark() -> $木. -- 2.30.2 From beed46a38b7567bded3f2506abd061f2ce9a44e0 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Tue, 2 Dec 2025 15:22:13 +0900 Subject: [PATCH 03/14] WIP: omg stuff --- src/hz_format.erl | 318 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 253 insertions(+), 65 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index a516fb2..ad7f72e 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -4,7 +4,8 @@ -module(hz_format). --export([price/1, price/2, price/3, price/4, % read_price/1, +-export([price/1, price/2, price/3, price/4, + read/1, price_to_string/1, string_to_price/1]). -spec price(Pucks) -> Formatted @@ -19,9 +20,9 @@ price(Pucks) -> -spec price(Style, Pucks) -> Formatted - when Style :: us | ch | jp | {Separator, Period}, - Separator :: $, | $. | $_, - Period :: 3 | 4, + when Style :: us | ch | jp | {Separator, Myriad}, + Separator :: $, | $. | $_, + Myriad :: 3 | 4, Pucks :: integer(), Formatted :: string(). %% @doc @@ -33,42 +34,42 @@ price(Style, Pucks) -> -spec price(Unit, Style, Pucks) -> Formatted - when Unit :: gaju | puck, - Style :: us | ch | jp | {Separator, Period}, + when Unit :: gaju | puck, + Style :: us | ch | jp | {Separator, Myriad}, Separator :: $, | $. | $_, - Period :: 3 | 4, - Pucks :: integer(), - Formatted :: string(). + Myriad :: 3 | 4, + Pucks :: integer(), + Formatted :: string(). %% @doc %% A simplified format function covering the most common formats desired. %% %% ``` price(gaju, us, Pucks) -> - wetsern($,, $., 3, all, Pucks); + western($,, $., 3, all, Pucks); price(puck, us, Pucks) -> - wetsern($,, 3, Pucks); + western($,, 3, Pucks); price(gaju, ch, Pucks) -> - wetsern($., $,, 3, all, Pucks); + western($., $,, 3, all, Pucks); price(puck, ch, Pucks) -> - wetsern($,, 3, Pucks); + western($,, 3, Pucks); price(Unit, jp, Pucks) -> jp(Unit, all, Pucks); -price(gaju, {$., Period}, Pucks) -> - wetsern($., $,, Period, all, Pucks); -price(gaju, {Separator, Period}, Pucks) -> - wetsern(Separator, $., Period, all, Pucks); -price(puck, {Separator, Period}, Pucks) -> - wetsern(Separator, Period, Pucks). +price(gaju, {$., Myriad}, Pucks) -> + western($., $,, Myriad, all, Pucks); +price(gaju, {Separator, Myriad}, Pucks) -> + western(Separator, $., Myriad, all, Pucks); +price(puck, {Separator, Myriad}, Pucks) -> + western(Separator, Myriad, Pucks). -spec price(Unit, Style, Precision, Pucks) -> Serialized when Unit :: gaju | puck, - Style :: us | ch | jp | {Separator, Period}, + Style :: us | ch | jp | {Separator, Myriad}, Precision :: all | 0..18, Separator :: $, | $. | $_, - Period :: 3 | 4, + Myriad :: 3 | 4, Pucks :: integer(), Serialized :: string(). %% @doc @@ -110,36 +111,47 @@ price(puck, {Separator, Period}, Pucks) -> %% ''' price(gaju, us, Precision, Pucks) -> - wetsern($,, $., 3, Precision, Pucks); + western($,, $., 3, Precision, Pucks); price(gaju, ch, Precision, Pucks) -> - wetsern($., $,, 3, Precision, Pucks); + western($., $,, 3, Precision, Pucks); price(gaju, jp, Precision, Pucks) -> jp(gaju, Precision, Pucks); -price(gaju, {$., Period}, Precision, Pucks) -> - wetsern($., $,, Period, Precision, Pucks); -price(gaju, {Separator, Period}, Precision, Pucks) -> - wetsern(Separator, $., Period, Precision, Pucks); +price(gaju, {$., Myriad}, Precision, Pucks) -> + western($., $,, Myriad, Precision, Pucks); +price(gaju, {Separator, Myriad}, Precision, Pucks) -> + western(Separator, $., Myriad, Precision, Pucks); price(puck, us, _, Pucks) -> - wetsern($,, 3, Pucks); + western($,, 3, Pucks); price(puck, ch, _, Pucks) -> - wetsern($., 3, Pucks); + western($., 3, Pucks); price(puck, jp, _, Pucks) -> jp(puck, all, Pucks); -price(puck, {Separator, Period}, _, Pucks) -> - wetsern(Separator, Period, Pucks). +price(puck, {Separator, Myriad}, _, Pucks) -> + western(Separator, Myriad, Pucks). -wetsern(Separator, Period, Pucks) -> +western(Separator, Myriad, Pucks) when Pucks >= 0 -> + western2(Separator, Myriad, Pucks); +western(Separator, Myriad, Pucks) when Pucks < 0 -> + [$- | western2(Separator, Myriad, Pucks * -1)]. + +western2(Separator, Myriad, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), - [puck_mark() | separate(Separator, Period, P)]. + [puck_mark() | separate(Separator, Myriad, P)]. -wetsern(Separator, _, Period, 0, Pucks) -> +western(Separator, Break, Myriad, Precision, Pucks) when Pucks >= 0 -> + western2(Separator, Break, Myriad, Precision, Pucks); +western(Separator, Break, Myriad, Precision, Pucks) when Pucks < 0 -> + [$- | western2(Separator, Break, Myriad, Precision, Pucks)]. + + +western2(Separator, _, Myriad, 0, Pucks) -> G = lists:reverse(integer_to_list(Pucks div one_gaju())), - [gaju_mark() | separate(Separator, Period, G)]; -wetsern(Separator, Break, Period, Precision, Pucks) -> + [gaju_mark() | separate(Separator, Myriad, G)]; +western2(Separator, Break, Myriad, Precision, Pucks) -> SP = integer_to_list(Pucks), Length = length(SP), Over18 = Length > 18, @@ -147,19 +159,19 @@ wetsern(Separator, Break, Period, Precision, Pucks) -> case {Over18, NoPucks} of {true, true} -> Gs = lists:reverse(lists:sublist(SP, Length - 18)), - [gaju_mark() | separate(Separator, Period, Gs)]; + [gaju_mark() | separate(Separator, Myriad, Gs)]; {true, false} -> {PChars, GChars} = lists:split(18, lists:reverse(SP)), - H = [gaju_mark() | separate(Separator, Period, GChars)], + H = [gaju_mark() | separate(Separator, Myriad, GChars)], {P, E} = decimal_pucks(Precision, lists:reverse(PChars)), - T = lists:reverse(separate(Separator, Period, P)), + T = lists:reverse(separate(Separator, Myriad, P)), lists:flatten([H, Break, T, E]); {false, true} -> [gaju_mark(), $0]; {false, false} -> PChars = lists:flatten(string:pad(SP, 18, leading, $0)), {P, E} = decimal_pucks(Precision, PChars), - T = lists:reverse(separate(Separator, Period, P)), + T = lists:reverse(separate(Separator, Myriad, P)), lists:flatten([gaju_mark(), $0, Break, T, E]) end. @@ -189,24 +201,44 @@ separate(S, P, N, [H | T], A) -> separate(S, P, N + 1, T, [H | A]). -jp(gaju, 0, Pucks) -> +jp(Unit, Precision, Pucks) when Pucks >= 0 -> + jp2(Unit, Precision, Pucks); +jp(Unit, Precision, Pucks) when Pucks < 0 -> + [$-, Formatted = jp2(Unit, Precision, Pucks * -1)]. + +jp2(gaju, 0, Pucks) -> G = lists:reverse(integer_to_list(Pucks div one_gaju())), - rank4(gaju_mark(), G); -jp(gaju, all, Pucks) -> + myriad4(gaju_mark(), h, G); +jp2(gaju, all, Pucks) -> H = jp(gaju, 0, Pucks), - T = jp(puck, all, Pucks rem one_gaju()), + P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0), + T = myriad4("", l, lists:reverse(P)), lists:flatten([H, " ", T]); -jp(gaju, Precision, Pucks) -> +jp2(gaju, Precision, Pucks) -> H = jp(gaju, 0, Pucks), - T = jp(puck, Precision, Pucks rem one_gaju()), + P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0), + Digits = min(Precision, 18), + T = + case length(P) > Digits of + true -> + []; + 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 -> myriad4(puck_mark(), l, PuckingString); + true -> "" + end + end, + T = myriad4("", l, lists:reverse(P)), lists:flatten([H, " ", T]); -jp(puck, all, Pucks) -> +jp2(puck, all, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), case lists:all(fun(C) -> C =:= $0 end, P) of - false -> rank4(puck_mark(), P); + false -> myriad4(puck_mark(), h, P); true -> [$0, puck_mark()] end; -jp(puck, Precision, Pucks) -> +jp2(puck, Precision, Pucks) -> Digits = min(Precision, 18), P = lists:flatten(string:pad(integer_to_list(Pucks), 18, leading, $0)), case length(P) < Digits of @@ -215,27 +247,27 @@ jp(puck, Precision, Pucks) -> false -> PuckingString = lists:flatten(string:pad(lists:reverse(lists:sublist(P, Digits)), 18, leading, $0)), case lists:all(fun(C) -> C =:= $0 end, PuckingString) of - false -> rank4(puck_mark(), PuckingString); + false -> myriad4(puck_mark(), h, PuckingString); true -> [$0, puck_mark()] end end. -rank4(Symbol, [$0, $0, $0, $0 | PT]) -> - rank4(ranks(jp), PT, [Symbol]); -rank4(Symbol, [P4, $0, $0, $0 | PT]) -> - rank4(ranks(jp), PT, [P4, Symbol]); -rank4(Symbol, [P4, P3, $0, $0 | PT]) -> - rank4(ranks(jp), PT, [P3, P4, Symbol]); -rank4(Symbol, [P4, P3, P2, $0 | PT]) -> - rank4(ranks(jp), PT, [P2, P3, P4, Symbol]); -rank4(Symbol, [P4, P3, P2, P1 | PT]) -> - rank4(ranks(jp), PT, [P1, P2, P3, P4, Symbol]); -rank4(Symbol, [P4]) -> +myriad4(Symbol, Segment, [$0, $0, $0, $0 | PT]) -> + rank4(ranks(Segment), PT, [Symbol]); +myriad4(Symbol, Segment, [P4, $0, $0, $0 | PT]) -> + rank4(ranks(Segment), PT, [P4, Symbol]); +myriad4(Symbol, Segment, [P4, P3, $0, $0 | PT]) -> + rank4(ranks(Segment), PT, [P3, P4, Symbol]); +myriad4(Symbol, Segment, [P4, P3, P2, $0 | PT]) -> + rank4(ranks(Segment), PT, [P2, P3, P4, Symbol]); +myriad4(Symbol, Segment, [P4, P3, P2, P1 | PT]) -> + rank4(ranks(Segment), PT, [P1, P2, P3, P4, Symbol]); +myriad4(Symbol, _, [P4]) -> [P4, Symbol]; -rank4(Symbol, [P4, P3]) -> +myriad4(Symbol, _, [P4, P3]) -> [P3, P4, Symbol]; -rank4(Symbol, [P4, P3, P2]) -> +myriad4(Symbol, _, [P4, P3, P2]) -> [P2, P3, P4, Symbol]. rank4([_ | RT], [$0, $0, $0, $0 | PT], A) -> @@ -279,8 +311,11 @@ rank4([RH | _], [P4, P3, P2], A) -> rank4([RH | _], [P4, P3, P2, P1], A) -> [P1, P2, P3, P4, RH | A]. -ranks(jp) -> - "万億兆京垓秭穣溝澗正載極". +ranks(h) -> + "万億兆京垓秭穣溝澗正載極"; +ranks(l) -> +% "曼戌弒俿咳". + "満億照鏡咳". gaju_mark() -> $木. @@ -290,6 +325,159 @@ puck_mark() -> $本. one_gaju() -> 1_000_000_000_000_000_000. +-spec read(Format) -> Result + when Format :: string(), + Result :: {ok, Pucks} | {error, Reason}, + Pucks :: integer(), + Reason :: {badarg, Partial :: string(), Rest :: term()} + | {incomplete, Partial :: string(), Rest :: binary()} + | format. +%% @doc +%% Convery any valid string formatted representation and output a value in pucks. +%% This routine can fail in the special case of `ch' style formatting with a single +%% comma and/or a single period in it, as this can trigger misinterpretation as `us' +%% style. When in doubt, always call `read/2' with a style specified. + +read(Format) -> + case assess_style(Format) of + us -> read(us, Format); + ch -> read(ch, Format); + jp -> read(jp, Format); + Error -> Error + end. + +-spec read(Style, Formatted) -> Result + when Style :: us | ch | jp | undefined, + Format :: string(), + Result :: {ok, Pucks} | {error, Reason}, + Pucks :: integer(), + Reason :: {badarg, Partial :: string(), Rest :: term()} + | {incomplete, Partial :: string(), Rest :: binary()} + | format. +%% @doc +%% Convert any valid string formatted representation and output a value in pucks. +%% Note that this function is deliberately a bit too permissive in the case of +%% western formatting, stripping all non-halfwidth digit characters on the high +%% and low sides of the format once the break (decimal) character is identified +%% and sign is determined. That is to say, there are many ways to feed this wacky +%% strings and get a number out of it, so be aware. + +read(Style, Format) -> + case unicode:characters_to_list(Format) of + String when is_list(String) -> + Trimmed = string:trim(String), + read2(Style, Trimmed); + {error, Partial, Rest} -> + {error, {badarg, Partial, Rest}}; + Incomplete -> + {error, Incomplete} + end. + +read2(us, Format) -> + read_western($., Format); +read2(ch, Format) -> + read_western($,, Format); +read2(jp, Format) -> + read_jp(Format); +read2(undefined, Format) -> + read(Format). + +read_western(Break, [$-, Rest]) -> + case read_western2(Break, Format) of + {ok, Pucks} -> {ok, Pucks * -1}; + Error -> Error + end; +read_western(Break, Format) -> + read_western2(Break, Format). + +read_western2(Break, Format) -> + case string:split(Format, [Break], all) of + [[], L] -> read_western3(0, L); + [H, []] -> read_western3(H, 0); + [H, L] -> read_western3(H, L); + [H] -> read_western3(H, 0); + _ -> {error, format} + end. + +read_western3(0, L) -> + read_l(L); +read_western3(H, 0) -> + case read_h(H) of + {ok, Gajus} -> {ok, Gajus * one_gaju()}; + Error -> Error + end; +read_western3(H, L) -> + Gajus = read_h(H), + Pucks = read_l(L), + {ok, (Gajus * one_gaju()) + Pucks}. + +read_h(S) -> + case lists:filter(fun is_numchar/1, S) of + [] -> 0; + F -> list_to_integer(Filtered) + end. + +read_l(L) -> + case lists:filter(fun is_numchar/1, S) of + [] -> 0; + F -> list_to_integer(lists:flatten(string:pad(F, 18, trailing, $0))) + end. + +is_numchar(C) -> $0 =< C andalso C =< $9. + +read_jp([$-, Format]) -> + read_jp_neg(Format); +read_jp([$-, Format]) -> + read_jp_neg(Format); +read_jp(Format) -> + read_jp2(Format). + +read_jp_neg(Format) -> + case read_jp2(Format) of + {ok, Pucks} -> {ok, Pucks * -1}; + Error -> Error + end. + +read_jp2(Format) -> + case segment_jp(Format) of + {ok, Segments} -> assemble_jp(Segments); + Error -> Error + end. + +segment_jp(Format) -> + case string:split(Format, [gaju_mark()], all) of + [Gajus, Pucks] -> + case read_segment(Gajus) of + {ok, + [Gajus] -> + case read_segment(Gajus) of + {ok, GajuSegments} -> {ok, GajuSegments, ["0"]}; + Error -> Error + end; + [] -> + {ok, ["0"], ["0"]}; + _ -> + {error, format} + end. + +assemble_jp({GajuSegments, PuckSegments}) -> + GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), + PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), + {ok, integer_to_list(lists:append(GajuString, PuckString))}. +assemble_jp(PuckSegments) -> + PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), + {ok, integer_to_list(PuckString)}. + +expand_jp_myriad(String) -> + string:pad(String, 4, leading, $0). + + +lower_jp_numchar(C) when $0 =< C andalso C =< $9 -> + C - $0; +lower_jp_numchar(C) -> + C. + + -spec price_to_string(Pucks) -> Gajus when Pucks :: integer(), Gajus :: string(). -- 2.30.2 From 975325db149ca693b62a2f770a7b534f78c455dc Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Tue, 2 Dec 2025 19:40:33 +0900 Subject: [PATCH 04/14] WIP --- src/hz_format.erl | 48 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index ad7f72e..f27be1d 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -5,7 +5,7 @@ -module(hz_format). -export([price/1, price/2, price/3, price/4, - read/1, + read/1, read/2, price_to_string/1, string_to_price/1]). -spec price(Pucks) -> Formatted @@ -211,12 +211,12 @@ jp2(gaju, 0, Pucks) -> myriad4(gaju_mark(), h, G); jp2(gaju, all, Pucks) -> H = jp(gaju, 0, Pucks), - P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0), + P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0)), T = myriad4("", l, lists:reverse(P)), lists:flatten([H, " ", T]); jp2(gaju, Precision, Pucks) -> H = jp(gaju, 0, Pucks), - P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0), + P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0)), Digits = min(Precision, 18), T = case length(P) > Digits of @@ -346,7 +346,30 @@ read(Format) -> Error -> Error end. --spec read(Style, Formatted) -> Result +assess_style(Format) -> + CharIndex = count_chars(Format), + case maps:find($., CharIndex) of + {ok, 1} -> + us; + {ok, N} when N > 1 -> + ch; + error -> + case maps:is_key($木, CharIndex) orelse maps:is_key($本, CharIndex) of + true -> read(jp, Format); + false -> {error, format} + end + end. + +count_chars(Format) -> + count_chars(Format, #{}). + +count_chars([H | T], A) -> count_chars(T, maps:update_with(H, fun inc/1, 1, A)); +count_chars([], A) -> A. + +inc(N) -> N + 1. + + +-spec read(Style, Format) -> Result when Style :: us | ch | jp | undefined, Format :: string(), Result :: {ok, Pucks} | {error, Reason}, @@ -382,7 +405,7 @@ read2(jp, Format) -> read2(undefined, Format) -> read(Format). -read_western(Break, [$-, Rest]) -> +read_western(Break, [$-, Format]) -> case read_western2(Break, Format) of {ok, Pucks} -> {ok, Pucks * -1}; Error -> Error @@ -448,7 +471,14 @@ segment_jp(Format) -> case string:split(Format, [gaju_mark()], all) of [Gajus, Pucks] -> case read_segment(Gajus) of - {ok, + {ok, GajuSegments} -> + case read_segment(Pucks) of + {ok, PuckSegments} -> {ok, GajuSegments, PuckSegments}; + Error -> Error + end; + Error -> + Error + end; [Gajus] -> case read_segment(Gajus) of {ok, GajuSegments} -> {ok, GajuSegments, ["0"]}; @@ -460,6 +490,8 @@ segment_jp(Format) -> {error, format} end. +read_segment( + assemble_jp({GajuSegments, PuckSegments}) -> GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), @@ -472,9 +504,9 @@ expand_jp_myriad(String) -> string:pad(String, 4, leading, $0). -lower_jp_numchar(C) when $0 =< C andalso C =< $9 -> +hw_jp_numchar(C) when $0 =< C andalso C =< $9 -> C - $0; -lower_jp_numchar(C) -> +hw_jp_numchar(C) -> C. -- 2.30.2 From 11730de24a0aced1be2c079b860758cd2aee57c9 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Fri, 5 Dec 2025 17:38:57 +0900 Subject: [PATCH 05/14] WIP --- src/hz_format.erl | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index f27be1d..03bd2d3 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -42,8 +42,6 @@ price(Style, Pucks) -> Formatted :: string(). %% @doc %% A simplified format function covering the most common formats desired. -%% -%% ``` price(gaju, us, Pucks) -> western($,, $., 3, all, Pucks); @@ -245,7 +243,8 @@ jp2(puck, Precision, Pucks) -> true -> [$0, puck_mark()]; false -> - PuckingString = lists:flatten(string:pad(lists:reverse(lists:sublist(P, Digits)), 18, leading, $0)), + 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 -> myriad4(puck_mark(), h, PuckingString); true -> [$0, puck_mark()] @@ -339,34 +338,30 @@ one_gaju() -> 1_000_000_000_000_000_000. %% style. When in doubt, always call `read/2' with a style specified. read(Format) -> - case assess_style(Format) of + case assess_style(string:trim(Format)) of us -> read(us, Format); ch -> read(ch, Format); jp -> read(jp, Format); Error -> Error end. +assess_style([H1, H2 | Numbers]) + when H1 =:= $木 orelse H1 =:= $本 orelse H2 =:= $木 orelse H2 =:= $本 -> + case count($., Numbers) > 1 of + false -> us; + true -> ch + end; assess_style(Format) -> - CharIndex = count_chars(Format), - case maps:find($., CharIndex) of - {ok, 1} -> - us; - {ok, N} when N > 1 -> - ch; - error -> - case maps:is_key($木, CharIndex) orelse maps:is_key($本, CharIndex) of - true -> read(jp, Format); - false -> {error, format} - end + case lists:member($木, Format) orelse lists:member($本, Format) of + true -> jp; + false -> {error, format} end. -count_chars(Format) -> - count_chars(Format, #{}). +count(Char, String) -> count(Char, String, 0). -count_chars([H | T], A) -> count_chars(T, maps:update_with(H, fun inc/1, 1, A)); -count_chars([], A) -> A. - -inc(N) -> N + 1. +count(C, [C | T], A) -> count(C, T, A + 1); +count(C, [_ | T], A) -> count(C, T, A); +count(_, [], A) -> A. -spec read(Style, Format) -> Result -- 2.30.2 From 5a145207daf7fdabe3058b979d0f11038d1e4068 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Thu, 11 Dec 2025 17:13:02 +0900 Subject: [PATCH 06/14] WIP --- src/hz_format.erl | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 03bd2d3..94c8d86 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -34,12 +34,12 @@ price(Style, Pucks) -> -spec price(Unit, Style, Pucks) -> Formatted - when Unit :: gaju | puck, - Style :: us | ch | jp | {Separator, Myriad}, - Separator :: $, | $. | $_, - Myriad :: 3 | 4, - Pucks :: integer(), - Formatted :: string(). + when Unit :: gaju | puck, + Style :: us | ch | jp | {Separator, Myriad}, + Separator :: $, | $. | $_, + Myriad :: 3 | 4, + Pucks :: integer(), + Formatted :: string(). %% @doc %% A simplified format function covering the most common formats desired. @@ -485,7 +485,12 @@ segment_jp(Format) -> {error, format} end. -read_segment( +read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> + N = C - $0, + read_segment(T, R, [NA); +read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> + + assemble_jp({GajuSegments, PuckSegments}) -> GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), -- 2.30.2 From b219d0f784acdda37d6f92ff856cf6734053e653 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 00:56:13 +0900 Subject: [PATCH 07/14] Add "metric" formatting --- src/hz_format.erl | 641 ++++++++++++++++++++++++++-------------------- 1 file changed, 358 insertions(+), 283 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 94c8d86..f9598e8 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -5,7 +5,7 @@ -module(hz_format). -export([price/1, price/2, price/3, price/4, - read/1, read/2, +% read/1, read/2, price_to_string/1, string_to_price/1]). -spec price(Pucks) -> Formatted @@ -20,13 +20,13 @@ price(Pucks) -> -spec price(Style, Pucks) -> Formatted - when Style :: us | ch | jp | {Separator, Myriad}, - Separator :: $, | $. | $_, - Myriad :: 3 | 4, + when Style :: us | jp | metric | heresy | {Separator, Span}, + Separator :: $, | $_, + Span :: 3 | 4, Pucks :: integer(), Formatted :: string(). %% @doc -%% A convenient formatting function. +%% A money formatting function. %% @equiv price(gaju, Style, Pucks). price(Style, Pucks) -> @@ -35,9 +35,9 @@ price(Style, Pucks) -> -spec price(Unit, Style, Pucks) -> Formatted when Unit :: gaju | puck, - Style :: us | ch | jp | {Separator, Myriad}, - Separator :: $, | $. | $_, - Myriad :: 3 | 4, + Style :: us | jp | metric | heresy | {Separator, Span}, + Separator :: $, | $_, + Span :: 3 | 4, Pucks :: integer(), Formatted :: string(). %% @doc @@ -47,27 +47,24 @@ price(gaju, us, Pucks) -> western($,, $., 3, all, Pucks); price(puck, us, Pucks) -> western($,, 3, Pucks); -price(gaju, ch, Pucks) -> - western($., $,, 3, all, Pucks); -price(puck, ch, Pucks) -> - western($,, 3, Pucks); price(Unit, jp, Pucks) -> jp(Unit, all, Pucks); -price(gaju, {$., Myriad}, Pucks) -> - western($., $,, Myriad, all, Pucks); -price(gaju, {Separator, Myriad}, Pucks) -> - western(Separator, $., Myriad, all, Pucks); -price(puck, {Separator, Myriad}, Pucks) -> - western(Separator, Myriad, Pucks). - +price(Unit, metric, Pucks) -> + bestern(Unit, ranks(metric), all, Pucks); +price(Unit, heresy, Pucks) -> + bestern(Unit, ranks(heresy), all, Pucks); +price(gaju, {Separator, Span}, Pucks) -> + western(Separator, $., Span, all, Pucks); +price(puck, {Separator, Span}, Pucks) -> + western(Separator, Span, Pucks). -spec price(Unit, Style, Precision, Pucks) -> Serialized when Unit :: gaju | puck, - Style :: us | ch | jp | {Separator, Myriad}, + Style :: us | jp | metric | heresy | {Separator, Span}, Precision :: all | 0..18, - Separator :: $, | $. | $_, - Myriad :: 3 | 4, + Separator :: $, | $_, + Span :: 3 | 4, Pucks :: integer(), Serialized :: string(). %% @doc @@ -77,9 +74,6 @@ price(puck, {Separator, Myriad}, Pucks) -> %% price(gaju, us, 3, 123456789123456789123456789) -> %% "木123,456,789.123...". %% -%% price(gaju, ch, 3, 123456789123456789123456789) -> -%% "木123.456.789,123...". -%% %% price(gaju, us, 3, 123456789123000000000000000) -> %% "木123,456,789.123". %% @@ -89,9 +83,6 @@ price(puck, {Separator, Myriad}, Pucks) -> %% price(gaju, {$,, 3}, 6, 123456789123000000000000000) -> %% "木123,456,789.123" %% -%% price(gaju, {$., 3}, all, 123456789123456789123456789) -> -%% "木123.456.789,123.456.789.123.456.789" -%% %% price(gaju, {$_, 4}, 10, 123456789123456789123456789) -> %% "木1_2345_6789.1234_5678_91..." %% @@ -110,46 +101,46 @@ price(puck, {Separator, Myriad}, Pucks) -> price(gaju, us, Precision, Pucks) -> western($,, $., 3, Precision, Pucks); -price(gaju, ch, Precision, Pucks) -> - western($., $,, 3, Precision, Pucks); price(gaju, jp, Precision, Pucks) -> jp(gaju, Precision, Pucks); -price(gaju, {$., Myriad}, Precision, Pucks) -> - western($., $,, Myriad, Precision, Pucks); -price(gaju, {Separator, Myriad}, Precision, Pucks) -> - western(Separator, $., Myriad, Precision, Pucks); +price(gaju, metric, Precision, Pucks) -> + bestern(gaju, ranks(metric), Precision, Pucks); +price(gaju, legacy, Precision, Pucks) -> + bestern(gaju, ranks(heresy), Precision, Pucks); +price(gaju, {Separator, Span}, Precision, Pucks) -> + western(Separator, $., Span, Precision, Pucks); price(puck, us, _, Pucks) -> western($,, 3, Pucks); -price(puck, ch, _, Pucks) -> - western($., 3, Pucks); price(puck, jp, _, Pucks) -> jp(puck, all, Pucks); -price(puck, {Separator, Myriad}, _, Pucks) -> - western(Separator, Myriad, Pucks). +price(puck, metric, _, Pucks) -> + bestern(puck, ranks(metric), all, Pucks); +price(puck, legacy, _, Pucks) -> + bestern(puck, ranks(heresy), all, Pucks); +price(puck, {Separator, Span}, _, Pucks) -> + western(Separator, Span, Pucks). +western(Separator, Span, Pucks) when Pucks >= 0 -> + western2(Separator, Span, Pucks); +western(Separator, Span, Pucks) when Pucks < 0 -> + [$- | western2(Separator, Span, Pucks * -1)]. - -western(Separator, Myriad, Pucks) when Pucks >= 0 -> - western2(Separator, Myriad, Pucks); -western(Separator, Myriad, Pucks) when Pucks < 0 -> - [$- | western2(Separator, Myriad, Pucks * -1)]. - -western2(Separator, Myriad, Pucks) -> +western2(Separator, Span, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), - [puck_mark() | separate(Separator, Myriad, P)]. + [puck_mark() | separate(Separator, Span, P)]. -western(Separator, Break, Myriad, Precision, Pucks) when Pucks >= 0 -> - western2(Separator, Break, Myriad, Precision, Pucks); -western(Separator, Break, Myriad, Precision, Pucks) when Pucks < 0 -> - [$- | western2(Separator, Break, Myriad, Precision, Pucks)]. +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)]. -western2(Separator, _, Myriad, 0, Pucks) -> +western2(Separator, _, Span, 0, Pucks) -> G = lists:reverse(integer_to_list(Pucks div one_gaju())), - [gaju_mark() | separate(Separator, Myriad, G)]; -western2(Separator, Break, Myriad, Precision, Pucks) -> + [gaju_mark() | separate(Separator, Span, G)]; +western2(Separator, Break, Span, Precision, Pucks) -> SP = integer_to_list(Pucks), Length = length(SP), Over18 = Length > 18, @@ -157,19 +148,19 @@ western2(Separator, Break, Myriad, Precision, Pucks) -> case {Over18, NoPucks} of {true, true} -> Gs = lists:reverse(lists:sublist(SP, Length - 18)), - [gaju_mark() | separate(Separator, Myriad, Gs)]; + [gaju_mark() | separate(Separator, Span, Gs)]; {true, false} -> {PChars, GChars} = lists:split(18, lists:reverse(SP)), - H = [gaju_mark() | separate(Separator, Myriad, GChars)], + H = [gaju_mark() | separate(Separator, Span, GChars)], {P, E} = decimal_pucks(Precision, lists:reverse(PChars)), - T = lists:reverse(separate(Separator, Myriad, P)), + T = lists:reverse(separate(Separator, Span, P)), lists:flatten([H, Break, T, E]); {false, true} -> [gaju_mark(), $0]; {false, false} -> PChars = lists:flatten(string:pad(SP, 18, leading, $0)), {P, E} = decimal_pucks(Precision, PChars), - T = lists:reverse(separate(Separator, Myriad, P)), + T = lists:reverse(separate(Separator, Span, P)), lists:flatten([gaju_mark(), $0, Break, T, E]) end. @@ -199,74 +190,159 @@ separate(S, P, N, [H | T], A) -> separate(S, P, N + 1, T, [H | A]). -jp(Unit, Precision, Pucks) when Pucks >= 0 -> - jp2(Unit, Precision, Pucks); -jp(Unit, Precision, Pucks) when Pucks < 0 -> - [$-, Formatted = jp2(Unit, Precision, Pucks * -1)]. +bestern(gaju, Ranks, Precision, Pucks) when Pucks >= 0 -> + [gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks)]; +bestern(gaju, Ranks, Precision, Pucks) when Pucks < 0 -> + [$-, gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)]; +bestern(puck, Ranks, Precision, Pucks) when Pucks >= 0 -> + [puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks)]; +bestern(puck, Ranks, Precision, Pucks) when Pucks < 0 -> + [$-, puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks * -1)]. -jp2(gaju, 0, Pucks) -> +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())), - myriad4(gaju_mark(), h, G); -jp2(gaju, all, Pucks) -> - H = jp(gaju, 0, Pucks), + case Span of + 3 -> period(" G", Ranks, G); + 4 -> myriad(gaju_mark(), Ranks, G) + end; +bestern2(gaju, Ranks, Span, all, Pucks) -> P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0)), - T = myriad4("", l, lists:reverse(P)), + {H, T} = + case Span of + 3 -> {bestern2(gaju, Ranks, 3, 0, Pucks), period(" P", Ranks, lists:reverse(P))}; + 4 -> {jp(gaju, 0, Pucks), myriad(puck_mark(), Ranks, lists:reverse(P))} + end, lists:flatten([H, " ", T]); -jp2(gaju, Precision, Pucks) -> - H = jp(gaju, 0, Pucks), +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 - true -> - []; + 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 -> myriad4(puck_mark(), l, PuckingString); - true -> "" - end + false -> + case Span of + 3 -> period(" P", Ranks, PuckingString); + 4 -> myriad(puck_mark(), Ranks, PuckingString) + end; + true -> + "" + end; + true -> + [] end, - T = myriad4("", l, lists:reverse(P)), lists:flatten([H, " ", T]); -jp2(puck, all, Pucks) -> +bestern2(puck, Ranks, Span, all, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), case lists:all(fun(C) -> C =:= $0 end, P) of - false -> myriad4(puck_mark(), h, P); - true -> [$0, puck_mark()] + false -> + case Span of + 3 -> period(" P", Ranks, P); + 4 -> myriad(puck_mark(), Ranks, P) + end; + true -> + case Span of + 3 -> [$0, " P"]; + 4 -> [$0, puck_mark()] + end end; -jp2(puck, Precision, Pucks) -> +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 -> - [$0, puck_mark()]; + case Span of + 3 -> [$0, " P"]; + 4 -> [$0, puck_mark()] + 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 -> myriad4(puck_mark(), h, PuckingString); - true -> [$0, puck_mark()] + false -> + case Span of + 3 -> period(" P", Ranks, PuckingString); + 4 -> myriad(puck_mark(), Ranks, PuckingString) + end; + true -> + case Span of + 3 -> [$0, " P"]; + 4 -> [$0, puck_mark()] + end end end. -myriad4(Symbol, Segment, [$0, $0, $0, $0 | PT]) -> - rank4(ranks(Segment), PT, [Symbol]); -myriad4(Symbol, Segment, [P4, $0, $0, $0 | PT]) -> - rank4(ranks(Segment), PT, [P4, Symbol]); -myriad4(Symbol, Segment, [P4, P3, $0, $0 | PT]) -> - rank4(ranks(Segment), PT, [P3, P4, Symbol]); -myriad4(Symbol, Segment, [P4, P3, P2, $0 | PT]) -> - rank4(ranks(Segment), PT, [P2, P3, P4, Symbol]); -myriad4(Symbol, Segment, [P4, P3, P2, P1 | PT]) -> - rank4(ranks(Segment), PT, [P1, P2, P3, P4, Symbol]); -myriad4(Symbol, _, [P4]) -> +period(Symbol, Ranks, [$0, $0, $0 | PT]) -> + rank3(Ranks, PT, [Symbol]); +period(Symbol, Ranks, [P3, $0, $0 | PT]) -> + rank3(Ranks, PT, [P3, Symbol]); +period(Symbol, Ranks, [P3, P2, $0 | PT]) -> + rank3(Ranks, PT, [P2, P3, Symbol]); +period(Symbol, Ranks, [P3, P2, P1 | PT]) -> + rank3(Ranks, PT, [P1, P2, P3, Symbol]); +period(Symbol, _, [P3]) -> + [P3, Symbol]; +period(Symbol, _, [P3, P2]) -> + [P2, P3, 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]; -myriad4(Symbol, _, [P4, P3]) -> +myriad(Symbol, _, [P4, P3]) -> [P3, P4, Symbol]; -myriad4(Symbol, _, [P4, P3, P2]) -> +myriad(Symbol, _, [P4, P3, P2]) -> [P2, P3, P4, Symbol]. rank4([_ | RT], [$0, $0, $0, $0 | PT], A) -> @@ -310,11 +386,12 @@ rank4([RH | _], [P4, P3, P2], A) -> rank4([RH | _], [P4, P3, P2, P1], A) -> [P1, P2, P3, P4, RH | A]. -ranks(h) -> +ranks(jp) -> "万億兆京垓秭穣溝澗正載極"; -ranks(l) -> -% "曼戌弒俿咳". - "満億照鏡咳". +ranks(metric) -> + ["k ", "m ", "g ", "t ", "p ", "e ", "z ", "y ", "r ", "Q "]; +ranks(heresy) -> + ["k ", "m ", "b ", "t ", "q ", "e ", "z ", "y ", "r ", "Q "]. gaju_mark() -> $木. @@ -324,190 +401,188 @@ puck_mark() -> $本. one_gaju() -> 1_000_000_000_000_000_000. --spec read(Format) -> Result - when Format :: string(), - Result :: {ok, Pucks} | {error, Reason}, - Pucks :: integer(), - Reason :: {badarg, Partial :: string(), Rest :: term()} - | {incomplete, Partial :: string(), Rest :: binary()} - | format. -%% @doc -%% Convery any valid string formatted representation and output a value in pucks. -%% This routine can fail in the special case of `ch' style formatting with a single -%% comma and/or a single period in it, as this can trigger misinterpretation as `us' -%% style. When in doubt, always call `read/2' with a style specified. - -read(Format) -> - case assess_style(string:trim(Format)) of - us -> read(us, Format); - ch -> read(ch, Format); - jp -> read(jp, Format); - Error -> Error - end. - -assess_style([H1, H2 | Numbers]) - when H1 =:= $木 orelse H1 =:= $本 orelse H2 =:= $木 orelse H2 =:= $本 -> - case count($., Numbers) > 1 of - false -> us; - true -> ch - end; -assess_style(Format) -> - case lists:member($木, Format) orelse lists:member($本, Format) of - true -> jp; - false -> {error, format} - end. - -count(Char, String) -> count(Char, String, 0). - -count(C, [C | T], A) -> count(C, T, A + 1); -count(C, [_ | T], A) -> count(C, T, A); -count(_, [], A) -> A. - - --spec read(Style, Format) -> Result - when Style :: us | ch | jp | undefined, - Format :: string(), - Result :: {ok, Pucks} | {error, Reason}, - Pucks :: integer(), - Reason :: {badarg, Partial :: string(), Rest :: term()} - | {incomplete, Partial :: string(), Rest :: binary()} - | format. -%% @doc -%% Convert any valid string formatted representation and output a value in pucks. -%% Note that this function is deliberately a bit too permissive in the case of -%% western formatting, stripping all non-halfwidth digit characters on the high -%% and low sides of the format once the break (decimal) character is identified -%% and sign is determined. That is to say, there are many ways to feed this wacky -%% strings and get a number out of it, so be aware. - -read(Style, Format) -> - case unicode:characters_to_list(Format) of - String when is_list(String) -> - Trimmed = string:trim(String), - read2(Style, Trimmed); - {error, Partial, Rest} -> - {error, {badarg, Partial, Rest}}; - Incomplete -> - {error, Incomplete} - end. - -read2(us, Format) -> - read_western($., Format); -read2(ch, Format) -> - read_western($,, Format); -read2(jp, Format) -> - read_jp(Format); -read2(undefined, Format) -> - read(Format). - -read_western(Break, [$-, Format]) -> - case read_western2(Break, Format) of - {ok, Pucks} -> {ok, Pucks * -1}; - Error -> Error - end; -read_western(Break, Format) -> - read_western2(Break, Format). - -read_western2(Break, Format) -> - case string:split(Format, [Break], all) of - [[], L] -> read_western3(0, L); - [H, []] -> read_western3(H, 0); - [H, L] -> read_western3(H, L); - [H] -> read_western3(H, 0); - _ -> {error, format} - end. - -read_western3(0, L) -> - read_l(L); -read_western3(H, 0) -> - case read_h(H) of - {ok, Gajus} -> {ok, Gajus * one_gaju()}; - Error -> Error - end; -read_western3(H, L) -> - Gajus = read_h(H), - Pucks = read_l(L), - {ok, (Gajus * one_gaju()) + Pucks}. - -read_h(S) -> - case lists:filter(fun is_numchar/1, S) of - [] -> 0; - F -> list_to_integer(Filtered) - end. - -read_l(L) -> - case lists:filter(fun is_numchar/1, S) of - [] -> 0; - F -> list_to_integer(lists:flatten(string:pad(F, 18, trailing, $0))) - end. - -is_numchar(C) -> $0 =< C andalso C =< $9. - -read_jp([$-, Format]) -> - read_jp_neg(Format); -read_jp([$-, Format]) -> - read_jp_neg(Format); -read_jp(Format) -> - read_jp2(Format). - -read_jp_neg(Format) -> - case read_jp2(Format) of - {ok, Pucks} -> {ok, Pucks * -1}; - Error -> Error - end. - -read_jp2(Format) -> - case segment_jp(Format) of - {ok, Segments} -> assemble_jp(Segments); - Error -> Error - end. - -segment_jp(Format) -> - case string:split(Format, [gaju_mark()], all) of - [Gajus, Pucks] -> - case read_segment(Gajus) of - {ok, GajuSegments} -> - case read_segment(Pucks) of - {ok, PuckSegments} -> {ok, GajuSegments, PuckSegments}; - Error -> Error - end; - Error -> - Error - end; - [Gajus] -> - case read_segment(Gajus) of - {ok, GajuSegments} -> {ok, GajuSegments, ["0"]}; - Error -> Error - end; - [] -> - {ok, ["0"], ["0"]}; - _ -> - {error, format} - end. - -read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> - N = C - $0, - read_segment(T, R, [NA); -read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> - - - -assemble_jp({GajuSegments, PuckSegments}) -> - GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), - PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), - {ok, integer_to_list(lists:append(GajuString, PuckString))}. -assemble_jp(PuckSegments) -> - PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), - {ok, integer_to_list(PuckString)}. - -expand_jp_myriad(String) -> - string:pad(String, 4, leading, $0). - - -hw_jp_numchar(C) when $0 =< C andalso C =< $9 -> - C - $0; -hw_jp_numchar(C) -> - C. +%-spec read(Format) -> Result +% when Format :: string(), +% Result :: {ok, Pucks} | {error, Reason}, +% Pucks :: integer(), +% Reason :: {badarg, Partial :: string(), Rest :: term()} +% | {incomplete, Partial :: string(), Rest :: binary()} +% | format. +%%% @doc +%%% Convery any valid string formatted representation and output a value in pucks. +%%% This routine can fail in the special case of `ch' style formatting with a single +%%% comma and/or a single period in it, as this can trigger misinterpretation as `us' +%%% style. When in doubt, always call `read/2' with a style specified. +% +%read(Format) -> +% case assess_style(string:trim(Format)) of +% us -> read(us, Format); +% ch -> read(ch, Format); +% jp -> read(jp, Format); +% Error -> Error +% end. +% +%assess_style([H1, H2 | Numbers]) +% when H1 =:= $木 orelse H1 =:= $本 orelse H2 =:= $木 orelse H2 =:= $本 -> +% case count($., Numbers) > 1 of +% false -> us; +% true -> ch +% end; +%assess_style(Format) -> +% case lists:member($木, Format) orelse lists:member($本, Format) of +% true -> jp; +% false -> {error, format} +% end. +% +%count(Char, String) -> count(Char, String, 0). +% +%count(C, [C | T], A) -> count(C, T, A + 1); +%count(C, [_ | T], A) -> count(C, T, A); +%count(_, [], A) -> A. +% +% +%-spec read(Style, Format) -> Result +% when Style :: us | jp | metric | legacy | undefined, +% Format :: string(), +% Result :: {ok, Pucks} | {error, Reason}, +% Pucks :: integer(), +% Reason :: {badarg, Partial :: string(), Rest :: term()} +% | {incomplete, Partial :: string(), Rest :: binary()} +% | format. +%%% @doc +%%% Convert any valid string formatted representation and output a value in pucks. +%%% Note that this function is deliberately a bit too permissive in the case of +%%% western formatting, stripping all non-halfwidth digit characters on the high +%%% and low sides of the format once the break (decimal) character is identified +%%% and sign is determined. That is to say, there are many ways to feed this wacky +%%% strings and get a number out of it, so be aware. +% +%read(Style, Format) -> +% case unicode:characters_to_list(Format) of +% String when is_list(String) -> +% Trimmed = string:trim(String), +% read2(Style, Trimmed); +% {error, Partial, Rest} -> +% {error, {badarg, Partial, Rest}}; +% Incomplete -> +% {error, Incomplete} +% end. +% +%read2(us, Format) -> +% read_western($., Format); +%read2(jp, Format) -> +% read_jp(Format); +%read2(undefined, Format) -> +% read(Format). +% +%read_western(Break, [$-, Format]) -> +% case read_western2(Break, Format) of +% {ok, Pucks} -> {ok, Pucks * -1}; +% Error -> Error +% end; +%read_western(Break, Format) -> +% read_western2(Break, Format). +% +%read_western2(Break, Format) -> +% case string:split(Format, [Break], all) of +% [[], L] -> read_western3(0, L); +% [H, []] -> read_western3(H, 0); +% [H, L] -> read_western3(H, L); +% [H] -> read_western3(H, 0); +% _ -> {error, format} +% end. +% +%read_western3(0, L) -> +% read_l(L); +%read_western3(H, 0) -> +% case read_h(H) of +% {ok, Gajus} -> {ok, Gajus * one_gaju()}; +% Error -> Error +% end; +%read_western3(H, L) -> +% Gajus = read_h(H), +% Pucks = read_l(L), +% {ok, (Gajus * one_gaju()) + Pucks}. +% +%read_h(S) -> +% case lists:filter(fun is_numchar/1, S) of +% [] -> 0; +% F -> list_to_integer(Filtered) +% end. +% +%read_l(L) -> +% case lists:filter(fun is_numchar/1, S) of +% [] -> 0; +% F -> list_to_integer(lists:flatten(string:pad(F, 18, trailing, $0))) +% end. +% +%is_numchar(C) -> $0 =< C andalso C =< $9. +% +%read_jp([$-, Format]) -> +% read_jp_neg(Format); +%read_jp([$-, Format]) -> +% read_jp_neg(Format); +%read_jp(Format) -> +% read_jp2(Format). +% +%read_jp_neg(Format) -> +% case read_jp2(Format) of +% {ok, Pucks} -> {ok, Pucks * -1}; +% Error -> Error +% end. +% +%read_jp2(Format) -> +% case segment_jp(Format) of +% {ok, Segments} -> assemble_jp(Segments); +% Error -> Error +% end. +% +%segment_jp(Format) -> +% case string:split(Format, [gaju_mark()], all) of +% [Gajus, Pucks] -> +% case read_segment(Gajus) of +% {ok, GajuSegments} -> +% case read_segment(Pucks) of +% {ok, PuckSegments} -> {ok, GajuSegments, PuckSegments}; +% Error -> Error +% end; +% Error -> +% Error +% end; +% [Gajus] -> +% case read_segment(Gajus) of +% {ok, GajuSegments} -> {ok, GajuSegments, ["0"]}; +% Error -> Error +% end; +% [] -> +% {ok, ["0"], ["0"]}; +% _ -> +% {error, format} +% end. +% +%read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> +% N = C - $0, +% read_segment(T, R, [NA); +%read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> +% +% +% +%assemble_jp({GajuSegments, PuckSegments}) -> +% GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), +% PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), +% {ok, integer_to_list(lists:append(GajuString, PuckString))}. +%assemble_jp(PuckSegments) -> +% PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), +% {ok, integer_to_list(PuckString)}. +% +%expand_jp_myriad(String) -> +% string:pad(String, 4, leading, $0). +% +% +%hw_jp_numchar(C) when $0 =< C andalso C =< $9 -> +% C - $0; +%hw_jp_numchar(C) -> +% C. -spec price_to_string(Pucks) -> Gajus -- 2.30.2 From 23137a677e8ccfc657db3abb7db1fd2555862f43 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 01:50:15 +0900 Subject: [PATCH 08/14] WIP --- src/hz_format.erl | 271 ++++++++++++++++++++-------------------------- 1 file changed, 117 insertions(+), 154 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index f9598e8..87d3b16 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -4,22 +4,22 @@ -module(hz_format). --export([price/1, price/2, price/3, price/4, -% read/1, read/2, +-export([amount/1, amount/2, amount/3, amount/4, + read/1, price_to_string/1, string_to_price/1]). --spec price(Pucks) -> Formatted +-spec amount(Pucks) -> Formatted when Pucks :: integer(), Formatted :: string(). %% @doc %% A convenience formatting function. -%% @equiv price(us, Pucks). +%% @equiv amount(us, Pucks). -price(Pucks) -> - price(us, Pucks). +amount(Pucks) -> + amount(us, Pucks). --spec price(Style, Pucks) -> Formatted +-spec amount(Style, Pucks) -> Formatted when Style :: us | jp | metric | heresy | {Separator, Span}, Separator :: $, | $_, Span :: 3 | 4, @@ -27,13 +27,13 @@ price(Pucks) -> Formatted :: string(). %% @doc %% A money formatting function. -%% @equiv price(gaju, Style, Pucks). +%% @equiv amount(gaju, Style, Pucks). -price(Style, Pucks) -> - price(gaju, Style, Pucks). +amount(Style, Pucks) -> + amount(gaju, Style, Pucks). --spec price(Unit, Style, Pucks) -> Formatted +-spec amount(Unit, Style, Pucks) -> Formatted when Unit :: gaju | puck, Style :: us | jp | metric | heresy | {Separator, Span}, Separator :: $, | $_, @@ -43,23 +43,23 @@ price(Style, Pucks) -> %% @doc %% A simplified format function covering the most common formats desired. -price(gaju, us, Pucks) -> +amount(gaju, us, Pucks) -> western($,, $., 3, all, Pucks); -price(puck, us, Pucks) -> +amount(puck, us, Pucks) -> western($,, 3, Pucks); -price(Unit, jp, Pucks) -> +amount(Unit, jp, Pucks) -> jp(Unit, all, Pucks); -price(Unit, metric, Pucks) -> +amount(Unit, metric, Pucks) -> bestern(Unit, ranks(metric), all, Pucks); -price(Unit, heresy, Pucks) -> +amount(Unit, heresy, Pucks) -> bestern(Unit, ranks(heresy), all, Pucks); -price(gaju, {Separator, Span}, Pucks) -> +amount(gaju, {Separator, Span}, Pucks) -> western(Separator, $., Span, all, Pucks); -price(puck, {Separator, Span}, Pucks) -> +amount(puck, {Separator, Span}, Pucks) -> western(Separator, Span, Pucks). --spec price(Unit, Style, Precision, Pucks) -> Serialized +-spec amount(Unit, Style, Precision, Pucks) -> Serialized when Unit :: gaju | puck, Style :: us | jp | metric | heresy | {Separator, Span}, Precision :: all | 0..18, @@ -71,53 +71,53 @@ price(puck, {Separator, Span}, Pucks) -> %% A flexible, if annoyingly complex, formatting function. %% %% ``` -%% price(gaju, us, 3, 123456789123456789123456789) -> +%% amount(gaju, us, 3, 123456789123456789123456789) -> %% "木123,456,789.123...". %% -%% price(gaju, us, 3, 123456789123000000000000000) -> +%% amount(gaju, us, 3, 123456789123000000000000000) -> %% "木123,456,789.123". %% -%% price(gaju, {$,, 3}, 3, 123456789123456789123456789) -> +%% amount(gaju, {$,, 3}, 3, 123456789123456789123456789) -> %% "木123,456,789.123...". %% -%% price(gaju, {$,, 3}, 6, 123456789123000000000000000) -> +%% amount(gaju, {$,, 3}, 6, 123456789123000000000000000) -> %% "木123,456,789.123" %% -%% price(gaju, {$_, 4}, 10, 123456789123456789123456789) -> +%% amount(gaju, {$_, 4}, 10, 123456789123456789123456789) -> %% "木1_2345_6789.1234_5678_91..." %% -%% price(gaju, jp, 3, 123456789123456789123456789) -> +%% amount(gaju, jp, 3, 123456789123456789123456789) -> %% "1億2345万6789木 12京3000兆本" %% -%% price(gaju, jp, 6, 123456789123456789123456789) -> +%% amount(gaju, jp, 6, 123456789123456789123456789) -> %% "1億2345万6789木 12京3456兆本" %% -%% price(gaju, jp, 0, 123456789123456789123456789) -> +%% amount(gaju, jp, 0, 123456789123456789123456789) -> %% "1億2345万6789木" %% -%% price(puck, jp, all, 123456789123456789123456789) -> +%% amount(puck, jp, all, 123456789123456789123456789) -> %% "123秭4567垓8912京3456兆7891億2345万6789本" %% ''' -price(gaju, us, Precision, Pucks) -> +amount(gaju, us, Precision, Pucks) -> western($,, $., 3, Precision, Pucks); -price(gaju, jp, Precision, Pucks) -> +amount(gaju, jp, Precision, Pucks) -> jp(gaju, Precision, Pucks); -price(gaju, metric, Precision, Pucks) -> +amount(gaju, metric, Precision, Pucks) -> bestern(gaju, ranks(metric), Precision, Pucks); -price(gaju, legacy, Precision, Pucks) -> +amount(gaju, legacy, Precision, Pucks) -> bestern(gaju, ranks(heresy), Precision, Pucks); -price(gaju, {Separator, Span}, Precision, Pucks) -> +amount(gaju, {Separator, Span}, Precision, Pucks) -> western(Separator, $., Span, Precision, Pucks); -price(puck, us, _, Pucks) -> +amount(puck, us, _, Pucks) -> western($,, 3, Pucks); -price(puck, jp, _, Pucks) -> +amount(puck, jp, _, Pucks) -> jp(puck, all, Pucks); -price(puck, metric, _, Pucks) -> +amount(puck, metric, _, Pucks) -> bestern(puck, ranks(metric), all, Pucks); -price(puck, legacy, _, Pucks) -> +amount(puck, legacy, _, Pucks) -> bestern(puck, ranks(heresy), all, Pucks); -price(puck, {Separator, Span}, _, Pucks) -> +amount(puck, {Separator, Span}, _, Pucks) -> western(Separator, Span, Pucks). @@ -165,7 +165,6 @@ western2(Separator, Break, Span, Precision, Pucks) -> end. decimal_pucks(all, PChars) -> - io:format("decimal_pucks: PChars: ~p~n", [PChars]), RTrailing = lists:reverse(PChars), {lists:reverse(lists:dropwhile(fun(C) -> C =:= $0 end, RTrailing)), ""}; decimal_pucks(Precision, PChars) -> @@ -401,121 +400,85 @@ puck_mark() -> $本. one_gaju() -> 1_000_000_000_000_000_000. -%-spec read(Format) -> Result -% when Format :: string(), -% Result :: {ok, Pucks} | {error, Reason}, -% Pucks :: integer(), -% Reason :: {badarg, Partial :: string(), Rest :: term()} -% | {incomplete, Partial :: string(), Rest :: binary()} -% | format. -%%% @doc -%%% Convery any valid string formatted representation and output a value in pucks. -%%% This routine can fail in the special case of `ch' style formatting with a single -%%% comma and/or a single period in it, as this can trigger misinterpretation as `us' -%%% style. When in doubt, always call `read/2' with a style specified. -% -%read(Format) -> -% case assess_style(string:trim(Format)) of -% us -> read(us, Format); -% ch -> read(ch, Format); -% jp -> read(jp, Format); -% Error -> Error -% end. -% -%assess_style([H1, H2 | Numbers]) -% when H1 =:= $木 orelse H1 =:= $本 orelse H2 =:= $木 orelse H2 =:= $本 -> -% case count($., Numbers) > 1 of -% false -> us; -% true -> ch -% end; -%assess_style(Format) -> -% case lists:member($木, Format) orelse lists:member($本, Format) of -% true -> jp; -% false -> {error, format} -% end. -% -%count(Char, String) -> count(Char, String, 0). -% -%count(C, [C | T], A) -> count(C, T, A + 1); -%count(C, [_ | T], A) -> count(C, T, A); -%count(_, [], A) -> A. -% -% -%-spec read(Style, Format) -> Result -% when Style :: us | jp | metric | legacy | undefined, -% Format :: string(), -% Result :: {ok, Pucks} | {error, Reason}, -% Pucks :: integer(), -% Reason :: {badarg, Partial :: string(), Rest :: term()} -% | {incomplete, Partial :: string(), Rest :: binary()} -% | format. -%%% @doc -%%% Convert any valid string formatted representation and output a value in pucks. -%%% Note that this function is deliberately a bit too permissive in the case of -%%% western formatting, stripping all non-halfwidth digit characters on the high -%%% and low sides of the format once the break (decimal) character is identified -%%% and sign is determined. That is to say, there are many ways to feed this wacky -%%% strings and get a number out of it, so be aware. -% -%read(Style, Format) -> -% case unicode:characters_to_list(Format) of -% String when is_list(String) -> -% Trimmed = string:trim(String), -% read2(Style, Trimmed); -% {error, Partial, Rest} -> -% {error, {badarg, Partial, Rest}}; -% Incomplete -> -% {error, Incomplete} -% end. -% -%read2(us, Format) -> -% read_western($., Format); -%read2(jp, Format) -> -% read_jp(Format); -%read2(undefined, Format) -> -% read(Format). -% -%read_western(Break, [$-, Format]) -> -% case read_western2(Break, Format) of -% {ok, Pucks} -> {ok, Pucks * -1}; -% Error -> Error -% end; -%read_western(Break, Format) -> -% read_western2(Break, Format). -% -%read_western2(Break, Format) -> -% case string:split(Format, [Break], all) of -% [[], L] -> read_western3(0, L); -% [H, []] -> read_western3(H, 0); -% [H, L] -> read_western3(H, L); -% [H] -> read_western3(H, 0); -% _ -> {error, format} -% end. -% -%read_western3(0, L) -> -% read_l(L); -%read_western3(H, 0) -> -% case read_h(H) of -% {ok, Gajus} -> {ok, Gajus * one_gaju()}; -% Error -> Error -% end; -%read_western3(H, L) -> -% Gajus = read_h(H), -% Pucks = read_l(L), -% {ok, (Gajus * one_gaju()) + Pucks}. -% -%read_h(S) -> -% case lists:filter(fun is_numchar/1, S) of -% [] -> 0; -% F -> list_to_integer(Filtered) -% end. -% -%read_l(L) -> -% case lists:filter(fun is_numchar/1, S) of -% [] -> 0; -% F -> list_to_integer(lists:flatten(string:pad(F, 18, trailing, $0))) -% end. -% +-spec read(Format) -> Result + when Format :: string(), + Result :: {ok, Pucks} | {error, Reason}, + Pucks :: integer(), + Reason :: {badarg, Partial :: string(), Rest :: term()} + | {incomplete, Partial :: string(), Rest :: binary()} + | format. +%% @doc +%% Convery any valid string formatted representation and output a value in pucks. +%% This routine can fail in the special case of `ch' style formatting with a single +%% comma and/or a single period in it, as this can trigger misinterpretation as `us' +%% style. When in doubt, always call `read/2' with a style specified. + +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(_) -> + io:format("Barfing~n"), + {error, format}. + +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_b_gajus(Rest, [{C, A}]); +read_w_gajus(_, _) -> + io:format("Derping~n"), + {error, format}. + +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([], A) -> + Padded = lists:flatten(string:pad(lists:reverse(A), 18, trailing, $0)), + {ok, list_to_integer(Padded)}. + +read_b_gajus(_, _) -> + {error, nyi}. + + %is_numchar(C) -> $0 =< C andalso C =< $9. % %read_jp([$-, Format]) -> -- 2.30.2 From 81fef99d9cf68893995c15341bfb9fcad2026f89 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 04:23:34 +0900 Subject: [PATCH 09/14] WIP --- src/hz_format.erl | 175 ++++++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 75 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 87d3b16..08a83f6 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -20,7 +20,7 @@ amount(Pucks) -> -spec amount(Style, Pucks) -> Formatted - when Style :: us | jp | metric | heresy | {Separator, Span}, + when Style :: us | jp | metric | legacy | {Separator, Span}, Separator :: $, | $_, Span :: 3 | 4, Pucks :: integer(), @@ -35,7 +35,7 @@ amount(Style, Pucks) -> -spec amount(Unit, Style, Pucks) -> Formatted when Unit :: gaju | puck, - Style :: us | jp | metric | heresy | {Separator, Span}, + Style :: us | jp | metric | legacy | {Separator, Span}, Separator :: $, | $_, Span :: 3 | 4, Pucks :: integer(), @@ -51,7 +51,7 @@ amount(Unit, jp, Pucks) -> jp(Unit, all, Pucks); amount(Unit, metric, Pucks) -> bestern(Unit, ranks(metric), all, Pucks); -amount(Unit, heresy, Pucks) -> +amount(Unit, legacy, Pucks) -> bestern(Unit, ranks(heresy), all, Pucks); amount(gaju, {Separator, Span}, Pucks) -> western(Separator, $., Span, all, Pucks); @@ -61,7 +61,7 @@ amount(puck, {Separator, Span}, Pucks) -> -spec amount(Unit, Style, Precision, Pucks) -> Serialized when Unit :: gaju | puck, - Style :: us | jp | metric | heresy | {Separator, Span}, + Style :: us | jp | metric | legacy | {Separator, Span}, Precision :: all | 0..18, Separator :: $, | $_, Span :: 3 | 4, @@ -134,7 +134,7 @@ western2(Separator, Span, Pucks) -> 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)]. + [$- | western2(Separator, Break, Span, Precision, Pucks * -1)]. western2(Separator, _, Span, 0, Pucks) -> @@ -432,6 +432,11 @@ read([C | Rest]) 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(_) -> io:format("Barfing~n"), {error, format}. @@ -457,7 +462,15 @@ read_w_gajus([], A) -> G = list_to_integer(lists:reverse(A)) * one_gaju(), {ok, G}; read_w_gajus([C, 32 | Rest], A) -> - read_b_gajus(Rest, [{C, A}]); + read(Rest, [], [{C, A}]); +read_w_gajus([32, $G | Rest], A) -> + case read(Rest, [], [{$G, A}]) of + {ok, P} -> + G = list_to_integer(lists:reverse(A)) * one_gaju(), + {ok, G + P}; + Error -> + Error + end; read_w_gajus(_, _) -> io:format("Derping~n"), {error, format}. @@ -475,77 +488,89 @@ read_w_pucks([], A) -> Padded = lists:flatten(string:pad(lists:reverse(A), 18, trailing, $0)), {ok, list_to_integer(Padded)}. -read_b_gajus(_, _) -> - {error, nyi}. + +read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> + read(Rest, [C | A], S); +read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> + NumC = C - $0 + $0, + read(Rest, [NumC | A], S); +read([$木], A, S) -> + calc([{$木, A} | S]); +read([$木, 32 | Rest], A, S) -> + read(Rest, [], [{$木, A} | S]); +read([$本], A, S) -> + calc([{$本, A} | S]); +read([32, $G], A, S) -> + calc([{$G, A} | S]); +read([32, $G, 32 | Rest], A, S) -> + read(Rest, [], [{$G, A} | S]); +read([32, $P], A, S) -> + calc([{$P, A} | S]); +read([C, 32 | Rest], A, S) -> + read(Rest, [], [{C, A} | S]); +read([C | Rest], A, S) -> + read(Rest, [], [{C, A} | S]); +read(_, _, _) -> + {error, format}. + +calc(S) -> + calc(S, 0). + +calc([{_, []} | S], A) -> + calc(S, A); +calc([{M, Cs} | S], A) -> + case magnitude(M) of + {ok, J} -> + N = list_to_integer(lists:reverse(Cs)) * J, + calc(S, A + N); + Error -> + Error + end; +calc([], A) -> + {ok, A}. + +%magnitude_j("万") -> 1_0000; +%magnitude_j("億") -> 1_0000_0000; +%magnitude_j("兆") -> 1_0000_0000_0000; +%magnitude_j("京") -> 1_0000_0000_0000_0000; +%magnitude_j("垓") -> 1_0000_0000_0000_0000_0000; +%magnitude_j("秭") -> 1_0000_0000_0000_0000_0000_0000; +%magnitude_j("穣") -> 1_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("溝") -> 1_0000_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("澗") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("正") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("載") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("極") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; +%magnitude_j("木") -> one_gaju(); +%magnitude_j("本") -> 1. -%is_numchar(C) -> $0 =< C andalso C =< $9. -% -%read_jp([$-, Format]) -> -% read_jp_neg(Format); -%read_jp([$-, Format]) -> -% read_jp_neg(Format); -%read_jp(Format) -> -% read_jp2(Format). -% -%read_jp_neg(Format) -> -% case read_jp2(Format) of -% {ok, Pucks} -> {ok, Pucks * -1}; -% Error -> Error -% end. -% -%read_jp2(Format) -> -% case segment_jp(Format) of -% {ok, Segments} -> assemble_jp(Segments); -% Error -> Error -% end. -% -%segment_jp(Format) -> -% case string:split(Format, [gaju_mark()], all) of -% [Gajus, Pucks] -> -% case read_segment(Gajus) of -% {ok, GajuSegments} -> -% case read_segment(Pucks) of -% {ok, PuckSegments} -> {ok, GajuSegments, PuckSegments}; -% Error -> Error -% end; -% Error -> -% Error -% end; -% [Gajus] -> -% case read_segment(Gajus) of -% {ok, GajuSegments} -> {ok, GajuSegments, ["0"]}; -% Error -> Error -% end; -% [] -> -% {ok, ["0"], ["0"]}; -% _ -> -% {error, format} -% end. -% -%read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> -% N = C - $0, -% read_segment(T, R, [NA); -%read_segment([C | T], R, A) when $0 =< C andalso C =< $9 -> -% -% -% -%assemble_jp({GajuSegments, PuckSegments}) -> -% GajuString = lists:flatten(lists:map(fun expand_jp_myriad/1, GajuSegments)), -% PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), -% {ok, integer_to_list(lists:append(GajuString, PuckString))}. -%assemble_jp(PuckSegments) -> -% PuckString = lists:flatten(lists:map(fun expand_jp_myriad/1, PuckDegments)), -% {ok, integer_to_list(PuckString)}. -% -%expand_jp_myriad(String) -> -% string:pad(String, 4, leading, $0). -% -% -%hw_jp_numchar(C) when $0 =< C andalso C =< $9 -> -% C - $0; -%hw_jp_numchar(C) -> -% C. +magnitude("木") -> + {ok, one_gaju()}; +magnitude("本") -> + {ok, 1}; +magnitude("G") -> + {ok, one_gaju()}; +magnitude("P") -> + {ok, 1}; +magnitude(Mark) -> + case rank(Mark, ranks(jp), 1_0000, 1) of + {ok, J} -> + {ok, J}; + error -> + case rank(Mark, ranks(metric), 1_000, 1) of + {ok, N} -> {ok, N}; + error -> rank(Mark, 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 -- 2.30.2 From 401f8dec7ab2783eb27686ea5ebdd25b2dca7ccd Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 04:52:42 +0900 Subject: [PATCH 10/14] WIP --- src/hz_format.erl | 103 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 08a83f6..78c83b5 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -402,11 +402,8 @@ one_gaju() -> 1_000_000_000_000_000_000. -spec read(Format) -> Result when Format :: string(), - Result :: {ok, Pucks} | {error, Reason}, - Pucks :: integer(), - Reason :: {badarg, Partial :: string(), Rest :: term()} - | {incomplete, Partial :: string(), Rest :: binary()} - | format. + Result :: {ok, Pucks} | error, + Pucks :: integer(). %% @doc %% Convery any valid string formatted representation and output a value in pucks. %% This routine can fail in the special case of `ch' style formatting with a single @@ -433,13 +430,12 @@ read([C | Rest]) C =:= $\n -> read(Rest); read([C | Rest]) when $0 =< C andalso C =< $9 -> - read(Rest, [C], []); + read(Rest, [C], {[], []}); read([C | Rest]) when $0 =< C andalso C =< $9 -> NumC = C - $0 + $0, - read(Rest, [NumC], []); + read(Rest, [NumC], {[], []}); read(_) -> - io:format("Barfing~n"), - {error, format}. + error. read_w_gajus([C | Rest], A) when $0 =< C andalso C =< $9 -> read_w_gajus(Rest, [C | A]); @@ -462,9 +458,9 @@ 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(Rest, [], {[{C, A}], []}); read_w_gajus([32, $G | Rest], A) -> - case read(Rest, [], [{$G, A}]) of + case read(Rest, [], {[{$G, A}], []}) of {ok, P} -> G = list_to_integer(lists:reverse(A)) * one_gaju(), {ok, G + P}; @@ -472,8 +468,7 @@ read_w_gajus([32, $G | Rest], A) -> Error end; read_w_gajus(_, _) -> - io:format("Derping~n"), - {error, format}. + error. read_w_pucks([C | Rest], A) when $0 =< C andalso C =< $9 -> read_w_pucks(Rest, [C | A]); @@ -494,27 +489,44 @@ read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> NumC = C - $0 + $0, read(Rest, [NumC | A], S); -read([$木], A, S) -> - calc([{$木, A} | S]); -read([$木, 32 | Rest], A, S) -> - read(Rest, [], [{$木, A} | S]); -read([$本], A, S) -> - calc([{$本, A} | S]); -read([32, $G], A, S) -> - calc([{$G, A} | S]); -read([32, $G, 32 | Rest], A, S) -> - read(Rest, [], [{$G, A} | S]); -read([32, $P], A, S) -> - calc([{$P, A} | S]); -read([C, 32 | Rest], A, S) -> - read(Rest, [], [{C, A} | S]); -read([C | Rest], A, S) -> - read(Rest, [], [{C, A} | S]); +read([$木], A, {G, P}) -> + calc({[{$木, A} | G], P}); +read([$木, 32 | Rest], A, {G, P}) -> + read(Rest, [], {[{$木, A} | G], P}); +read([$本], A, {G, []}) -> + calc({[], [{$本, A} | G]}); +read([$本], A, {G, P}) -> + calc({G, [{$本, A} | P]}); +read([32, $G], A, {G, P}) -> + calc({[{$G, A} | G], P}); +read([32, $G, 32 | Rest], A, {G, P}) -> + read(Rest, [], {[{$G, A} | G], P}); +read([32, $P], A, {G, []}) -> + calc({[], [{$P, A} | G]}); +read([32, $P], A, {G, P}) -> + calc({G, [{$P, A} | P]}); +read([C, 32 | Rest], A, {G, []}) -> + read(Rest, [], {[{C, A} | G], []}); +read([C, 32 | Rest], A, {G, P}) -> + read(Rest, [], {G, [{C, A} | P]}); +read([C | Rest], A, {G, []}) -> + read(Rest, [], {[{C, A} | G], []}); +read([C | Rest], A, {G, P}) -> + read(Rest, [], {G, [{C, A} | P]}); read(_, _, _) -> - {error, format}. + error. -calc(S) -> - calc(S, 0). +calc({G, P}) -> + io:format("G: ~tp, P: ~tp~n", [G, P]), + case calc(G, 0) of + {ok, Gajus} -> + case calc(P, 0) of + {ok, Pucks} -> Gajus + Pucks; + error -> error + end; + error -> + error + end. calc([{_, []} | S], A) -> calc(S, A); @@ -529,37 +541,23 @@ calc([{M, Cs} | S], A) -> calc([], A) -> {ok, A}. -%magnitude_j("万") -> 1_0000; -%magnitude_j("億") -> 1_0000_0000; -%magnitude_j("兆") -> 1_0000_0000_0000; -%magnitude_j("京") -> 1_0000_0000_0000_0000; -%magnitude_j("垓") -> 1_0000_0000_0000_0000_0000; -%magnitude_j("秭") -> 1_0000_0000_0000_0000_0000_0000; -%magnitude_j("穣") -> 1_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("溝") -> 1_0000_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("澗") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("正") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("載") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("極") -> 1_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000; -%magnitude_j("木") -> one_gaju(); -%magnitude_j("本") -> 1. - -magnitude("木") -> +magnitude($木) -> {ok, one_gaju()}; -magnitude("本") -> +magnitude($本) -> {ok, 1}; -magnitude("G") -> +magnitude($G) -> {ok, one_gaju()}; -magnitude("P") -> +magnitude($P) -> {ok, 1}; magnitude(Mark) -> + io:format("Mark: ~ts~n", [[Mark]]), case rank(Mark, ranks(jp), 1_0000, 1) of {ok, J} -> {ok, J}; error -> case rank(Mark, ranks(metric), 1_000, 1) of - {ok, N} -> {ok, N}; + {ok, J} -> {ok, J}; error -> rank(Mark, ranks(heresy), 1_000, 1) end end. @@ -570,6 +568,7 @@ rank(Mark, [Mark | _], Magnitude, Sum) -> rank(Mark, [_ | Rest], Magnitude, Sum) -> rank(Mark, Rest, Magnitude, Sum * Magnitude); rank(_, [], _, _) -> + io:format("failure to rank!~n"), error. -- 2.30.2 From 9ac442ffb46e807128a44d13890d27168b8ae2df Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 17:26:46 +0900 Subject: [PATCH 11/14] WIP --- src/hz_format.erl | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 78c83b5..ec6ce73 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -1,8 +1,30 @@ %%% @doc -%%% GajuDesk Helper Functions +%%% 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 "). +-copyright("Craig Everett "). +-license("GPL-3.0-or-later"). -export([amount/1, amount/2, amount/3, amount/4, read/1, @@ -211,10 +233,13 @@ bestern2(gaju, Ranks, Span, 0, Pucks) -> 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 of - 3 -> {bestern2(gaju, Ranks, 3, 0, Pucks), period(" P", Ranks, lists:reverse(P))}; - 4 -> {jp(gaju, 0, Pucks), myriad(puck_mark(), Ranks, lists:reverse(P))} + 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(puck_mark(), 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) -> @@ -230,15 +255,15 @@ bestern2(gaju, Ranks, Span, Precision, Pucks) -> 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(puck_mark(), Ranks, PuckingString) - end; - true -> - "" - end; + case lists:all(fun(C) -> C =:= $0 end, PuckingString) of + false -> + case Span of + 3 -> period(" P", Ranks, PuckingString); + 4 -> myriad(puck_mark(), Ranks, PuckingString) + end; + true -> + "" + end; true -> [] end, -- 2.30.2 From 79eb881208c821a69d1854e7460acc8315e12c14 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 20:22:20 +0900 Subject: [PATCH 12/14] Formatters work. Still need to write random testers. --- src/hz_format.erl | 142 +++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index ec6ce73..859f3f8 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -228,7 +228,7 @@ jp(Unit, Precision, Pucks) when Pucks < 0 -> bestern2(gaju, Ranks, Span, 0, Pucks) -> G = lists:reverse(integer_to_list(Pucks div one_gaju())), case Span of - 3 -> period(" G", Ranks, G); + 3 -> period("G", Ranks, G); 4 -> myriad(gaju_mark(), Ranks, G) end; bestern2(gaju, Ranks, Span, all, Pucks) -> @@ -236,7 +236,7 @@ bestern2(gaju, Ranks, Span, all, Pucks) -> 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))}; + {3, false} -> {bestern2(gaju, Ranks, 3, 0, Pucks), period("P", Ranks, lists:reverse(P))}; {4, false} -> {jp(gaju, 0, Pucks), myriad(puck_mark(), Ranks, lists:reverse(P))}; {3, true} -> {bestern2(gaju, Ranks, 3, 0, Pucks), ""}; {4, true} -> {jp(gaju, 0, Pucks), ""} @@ -258,7 +258,7 @@ bestern2(gaju, Ranks, Span, Precision, Pucks) -> case lists:all(fun(C) -> C =:= $0 end, PuckingString) of false -> case Span of - 3 -> period(" P", Ranks, PuckingString); + 3 -> period("P", Ranks, PuckingString); 4 -> myriad(puck_mark(), Ranks, PuckingString) end; true -> @@ -273,7 +273,7 @@ bestern2(puck, Ranks, Span, all, Pucks) -> case lists:all(fun(C) -> C =:= $0 end, P) of false -> case Span of - 3 -> period(" P", Ranks, P); + 3 -> period("P", Ranks, P); 4 -> myriad(puck_mark(), Ranks, P) end; true -> @@ -297,7 +297,7 @@ bestern2(puck, Ranks, Span, Precision, Pucks) -> case lists:all(fun(C) -> C =:= $0 end, PuckingString) of false -> case Span of - 3 -> period(" P", Ranks, PuckingString); + 3 -> period("P", Ranks, PuckingString); 4 -> myriad(puck_mark(), Ranks, PuckingString) end; true -> @@ -312,15 +312,15 @@ bestern2(puck, Ranks, Span, Precision, Pucks) -> period(Symbol, Ranks, [$0, $0, $0 | PT]) -> rank3(Ranks, PT, [Symbol]); period(Symbol, Ranks, [P3, $0, $0 | PT]) -> - rank3(Ranks, PT, [P3, Symbol]); + rank3(Ranks, PT, [P3, 32, Symbol]); period(Symbol, Ranks, [P3, P2, $0 | PT]) -> - rank3(Ranks, PT, [P2, P3, Symbol]); + rank3(Ranks, PT, [P2, P3, 32, Symbol]); period(Symbol, Ranks, [P3, P2, P1 | PT]) -> - rank3(Ranks, PT, [P1, P2, P3, Symbol]); + rank3(Ranks, PT, [P1, P2, P3, 32, Symbol]); period(Symbol, _, [P3]) -> - [P3, Symbol]; + [P3, 32, Symbol]; period(Symbol, _, [P3, P2]) -> - [P2, P3, Symbol]. + [P2, P3, 32, Symbol]. rank3([_ | RT], [$0, $0, $0 | PT], A) -> rank3(RT, PT, A); @@ -455,10 +455,10 @@ read([C | Rest]) C =:= $\n -> read(Rest); read([C | Rest]) when $0 =< C andalso C =< $9 -> - read(Rest, [C], {[], []}); + read(Rest, [C], []); read([C | Rest]) when $0 =< C andalso C =< $9 -> NumC = C - $0 + $0, - read(Rest, [NumC], {[], []}); + read(Rest, [NumC], []); read(_) -> error. @@ -483,15 +483,11 @@ 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 | Rest], A) -> - case read(Rest, [], {[{$G, A}], []}) of - {ok, P} -> - G = list_to_integer(lists:reverse(A)) * one_gaju(), - {ok, G + P}; - Error -> - Error - end; + 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(_, _) -> error. @@ -509,43 +505,53 @@ read_w_pucks([], A) -> {ok, list_to_integer(Padded)}. -read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> - read(Rest, [C | A], S); -read([C | Rest], A, S) when $0 =< C andalso C =< $9 -> +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], S); -read([$木], A, {G, P}) -> - calc({[{$木, A} | G], P}); -read([$木, 32 | Rest], A, {G, P}) -> - read(Rest, [], {[{$木, A} | G], P}); -read([$本], A, {G, []}) -> - calc({[], [{$本, A} | G]}); -read([$本], A, {G, P}) -> - calc({G, [{$本, A} | P]}); -read([32, $G], A, {G, P}) -> - calc({[{$G, A} | G], P}); -read([32, $G, 32 | Rest], A, {G, P}) -> - read(Rest, [], {[{$G, A} | G], P}); -read([32, $P], A, {G, []}) -> - calc({[], [{$P, A} | G]}); -read([32, $P], A, {G, P}) -> - calc({G, [{$P, A} | P]}); -read([C, 32 | Rest], A, {G, []}) -> - read(Rest, [], {[{C, A} | G], []}); -read([C, 32 | Rest], A, {G, P}) -> - read(Rest, [], {G, [{C, A} | P]}); -read([C | Rest], A, {G, []}) -> - read(Rest, [], {[{C, A} | G], []}); -read([C | Rest], A, {G, P}) -> - read(Rest, [], {G, [{C, A} | P]}); -read(_, _, _) -> + read(Rest, [NumC | A], G); +read([$木], A, G) -> + calc([{$G, A} | G], []); +read([$G], A, G) -> + calc([{$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([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. -calc({G, P}) -> - io:format("G: ~tp, P: ~tp~n", [G, P]), - case calc(G, 0) of + +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([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(P, 0) of + case calc(puck, P, 0) of {ok, Pucks} -> Gajus + Pucks; error -> error end; @@ -553,47 +559,43 @@ calc({G, P}) -> error end. -calc([{_, []} | S], A) -> - calc(S, A); -calc([{M, Cs} | S], A) -> +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, - calc(S, A + N); + N = list_to_integer(lists:reverse(Cs)) * J * unit(U), + calc(U, S, A + N); Error -> Error end; -calc([], A) -> +calc(_, [], A) -> {ok, A}. +unit(gaju) -> one_gaju(); +unit(puck) -> 1. + -magnitude($木) -> - {ok, one_gaju()}; -magnitude($本) -> - {ok, 1}; magnitude($G) -> - {ok, one_gaju()}; + {ok, 1}; magnitude($P) -> {ok, 1}; magnitude(Mark) -> - io:format("Mark: ~ts~n", [[Mark]]), case rank(Mark, ranks(jp), 1_0000, 1) of {ok, J} -> {ok, J}; error -> - case rank(Mark, ranks(metric), 1_000, 1) of + case rank([Mark, 32], ranks(metric), 1_000, 1) of {ok, J} -> {ok, J}; - error -> rank(Mark, ranks(heresy), 1_000, 1) + 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(_, [], _, _) -> - io:format("failure to rank!~n"), error. -- 2.30.2 From e10ef8a460144d4f20c681a1e3301a9b455ff05b Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Sat, 20 Dec 2025 20:38:14 +0900 Subject: [PATCH 13/14] Remember to wrap final values --- src/hz_format.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index 859f3f8..cbce80a 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -552,7 +552,7 @@ calc(G, P) -> case calc(gaju, G, 0) of {ok, Gajus} -> case calc(puck, P, 0) of - {ok, Pucks} -> Gajus + Pucks; + {ok, Pucks} -> {ok, Gajus + Pucks}; error -> error end; error -> -- 2.30.2 From 2f93c4d5036efb5b4c9e740b6013712c326a0c3a Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Mon, 22 Dec 2025 11:14:13 +0900 Subject: [PATCH 14/14] Fix docs --- src/hz_format.erl | 252 ++++++++++++++++++++++++++++++---------------- 1 file changed, 163 insertions(+), 89 deletions(-) diff --git a/src/hz_format.erl b/src/hz_format.erl index cbce80a..d99acd8 100644 --- a/src/hz_format.erl +++ b/src/hz_format.erl @@ -26,8 +26,10 @@ -copyright("Craig Everett "). -license("GPL-3.0-or-later"). --export([amount/1, amount/2, amount/3, amount/4, +-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 @@ -35,6 +37,19 @@ 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) -> @@ -49,6 +64,25 @@ amount(Pucks) -> 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) -> @@ -64,6 +98,31 @@ amount(Style, Pucks) -> 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); @@ -81,66 +140,51 @@ amount(puck, {Separator, Span}, Pucks) -> western(Separator, Span, Pucks). --spec amount(Unit, Style, Precision, Pucks) -> Serialized - when Unit :: gaju | puck, - Style :: us | jp | metric | legacy | {Separator, Span}, + +-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 flexible, if annoyingly complex, formatting function. -%% +%% A formatter for decimal notation which permits a precision +%% value to be applied to the puck side of the format. %% ``` -%% amount(gaju, us, 3, 123456789123456789123456789) -> -%% "木123,456,789.123...". +%% hz_format:approx_amount({$_, 3}, 3, 100500040123000004500000001) -> +%% 木100_500_040.123... %% -%% amount(gaju, us, 3, 123456789123000000000000000) -> -%% "木123,456,789.123". -%% -%% amount(gaju, {$,, 3}, 3, 123456789123456789123456789) -> -%% "木123,456,789.123...". -%% -%% amount(gaju, {$,, 3}, 6, 123456789123000000000000000) -> -%% "木123,456,789.123" -%% -%% amount(gaju, {$_, 4}, 10, 123456789123456789123456789) -> -%% "木1_2345_6789.1234_5678_91..." -%% -%% amount(gaju, jp, 3, 123456789123456789123456789) -> -%% "1億2345万6789木 12京3000兆本" -%% -%% amount(gaju, jp, 6, 123456789123456789123456789) -> -%% "1億2345万6789木 12京3456兆本" -%% -%% amount(gaju, jp, 0, 123456789123456789123456789) -> -%% "1億2345万6789木" -%% -%% amount(puck, jp, all, 123456789123456789123456789) -> -%% "123秭4567垓8912京3456兆7891億2345万6789本" +%% hz_format:approx_amount({$_, 4}, 12, 100500040123000004500000001) -> +%% 木1_0050_0040.1230_0000_45... %% ''' -amount(gaju, us, Precision, Pucks) -> +approx_amount(us, Precision, Pucks) -> western($,, $., 3, Precision, Pucks); -amount(gaju, jp, Precision, Pucks) -> - jp(gaju, Precision, Pucks); -amount(gaju, metric, Precision, Pucks) -> - bestern(gaju, ranks(metric), Precision, Pucks); -amount(gaju, legacy, Precision, Pucks) -> - bestern(gaju, ranks(heresy), Precision, Pucks); -amount(gaju, {Separator, Span}, Precision, Pucks) -> - western(Separator, $., Span, Precision, Pucks); -amount(puck, us, _, Pucks) -> - western($,, 3, Pucks); -amount(puck, jp, _, Pucks) -> - jp(puck, all, Pucks); -amount(puck, metric, _, Pucks) -> - bestern(puck, ranks(metric), all, Pucks); -amount(puck, legacy, _, Pucks) -> - bestern(puck, ranks(heresy), all, Pucks); -amount(puck, {Separator, Span}, _, Pucks) -> - western(Separator, Span, Pucks). +approx_amount({Separator, Span}, Precision, Pucks) -> + western(Separator, $., Span, Precision, Pucks). western(Separator, Span, Pucks) when Pucks >= 0 -> @@ -150,7 +194,7 @@ western(Separator, Span, Pucks) when Pucks < 0 -> western2(Separator, Span, Pucks) -> P = lists:reverse(integer_to_list(Pucks)), - [puck_mark() | separate(Separator, Span, P)]. + [mark(puck) | separate(Separator, Span, P)]. western(Separator, Break, Span, Precision, Pucks) when Pucks >= 0 -> @@ -160,30 +204,30 @@ western(Separator, Break, Span, Precision, Pucks) when Pucks < 0 -> western2(Separator, _, Span, 0, Pucks) -> - G = lists:reverse(integer_to_list(Pucks div one_gaju())), - [gaju_mark() | separate(Separator, Span, G)]; + 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, + NoPucks = (Pucks rem one(gaju)) =:= 0, case {Over18, NoPucks} of {true, true} -> Gs = lists:reverse(lists:sublist(SP, Length - 18)), - [gaju_mark() | separate(Separator, Span, Gs)]; + [mark(gaju) | separate(Separator, Span, Gs)]; {true, false} -> {PChars, GChars} = lists:split(18, lists:reverse(SP)), - H = [gaju_mark() | separate(Separator, Span, GChars)], + 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} -> - [gaju_mark(), $0]; + [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([gaju_mark(), $0, Break, T, E]) + lists:flatten([mark(gaju), $0, Break, T, E]) end. decimal_pucks(all, PChars) -> @@ -212,13 +256,13 @@ separate(S, P, N, [H | T], A) -> bestern(gaju, Ranks, Precision, Pucks) when Pucks >= 0 -> - [gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks)]; + [mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks)]; bestern(gaju, Ranks, Precision, Pucks) when Pucks < 0 -> - [$-, gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)]; + [$-, mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)]; bestern(puck, Ranks, Precision, Pucks) when Pucks >= 0 -> - [puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks)]; + [mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks)]; bestern(puck, Ranks, Precision, Pucks) when Pucks < 0 -> - [$-, puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks * -1)]. + [$-, mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks * -1)]. jp(Unit, Precision, Pucks) when Pucks >= 0 -> bestern2(Unit, ranks(jp), 4, Precision, Pucks); @@ -226,24 +270,24 @@ 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())), + G = lists:reverse(integer_to_list(Pucks div one(gaju))), case Span of 3 -> period("G", Ranks, G); - 4 -> myriad(gaju_mark(), 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)), + 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(puck_mark(), 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)), + 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); @@ -259,7 +303,7 @@ bestern2(gaju, Ranks, Span, Precision, Pucks) -> false -> case Span of 3 -> period("P", Ranks, PuckingString); - 4 -> myriad(puck_mark(), Ranks, PuckingString) + 4 -> myriad(mark(puck), Ranks, PuckingString) end; true -> "" @@ -274,12 +318,12 @@ bestern2(puck, Ranks, Span, all, Pucks) -> false -> case Span of 3 -> period("P", Ranks, P); - 4 -> myriad(puck_mark(), Ranks, P) + 4 -> myriad(mark(puck), Ranks, P) end; true -> case Span of 3 -> [$0, " P"]; - 4 -> [$0, puck_mark()] + 4 -> [$0, mark(puck)] end end; bestern2(puck, Ranks, Span, Precision, Pucks) -> @@ -289,7 +333,7 @@ bestern2(puck, Ranks, Span, Precision, Pucks) -> true -> case Span of 3 -> [$0, " P"]; - 4 -> [$0, puck_mark()] + 4 -> [$0, mark(puck)] end; false -> PucksToGive = lists:sublist(P, Digits), @@ -298,12 +342,12 @@ bestern2(puck, Ranks, Span, Precision, Pucks) -> false -> case Span of 3 -> period("P", Ranks, PuckingString); - 4 -> myriad(puck_mark(), Ranks, PuckingString) + 4 -> myriad(mark(puck), Ranks, PuckingString) end; true -> case Span of 3 -> [$0, " P"]; - 4 -> [$0, puck_mark()] + 4 -> [$0, mark(puck)] end end end. @@ -418,11 +462,11 @@ ranks(heresy) -> ["k ", "m ", "b ", "t ", "q ", "e ", "z ", "y ", "r ", "Q "]. -gaju_mark() -> $木. +mark(gaju) -> $木; +mark(puck) -> $本. -puck_mark() -> $本. - -one_gaju() -> 1_000_000_000_000_000_000. +one(gaju) -> 1_000_000_000_000_000_000; +one(puck) -> 1. -spec read(Format) -> Result @@ -430,10 +474,26 @@ one_gaju() -> 1_000_000_000_000_000_000. Result :: {ok, Pucks} | error, Pucks :: integer(). %% @doc -%% Convery any valid string formatted representation and output a value in pucks. -%% This routine can fail in the special case of `ch' style formatting with a single -%% comma and/or a single period in it, as this can trigger misinterpretation as `us' -%% style. When in doubt, always call `read/2' with a style specified. +%% 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, []); @@ -459,6 +519,8 @@ read([C | Rest]) when $0 =< C andalso C =< $9 -> 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. @@ -474,13 +536,13 @@ 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(), + 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(), + G = list_to_integer(lists:reverse(A)) * one(gaju), {ok, G}; read_w_gajus([C, 32 | Rest], A) -> read(Rest, [], [{C, A}]); @@ -488,7 +550,10 @@ read_w_gajus([32, $G, 32 | Rest], A) -> read(Rest, [], [{$G, A}], []); read_w_gajus([32, $G], A) -> calc([{$G, A}], []); -read_w_gajus(_, _) -> +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 -> @@ -500,6 +565,10 @@ 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)}. @@ -514,6 +583,10 @@ 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) -> @@ -522,6 +595,8 @@ 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) -> @@ -540,6 +615,8 @@ 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) -> @@ -564,7 +641,7 @@ calc(U, [{_, []} | S], A) -> calc(U, [{M, Cs} | S], A) -> case magnitude(M) of {ok, J} -> - N = list_to_integer(lists:reverse(Cs)) * J * unit(U), + N = list_to_integer(lists:reverse(Cs)) * J * one(U), calc(U, S, A + N); Error -> Error @@ -572,9 +649,6 @@ calc(U, [{M, Cs} | S], A) -> calc(_, [], A) -> {ok, A}. -unit(gaju) -> one_gaju(); -unit(puck) -> 1. - magnitude($G) -> {ok, 1}; @@ -607,7 +681,7 @@ rank(_, [], _, _) -> %% in Gajus. Useful for formatting generic output for UI elements price_to_string(Pucks) -> - Gaju = 1_000_000_000_000_000_000, + 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 -- 2.30.2