Files
mercury/tests/tabling/fib_string.m
2018-07-08 23:20:52 +02:00

185 lines
5.0 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ts=4 sw=4 et ft=mercury
%---------------------------------------------------------------------------%
:- module fib_string.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is cc_multi.
:- implementation.
:- import_module assoc_list.
:- import_module benchmarking.
:- import_module int.
:- import_module list.
:- import_module pair.
:- import_module require.
:- import_module solutions.
:- import_module string.
:- pragma require_feature_set([memo]).
main(!IO) :-
perform_trials("oneone", !IO).
:- pred perform_trials(string::in, io::di, io::uo) is cc_multi.
perform_trials(N, !IO) :-
trial(N, Time, MTime),
% io.write_string(N, !IO),
% io.write_string(": ", !IO),
% io.write_int(Time, !IO),
% io.write_string("ms vs ", !IO),
% io.write_int(MTime, !IO),
% io.write_string("ms\n", !IO),
( if
(
Time > 10 * MTime,
MTime > 0 % untabled takes ten times as long
;
Time > 100, % untabled takes at least 100 ms
MTime < 1 % while tabled takes at most 1 ms
)
then
io.write_string("tabling works\n", !IO)
else if
Time > 10000 % untabled takes at least 10 seconds
then
io.write_string("tabling does not appear to work\n", !IO)
else
% We couldn't get a measurable result with N,
% and it looks like we can afford a bigger trial
perform_trials(add_strings(N, "three"), !IO)
).
:- pred trial(string::in, int::out, int::out) is cc_multi.
trial(N, Time, MTime) :-
benchmark_det(fib, N, Res, 1, Time),
benchmark_det(mfib, N, MRes, 1, MTime),
require(unify(Res, MRes), "tabling produces wrong answer").
:- pred fib(string::in, string::out) is det.
fib(N, F) :-
( if string_to_num(N) < 2 then
F = num_to_string(1)
else
One = num_to_string(1),
Two = num_to_string(2),
fib(subtract_strings(N, One), F1),
fib(subtract_strings(N, Two), F2),
F = add_strings(F1, F2)
).
:- pred mfib(string::in, string::out) is det.
:- pragma memo(mfib/2).
mfib(N, F) :-
( if string_to_num(N) < 2 then
F = num_to_string(1)
else
One = num_to_string(1),
Two = num_to_string(2),
mfib(subtract_strings(N, One), F1),
mfib(subtract_strings(N, Two), F2),
F = add_strings(F1, F2)
).
:- func add_strings(string, string) = string.
add_strings(S1, S2) =
num_to_string(string_to_num(S1) + string_to_num(S2)).
:- func subtract_strings(string, string) = string.
subtract_strings(S1, S2) =
num_to_string(string_to_num(S1) - string_to_num(S2)).
:- func string_to_num(string) = int.
string_to_num(String) = Num :-
translate_last_digit(String, LastNum, RestString),
( if RestString = "" then
Num = LastNum
else
Num = string_to_num(RestString) * 10 + LastNum
).
:- pred translate_last_digit(string::in, int::out, string::out) is det.
translate_last_digit(String, LastDigit, Rest) :-
digits(Pairs),
translate_last_digit_2(Pairs, String, LastDigit, Rest).
:- pred translate_last_digit_2(assoc_list(string, int)::in, string::in,
int::out, string::out) is det.
translate_last_digit_2([], _, _, _) :-
error("cannot determine last digit").
translate_last_digit_2([DigitStr - DigitNum | Digits], String, Last, Rest) :-
( if string.remove_suffix(String, DigitStr, RestPrime) then
Last = DigitNum,
Rest = RestPrime
else
translate_last_digit_2(Digits, String, Last, Rest)
).
:- func num_to_string(int) = string.
num_to_string(Int) = String :-
translate_digits(Int, Digits),
string.append_list(Digits, String).
:- pred translate_digits(int::in, list(string)::out) is det.
translate_digits(N, Digits) :-
( if N < 10 then
translate_digit(N, Digit),
Digits = [Digit]
else
Last = N mod 10,
Rest = N // 10,
translate_digit(Last, LastDigit),
translate_digits(Rest, RestDigits),
list.append(RestDigits, [LastDigit], Digits)
).
:- pred translate_digit(int::in, string::out) is det.
translate_digit(Int, String) :-
( if translate_digit_2(Int, StringPrime) then
String = StringPrime
else
error("translate_digit give non-digit")
).
:- pred translate_digit_2(int, string).
:- mode translate_digit_2(in, out) is semidet.
:- mode translate_digit_2(out, out) is multi.
translate_digit_2(0, "zero").
translate_digit_2(1, "one").
translate_digit_2(2, "two").
translate_digit_2(3, "three").
translate_digit_2(4, "four").
translate_digit_2(5, "five").
translate_digit_2(6, "six").
translate_digit_2(7, "seven").
translate_digit_2(8, "eight").
translate_digit_2(9, "nine").
:- pred digits(assoc_list(string, int)::out) is det.
:- pragma memo(digits/1).
digits(PairList) :-
solutions(
( pred(Pair::out) is multi :-
translate_digit_2(Int, String),
Pair = String - Int
), PairList).