% -----------------------------------------------------------------------------
%  (C) Altran Praxis Limited
% -----------------------------------------------------------------------------
% 
%  The SPARK toolset is free software; you can redistribute it and/or modify it
%  under terms of the GNU General Public License as published by the Free
%  Software Foundation; either version 3, or (at your option) any later
%  version. The SPARK toolset is distributed in the hope that it will be
%  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
%  Public License for more details. You should have received a copy of the GNU
%  General Public License distributed with the SPARK toolset; see file
%  COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy of
%  the license.
% 
% =============================================================================

%###############################################################################
% PURPOSE
%-------------------------------------------------------------------------------
% Writes a verification condition or path function to the simplifier output
% file.
%###############################################################################

%###############################################################################
% MODULE
%###############################################################################

%###############################################################################
% DEPENDENCIES
%###############################################################################

%###############################################################################
% TYPES
%###############################################################################

%###############################################################################
% DATA
%###############################################################################

dead_path_found_message('*** Dead path detected.').
dead_path_not_found_message('*** No dead path detected.').
dead_path_not_checked_message('*** DPC not required for intermediate check.').

%###############################################################################
% PREDICATES
%###############################################################################

write_vc(SDPFile_Stream, VC_Id) :-
    get_switch_deadpaths(on),
    !,
    write_dead_path(SDPFile_Stream, VC_Id),
    !.

write_vc(SimplifiedVcgFile_Stream, _VC_Id) :-


        write_pre_hyp_part(SimplifiedVcgFile_Stream),
        write_hypotheses(SimplifiedVcgFile_Stream),
        write_pre_conc_part(SimplifiedVcgFile_Stream),
        find_max_conc_no,
        write_conclusions(SimplifiedVcgFile_Stream),
        !.

%-------------------------------------------------------------------------------
%===============================================================================
% write_dead_path(+SDPFile_Stream).
%-------------------------------------------------------------------------------
% Write to SDPFile_Stream the result of the dead path search.
%===============================================================================

write_dead_path(SDPFile_Stream, _VC_Id) :-
    % A dead path is found when a contradiction has been found.
    found_contradiction,
    !,
    write_dead_path_found(SDPFile_Stream).

write_dead_path(SDPFile_Stream, _VC_Id) :-
    % A dead path is found when all conclusions (assume that the dpc file
    % contains a single conclusion of false) have been proved.
    proved_all_conclusions,
    !,
    write_dead_path_found(SDPFile_Stream).

write_dead_path(SDPFile_Stream, VC_Id) :-
    \+ get_zombiescope_no_check_required(VC_Id),
    !,
    dead_path_not_found_message(DeadPathNotFound_CharList),
    write(SDPFile_Stream, DeadPathNotFound_CharList),
    nl(SDPFile_Stream), nl(SDPFile_Stream).

write_dead_path(SDPFile_Stream, VC_Id) :-
    get_zombiescope_no_check_required(VC_Id),
    !,
    dead_path_not_checked_message(DeadPathNotChecked_CharList),
    write(SDPFile_Stream, DeadPathNotChecked_CharList),
    nl(SDPFile_Stream), nl(SDPFile_Stream).

%===============================================================================
% write_dead_path_found(+SDPFile_Stream).
%-------------------------------------------------------------------------------
% Write to SDPFile_Stream a dead path that has been found.
%===============================================================================

write_dead_path_found(SDPFile_Stream) :-
    dead_path_found_message(DeadPathFound_CharList),
    write(SDPFile_Stream, DeadPathFound_CharList),
    nl(SDPFile_Stream), nl(SDPFile_Stream).
    
%-------------------------------------------------------------------------------

write_pre_hyp_part(_SimplifiedVcgFile_Stream) :-
        \+ path_functions,
        !.

write_pre_hyp_part(_SimplifiedVcgFile_Stream) :-
        found_contradiction,
        !.

write_pre_hyp_part(SimplifiedVcgFile_Stream) :-
        !,
        write(SimplifiedVcgFile_Stream, '      Traversal condition:'),
        nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

write_hypotheses(SimplifiedVcgFile_Stream) :-
        found_contradiction,
        !,
        (

           path_functions,
           write(SimplifiedVcgFile_Stream, '          Path eliminated.  (Contradictory traversal condition)')
	; 
           write(SimplifiedVcgFile_Stream, '*** true .   /* contradiction within hypotheses. */'),
           nl(SimplifiedVcgFile_Stream), nl(SimplifiedVcgFile_Stream)
        ),
        !,
        nl(SimplifiedVcgFile_Stream).

