diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/stdlib/test/io_SUITE.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/stdlib/test/io_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/io_SUITE.erl | 1900 |
1 files changed, 1900 insertions, 0 deletions
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl new file mode 100644 index 0000000000..73efeb004a --- /dev/null +++ b/lib/stdlib/test/io_SUITE.erl @@ -0,0 +1,1900 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(io_SUITE). + +-export([all/1]). + +-export([init_per_testcase/2, fin_per_testcase/2]). + +-export([error_1/1, float_g/1, otp_5403/1, otp_5813/1, otp_6230/1, + otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1, + manpage/1, otp_6708/1, otp_7084/1, otp_7421/1, + io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1, + io_fread_newlines/1]). + +%-define(debug, true). + +-ifdef(debug). +-define(format(S, A), io:format(S, A)). +-define(line, put(line, ?LINE), ). +-define(config(X,Y), foo). +-define(t, test_server). +-define(privdir(_), "./io_SUITE_priv"). +-else. +-include("test_server.hrl"). +-define(format(S, A), ok). +-define(privdir(Conf), ?config(priv_dir, Conf)). +-endif. + + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + ?line Dog = ?t:timetrap(?default_timeout), + [{watchdog, Dog} | Config]. +fin_per_testcase(_Case, _Config) -> + Dog = ?config(watchdog, _Config), + test_server:timetrap_cancel(Dog), + ok. + +all(doc) -> + ["Test cases for io."]; +all(suite) -> + [error_1,float_g,otp_5403,otp_5813,otp_6230,otp_6282,otp_6354,otp_6495, + otp_6517,otp_6502,manpage,otp_6708,otp_7084,otp_7421, + io_lib_collect_line_3_wb,cr_whitespace_in_string,io_fread_newlines]. + +error_1(doc) -> + ["Error cases for output"]; +error_1(suite) -> + []; +error_1(Config) when is_list(Config) -> + %% We don't do erroneous output on stdout - the test server + %% seems to catch that somehow. + ?line PrivDir = ?privdir(Config), + ?line File = filename:join(PrivDir, "slask"), + ?line {ok, F1} = file:open(File, [write]), + ?line {'EXIT', _} = (catch io:format(muttru, "hej", [])), + ?line {'EXIT', _} = (catch io:format(F1, pelle, "hej")), + ?line {'EXIT', _} = (catch io:format(F1, 1, "hej")), + ?line {'EXIT', _} = (catch io:format(F1, "~p~", [kaka])), + ?line {'EXIT', _} = (catch io:format(F1, "~m~n", [kaka])), + + %% This causes the file process to die, and it is linked to us, + %% so we can't catch the error this easily. +% ?line {'EXIT', _} = (catch io:put_chars(F1, 666)), + + ?line file:close(F1), + ?line {'EXIT', _} = (catch io:format(F1, "~p", ["hej"])), + ok. + +float_g(Config) when is_list(Config) -> + ?line ["5.00000e-2", + "0.500000", + "5.00000", + "50.0000", + "500.000", + "5000.00", + "5.00000e+4", + "5.00000e+5"] = float_g_1("~g", 5.0, -2, 5), + + ?line ["-5.0000e-2", + "-0.50000", + "-5.0000", + "-50.000", + "-500.00", + "-5000.0", + "-5.0000e+4", + "-5.0000e+5"] = float_g_1("~.5g", -5.0, -2, 5), + + ?line ["5.000e-2", + "0.5000", + "5.000", + "50.00", + "500.0", + "5.000e+3", + "5.000e+4", + "5.000e+5"] = float_g_1("~.4g", 5.0, -2, 5), + + ?line ["-5.00e-2", + "-0.500", + "-5.00", + "-50.0", + "-5.00e+2", + "-5.00e+3", + "-5.00e+4", + "-5.00e+5"] = float_g_1("~.3g", -5.0, -2, 5), + + ?line ["5.0e-2", + "0.50", + "5.0", + "5.0e+1", + "5.0e+2", + "5.0e+3", + "5.0e+4", + "5.0e+5"] = float_g_1("~.2g", 5.0, -2, 5), + + ?line + case catch fmt("~.1g", [0.5]) of + "0.5" -> + ?line + ["5.0e-2", + "0.5", + "5.0e+0", + "5.0e+1", + "5.0e+2", + "5.0e+3", + "5.0e+4", + "5.0e+5"] = float_g_1("~.1g", 5.0, -2, 5); + {'EXIT',_} -> ok + end, + + ?line ["4.99999e-2", + "0.499999", + "4.99999", + "49.9999", + "499.999", + "4999.99", + "4.99999e+4", + "4.99999e+5"] = float_g_1("~g", 4.9999949999, -2, 5), + + ?line ["-5.00000e-2", + "-0.500000", + "-5.00000", + "-50.0000", + "-500.000", + "-5000.00", + "-5.00000e+4", + "-5.00000e+5"] = float_g_1("~g", -4.9999950001, -2, 5), + ok. + +float_g_1(Fmt, V, Min, Max) -> + [fmt(Fmt, [V*math:pow(10, E)]) || E <- lists:seq(Min, Max)]. + +otp_5403(doc) -> + ["OTP-5403. ~s formats I/O lists and a single binary."]; +otp_5403(suite) -> + []; +otp_5403(Config) when is_list(Config) -> + ?line "atom" = fmt("~s", [atom]), + ?line "binary" = fmt("~s", [<<"binary">>]), + ?line "atail" = fmt("~s", [["a" | <<"tail">>]]), + ?line "deepcharlist" = fmt("~s", [["deep",["char",["list"]]]]), + ?line "somebinaries" = fmt("~s", [[<<"some">>,[<<"binaries">>]]]), + ok. + +otp_5813(doc) -> + ["OTP-5813. read/3 is new."]; +otp_5813(suite) -> + []; +otp_5813(Config) when is_list(Config) -> + ?line PrivDir = ?privdir(Config), + ?line File = filename:join(PrivDir, "test"), + + ?line ok = file:write_file(File, <<"a. ">>), + ?line {ok, Fd} = file:open(File, [read]), + ?line {ok, a, 1} = io:read(Fd, '', 1), + ?line {eof,1} = io:read(Fd, '', 1), + ok = file:close(Fd), + + ?line ok = file:write_file(File, <<"[}.">>), + ?line {ok, Fd2} = file:open(File, [read]), + ?line {error,{1,_,_},1} = io:read(Fd2, '', 1), + ?line ok = file:close(Fd), + + file:delete(File), + ok. + +otp_6230(doc) -> + ["OTP-6230. ~p and ~P with (huge) binaries."]; +otp_6230(suite) -> + []; +otp_6230(Config) when is_list(Config) -> + %% The problem is actually huge binaries, but the small tests here + %% just run through most of the modified code. + ?line "<<>>" = fmt("~P", [<<"">>,-1]), + ?line "<<\"hej\">>" = fmt("~P", [<<"hej">>,-1]), + ?line "{hej,...}" = fmt("~P", [{hej,<<"hej">>},2]), + ?line "{hej,<<...>>}" = fmt("~P", [{hej,<<"hej">>},3]), + ?line "{hej,<<\"hejs\"...>>}" = fmt("~P", [{hej,<<"hejsan">>},4]), + ?line "{hej,<<\"hej\">>}" = fmt("~P", [{hej,<<"hej">>},6]), + ?line "<<...>>" = fmt("~P", [<<"hej">>,1]), + ?line "<<\"hejs\"...>>" = fmt("~P", [<<"hejsan">>,2]), + ?line "<<\"hej\">>" = fmt("~P", [<<"hej">>,4]), + ?line "{hej,<<127,...>>}" = + fmt("~P", [{hej,<<127:8,<<"hej">>/binary>>},4]), + ?line "{hej,<<127,104,101,...>>}" = + fmt("~P", [{hej,<<127:8,<<"hej">>/binary>>},6]), + + B = list_to_binary(lists:duplicate(30000, $a)), + ?line "<<\"aaaa"++_ = fmt("~P", [B, 20000]), + ok. + +otp_6282(doc) -> + ["OTP-6282. ~p truncates strings (like binaries) depending on depth."]; +otp_6282(suite) -> + []; +otp_6282(Config) when is_list(Config) -> + ?line "[]" = p("", 1, 20, 1), + ?line "[]" = p("", 1, 20, -1), + ?line "[...]" = p("a", 1, 20, 1), + ?line "\"a\"" = p("a", 1, 20, 2), + ?line "\"aa\"" = p("aa", 1, 20, 2), + ?line "\"aaa\"" = p("aaa", 1, 20, 2), + ?line "\"aaaa\"" = p("aaaa", 1, 20, 2), + % ?line "\"aaaa\"..." = p("aaaaaa", 1, 20, 2), + ?line "\"a\"" = p("a", 1, 20, -1), + % ?line "\"aa\"..." = p([$a,$a,1000], 1, 20, 2), + % ?line "\"aa\"..." = p([$a,$a,1000], 1, 20, 3), + ?line "[97,97,1000]" = p([$a,$a,1000], 1, 20, 4), + S1 = lists:duplicate(200,$a), + ?line "[...]" = p(S1, 1, 20, 1), + % ?line "\"aaaaaaaaaaaaaaaa\"\n \"aaaaaaaaaaaaaaaa\"\n \"aaaa\"..." = + % ?line "\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"..." = + % p(S1, 1, 20, 10), + ?line true = "\"" ++ S1 ++ "\"" =:= p(S1, 1, 205, -1), + ?line "[97,97,1000|...]" = p([$a,$a,1000,1000], 1, 20, 4), + + ?line "[[]]" = p([""], 1, 20, 2), + ?line "[[]]" = p([""], 1, 20, -1), + ?line "[[...]]" = p(["a"], 1, 20, 2), + ?line "[\"a\"]" = p(["a"], 1, 20, 3), + ?line "[\"aa\"]" = p(["aa"], 1, 20, 3), + ?line "[\"aaa\"]" = p(["aaa"], 1, 20, 3), + ?line "[\"a\"]" = p(["a"], 1, 20, -1), + % ?line "[\"aa\"...]" = p([[$a,$a,1000]], 1, 20, 3), + % ?line "[\"aa\"...]" = p([[$a,$a,1000]], 1, 20, 4), + ?line "[[97,97,1000]]" = p([[$a,$a,1000]], 1, 20, 5), + ?line "[[...]]" = p([S1], 1, 20, 2), + % ?line "[\"aaaaaaaaaaaaaa\"\n \"aaaaaaaaaaaaaa\"\n \"aaaaaaaa\"...]" = + % ?line "[\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"...]" = + % p([S1], 1, 20, 11), + ?line true = "[\"" ++ S1 ++ "\"]" =:= p([S1], 1, 210, -1), + ?line "[[97,97,1000|...]]" = p([[$a,$a,1000,1000]], 1, 20, 5), + + % ?line "[\"aaaa\"...]" = p(["aaaaa"], 1, 10, 3), + ?line "[\"aaaaa\"]" = p(["aaaaa"], 1, 10, 6), + + ok. + +otp_6354(doc) -> + ["OTP-6354. io_lib_pretty rewritten."]; +otp_6354(suite) -> + []; +otp_6354(Config) when is_list(Config) -> + %% A few tuples: + ?line "{}" = p({}, 1, 20, -1), + ?line "..." = p({}, 1, 20, 0), + ?line "{}" = p({}, 1, 20, 1), + ?line "{}" = p({}, 1, 20, 2), + ?line "{a}" = p({a}, 1, 20, -1), + ?line "..." = p({a}, 1, 20, 0), + ?line "{...}" = p({a}, 1, 20, 1), + ?line "{a}" = p({a}, 1, 20, 2), + ?line "{a,b}" = p({a,b}, 1, 20, -1), + ?line "..." = p({a,b}, 1, 20, 0), + ?line "{...}" = p({a,b}, 1, 20, 1), + ?line "{a,...}" = p({a,b}, 1, 20, 2), + ?line "{a,b}" = p({a,b}, 1, 20, 3), + ?line "{}" = p({}, 1, 1, -1), + ?line "..." = p({}, 1, 1, 0), + ?line "{}" = p({}, 1, 1, 1), + ?line "{}" = p({}, 1, 1, 2), + ?line "{a}" = p({a}, 1, 1, -1), + ?line "..." = p({a}, 1, 1, 0), + ?line "{...}" = p({a}, 1, 1, 1), + ?line "{a}" = p({a}, 1, 1, 2), + ?line "{a,\n b}" = p({a,b}, 1, 1, -1), + ?line "{1,\n b}" = p({1,b}, 1, 1, -1), + ?line "..." = p({a,b}, 1, 1, 0), + ?line "{...}" = p({a,b}, 1, 1, 1), + ?line "{a,...}" = p({a,b}, 1, 1, 2), + ?line "{a,\n b}" = p({a,b}, 1, 1, 3), + ?line "{{}}" = p({{}}, 1, 1, 2), + ?line "{[]}" = p({[]}, 1, 1, 2), + ?line bt(<<"{1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]}">>, + p({1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]}, -1)), + ?line bt(<<"{abcd,ddddd,\n ddddd}">>, + p({abcd,ddddd,ddddd}, 1,16, -1)), + ?line bt(<<"{1,2,a,b,\n {sfdsf,sdfdsfs},\n [sfsdf,sdfsdf]}">>, + p({1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]}, 1, 35, 100)), + % With other terms than atomic ones on the same line: +% ?line bt(<<"{1,2,a,b,{sfdsf,sdfdsfs},\n [sfsdf,sdfsdf]}">>, +% p({1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]}, 1, 35, 100)), + % With line breaks: +% ?line bt(<<"{1,\n" +% " 2,\n" +% " a,\n" +% " b,\n" +% " {sfdsf,sdfdsfs},\n" +% " [sfsdf,sdfsdf]}">>, +% p({1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]}, 1, 35, 100)), + ?line "{1,{1,{2,3}}}" = p({1,{1,{2,3}}}, 1, 80, 100), + + ?line bt(<<"{wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,klsdjfjklds,\n" + " sdkfjdsl,sdakfjdsklj,sdkljfsdj}}}}}">>, + p({wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,klsdjfjklds, + sdkfjdsl,sdakfjdsklj,sdkljfsdj}}}}}, -1)), + + % With no restriction on number of characters per line: +% ?line bt(<<"{wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,klsdjfjklds," +% "sdkfjdsl,sdakfjdsklj,\n" +% " sdkljfsdj}}}}}">>, +% p({wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,klsdjfjklds, +% sdkfjdsl,sdakfjdsklj,sdkljfsdj}}}}}, -1)), + + % With line breaks: +% ?line bt(<<"{wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,\n" +% " klsdjfjklds,\n" +% " sdkfjdsl,\n" +% " sdakfjdsklj,\n" +% " sdkljfsdj}}}}}">>, +% p({wwwww,{wwwww,{wwwww,{wwwww,{wwwww,lkjsldfj,klsdjfjklds, +% sdkfjdsl,sdakfjdsklj,sdkljfsdj}}}}}, -1)), + ?line bt(<<"{wwwww,\n" + " {wwwww,\n" + " {wwwww,\n" + " {wwwww,\n" + " {wwwww,\n" + " {lkjsldfj,\n" + " {klsdjfjklds,\n" + " {klajsljls,\n" + " #aaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa{}}}}}}}}}">>, + p({wwwww,{wwwww,{wwwww,{wwwww,{wwwww,{lkjsldfj, + {klsdjfjklds,{klajsljls, + {aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}}}}}}}}}, + -1)), + ?line "{{...},...}" = p({{a,b},{a,b,c},{d,e,f}},1,8,2), + %% Closing brackets and parentheses count: + ?line "{{a,b,c},\n {{1,2,\n 3}}}" = p({{a,b,c},{{1,2,3}}},1,11,-1), + % With line breaks: +% ?line "{{a,b,c},\n {{1,\n 2,\n 3}}}" = p({{a,b,c},{{1,2,3}}},1,11,-1), + ?line "{{a,b,c},\n [1,2,\n 3]}" = p({{a,b,c},[1,2,3]},1,10,-1), + % With line breaks: +% ?line "{{a,b,c},\n [1,\n 2,\n 3]}" = p({{a,b,c},[1,2,3]},1,10,-1), + ?line "[{{a,b,c},\n {1,2,\n 3}}]" = p([{{a,b,c},{1,2,3}}],1,12,-1), + % With line breaks: +% ?line "[{{a,b,c},\n {1,\n 2,\n 3}}]" = p([{{a,b,c},{1,2,3}}],1,12,-1), + + %% A few lists: + ?line "[]" = p([], 1, 20, -1), + ?line "..." = p([], 1, 20, 0), + ?line "[]" = p([], 1, 20, 1), + ?line "[]" = p([], 1, 20, 2), + ?line "[a]" = p([a], 1, 20, -1), + ?line "..." = p([a], 1, 20, 0), + ?line "[...]" = p([a], 1, 20, 1), + ?line "[a]" = p([a], 1, 20, 2), + ?line "[a,b]" = p([a,b], 1, 20, -1), + ?line "..." = p([a,b], 1, 20, 0), + ?line "[...]" = p([a,b], 1, 20, 1), + ?line "[a|...]" = p([a,b], 1, 20, 2), + ?line "[a,b]" = p([a,b], 1, 20, 3), + ?line "[a|b]" = p([a|b], 1, 20, -1), + ?line "..." = p([a|b], 1, 20, 0), + ?line "[...]" = p([a|b], 1, 20, 1), + ?line "[a|...]" = p([a|b], 1, 20, 2), + ?line "[a|b]" = p([a|b], 1, 20, 3), + ?line "[]" = p([], 1, 1, -1), + ?line "..." = p([], 1, 1, 0), + ?line "[]" = p([], 1, 1, 1), + ?line "[]" = p([], 1, 1, 2), + ?line "[a]" = p([a], 1, 1, -1), + ?line "..." = p([a], 1, 1, 0), + ?line "[...]" = p([a], 1, 1, 1), + ?line "[a]" = p([a], 1, 1, 2), + ?line "[a,\n b]" = p([a,b], 1, 1, -1), + ?line "..." = p([a,b], 1, 1, 0), + ?line "[...]" = p([a,b], 1, 1, 1), + ?line "[a|...]" = p([a,b], 1, 1, 2), + ?line "[a,\n b]" = p([a,b], 1, 1, 3), + ?line "[a|\n b]" = p([a|b], 1, 1, -1), + ?line "..." = p([a|b], 1, 1, 0), + ?line "[...]" = p([a|b], 1, 1, 1), + ?line "[a|...]" = p([a|b], 1, 1, 2), + ?line "[a|\n b]" = p([a|b], 1, 1, 3), + ?line "[{}]" = p([{}], 1, 1, 2), + ?line "[[]]" = p([[]], 1, 1, 2), + ?line bt(<<"[1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]]">>, + p([1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]], -1)), + ?line bt(<<"[1,2,a,b,\n {sfdsf,sdfdsfs},\n [sfsdf,sdfsdf]]">>, + p([1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]], 1, 35, 100)), + % With other terms than atomic ones on the same line: +% ?line bt(<<"[1,2,a,b,{sfdsf,sdfdsfs},\n [sfsdf,sdfsdf]]">>, +% p([1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]], 1, 35, 100)), + % With line breaks: +% ?line bt(<<"[1,\n" +% " 2,\n" +% " a,\n" +% " b,\n" +% " {sfdsf,sdfdsfs},\n" +% " [sfsdf,sdfsdf]]">>, +% p([1,2,a,b,{sfdsf,sdfdsfs},[sfsdf,sdfsdf]], 1, 35, 100)), + %% Element #8 is not printable: + ?line "[49," ++ _ = p("1234567"++[3,4,5,6,7], 1, 100, 9), + % ?line "\"1234567\"..." = p("1234567"++[3,4,5,6,7], 1, 100, 8), + + %% A few records: + %% -record(a, {}). + %% -record(a, {}). + ?line "..." = p({a}, 0), + ?line "{...}" = p({a}, 1), + ?line "#a{}" = p({a}, 2), + ?line "#a{}" = p({a}, -1), + %% -record(b, {f}). + ?line "{...}" = p({b}, 1), + ?line "..." = p({b,c}, 0), + ?line "{...}" = p({b,c}, 1), + ?line "#b{...}" = p({b,c}, 2), + ?line "#b{f = c}" = p({b,c}, 3), + ?line "#b{f = c}" = p({b,c}, -1), + ?line "..." = p({b,{c,d}}, 0), + ?line "{...}" = p({b,{c,d}}, 1), + ?line "#b{...}" = p({b,{c,d}}, 2), + ?line "#b{f = {...}}" = p({b,{c,d}}, 3), + ?line "#b{f = {c,...}}" = p({b,{c,d}}, 4), + ?line "#b{f = {c,d}}" = p({b,{c,d}}, 5), + ?line "#b{f = {...}}" = p({b,{b,c}}, 3), + ?line "#b{f = #b{...}}" = p({b,{b,c}}, 4), + ?line "#b{f = #b{f = c}}" = p({b,{b,c}}, 5), + %% -record(c, {f1, f2}). + ?line "#c{f1 = d,f2 = e}" = p({c,d,e}, -1), + ?line "..." = p({c,d,e}, 0), + ?line "{...}" = p({c,d,e}, 1), + ?line "#c{...}" = p({c,d,e}, 2), + ?line "#c{f1 = d,...}" = p({c,d,e}, 3), + ?line "#c{f1 = d,f2 = e}" = p({c,d,e}, 4), + %% -record(d, {a..., b..., c.., d...}). + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2,\n" + " cccccccccccccccccccc = 3,dddddddddddddddddddd = 4,\n" + " eeeeeeeeeeeeeeeeeeee = 5}">>, + p({d,1,2,3,4,5}, -1)), + % With no restriction on number of characters per line: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2," +% "cccccccccccccccccccc = 3,\n dddddddddddddddddddd = 4," +% "eeeeeeeeeeeeeeeeeeee = 5}">>, +% p({d,1,2,3,4,5}, -1)), + % With line breaks: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = 2,\n" +% " cccccccccccccccccccc = 3,\n" +% " dddddddddddddddddddd = 4,\n" +% " eeeeeeeeeeeeeeeeeeee = 5}">>, +% p({d,1,2,3,4,5}, -1)), + ?line "..." = p({d,1,2,3,4,5}, 0), + ?line "{...}" = p({d,1,2,3,4,5}, 1), + ?line "#d{...}" = p({d,1,2,3,4,5}, 2), + ?line "#d{aaaaaaaaaaaaaaaaaaaa = 1,...}" = p({d,1,2,3,4,5}, 3), + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2,...}">>, + p({d,1,2,3,4,5}, 4)), + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2,\n" + " cccccccccccccccccccc = 3,...}">>, + p({d,1,2,3,4,5}, 5)), % longer than 80 characters... + % With no restriction on number of characters per line: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2," +% "cccccccccccccccccccc = 3,...}">>, +% p({d,1,2,3,4,5}, 5)), % longer than 80 characters... + % With line breaks: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = 2,\n" +% " cccccccccccccccccccc = 3,...}">>, +% p({d,1,2,3,4,5}, 5)), + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2,\n" + " cccccccccccccccccccc = 3,dddddddddddddddddddd = 4,...}">>, + p({d,1,2,3,4,5}, 6)), + % With no restriction on number of characters per line: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2," +% "cccccccccccccccccccc = 3,\n dddddddddddddddddddd = 4,...}">>, +% p({d,1,2,3,4,5}, 6)), + % With line breaks: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = 2,\n" +% " cccccccccccccccccccc = 3,\n" +% " dddddddddddddddddddd = 4,...}">>, +% p({d,1,2,3,4,5}, 6)), + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2,\n" + " cccccccccccccccccccc = 3,dddddddddddddddddddd = 4,\n" + " eeeeeeeeeeeeeeeeeeee = 5}">>, + p({d,1,2,3,4,5}, 7)), + % With no restriction on number of characters per line: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,bbbbbbbbbbbbbbbbbbbb = 2," +% "cccccccccccccccccccc = 3,\n dddddddddddddddddddd = 4," +% "eeeeeeeeeeeeeeeeeeee = 5}">>, +% p({d,1,2,3,4,5}, 7)), + % With line breaks: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = 2,\n" +% " cccccccccccccccccccc = 3,\n" +% " dddddddddddddddddddd = 4,\n" +% " eeeeeeeeeeeeeeeeeeee = 5}">>, +% p({d,1,2,3,4,5}, 7)), + ?line bt(<<"#rrrrr{\n" + " f1 = 1,\n" + " f2 = #rrrrr{f1 = a,f2 = b,f3 = c},\n" + " f3 = \n" + " #rrrrr{\n" + " f1 = h,f2 = i,\n" + " f3 = \n" + " #rrrrr{\n" + " f1 = aa,\n" + " f2 = \n" + " #rrrrr{\n" + " f1 = #rrrrr{f1 = a,f2 = b,f3 = c},\n" + " f2 = 2,f3 = 3},\n" + " f3 = bb}}}">>, + p({rrrrr,1,{rrrrr,a,b,c},{rrrrr,h,i, + {rrrrr,aa,{rrrrr,{rrrrr,a,b,c}, + 2,3},bb}}}, + -1)), + % With other terms than atomic ones on the same line: +% ?line bt(<<"#rrrrr{\n" +% " f1 = 1,f2 = #rrrrr{f1 = a,f2 = b,f3 = c},\n" +% " f3 = \n" +% " #rrrrr{\n" +% " f1 = h,f2 = i,\n" +% " f3 = \n" +% " #rrrrr{\n" +% " f1 = aa,\n" +% " f2 = \n" +% " #rrrrr{\n" +% " f1 = #rrrrr{f1 = a,f2 = b," +% "f3 = c},f2 = 2,f3 = 3},\n" +% " f3 = bb}}}">>, +% p({rrrrr,1,{rrrrr,a,b,c},{rrrrr,h,i, +% {rrrrr,aa,{rrrrr,{rrrrr,a,b,c}, +% 2,3},bb}}}, +% -1)), + % With line breaks: +% ?line bt(<<"#rrrrr{\n" +% " f1 = 1,\n" +% " f2 = #rrrrr{f1 = a,f2 = b,f3 = c},\n" +% " f3 = \n" +% " #rrrrr{\n" +% " f1 = h,\n" +% " f2 = i,\n" +% " f3 = \n" +% " #rrrrr{\n" +% " f1 = aa,\n" +% " f2 = \n" +% " #rrrrr{\n" +% " f1 = #rrrrr{f1 = a,f2 = b," +% "f3 = c},\n" +% " f2 = 2,\n" +% " f3 = 3},\n" +% " f3 = bb}}}">>, +% p({rrrrr,1,{rrrrr,a,b,c},{rrrrr,h,i, +% {rrrrr,aa,{rrrrr,{rrrrr,a,b,c}, +% 2,3},bb}}}, +% -1)), + ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" + " bbbbbbbbbbbbbbbbbbbb = \n" + " #d{aaaaaaaaaaaaaaaaaaaa = a,bbbbbbbbbbbbbbbbbbbb = b,\n" + " cccccccccccccccccccc = c,dddddddddddddddddddd = d,\n" + " eeeeeeeeeeeeeeeeeeee = e},\n" + " cccccccccccccccccccc = 3,\n" + " dddddddddddddddddddd = \n" + " #d{aaaaaaaaaaaaaaaaaaaa = h,bbbbbbbbbbbbbbbbbbbb = i,\n" + " cccccccccccccccccccc = \n" + " #d{aaaaaaaaaaaaaaaaaaaa = aa," + "bbbbbbbbbbbbbbbbbbbb = bb,\n" + " cccccccccccccccccccc = \n" + " #d{aaaaaaaaaaaaaaaaaaaa = 1," + "bbbbbbbbbbbbbbbbbbbb = 2,\n" + " cccccccccccccccccccc = 3," + "dddddddddddddddddddd = 4,\n" + " eeeeeeeeeeeeeeeeeeee = 5},\n" + " dddddddddddddddddddd = dd," + "eeeeeeeeeeeeeeeeeeee = ee},\n" + " dddddddddddddddddddd = k," + "eeeeeeeeeeeeeeeeeeee = l},\n" + " eeeeeeeeeeeeeeeeeeee = 5}">>, + p({d,1,{d,a,b,c,d,e},3,{d,h,i,{d,aa,bb,{d,1,2,3,4,5},dd,ee}, + k,l},5}, -1)), + % With line breaks: +% ?line bt(<<"#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = \n" +% " #d{aaaaaaaaaaaaaaaaaaaa = a,\n" +% " bbbbbbbbbbbbbbbbbbbb = b,\n" +% " cccccccccccccccccccc = c,\n" +% " dddddddddddddddddddd = d,\n" +% " eeeeeeeeeeeeeeeeeeee = e},\n" +% " cccccccccccccccccccc = 3,\n" +% " dddddddddddddddddddd = \n" +% " #d{aaaaaaaaaaaaaaaaaaaa = h,\n" +% " bbbbbbbbbbbbbbbbbbbb = i,\n" +% " cccccccccccccccccccc = \n" +% " #d{aaaaaaaaaaaaaaaaaaaa = aa,\n" +% " bbbbbbbbbbbbbbbbbbbb = bb,\n" +% " cccccccccccccccccccc = \n" +% " #d{aaaaaaaaaaaaaaaaaaaa = 1,\n" +% " bbbbbbbbbbbbbbbbbbbb = 2,\n" +% " cccccccccccccccccccc = 3,\n" +% " dddddddddddddddddddd = 4,\n" +% " eeeeeeeeeeeeeeeeeeee = 5},\n" +% " dddddddddddddddddddd = dd,\n" +% " eeeeeeeeeeeeeeeeeeee = ee},\n" +% " dddddddddddddddddddd = k,\n" +% " eeeeeeeeeeeeeeeeeeee = l},\n" +% " eeeeeeeeeeeeeeeeeeee = 5}">>, +% p({d,1,{d,a,b,c,d,e},3,{d,h,i,{d,aa,bb,{d,1,2,3,4,5},dd,ee}, +% k,l},5}, -1)), + + A = aaaaaaaaaaaaa, + %% Print the record with dots at the end of the line (Ll = 80). + ?line "{aaaaaaa" ++ _ = + p({A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{ggg,{hhh,{ii,{jj,{kk,{ll,{mm,{nn,{oo,{d,1,2,3,4,5} + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} + }}}}}}}}}}}}}}}}, 146), + ?line "{aaaaaaa" ++ _ = + p({A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{A, + {A,{A,{A,{A,{A,{ggg,{hhh,{ii,{jj,{kk,{ll,{mm,{nn,{oo,{a} + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} + }}}}}}}}}}}}}}}}}}}, 152), + + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {g,{h,{i,{j,{k,{l,{m,{n,{o,#" + "d{...}}}}}}}}}}}}}}}}">>, + p({A,{A,{A,{A,{A,{A, + {g,{h,{i,{j,{k,{l,{m,{n,{o,{d,1,2,3,4,5}}}}}}}}}}}}}}}}, 32)), + ?line bt(<<"{a,#b{f = {c,{d,{e,{f,...}}}}}}">>, + p({a,{b,{c,{d,{e,{f,g}}}}}}, 12)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,#c{f1 = ddd," + "f2 = eee}}}}}}}}}}">>, + p({A,{A,{A,{A,{A,{A,{A,{A,{A,{c,ddd,eee}}}}}}}}}}, 100)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,{aaaaaaaaaaaaa,{aaaaaaaaaaaaa,...}}}}">>, + p({A,{A,{A,{A,{b}}}}}, 8)), + % With no restriction on number of characters per line: +% ?line bt(<<"{aaaaaaaaaaaaa,{aaaaaaaaaaaaa,{aaaaaaaaaaaaa," +% "{aaaaaaaaaaaaa,...}}}}">>, +% p({A,{A,{A,{A,{b}}}}}, 8)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,{aaaaaaaaaaaaa,{aaaaaaaaaaaaa,...}}}}}">>, + p({A,{A,{A,{A,{A,{b}}}}}}, 10)), + % With no restriction on number of characters per line: +% ?line bt(<<"{aaaaaaaaaaaaa,\n" +% " {aaaaaaaaaaaaa,{aaaaaaaaaaaaa,{aaaaaaaaaaaaa," +% "{aaaaaaaaaaaaa,...}}}}}">>, +% p({A,{A,{A,{A,{A,{b}}}}}}, 10)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa," + "{aaaaaaaaaaaaa,#a{}}}}}}}}}}}">>, + p({A,{A,{A,{A,{A,{A,{A,{A,{A,{A,{a}}}}}}}}}}}, 23)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n", + " #rrrrr{\n" + " f1 = kljlkjlksfdgkljlsdkjf," + "f2 = kljkljsdaflkjlkjsdf,...}}}}">>, + p({A,{A,{A,{rrrrr, kljlkjlksfdgkljlsdkjf, + kljkljsdaflkjlkjsdf, + asdfkldsjfklkljsdklfds}}}}, 10)), + % With no restriction on number of characters per line: +% ?line bt(<<"{aaaaaaaaaaaaa,\n" +% " {aaaaaaaaaaaaa,\n" +% " {aaaaaaaaaaaaa,\n", +% " #rrrrr{f1 = kljlkjlksfdgkljlsdkjf,f2 = " +% "kljkljsdaflkjlkjsdf,...}}}}">>, +% p({A,{A,{A,{rrrrr, kljlkjlksfdgkljlsdkjf, +% kljkljsdaflkjlkjsdf, +% asdfkldsjfklkljsdklfds}}}}, 10)), + ?line bt(<<"{aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {aaaaaaaaaaaaa,\n" + " {g,{h,{i,{j,{k,{l,{m,{n," + "{o,#a{}}}}}}}}}}}}}}}}}">>, + p({A,{A,{A,{A,{A,{A,{A, + {g,{h,{i,{j,{k,{l,{m,{n,{o,{a}}}}}}}}}}}}}}}}}, 100)), + ?line bt(<<"#c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = \n" + " #c{\n" + " f1 = #c{f1 = #c{f1 = #c{f1 = a," + "f2 = b},f2 = b},f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b},\n" + " f2 = b}">>, + p({c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,{c,a,b},b},b},b},b},b}, + b},b},b},b},b},b}, -1)), + ?line bt(<<"#rrrrr{\n" + " f1 = \n" + " #rrrrr{\n" + " f1 = \n" + " #rrrrr{\n" + " f1 = \n" + " #rrrrr{\n" + " f1 = \n" + " {rrrrr,{rrrrr,a,#rrrrr{f1 = {rrrrr,1,2},f2 = a," + "f3 = b}},b},\n" + " f2 = {rrrrr,c,d},\n" + " f3 = {rrrrr,1,2}},\n" + " f2 = 1,f3 = 2},\n" + " f2 = 3,f3 = 4},\n" + " f2 = 5,f3 = 6}">>, + p({rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,a,{rrrrr, + {rrrrr,1,2},a,b}},b},{rrrrr,c,d},{rrrrr,1,2}}, + 1,2},3,4},5,6}, -1)), + % With other terms than atomic ones on the same line: +% ?line bt(<<"#rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " {rrrrr,{rrrrr,a,#rrrrr{f1 = {rrrrr,1,2},f2 = a," +% "f3 = b}},b},\n" +% " f2 = {rrrrr,c,d},f3 = {rrrrr,1,2}},\n" +% " f2 = 1,f3 = 2},\n" +% " f2 = 3,f3 = 4},\n" +% " f2 = 5,f3 = 6}">>, +% p({rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,a,{rrrrr, +% {rrrrr,1,2},a,b}},b},{rrrrr,c,d},{rrrrr,1,2}}, +% 1,2},3,4},5,6}, -1)), + % With no restriction on number of characters per line: +% ?line bt(<<"#rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = {rrrrr,{rrrrr,a,#rrrrr{f1 = {rrrrr,1,2},f2 = a," +% "f3 = b}},b},\n" +% " f2 = {rrrrr,c,d},f3 = {rrrrr,1,2}},\n" +% " f2 = 1,f3 = 2},\n" +% " f2 = 3,f3 = 4},\n" +% " f2 = 5,f3 = 6}">>, +% p({rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,a,{rrrrr, +% {rrrrr,1,2},a,b}},b},{rrrrr,c,d},{rrrrr,1,2}}, +% 1,2},3,4},5,6}, -1)), + % With line breaks: +% ?line bt(<<"#rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = \n" +% " #rrrrr{\n" +% " f1 = {rrrrr,{rrrrr,a,#rrrrr{f1 = {rrrrr,1,2},f2 = a," +% "f3 = b}},b},\n" +% " f2 = {rrrrr,c,d},\n" +% " f3 = {rrrrr,1,2}},\n" +% " f2 = 1,\n" +% " f3 = 2},\n" +% " f2 = 3,\n" +% " f3 = 4},\n" +% " f2 = 5,\n" +% " f3 = 6}">>, +% p({rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,{rrrrr,a,{rrrrr, +% {rrrrr,1,2},a,b}},b},{rrrrr,c,d},{rrrrr,1,2}}, +% 1,2},3,4},5,6}, -1)), + ?line "{aaa,\n {aaa," ++ _ = + p({aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa, + {aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa, + {aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa, + {aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa,{aaa, + {aaa,a}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, + 1, 80, -1), + + %% A few other cases... + ?line "{a,#Fun<" ++ _ = lists:flatten(io_lib_pretty:print({a,fun fmt/2})), + ?line "#Fun<" ++ _ = io_lib_pretty:print(fun() -> foo end), + % ?line "[<<\"foobar\">>|<<\"barf\"...>>]" = + % p([<<"foobar">>|<<"barfoo">>], 1, 30, 4), + %% No support for negative columns any more: + ?line "[a,\n [b,\n c,\n d,\n [e,\n f]],\n c]" = + p([a,[b,c,d,[e,f]],c], -1, 2, 10), + ?line "[a,\n [b,\n c,\n d,\n [e,\n f]],\n c]" = + p([a,[b,c,d,[e,f]],c], 0, 2, 10), + %% 20 bytes are tried first, then the rest. Try 21 bytes: + L = lists:duplicate(20, $a), + % ?line bt(<<"<<\"aaaaaa\"\n \"aaaaaa\"\n \"aaaaaa\"\n \"aaa\">>">>, + ?line bt(<<"<<\"aaaaaaaaaaaaaaaaaaaaa\">>">>, + p(list_to_binary([$a | L]), 1, 10, -1)), + ?line "<<97," ++ _ = p(list_to_binary(L ++ [3]), 1, 10, -1), + % ?line "<<\"aaaa\"...>>" = p(list_to_binary(L ++ [3]), 1, 10, 2), + % ?line "<<\"aaaaaa\"\n \"aa\"...>>" = + % ?line "<<\"aaaaaaaa\"...>>" = + % p(list_to_binary(L ++ [3]), 1, 10, 3), + % ?line "<<\"aaaaaa\"\n \"aaaaaa\"\n \"aaaaaa\"\n \"aa\"...>>" = + % ?line "<<\"aaaaaaaaaaaaaaaaaaaa\"...>>" = + % p(list_to_binary(L ++ [3]), 1, 10, 21), + ?line "<<97," ++ _ = p(list_to_binary(L ++ [3]), 1, 10, 22), + + ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = + p([8,9,10,11,12,13,27,168], 1, 40, -1), + % ?line "\"\\b\\t\\n\"\n \"\\v\\f\\r\"\n \"\\e\250\"" = + ?line "\"\\b\\t\\n\\v\\f\\r\\e�\"" = + p([8,9,10,11,12,13,27,168], 1, 10, -1), + ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = + p([8,9,10,11,12,13,27,168], 1, 40, 100), + % ?line "\"\\e\\t\\nab\"\n \"cd\"" = + ?line "\"\\e\\t\\nabcd\"" = + p("\e\t\nabcd", 1, 12, -1), + + %% DEL (127) is special... + ?line "[127]" = p("\d", 1, 10, -1), + ?line "[127]" = p([127], 1, 10, 100), + + ?line "<<\"\\b\\t\\n\\v\\f\\r\\e\250\">>" = + p(<<8,9,10,11,12,13,27,168>>, 1, 40, -1), + ?line "<<\"\\b\\t\\n\\v\\f\\r\\e\250\">>" = + p(<<8,9,10,11,12,13,27,168>>, 1, 10, -1), + ?line "<<127>>" = p(<<127>>, 1, 10, 100), + + %% "Partial" string binaries: + ?line "<<\"he\"...>>" = p(list_to_binary("he"++[3]), 1, 80, 2), + ?line "<<\"he\"...>>" = p(list_to_binary("he"++[3]), 1, 80, 3), + ?line "<<104,101,3>>" = p(list_to_binary("he"++[3]), 1, 80, 4), + ?line "<<...>>" = p(list_to_binary([3] ++ "he"), 1, 80, 1), + ?line "<<3,...>>" = p(list_to_binary([3] ++ "he"), 1, 80, 2), + ?line "<<3,104,...>>" = p(list_to_binary([3] ++ "he"), 1, 80, 3), + + ?line "<<\"12345678901234567890\"...>>" = + p(list_to_binary("12345678901234567890"++[3]), 1, 80, 8), + ?line "<<\"12345678901234567890\"...>>" = + p(list_to_binary("12345678901234567890"++[3]), 1, 80, 21), + ?line "<<49," ++ _ = + p(list_to_binary("12345678901234567890"++[3]), 1, 80, 22), + + ?line "{sdfsdfj,\n 23" ++ _ = + p({sdfsdfj,23423423342.23432423}, 1, 17, -1), + + ?line bt(<<"kljkljlksdjjlf kljalkjlsdajafasjdfj [kjljklasdf,kjlljsfd,sdfsdkjfsd,kjjsdf,jl, + lkjjlajsfd|jsdf]">>, + fmt("~w ~w ~p", + [kljkljlksdjjlf, + kljalkjlsdajafasjdfj, + [kjljklasdf,kjlljsfd,sdfsdkjfsd,kjjsdf,jl,lkjjlajsfd | + jsdf]])), + + %% Binaries are split as well: + ?line bt(<<"<<80,100,0,55,55,55,55,55,55,55,55,55,\n " + "55,55,55,55,55,55,55,...>>">>, + p(<<80,100,0,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, + 55,55,55,55,55,55,55,55,55,55,55,55>>,1,40,20)), + ?line bt(<<"<<80,100,0,55,55,55,55,55,55,55,55,55,\n " + "55,55,55,55,55,55,55,55,55,55,55,55,\n 55,55,55,55,55,55>>">>, + p(<<80,100,0,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55, + 55,55,55,55,55,55,55,55,55,55,55,55>>,1,40,-1)), + ?line "<<0,0,0,\n ...>>" = p(<<0,0,0,0,0>>, 1, 10, 4), + + %% ~W now uses ",..." when printing tuples + ?line "[a,b|...]" = fmt("~W", [[a,b,c,d,e], 3]), + ?line "{a,b,...}" = fmt("~W", [{a,b,c,d,e}, 3]), + ok. + +otp_6495(doc) -> + ["OTP-6495. io_lib_pretty bugfix."]; +otp_6495(suite) -> + []; +otp_6495(Config) when is_list(Config) -> + ?line bt(<<"[120,120,120,120,120,120,120,120,120,120,120,120,120,120," + "120,120,120,120,120]<<1>>">>, + fmt("~w~p", ["xxxxxxxxxxxxxxxxxxx", <<1>>])), + ok. + +otp_6517(doc) -> + ["OTP-6517. The Format argument of fwrite can be a binary."]; +otp_6517(suite) -> + []; +otp_6517(Config) when is_list(Config) -> + ?line "string" = fmt(<<"~s">>, [<<"string">>]), + ok. + +otp_6502(doc) -> + ["OTP-6502. Bits."]; +otp_6502(suite) -> + []; +otp_6502(Config) when is_list(Config) -> + ?line bt(<< + "[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]" + "<<0,0,8,\n" + " " + " 1:1>>">>, + fmt("~w~p", [lists:seq(0, 25), <<17:25>>])), + ok. + +otp_7421(doc) -> + ["OTP-7421. Soft limit of 60 chars removed when pretty printing."]; +otp_7421(suite) -> + []; +otp_7421(Config) when is_list(Config) -> + bt(<<"{aa,bb,\n" + " c,dd,\n" + " eee,\n" + " fff}">>, + rp({aa,bb,c,dd,eee,fff}, 1, 80, -1, 5, none)), + bt(<<"{aa,bb,\n" + " c,\n" + " dd,\n" + " eee,\n" + " fff}">>, + rp({aa,bb,c,dd,eee,fff}, 1, 80, -1, 4, none)), + ok. + +bt(Bin, R) -> + R = binary_to_list(Bin). + +p(Term, D) -> + rp(Term, 1, 80, D). + +p(Term, Col, Ll, D) -> + rp(Term, Col, Ll, D, no_fun). + +rp(Term, Col, Ll, D) -> + rp(Term, Col, Ll, D, fun rfd/2). + +-define(MAXCS, 60). + +rp(Term, Col, Ll, D, RF) -> + rp(Term, Col, Ll, D, ?MAXCS, RF). + +rp(Term, Col, Ll, D, M, RF) -> + %% io:format("~n~n*** Col = ~p Ll = ~p D = ~p~n~p~n-->~n", + %% [Col, Ll, D, Term]), + R = io_lib_pretty:print(Term, Col, Ll, D, M, RF), + %% io:format("~s~n<--~n", [R]), + lists:flatten(io_lib:format("~s", [R])). + +fmt(Fmt, Args) -> + lists:flatten(io_lib:format(Fmt, Args)). + +rfd(a, 0) -> + []; +rfd(b, 1) -> + [f]; +rfd(c, 2) -> + [f1, f2]; +rfd(e, 3) -> + [f, g, h]; +rfd(d, 5) -> + [aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccc, dddddddddddddddddddd, + eeeeeeeeeeeeeeeeeeee]; +rfd(rrrrr, 3) -> + [f1, f2, f3]; +rfd(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 0) -> + []; +rfd(_, _) -> + no. + +manpage(doc) -> + ["The examples in io(3) and io_lib(3)."]; +manpage(suite) -> + []; +manpage(Config) when is_list(Config) -> + %% The examples that write or print only, not the ones that read... + + ?line bt(<<"Hello world!\n">>, + fmt("Hello world!~n", [])), + ?line bt(<<"| aaaaa|bbbbb |ccccc|\n">>, % bugfix + fmt("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c])), + ?line bt(<<"|**********|\n">>, + fmt("|~10w|~n", [{hey, hey, hey}])), + ?line bt(<<"|{hey,hey,h|\n">>, + fmt("|~10s|~n", [io_lib:write({hey, hey, hey})])), + + T = [{attributes,[[{id,age,1.50000},{mode,explicit}, + {typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]}, + {typename,'Person'},{tag,{'PRIVATE',3}},{mode,implicit}], + ?line bt(<<"[{attributes,[[{id,age,1.5},{mode,explicit},{typename," + "[73,78,84,69,71,69,82]}],[{id,cho},{mode,explicit}," + "{typename,'Cho'}]]},{typename,'Person'},{tag,{'PRIVATE',3}}," + "{mode,implicit}]\n">>, + fmt("~w~n", [T])), + ?line bt(<<"[{attributes,[[{id,age,1.5},\n" + " {mode,explicit},\n" + " {typename,\"INTEGER\"}],\n" + " [{id,cho},{mode,explicit},{typename,'Cho'}]]},\n" + " {typename,'Person'},\n" + " {tag,{'PRIVATE',3}},\n" + " {mode,implicit}]\n">>, + fmt("~62p~n", [T])), + ?line bt(<<"Here T = [{attributes,[[{id,age,1.5},\n" + " {mode,explicit},\n" + " {typename,\"INTEGER\"}],\n" + " [{id,cho},\n" + " {mode,explicit},\n" + " {typename,'Cho'}]]},\n" + " {typename,'Person'},\n" + " {tag,{'PRIVATE',3}},\n" + " {mode,implicit}]\n">>, + fmt("Here T = ~62p~n", [T])), + ?line bt(<<"[{attributes,[[{id,age,1.5},{mode,explicit}," + "{typename,...}],[{id,cho},{mode,...},{...}]]}," + "{typename,'Person'},{tag,{'PRIVATE',3}},{mode,implicit}]\n">>, + fmt("~W~n", [T,9])), + ?line bt(<<"[{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}]," + "\n " + "[{id,cho},{mode,...},{...}]]},\n {typename,'Person'},\n " + "{tag,{'PRIVATE',3}},\n {mode,implicit}]\n">>, + fmt("~62P~n", [T,9])), + + ?line "1F\n" = fmt("~.16B~n", [31]), + ?line "-10011\n" = fmt("~.2B~n", [-19]), + ?line "5Z\n" = fmt("~.36B~n", [5*36+35]), + ?line "10#31\n" = fmt("~X~n", [31,"10#"]), + ?line "-0x1F\n" = fmt("~.16X~n", [-31,"0x"]), + ?line "10#31\n" = fmt("~.10#~n", [31]), + ?line "-16#1F\n" = fmt("~.16#~n", [-31]), + ?line "abc def 'abc def' {foo,1} A \n" = + fmt("~s ~w ~i ~w ~c ~n", + ['abc def', 'abc def', {foo, 1},{foo, 1}, 65]), + % fmt("~s", [65]), + + %% io_lib(3) + ?line bt(<<"{1,[2],[3],[...],...}">>, + lists:flatten(io_lib:write({1,[2],[3],[4,5],6,7,8,9}, 5))), + ok. + +otp_6708(doc) -> + ["OTP-6708. Fewer newlines when pretty-printing."]; +otp_6708(suite) -> + []; +otp_6708(Config) when is_list(Config) -> + ?line bt(<<"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,\n" + " 23,24,25,26,27,28,29|...]">>, + p(lists:seq(1,1000), 30)), + ?line bt(<<"{lkjasklfjsdak,mlkasjdflksj,klasdjfklasd,jklasdfjkl,\n" + " jklsdjfklsd,masdfjkkl}">>, + p({lkjasklfjsdak,mlkasjdflksj,klasdjfklasd,jklasdfjkl, + jklsdjfklsd, masdfjkkl}, -1)), + ?line bt(<<"#b{f = {lkjljalksdf,jklaskfjd,kljasdlf,kljasdf,kljsdlkf,\n" + " kjdd}}">>, + p({b, {lkjljalksdf,jklaskfjd,kljasdlf,kljasdf,kljsdlkf,kjdd}}, + -1)), + ?line bt(<<"#b{f = {lkjljalksdf,jklaskfjd,kljasdlf,kljasdf,kljsdlkf,\n" + " kdd}}">>, + p({b, {lkjljalksdf,jklaskfjd,kljasdlf,kljasdf,kljsdlkf,kdd}}, + -1)), + ?line bt(<<"#e{f = undefined,g = undefined,\n" + " h = #e{f = 11,g = 22,h = 333}}">>, + p({e,undefined,undefined,{e,11,22,333}}, -1)), + ?line bt(<<"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21|\n" + " apa11]">>, + p(lists:seq(1,21) ++ apa11, -1)), + ?line bt(<<"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,\n" + " 23,\n" + " {{abadalkjlasdjflksdajfksdklfsdjlkfdlskjflsdj" + "flsdjfldsdsdddd}}]">>, + p(lists:seq(1,23) ++ + [{{abadalkjlasdjflksdajfksdklfsdjlkfdlskjflsdjflsdjfldsdsdddd}}], + -1)), + ?line bt(<<"{lkjasdf,\n" + " {kjkjsd,\n" + " {kjsd,\n" + " {kljsdf,\n" + " {kjlsd,{dkjsdf,{kjlds,{kljsd,{kljs," + "{kljlkjsd}}}}}}}}}}">>, + p({lkjasdf,{kjkjsd,{kjsd, + {kljsdf, + {kjlsd, + {dkjsdf,{kjlds, + {kljsd,{kljs,{kljlkjsd}}}}}}}}}}, + -1)), + ?line bt(<<"{lkjasdf,\n" + " {kjkjsd,\n" + " {kjsd,{kljsdf,{kjlsd,{dkjsdf,{kjlds," + "{kljsd,{kljs}}}}}}}}}">>, + p({lkjasdf,{kjkjsd,{kjsd, + {kljsdf,{kjlsd,{dkjsdf, + {kjlds,{kljsd,{kljs}}}}}}}}}, + -1)), + ?line bt(<<"<<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,\n" + " 22,23>>">>, + p(list_to_binary(lists:seq(1,23)), -1)), + ?line bt(<<"<<100,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,\n" + " 27>>">>, + p(list_to_binary([100|lists:seq(10,27)]), -1)), + ?line bt(<<"<<100,101,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,\n" + " 26>>">>, + p(list_to_binary([100,101|lists:seq(10,26)]), -1)), + ?line bt(<<"{{<<100,101,102,10,11,12,13,14,15,16,17,18,19,20,21,22,\n" + " 23>>}}">>, + p({{list_to_binary([100,101,102|lists:seq(10,23)])}}, -1)), + ?line bt(<<"[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22|\n" + " ap]">>, + p(lists:seq(1,22) ++ ap, -1)), + ?line bt(<<"[1,2,3,4,5,6,7,8,9,10,{},[],\n <<>>,11,12,13,14,15]">>, + p(lists:seq(1,10) ++ [{},[],<<>>] ++ lists:seq(11,15),1,30,-1)), + ?line bt(<<"[ddd,ddd,\n" + " {1},\n" + " [1,2],\n" + " ddd,kdfd,\n" + " [[1,2],a,b,c],\n" + " <<\"foo\">>,<<\"bar\">>,1,\n" + " {2}]">>, + p([ddd,ddd,{1},[1,2],ddd,kdfd,[[1,2],a,b,c],<<"foo">>,<<"bar">>, + 1,{2}],1,50,-1)), + + ?line bt(<<"{dskljsadfkjsdlkjflksdjflksdjfklsdjklfjsdklfjlsdjfkl,jksd,\n" + " " + "lkjsdf,kljsdf,kljsf,kljsdf,kljsdf,jkldf,jklsdf,kljsdf,\n" + " " + "kljsdf,jklsdf,lkjfd,lkjsdf,kljsdf,kljsdf,lkjsdf,kljsdf,\n" + " " + "lkjsdfsd,kljsdf,kjsfj}">>, + p({dskljsadfkjsdlkjflksdjflksdjfklsdjklfjsdklfjlsdjfkl,jksd, + lkjsdf,kljsdf,kljsf,kljsdf,kljsdf,jkldf,jklsdf,kljsdf, + kljsdf,jklsdf,lkjfd,lkjsdf,kljsdf,kljsdf,lkjsdf,kljsdf, + lkjsdfsd,kljsdf,kjsfj}, 1, 110, -1)), + ?line bt(<<"{dskljsadfkjsdlkjflksdjflksdjfklsdjklfjsdklfjlsdjfkl," + "#d{aaaaaaaaaaaaaaaaaaaa = 1,\n" + " " + "bbbbbbbbbbbbbbbbbbbb = 2,cccccccccccccccccccc = 3,\n" + " " + "dddddddddddddddddddd = 4,eeeeeeeeeeeeeeeeeeee = 5}}">>, + rp({dskljsadfkjsdlkjflksdjflksdjfklsdjklfjsdklfjlsdjfkl, + {d,1,2,3,4,5}},1,200,-1)), + ok. + +-define(ONE(N), ((1 bsl N) - 1)). +-define(ALL_ONES, ((1 bsl 52) - 1)). + +otp_7084(doc) -> + ["OTP-7084. Printing floating point numbers nicely."]; +otp_7084(suite) -> + []; +otp_7084(Config) when is_list(Config) -> + OldDog=?config(watchdog, Config), + test_server:timetrap_cancel(OldDog), + Timeout = 180, + ?line Dog = test_server:timetrap({seconds,Timeout}), + L = [{g_warm_up, fun g_warm_up/0}, + {g_big_pos_float, fun g_big_pos_float/0}, + {g_small_neg_float, fun g_small_neg_float/0}, + {g_close_to_zero, fun g_close_to_zero/0}, + {g_denormalized, fun g_denormalized/0}, + {g_normalized, fun g_normalized/0}, + {g_choice, fun g_choice/0}, + {g_misc, fun g_misc/0}], + F = fun({M,T}) -> io:format("~p~n", [M]), T() end, + R = try + lists:foreach(fun(T) -> F(T) end, L), + ok + catch throw:Reason -> + Reason + end, + ?line test_server:timetrap_cancel(Dog), + R. + +g_warm_up() -> + g_t(0.5), + g_t(-0.5), + g_t((1 bsl 55) * 0.5), + g_t(-(1 bsl 55) * 0.5), + g_t(1.6799127650033296e+308), + g_t(pack(1, 0, 2#1010101010001100101000010111100101000000101100110001)), + g_t(pack(1, 0, 2#0001010000001001110100000101010101001110010001010110)), + g_t(234324324.23432432432432), + ok. + +g_big_pos_float() -> + %% The greatest positive float: + ft({{0,2046,?ONE(52)}, 100, 0}), + ok. + +g_small_neg_float() -> + %% The least negative float: + ft({{1,2046,?ONE(52)}, 0, 100}), + ok. + +g_close_to_zero() -> + %% A few denormalized floats close to zero: + ft({{0,0,0}, 100, 100}), + g_t(pack(1, 0, 0)), % -0.0 + ok. + +g_denormalized() -> + %% Denormalized floats (mantissa carry): +% D = 5, + %% Faster: + D = 1, + [ft({{S,0,?ONE(N)},D,D}) || S <- [0,1], N <- lists:seq(0, 52)], + ok. + +g_normalized() -> + %% Normalized floats (exponent carry): +% D = 5, + %% Faster: + D = 1, + [ft({{S,E,?ONE(52)},D,D}) || S <- [0,1], E <- lists:seq(0, 2045)], + ok. + +g_choice() -> + %% Exponent should be used when and only when the string is shorter. + %% (g_misc/0 checks this too, and probably more throughly). + L = [0.0003, 3.0e-5, 3.3e-5, 3.3e-4, + 314.0, 314.1, 310.0, 3.1e6, -100.0, + 3.34e4, 3.0e3, 3.34333e9, 3.3433323e10, 33433323700.0, + 0.00197963, 1.97963e-4], + lists:foreach(fun(V) -> g_t(V) end, L), + ok. + +g_misc() -> + L_0_308 = lists:seq(0, 308), + L_0_307 = lists:seq(0, 307), +% L_1_9 = lists:seq(1, 9), +% L_0_9 = lists:seq(0, 9), + %% Faster: + L_1_9 = [1,5,9], + L_0_9 = [0,1,5,9], + + %% 1.0,10.0, ... 2.0,20.0, ... 9.0,90.0, ... -1,-10, ... -2.0,-20.0... + [g_t(S*T*pow10(N)) || S <- [1.0, -1.0], T <- L_1_9, N <- L_0_307], + + %% 1.0,1.0/10,1.0/100,... 2.0,2.0/10,2.0/100, ... 9.0,9.0/10,9.0/100, + %% -1.0,-1.0/10,... -9.0,-9.0/10... + [g_t(S*T/pow10(N)) || S <- [1.0, -1.0], T <- L_1_9, N <- L_0_308], + + %% 0.0,1.0,2.0,...,9.0, 0.0,10.0,20.0,...,90.0,... + %% 0.0,-1.0,-2.0,...,-9.0, 0.0,-10.0,-20.0,...,-90.0,... + [g_t(S*list_to_float([D+$0]++lists:duplicate(N, $0)++".0")) || + S <- [1.0,-1.0], N <- lists:seq(0, 300), D <- L_0_9], + + %% 0.0,0.1,0.2,...0,9, 0.0,0.01,0.02,...,0.09, + %% 0.0,-0.1,-0.2,...-0,9, 0.0,-0.01,-0.02,...,-0.09, + [g_t(S*list_to_float("0."++lists:duplicate(N, $0)++[D+$0])) || + S <- [1.0,-1.0], N <- lists:seq(0, 300), D <- L_0_9], + ok. + +ft({{S,E,M}, L, G}) -> + ft({pack(S, E, M), L, G}); +ft({V, Less, Greater}) when is_float(V) -> + _ = g_t(V), + ft(V, fun inc/1, Greater), + ft(V, fun dec/1, Less). + +ft(V0, F, I) when I > 0, is_float(V0) -> + V = F(V0), + _ = g_t(V), + ft(V, F, I - 1); +ft(V, _F, 0) when is_float(V) -> + ok. + +g_t(V) when is_float(V) -> + %% io:format("Testing ~.17g~n", [V]), + Io = io_lib:format("~p", [V]), + Sv = binary_to_list(iolist_to_binary(Io)), + ok = g_t(V, Sv), + Sv. + +%% -> ok | THROW + +%% Checks that Sv is the shortest, correctly rounded string that +%% converts to V when read back with list_to_float/1. +%% Note: in a few cases the least significant digit has been +%% incremented by one, namely when the correctly rounded string +%% converts to another floating point number. +g_t(0.0, "0.0") -> + ok; +g_t(V, Sv) -> + try + g_t_1(V, Sv) + catch throw:Reason -> + throw({Reason, V, Sv}) + end. + +g_t_1(V, Sv) -> + %% Check that the least significant digit is correct. + %% If Sv is "3.14" then Sv- is "3.13" and Sv+ is "3.15". + %% Check that |V - Sv| =< (V - Sv-) and + %% that |V - Sv| =< (Sv+ - V) + Times = least_significant_digit(Sv), + case Times of + 0 -> throw(least_significant_digit_is_zero); + _ -> ok + end, + S = if V < 0 -> -1; true -> 1 end, + SvMinus = incr_lsd(Sv, -S), + SvPlus = incr_lsd(Sv, S), + Svr = s2r(Sv), + Svminusr = s2r(SvMinus), + Svplusr = s2r(SvPlus), + Vr = f2r(V), + + Abs_Sv_Vr = rat_abs(rat_minus(Svr, Vr)), + Svminus_Vr = rat_minus(Vr, Svminusr), + Svplus_Vr = rat_minus(Svplusr, Vr), + %% The are 45 (negative) floats where SvMinus (SvPlus) is closer + %% to V than Sv, but such that when reading SvMinus (SvPlus) wrong + %% float would be returned. + case rat_lte(Abs_Sv_Vr, Svminus_Vr) of + true -> + ok; + false -> + case list_to_float(SvMinus) of + V -> throw(vsminus_too_close_to_v); + _Vminus -> ok + end + end, + case rat_lte(Abs_Sv_Vr, Svplus_Vr) of + true -> + ok; + false -> + case list_to_float(SvPlus) of + V -> throw(vsplus_too_close_to_v); + _Vplus -> ok + end + end, + + %% Check that Sv is closer to V than to V- and V+. + %% Check that |V - Sv| =< (V - V-) and + %% that |V - Sv| =< (V+ - V) + %% (An alternative is V- + V =< 2*Sv =< V + V+.) + case inc(V) of + inf -> + ok; + Vplus -> + Vplusr = f2r(Vplus), + V_Vplusr = rat_minus(Vplusr, Vr), + case rat_lte(Abs_Sv_Vr, V_Vplusr) of + true -> ok; + false -> throw(vplus_too_close_to_sv) + end + end, + case dec(V) of + '-inf' -> + ok; + Vminus -> + Vminusr = f2r(Vminus), + V_Vminusr = rat_minus(Vr, Vminusr), + case rat_lte(Abs_Sv_Vr, V_Vminusr) of + true -> ok; + false -> throw(vminus_too_close_to_sv) + end + end, + + %% Check that no prefix of Sv yields V. + %% If Sv is "3.14" then Svlow is "3.1" and Svhigh is "3.2". + %% + %% This is just one way of getting Svlow and Svhigh: + if + V < 0 -> + SvHigh = step_lsd(Sv, -Times), + SvLow = step_lsd(Sv, 10 - Times); + true -> + SvHigh = step_lsd(Sv, 10 - Times), + SvLow = step_lsd(Sv, -Times) + end, + + case catch list_to_float(SvLow) of + V -> throw(low_is_v); + _ -> ok + end, + + case catch list_to_float(SvHigh) of + V -> throw(high_is_v); + _ -> ok + end, + + %% Check that Sv has enough digits. + case list_to_float(Sv) of + V -> ok; + _ -> throw(wrong_float) % cannot happen + end, + + g_choice(Sv), + + ok. + +%%% In "123450000.0", '5' is the lsd; +%%% in "1234.0000", (the last) '0' is the lsd; +%%% in "1234.0", '4' is the lsd (the Erlang syntax requires the final zero). + +%% Trailing zeroes are not significant ("3.0", "5.0e-324", "232000.0"). +least_significant_digit("-"++Ds) -> + least_significant_digit(Ds); +least_significant_digit("+"++Ds) -> + least_significant_digit(Ds); +least_significant_digit(Ds) -> + [MS|_E] = string:tokens(Ds, "eE"), + lsd0(lists:reverse(MS))-$0. + +lsd0("0."++Ds) -> + lsd1(Ds); +lsd0([D | _Ds]) -> + D. + +lsd1("0"++Ds) -> + lsd1(Ds); +lsd1([D | _Ds]) -> + D. + +step_lsd(Ds, 0) -> + Ds; +step_lsd(Ds, N) when N > 0 -> + NDs = incr_lsd(Ds, 1), + step_lsd(NDs, N - 1); +step_lsd(Ds, N) when N < 0 -> + NDs = incr_lsd(Ds, -1), + step_lsd(NDs, N + 1). + +%% Assumes Ds represents some other number than zero. +%% Increments or decrements the least significant digit. +incr_lsd("-"++Ds, I) -> + "-"++incr_lsd(Ds, I); +incr_lsd(Ds, I) when I =:= 1; I =:= -1 -> + [MS|E] = string:tokens(Ds, "eE"), + X = ["e" || true <- [E =/= []]], + lists:flatten([incr_lsd0(lists:reverse(MS), I, []), X, E]). + +incr_lsd0("0."++Ds, C, L) -> + incr_lsd1(Ds, C, [$., $0 | L]); +incr_lsd0(Ds, C, L) -> + incr_lsd2(Ds, C, L). + +incr_lsd1("0"++Ds, C, L) -> + incr_lsd1(Ds, C, [$0 | L]); +incr_lsd1(Ds, C, L) -> + incr_lsd2(Ds, C, L). + +incr_lsd2([], C, L) -> + [C + $0 | L]; +incr_lsd2("."++Ds, C, L) -> + incr_lsd2(Ds, C, [$. | L]); +incr_lsd2("9"++Ds, 1=C, L) -> + incr_lsd2(Ds, C, [$0 | L]); +incr_lsd2("0"++Ds, -1=C, L) -> + incr_lsd2(Ds, C, [$9 | L]); +incr_lsd2([D | Ds], C, L) -> + lists:reverse(Ds, [D + C | L]). + +s2r(S) when is_list(S) -> + case string:tokens(S, "eE") of + [MS] -> + s10(MS); + [MS, ES] -> + Mr = s10(MS), + E = list_to_integer(ES), + if + E < 0 -> + rat_multiply(Mr, {1,pow10(-E)}); + true -> + rat_multiply(Mr, {pow10(E), 1}) + end + end. + +s10("-"++S) -> + rat_multiply({-1,1},s10(S)); +s10(S) -> + [AS, BS] = string:tokens(S, "."), + Sc = length(BS), + A = list_to_integer(AS), + B = list_to_integer(BS), + F = pow10(Sc), + rat_multiply({1,1}, {A*F + B, F}). + +pow10(X) -> + int_pow(10, X). + +int_pow(X, 0) when is_integer(X) -> + 1; +int_pow(X, N) when is_integer(X), is_integer(N), N > 0 -> + int_pow(X, N, 1). + +int_pow(X, N, R) when N < 2 -> + R * X; +int_pow(X, N, R) -> + int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end). + +dec(F) when is_float(F) -> + <<S:1, BE:11, M:52>> = <<F:64/float>>, + dec({S,BE,M}); +dec({1,2046,?ALL_ONES}) -> + '-inf'; +dec({S,BE,M}) when 0 =< S, S =< 1, + 0 =< BE, BE =< 2046, + 0 =< M, M =< ?ALL_ONES -> + {S1,BE1,M1} = dec1(S, BE, M), + <<F:64/float>> = <<S:1, BE:11, M:52>>, + <<F1:64/float>> = <<S1:1, BE1:11, M1:52>>, + true = F1 < F, + F1. + + +dec1(0, 0, 0) -> + dec1(1, 0, 0); +dec1(0, BE, 0) -> + {0,BE-1,?ALL_ONES}; +dec1(0, BE, M) -> + {0,BE,M-1}; +dec1(1, BE, ?ALL_ONES) -> + {1,BE+1,0}; +dec1(1, BE, M) -> + {1,BE,M+1}. + +inc(F) when is_float(F) -> + <<S:1, BE:11, M:52>> = <<F:64/float>>, + inc({S,BE,M}); +inc({0,2046,?ALL_ONES}) -> + inf; +inc({S,BE,M}) when 0 =< S, S =< 1, + 0 =< BE, BE =< 2046, + 0 =< M, M =< ?ALL_ONES -> + {S1,BE1,M1} = inc1(S, BE, M), + <<F:64/float>> = <<S:1, BE:11, M:52>>, + <<F1:64/float>> = <<S1:1, BE1:11, M1:52>>, + true = F1 > F, + F1. + +inc1(0, BE, ?ALL_ONES) -> + {0,BE+1,0}; +inc1(0, BE, M) -> + {0,BE,M+1}; +inc1(1, 0, 0) -> + inc1(0, 0, 0); +inc1(1, BE, 0) -> + {1,BE-1,?ALL_ONES}; +inc1(1, BE, M) -> + {1,BE,M-1}. + +f2r(F) when is_float(F) -> + <<S:1, BE:11, M:52>> = <<F:64/float>>, + f2r({S,BE,M}); +f2r({S,BE,M}) when 0 =< S, S =< 1, + 0 =< BE, BE =< 2046, + 0 =< M, M =< ?ALL_ONES -> + Vr = {T,N} = f2r1(S, BE, M), + <<F:64/float>> = <<S:1, BE:11, M:52>>, + case catch T/N of + {'EXIT', _} -> ok; + TN -> true = F =:= TN + end, + Vr. + +f2r1(S, 0, M) -> + rat_multiply({sign(S),1}, {M, 1 bsl 1074}); +f2r1(S, BE, M) when BE - 1075 >= 0 -> + rat_multiply({sign(S),1}, {((1 bsl 52)+M) * (1 bsl (BE-1075)),1}); +f2r1(S, BE, M) -> + rat_multiply({sign(S),1}, {(1 bsl 52)+M, 1 bsl (1075-BE)}). + +sign(0) -> + 1; +sign(1) -> + -1. + +%%% Rational numbers (very scetchy). + +rat_abs({A,B}) when A < 0 -> + {-A,B}; +rat_abs({A,B}) -> + {A,B}. + +-ifdef(not_used). +rat_equal(R1, R2) -> + R1 =:= R2. + +rat_negate({A,B}) -> + {-A,B}. + +rat_divide({A,B},{C,D}) -> + rat_multiply({A,B},{D,C}). +-endif. + +rat_lte({A,B}, {C,D}) when B =/= 0, D =/= 0 -> + A*D =< C*B. + +rat_minus({A,B}, {C,D}) -> + rat_plus({A,B}, {-C,D}). + +rat_plus({A,B}, {C,D}) when B =/= 0, D =/= 0 -> + rat_normalize({A*D+B*C, B*D}). + +rat_multiply({A,B}, {C,D}) when B =/= 0, D =/= 0 -> + rat_normalize({A * C, B * D}). + +rat_normalize({T,N}) when N =/= 0 -> + G = gcd(T, N), + T2 = T div G, + N2 = N div G, + if + T2 < 0 -> + if + N2 < 0 -> {-T2,-N2}; + true -> {T2,N2} + end; + true -> + if + N2 < 0 -> {-T2,-N2}; + true -> {T2,N2} + end + end. + +gcd(A, 0) -> A; +gcd(A, B) -> gcd(B, A rem B). + +%%% End of rational numbers. + +%% Check that there is an exponent if and only if characters are +%% saved. Note: this assumes floating point numbers "Erlang style" +%% (with a single zero before and after the dot, and no extra leading +%% zero in the exponent). +g_choice(S) when is_list(S) -> + [MS | ES0] = string:tokens(S, "eE"), + [IS, FS] = string:tokens(MS, "."), + Il = length(IS), + Fl = length(FS), + Pre = z(MS), + Post = z(lists:reverse(MS)), + ES = lists:append(ES0), + El = length(ES), + I = list_to_integer(IS), + if + El =/= 0, ((I > 9) or (I < -9)) -> + throw(too_many_digits_before_the_dot); + El =/= 0, I =:= 0 -> + throw(zero_before_the_dot); + Pre =:= 0, Post > 0, El =:= 0 -> % DDDD0000.0 + Saving = if + I < 0, Il =:= Post + 2 -> + Post; + I > 0, Il =:= Post + 1 -> + Post; + I =/= 0, true -> + Post + 1 + end, + Cost = 1 + length(integer_to_list(Il - 1)), + if + Cost < Saving -> + throw(with_exponent_is_shorter); + true -> + ok + end; + Pre > 0, Post =:= 0, El =:= 0 -> % 0.000DDDD + Saving = if + Fl =:= Pre + 1 -> + Pre; + true -> + Pre + 1 + end, + Cost = 2 + length(integer_to_list(Pre + 1)), + if + Cost < Saving -> + throw(with_exponent_is_shorter); + true -> + ok + end; + Pre =:= 0, Post =:= 0, El > 0 -> % D.DDDeDD + E = list_to_integer(ES), + if + E >= 0 -> + Cost = E - (Fl - 1); + E < 0 -> + Cost = -E + end, + Saving = length(ES) + 1, + if + Cost =:= Saving -> + throw(draw_but_choose_form_without_exponent); + Cost < Saving -> + throw(without_exponent_is_shorter); + true -> + ok + end; + Pre =:= 0, Post =:= 0, El =:= 0 -> % DDD.DDD + ok; + true -> + throw(badly_formed_floating_point_string) + end. + +z("0."++Ds) -> + length(lists:takewhile(fun(D) -> D =:= $0 end, Ds)); +z(_Ds) -> + 0. + +pack(Sign, Exp, Frac) -> + <<Float:64/float>> = <<Sign:1, Exp:11, Frac:52>>, + Float. + +%% Whitebox test of io_lib:collect_line/3. +io_lib_collect_line_3_wb(Config) when is_list(Config) -> + ?line do_collect_line(binary, "\n"), + ?line do_collect_line(binary, "\r\n"), + ?line do_collect_line(list, "\n"), + ?line do_collect_line(list, "\r\n"), + ok. + +do_collect_line(Mode, Eol) -> + First = "abcde", + FirstNL = First++"\n", + Second = "unterminated line", + Data0 = First ++ Eol ++ Second, + {Data1,Result0} = do_collect_line_combine(Mode, Data0, FirstNL, Second), + do_collect_line_1(Mode, Data1, Result0, []), + + {Data,Result} = do_collect_line_combine(Mode, "unterm", "unterm", eof), + do_collect_line_1(Mode, Data, Result, []). + +do_collect_line_combine(binary, Data0, FirstNL, eof) -> + {list_to_binary(Data0), + {stop,list_to_binary(FirstNL),eof}}; +do_collect_line_combine(binary, Data0, FirstNL, Second) -> + {list_to_binary(Data0), + {stop,list_to_binary(FirstNL),list_to_binary(Second)}}; +do_collect_line_combine(list, Data0, FirstNL, Second) -> + {Data0,{stop,FirstNL,Second}}. + +do_collect_line_1(Mode, [H|T], Result, Acc0) -> + Acc = [H|Acc0], + Result = do_collect_line_2(lists:reverse(Acc), T), + do_collect_line_1(Mode, T, Result, Acc); +do_collect_line_1(Mode, <<H,T/binary>>, Result, Acc0) -> + Acc = [H|Acc0], + Result = do_collect_line_2(list_to_binary(lists:reverse(Acc)), T), + do_collect_line_1(Mode, T, Result, Acc); +do_collect_line_1(_Mode, [], _Result, _Acc) -> + ok; +do_collect_line_1(_Mode, <<>>, _Result, _Acc) -> + ok. + +do_collect_line_2(Part1, Part2) -> + Dummy = make_ref(), + do_collect_line_3(start, [Part1,Part2,eof], Dummy). + +do_collect_line_3(State0, [H|T], Dummy) -> + case io_lib:collect_line(State0, H, Dummy) of + {stop,Line,Rest} -> + {stop,Line,do_collect_line_adjust_rest(Rest, T)}; + State -> + do_collect_line_3(State, T, Dummy) + end. + +do_collect_line_adjust_rest(eof, []) -> eof; +do_collect_line_adjust_rest(eof, <<>>) -> eof; +do_collect_line_adjust_rest(Rest, [eof]) -> Rest; +do_collect_line_adjust_rest(Rest, [Bin|T]) when is_binary(Bin) -> + do_collect_line_adjust_rest(<<Rest/binary,Bin/binary>>, T); +do_collect_line_adjust_rest(Rest, [List|T]) when is_list(List) -> + do_collect_line_adjust_rest(Rest++List, T). + + + +cr_whitespace_in_string(Config) when is_list(Config) -> + ?line {ok,["abc"],[]} = io_lib:fread("~s", "\rabc"). + + + +io_fread_newlines(Config) when is_list(Config) -> + ?line PrivDir = ?privdir(Config), + ?line Fname = filename:join(PrivDir, "io_fread_newlines.txt"), + ?line F0 = [[0,1,2,3,4,5,6,7,8,9]], + ?line F1 = [[0,1,2,3,4,5,6,7,8],[9]], + ?line F2 = [[0,1,2,3,4,5,6,7],[8,9]], + ?line F3 = [[0,1,2,3,4,5,6],[7,8,9]], + ?line F4 = [[0,1,2,3,4,5],[6,7,8,9]], + ?line F5 = [[0,1,2,3,4],[5,6,7,8,9]], + ?line F6 = [[0,1,2,3],[4,5,6,7],[8,9]], + ?line F7 = [[0,1,2],[3,4,5],[6,7,8],[9]], + ?line F8 = [[0,1],[2,3],[4,5],[6,7],[8,9]], + ?line F9 = [[0],[1],[2],[3],[4],[5],[6],[7],[8],[9]], + ?line Newlines = ["\n", "\r\n", "\r"], + try + ?line io_fread_newlines_1([F0,F1,F2,F3,F4,F5,F6,F7,F8,F9], + Fname, Newlines) + after + file:delete(Fname) + end. + +io_fread_newlines_1(Fs, Fname, [Newline|Newlines]) -> + ?line ok = io_fread_newlines_2(Fs, Fname, Newline), + ?line io_fread_newlines_1(Fs, Fname, Newlines); +io_fread_newlines_1(_, _, []) -> ok. + +io_fread_newlines_2([F|Fs], Fname, Newline) -> + ?line N1 = write_newlines_file(Fname, F, Newline), + ?line {F2,N2} = read_newlines_file(Fname), + ?line io:format("~w ~p ~w~n~n", [N1,F,N2]), + ?line F2 = lists:flatten(F), + %% Intermediate newlines are not counted + ?line N2 = N1 - (length(F) - 1)*length(Newline), + ?line io_fread_newlines_2(Fs, Fname, Newline); +io_fread_newlines_2([], _, _) -> ok. + + +write_newlines_file(Fname, F, Newline) -> + Bytes = list_to_binary(digit_lines(F, Newline)), + io:format("Data: ~w~n~w~n", [Newline,Bytes]), + ok = file:write_file(Fname, Bytes), + size(Bytes). + +digit_lines([L], _) -> + digit_line(L); +digit_lines([L|Ls], Newline) -> + [digit_line(L),Newline|digit_lines(Ls, Newline)]. + +digit_line([D]) -> + integer_to_list(D); +digit_line([D|Ds]) -> + [integer_to_list(D), " ", digit_line(Ds)]. + +read_newlines_file(Fname) -> + {ok,Fd} = file:open(Fname, [read]), + try {L, N0} = R0 = read_newlines(Fd, [], 0), + case io:fread(Fd, "", "~*s~l") of + eof -> R0; + {ok,[N]} -> {L,N0+N} + end + after + file:close(Fd) + end. + + +read_newlines(Fd, Acc, N0) -> + case io:fread(Fd, "", "~d~l") of + {ok,[D,N]} -> + read_newlines(Fd, [D|Acc], N0+N); + eof -> + {lists:reverse(Acc),N0} + end. |