:- module(max, [max/3], []).

:- use_module(library(when)).

%% Two variables are ground

gmax(X, Y, Z):- 
    ground(X) ->            %% X is a number
    (
        ground(Y) ->        %% Then either Y or Z are ground
        gmax_1_2(X, Y, Z)
    ;
        gmax_1_3(X, Y, Z)
    )
 ;                              %% Y and Z must be ground (and numbers)
    gmax_1_3(Y, X, Z).      %% Note the argument swapping


gmax_1_2(X, Y, X):- X >= Y, !.         %% X is the greatest.
gmax_1_2(_, Y, Y).                     %% Y is the greatest.

gmax_1_3(X, Y, Z):- X < Z, Y = Z, !.   %% Y is the greatest
gmax_1_3(X, _Y, Z):- X > Z, !, fail.   %% Not possible: maximum is smaller!
gmax_1_3(X, Y, Z):-                    %% X = Z: we must wait until we know Y
    when(ground(Y), gmax_1_2(X, Y, Z)).


%% Max waits until the second and third variables are ground.
    
max(X, Y, Z):-
    when(
            (
                (ground(X),ground(Y)) ;
                (ground(X),ground(Z)) ;
                (ground(Z),ground(Y))
            ),
            gmax(X, Y, Z)).