write_hypotheses(SimplifiedVcgFile_Stream) :-
        \+ path_functions,
        proved_all_conclusions,
        proved_by_user_rules,
        write(SimplifiedVcgFile_Stream, '*** true .   /* proved using user-defined proof rules. */'),
        nl(SimplifiedVcgFile_Stream),
        !,
        nl(SimplifiedVcgFile_Stream).

write_hypotheses(_SimplifiedVcgFile_Stream) :-
        \+ path_functions,
        proved_all_conclusions,
        !.
write_hypotheses(SimplifiedVcgFile_Stream) :-
        retractall(hn(_)),
        retractall(nhn(_)),
        asserta(hn(0)),
        asserta(nhn(0)),
        !,
        repeat,
           get_next_hypothesis(H),
           process_next_hypothesis(SimplifiedVcgFile_Stream, H),
           % until
        H = '$DONE',
        !.

%-------------------------------------------------------------------------------

get_next_hypothesis(H) :-
        get_next_hn(N),
        fetch_hypothesis(N, H),
        !.

%-------------------------------------------------------------------------------

get_next_hn(N) :-
        retract(hn(K)),
        N is K+1,
        assertz(hn(N)),
        !.

%-------------------------------------------------------------------------------

fetch_hypothesis(N, _H) :-
        know_eliminated(N),
        !,
        fail.

fetch_hypothesis(N, H) :-
        fetch_complexities(hyp, N, X, S, SS),
        fetch_minimum_complexity(hyp, N, X, S, SS, H),
        !.

fetch_hypothesis(N, H) :-
    % Default if no complexity facts for hypotheses added later on.
    get_hyp(H, x, N),
    !.

fetch_hypothesis(N, '$DONE') :-
        max_hyp_no(MAX),
        N > MAX,
        !.

%-------------------------------------------------------------------------------

fetch_complexities(HorC, N, X, S, SS) :-
        complexity_fact(HorC, N, x, X),
        (
           complexity_fact(HorC, N, s, S)
        ;
           S = []
        ),
        !,
        (
           complexity_fact(HorC, N, ss, SS)
        ;
           SS = []
        ),
        !.

%-------------------------------------------------------------------------------

fetch_minimum_complexity(hyp, N, X, S, SS, HYP) :-
        minimum_choice(X, S, SS, CHOICE),
        !,
        get_hyp(HYP, CHOICE, N),
        !.

fetch_minimum_complexity(conc, N, X, S, SS, HYP) :-
        minimum_choice(X, S, SS, CHOICE),
        !,
        get_conc(HYP, CHOICE, N),
        !.

fetch_minimum_complexity(HorC, _N, _X, _S, _SS, _HYP) :-
       show_error('HorC is not hyp or conc but is ~a in fetch_minimum_complexity.', [HorC]).

%-------------------------------------------------------------------------------

minimum_choice(_X, [], [], x) :- !.

minimum_choice(X, S,  [], C) :-
        !,
        (
           X =< S + 1, C = x
        ;
           C = [s, _]
        ),
        !.
minimum_choice(X, [], SS, C) :-
        !,
        (
           X =< SS + 1, C = x
        ;
           C = ss
        ),
        !.
minimum_choice(X, S,  SS, C) :-
        (
           X =< SS + 1,
           (
              X =< S + 1, C = x
           ;
              C = [s, _]
           )
        ;
           (
              SS =< S, C = ss
           ;
              C = [s, _]
           )
        ),
        !.

%-------------------------------------------------------------------------------
%===============================================================================
% process_next_hypothesis(+Stream, +Hyp_Term)
%-------------------------------------------------------------------------------
% Write the hypothesis Hyp_Term to the output stream.
%===============================================================================

process_next_hypothesis(SimplifiedVcgFile_Stream, '$DONE') :-
    nhn(0),
    !,
    write_unit_hyp_part(SimplifiedVcgFile_Stream).

process_next_hypothesis(_SimplifiedVcgFile_Stream, '$DONE') :-
    !.

process_next_hypothesis(_SimplifiedVcgFile_Stream, true) :-
    !.

process_next_hypothesis(SimplifiedVcgFile_Stream, H) :-
    get_next_nhn(N),
    get_switch_renum(OnOrOff),
    process_next_hypothesis_x(SimplifiedVcgFile_Stream, N, H, OnOrOff),
    !.

