%% $Id: objinst.mercury,v 1.1 2001/07/29 00:07:28 doug Exp $
%% http://www.bagley.org/~doug/shootout/
%% from Fergus Henderson

:- module mytest.
:- interface.
:- import_module io.

:- pred main(io__state::di, io__state::uo) is det.

:- implementation.
:- import_module bool, int, string, list.

:- type toggle ---> toggle(toggle_value::bool).

:- typeclass toggle(T) where [
    func value(T) = bool,
    func 'value :='(T, bool) = T,
    func activate(T) = T
].

:- instance toggle(toggle) where [
    func(value/1) is toggle_value,
    func('value :='/2) is 'toggle_value :=',
    activate(toggle(yes)) = toggle(no),
    activate(toggle(no)) = toggle(yes)
].

:- type nth_toggle ---> nth_toggle(base::toggle, counter::int, limit::int).

:- func make_nth_toggle(bool, int) = nth_toggle.
make_nth_toggle(Val, Max) = nth_toggle(toggle(Val), 0, Max).

:- instance toggle(nth_toggle) where [
    value(T) = T^base^value,
    'value :='(T, V) = T^base^value := V,
    (activate(T) = NewT :-
        C = T^counter + 1,
        (if C >= T^limit then
            NewT = (T^counter := 0)^base := activate(T^base)
        else
            NewT = T^counter := C
        ))
].

main -->
    io__command_line_arguments(Args),
    { N = (if Args = [Arg], to_int(Arg, N0) then N0 else 1) },
    { Toggle1 = toggle(yes) },
    loop(5, (pred(T0::in, T::out, di, uo) is det -->
            { T = T0^activate },
            write_string(if T^value = yes then "true" else "false"),
            nl),
        Toggle1, Toggle2),
    loop(N, (pred(_T0::in, T::out, di, uo) is det -->
            { T = toggle(yes) }),
        Toggle2, _Toggle3),
    nl,

    { Toggle4 = make_nth_toggle(yes, 3) },
    loop(8, (pred(T0::in, T::out, di, uo) is det -->
            { T = T0^activate },
            write_string(if T^value = yes then "true" else "false"),
            nl),
        Toggle4, Toggle5),
    loop(N, (pred(_T0::in, T::out, di, uo) is det -->
            { T = make_nth_toggle(yes, 3) }),
        Toggle5, _Toggle6).

:- pred loop(int, pred(T1, T1, T2, T2), T1, T1, T2, T2).
:- mode loop(in, pred(in, out, di, uo) is det, in, out, di, uo) is det.
loop(N, P, X0, X) -->
    (if { N = 0 } then
        { X = X0 }
    else
        P(X0, X1),
        loop(N - 1, P, X1, X)
    ).