%===============================================================================
% process_next_hypothesis_x(+Stream, +HypNum_Int, +Hyp_Term, +Renum_OnOrOff)
%-------------------------------------------------------------------------------
% Use the actual hypothesis number (saved in hn) if Renum_OnOrOFf is 'off';
% Otherwise, use the hypothesis number HypNum_Int .
%===============================================================================

process_next_hypothesis_x(SimplifiedVcgFile_Stream, N, H, on) :-
    write_next_hypothesis(SimplifiedVcgFile_Stream, N, H),
    !.

process_next_hypothesis_x(SimplifiedVcgFile_Stream, _N1, H, off) :-
    hn(N),
    write_next_hypothesis(SimplifiedVcgFile_Stream, N, H),
    !.

%-------------------------------------------------------------------------------

get_next_nhn(N) :-
        retract(nhn(K)),
        N is K+1,
        assertz(nhn(N)),
        !.

%-------------------------------------------------------------------------------

write_unit_hyp_part(SimplifiedVcgFile_Stream) :-
        path_functions,
        !,
        write(SimplifiedVcgFile_Stream, '        true .    {path is always traversed.}'),
        nl(SimplifiedVcgFile_Stream).

write_unit_hyp_part(SimplifiedVcgFile_Stream) :-
        write(SimplifiedVcgFile_Stream, 'H1:    true .'),
        nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

write_next_hypothesis(SimplifiedVcgFile_Stream, N, H) :-
        path_functions,
        !,
        out_number_rj(SimplifiedVcgFile_Stream, N),
        print(SimplifiedVcgFile_Stream, H),
        write(SimplifiedVcgFile_Stream, ' .'),
        nl(SimplifiedVcgFile_Stream).

write_next_hypothesis(SimplifiedVcgFile_Stream, N, H) :-
        write(SimplifiedVcgFile_Stream, 'H'),
        out_number_lj(SimplifiedVcgFile_Stream, N),
        print(SimplifiedVcgFile_Stream, H),
        write(SimplifiedVcgFile_Stream, ' .'),
        nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

write_pre_conc_part(_SimplifiedVcgFile_Stream) :-
        found_contradiction,
        !.

write_pre_conc_part(SimplifiedVcgFile_Stream) :-
        path_functions,
        !,
        write(SimplifiedVcgFile_Stream, '      Action:'),
        nl(SimplifiedVcgFile_Stream).

write_pre_conc_part(_SimplifiedVcgFile_Stream) :-
        proved_all_conclusions,
        !.

write_pre_conc_part(SimplifiedVcgFile_Stream) :-
        write(SimplifiedVcgFile_Stream, '       ->'),
        nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

out_number_rj(SimplifiedVcgFile_Stream, N) :-
        size(N, DIGITS),
        (
           SPACES = 5-DIGITS,
           SPACES >= 0
        ;
           SPACES = 0
        ),
        spacer(SimplifiedVcgFile_Stream, SPACES),
        !,
        write(SimplifiedVcgFile_Stream, N),
        write(SimplifiedVcgFile_Stream, ':  '),
        !.

%-------------------------------------------------------------------------------

out_number_lj(SimplifiedVcgFile_Stream, N) :-
        size(N, DIGITS),
        (
           SPACES is 4-DIGITS,
           SPACES >= 0
        ;
           SPACES = 0
        ),
        write(SimplifiedVcgFile_Stream, N),
        write(SimplifiedVcgFile_Stream, ': '),
        !,
        spacer(SimplifiedVcgFile_Stream, SPACES),
        !.

%-------------------------------------------------------------------------------

size(N, 1)  :- N < 10,    !.
size(N, 2)  :- N < 100,   !.
size(N, 3)  :- N < 1000,  !.
size(N, 4)  :- N < 10000, !.
size(_N, 5) :-            !.

%-------------------------------------------------------------------------------

write_conclusions(_SimplifiedVcgFile_Stream) :-
        found_contradiction,
        !.

write_conclusions(_SimplifiedVcgFile_Stream) :-
        proved_all_conclusions,
        proved_by_user_rules,
        !.

write_conclusions(SimplifiedVcgFile_Stream) :-
        path_functions,
        !,
        get_conc(X, _, 1),
        write_path_action(SimplifiedVcgFile_Stream, X),
        write(SimplifiedVcgFile_Stream, ' .'),
        nl(SimplifiedVcgFile_Stream),
        !.

write_conclusions(SimplifiedVcgFile_Stream) :-
        retractall(hn(_)),
        retractall(nhn(_)),
        asserta(hn(0)),
        asserta(nhn(0)),
        !,
        repeat,
           get_next_conclusion(C),
           process_next_conclusion(SimplifiedVcgFile_Stream, C),
           % until
        C = '$DONE',
        !.

%-------------------------------------------------------------------------------

write_path_action(SimplifiedVcgFile_Stream, X & Y) :-
        !,
        write_path_action(SimplifiedVcgFile_Stream, X),
        !,
        write(SimplifiedVcgFile_Stream, ' &'),
        nl(SimplifiedVcgFile_Stream),
        !,
        write_path_action(SimplifiedVcgFile_Stream, Y),
        !.

write_path_action(SimplifiedVcgFile_Stream, X := Y) :-
        spacer(SimplifiedVcgFile_Stream, 8),
        print(SimplifiedVcgFile_Stream, X),
        write(SimplifiedVcgFile_Stream, ' := '),
        (
           simplify(Y, Z)
        ;
           Z = Y
        ),
        !,

        print(SimplifiedVcgFile_Stream, Z),
        !.
write_path_action(SimplifiedVcgFile_Stream, []) :-
        write(SimplifiedVcgFile_Stream, '        unit function'),
        !.

%-------------------------------------------------------------------------------

get_next_conclusion(C) :-
        get_next_hn(N),
        fetch_conclusion(N, C),
        !.

%-------------------------------------------------------------------------------

fetch_conclusion(N, _C) :-
        get_proved_conc(N),
        !,
        fail.

fetch_conclusion(N, C) :-
        fetch_complexities(conc, N, X, S, SS),
        fetch_minimum_complexity(conc, N, X, S, SS, C),
        !.

fetch_conclusion(N, C) :-
        get_conc(C, x, N),  % Default if no complexity facts,
        !.                  % for conclusions added later on.
fetch_conclusion(N, '$DONE') :-
        max_conc_no(MAX),
        N > MAX,
        !.

%-------------------------------------------------------------------------------

process_next_conclusion(SimplifiedVcgFile_Stream, '$DONE') :-
        nhn(0),
        !,
        write_unit_conc_part(SimplifiedVcgFile_Stream).

process_next_conclusion(SimplifiedVcgFile_Stream, '$DONE') :-
        nl(SimplifiedVcgFile_Stream),
        !.

% Do not renumber conclusions.
process_next_conclusion(SimplifiedVcgFile_Stream, C) :-
        renumber_conclusions(off),
        get_next_nhn(_DUMMY),
        hn(N),
        write_next_conclusion(SimplifiedVcgFile_Stream, N, C),
        !.

% Do renumber conclusions. (This is the default)
process_next_conclusion(SimplifiedVcgFile_Stream, C) :-
        renumber_conclusions(on),
        get_next_nhn(N),
        write_next_conclusion(SimplifiedVcgFile_Stream, N, C),
        !.

%-------------------------------------------------------------------------------

write_unit_conc_part(SimplifiedVcgFile_Stream) :-
        path_functions,
        !,
        write(SimplifiedVcgFile_Stream, '          (unit action: no variables affected)'),
        nl(SimplifiedVcgFile_Stream).

write_unit_conc_part(SimplifiedVcgFile_Stream) :-
        write(SimplifiedVcgFile_Stream, '*** true .          /* all conclusions proved */'),
        nl(SimplifiedVcgFile_Stream), nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

write_next_conclusion(SimplifiedVcgFile_Stream, N, C) :-
        write(SimplifiedVcgFile_Stream, 'C'),
        out_number_lj(SimplifiedVcgFile_Stream, N),
        nhn(UndischargedConcs_Int),
        asserta(max_written_conc_no(UndischargedConcs_Int)),
        print(SimplifiedVcgFile_Stream, C),
        write(SimplifiedVcgFile_Stream, ' .'),
        nl(SimplifiedVcgFile_Stream).

%-------------------------------------------------------------------------------

proved_all_conclusions :-
        \+ (get_conc(_, _, N), \+ get_proved_conc(N)),
        maybe_issue_proved_vc_message,
        !.


%###############################################################################
% END-OF-FILE
