https://github.com/RichardMoot/LinearOne
Raw File
Tip revision: 55540e9a91b7a5c7722775f39ffbf50f5b472a7e authored by Richard Moot on 12 November 2020, 17:47:42 UTC
Update .gitignore
Tip revision: 55540e9
proof_generation.pl
% -*- Mode: Prolog -*-

:- module(proof_generation, [generate_proof/2,
			     generate_sequent_proof/2,
			     generate_natural_deduction_proof/2,
			     generate_nd_proof/2,
			     generate_displacement_proof/2,
			     generate_hybrid_proof/2,
			     eta_reduce/2]).

:- use_module(latex,        [latex_proof/1,
			     latex_nd/1,
			     latex_hybrid/1,
			     latex_displacement/1]).
:- use_module(replace,      [rename_bound_variable/4,
			     rename_bound_variables/2,
			     replace_proofs_labels/4]).
:- use_module(auxiliaries,  [select_formula/4,
			     subproofs/2,
			     rulename/2,
			     is_axiom/1,
			     antecedent/2,
			     identical_prefix/3,
			     identical_postfix/3,
			     identical_lists/2,
			     split_list/4]).
:- use_module(translations, [linear_to_hybrid/2,
			     linear_to_hybrid/3,
			     linear_to_hybrid/4,
			     linear_to_lambek/3,
			     linear_to_displacement/3,
			     displacement_sort/2,
			     simple_to_pure/4,
			     pure_to_simple/3,
			     compute_pros_term/5,
			     formula_type/2]).
:- use_module(options, [generate_diagnostics/1,eta_short/1]).

:- dynamic free_var/2, d_hyp/2.

% =======================================
% =           Proof generation          =
% =======================================

% = generate_sequent_proof(+InitialGraph, +ProofTrace)
%
% generate a sequent proof from the initial graph and proof trace
% given as arguments, and set the output to LaTeX.

generate_sequent_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	latex_proof(Proof).


% = generate_natural_deduction_proof(+InitialGraph, +ProofTrace)
%
% generate a natural deduction proof from the initial graph and proof trace
% given as arguments, and set the output to LaTeX.

generate_natural_deduction_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	sequent_to_nd(Proof, NDProof),
	latex_proof(NDProof).

% = generate_nd_proof(+InitialGraph, +ProofTrace)
%
% as generate_natural_deduction_proof/2, but output the natural deduction
% proof with implicit antecedents and explicit hypothesis cancellation.

generate_nd_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	sequent_to_nd(Proof, NDProof),
	latex_nd(NDProof).

% = generate_hybrid_proof(+InitialGraph, +ProofTrace)
%
% generate a proof in hybrid type-logical grammars based on the first-order
% linear logic proof. This predicate presupposed the lexical entries are
% all hybrid lexical entries.

generate_hybrid_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	sequent_to_nd(Proof, NDProof),
	nd_to_hybrid(NDProof, HProof),
	latex_hybrid(HProof).

% = generate_displacement_proof(+InitialGraph, +ProofTrace)
%
% generate a displacement calculus proof (in natural deduction)
% from the first-order natural deduction proof

generate_displacement_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	sequent_to_nd(Proof, NDProof),
	latex_nd(NDProof),
	nd_to_displacement(NDProof, DProof),
	latex_displacement(DProof).

% = generate_proof(+InitialGraph, +ProofTrace)
%
% generate a sequent proof, natural deduction proof and hybrid proof of the
% same (hybrid) sequent.

generate_proof(Graph, Trace) :-
	node_proofs(Graph, Proofs),
	combine_proofs(Trace, Proofs, Proof),
	sequent_to_nd(Proof, NDProof),
    (
        /* use presence of (hybrid grammar) lex/4 predicate as indication */
	/* that the current grammar is a hybrid grammar */    
	current_predicate(lex/4),
	nd_to_hybrid(NDProof, HProof)
    ->	
	latex_hybrid(HProof)
     ;
        true
     ),
    (
        nd_to_displacement(NDProof, DisplacementProof)
    ->
        latex_displacement(DisplacementProof)
    ;
        true
    ),
        latex_nd(NDProof),
	latex_proof(Proof).

combine_proofs([], [Proof], Proof).
combine_proofs([N0-par(N1)|Rest], Ps0, Proof) :-
	selectchk(N0-P0, Ps0, Ps1),
	selectchk(N1-P1, Ps1, Ps2),
	combine(P0, P1, N0, N1, P2),
	replace_proofs_labels([P2|Ps2], N0, N1, Ps),
	!,
	combine_proofs(Rest, Ps, Proof).
combine_proofs([N0-univ(V,N1)|Rest], Ps0, Proof) :-
        selectchk(N0-P0, Ps0, Ps1),
	selectchk(N1-P1, Ps1, Ps2),
	combine_univ(P0, P1, N0, N1, V, P2),
	replace_proofs_labels([P2|Ps2], N0, N1, Ps),
	!,
	combine_proofs(Rest, Ps, Proof).
combine_proofs([ax(N1,AtV1,AtO1,N0,AtV0,AtO0)|Rest], Ps0, Proof) :-
	select_neg_axiom(Ps0, Ps1, AtV1, AtO1, _-CL, A, _-LeftProof),
	A = _-at(_, AtV0, AtO0, _),
	proof_diagnostics('~NNeg (~D) ~D,~D:~2n', [N0, AtV0,AtO0], LeftProof),
	select_pos_axiom(Ps1, Ps2, AtV0, AtO0, Gamma, _-CR, _-RightProof),
	Gamma = [_-at(_, AtV1, AtO1, _)],
	proof_diagnostics('~NPos (~D) ~D,~D:~2n', [N1, AtV1, AtO1], RightProof),
	RightProof = rule(_, Ant, D, _),
	LeftProof = rule(_, Gamma0, _, _),
	split_antecedent(Ant, AtV1, AtO1, Delta1, Delta2),
        append(Delta1, Gamma0, GDP1),
	append(GDP1, Delta2, GDP),
	print_diagnostics('~NUnifier: ~@~2n', [print_unifier(CL,CR)]),
	unify_atoms(_-CL, _-CR),
	try_cut_elimination_right(LeftProof, RightProof, GDP, D, Gamma0, _-CL, _-CR, Rule),
	proof_diagnostics('~NResults (~D):~2n', [N0], Rule),
	diagnostics_rule,
	replace_proofs_labels([N0-Rule|Ps2], N1, N0, Ps),
	!,
	combine_proofs(Rest, Ps, Proof).
combine_proofs([Next|_], CurrentProofs, Proof) :-
	/* dump all partial proofs in case of failure (useful for inspection) */
	format(user_error, '~N{Error: proof generation failed!}~nNext:~p~2n', [Next]),
	numbervars(CurrentProofs, 0, _),
	keysort(CurrentProofs, SortedProofs),
	/* exploits the fact that the top-level is a failure-driven loop */
	member(N-Proof, SortedProofs),
        format(latex, '~2n ~D. ', [N]).

% = split_antecedent(+GammaADelta, +V, +O, Gamma, Delta)
%
% split antecedent GammaADelta at the atomic formula indicated by identifier V, O returning
% the formulas before it in Gamma and thos after it in Delta.

split_antecedent([A|As], V, O, Bs, Delta) :-
   (
	A = _-at(_,AtV,AtO,_),
	AtV == V,
	AtO == O
   ->		       
        Bs = [],
	Delta = As
   ;			
        Bs = [A|Bs0],
        split_antecedent(As, V, O, Bs0, Delta)
   ).			

% = split_antecedent(+GammaADelta, +A, Gamma, Delta)
%
% split antecedent GammaADelta at the formula A returning
% the formulas before it in Gamma and thos after it in Delta.

split_antecedent([A0|As], A, Bs, Delta) :-
   (
        same_formula(A0, A)		
    ->
	Bs = [],
	Delta = As
    ;
        Bs = [A0|Bs0],
        split_antecedent(As, A, Bs0, Delta)
    ).
    
% = trivial_cut_elimination(+LeftSubProof, +RightSubProof, +ConclusionAntecedent, +ConclusionSuccedent, -NewProof)

trivial_cut_elimination(P1, P2, GDP, C, rule(Nm, GDP, C, R)) :-
        is_axiom(P1),
        !,
	rulename(P2, Nm),
        subproofs(P2, R).
trivial_cut_elimination(P1, P2, GDP, C, rule(Nm, GDP, C, R)) :-
        is_axiom(P2),
        !,
	rulename(P1, Nm),		    
        subproofs(P1, R).
trivial_cut_elimination(P1, P2, GDP, C, rule(cut, GDP, C, [P2,P1])).

% = try_cut_elimination_left(+LeftProof, +RightProof, +GammaDelta, +Delta1, +Delta2, +A, +CL, +CR, -Proof)
%
% try to perform cut elimination of C (occurring as CL in LeftProof and as CR in RightProof) obtaining a Proof
% which conclusion GammaDelta |- A.
% More specifically, the RightProof has conclusion Delta1, CR, Delta2 |- A, LeftProof has conclusion Gamma |- CL
% and GammaDelta is equal to Delta1, Gamma, Delta2

try_cut_elimination_left(LeftProof, RightProof, GammaDelta, Delta1, Delta2, A, _-CL, _-CR, Proof) :-
	turbo_cut_elimination_left(LeftProof, RightProof, Delta1, Delta2, A, CL, CR, Proof0),
	!,
	rulename(LeftProof, LeftN),
	rulename(RightProof, RightN),
	update_proof_cheat(Proof0, GammaDelta, A, Proof, LeftN, RightN).
try_cut_elimination_left(LeftProof, RightProof, GammaDelta, _, _, A, _, _, rule(cut, GammaDelta, A, [LeftProof,RightProof])).

% = try_cut_elimination_right(+LeftProof, +RightProof, +GammaDelta, +A, +Gamma, +CL, +CR, -Proof)
%
% try to perform cut elimination of C (occurring as CL in LeftProof and as CR in RightProof) obtaining a Proof
% with conclusion GammaDelta |- A.
% The subproofs are of Gamma |- CL and of  Delta1, CR, Delta2 |- A

try_cut_elimination_right(LeftProof, RightProof, GammaDelta, A, Gamma, _-CL, _-CR, Proof) :-
	turbo_cut_elimination_right(RightProof, LeftProof, Gamma, CL, CR, Proof0),
	!,
	rulename(LeftProof, LeftN),
	rulename(RightProof, RightN),
	update_proof_cheat(Proof0, GammaDelta, A, Proof, LeftN, RightN).
try_cut_elimination_right(LeftProof, RightProof, GammaDelta, A, _Gamma, _CL, _CR, rule(cut, GammaDelta, A, [LeftProof,RightProof])).

% = as the name indicates, this predicate needs to be subsumed by the main cut elimination predicates later

update_proof_cheat(rule(Nm, Gamma0, _, Rs), Gamma, A, rule(Nm, Gamma, A, Rs), LeftN, RightN) :-
   (	
	generate_diagnostics(true),
	Gamma0 \= Gamma
    ->
        format(user_error, '~n= Computed ~w ~w =~n', [LeftN,RightN]),
	print_antecedent(Gamma0),
        format(user_error, '~n= Correct =~n', []),	
	print_antecedent(Gamma)    
    ;
        true
    ).		   


print_antecedent(Ant) :-
	print_antecedent(Ant, 0).

print_antecedent([], _).
print_antecedent([A|As], N0) :-
	copy_term(A, AA),
	numbervars(AA, N0, N),
	format(user_error, '~p~n', [AA]),
	print_antecedent(As, N).
	
%                      CL |- CL             Gamma |- CR
%                         .                       .
%                         .                       .
%  Gamma |- CR  Delta, CL |- A        Gamma, Delta |- A
%
% the proof on the left keeps CL constant in its right branch; in the proof of the right, we replace
% CL by Gamma throughout (and CL in the succedent by CR).

turbo_cut_elimination_left(rule(Nm, Gamma, _-CL, Rs0), RightProof, Delta1, Delta2, A, CL, CR, Proof) :-
    (
        Gamma = [_-CL], Rs0 = []
    ->
	/* reached axiom */     
	Proof = RightProof    
    ;				      		
        /* add Delta to Gamma and replace CL by A */
        append(Delta1, Gamma, GammaDelta1),
	append(GammaDelta1, Delta2, GammaDelta),
	Proof = rule(Nm, GammaDelta, A, Rs),
	turbo_cut_elimination_left1(Rs0, RightProof, Delta1, Delta2, A, CL, CR, Rs)
    ).

turbo_cut_elimination_left1([R0|Rs0], RightProof, Delta1, Delta2, A, CL0, CR, [R|Rs]) :-
    (
	R0 = rule(_, _,_-CL, _),
	same_formula1(CL0, CL)
    ->
	Rs = Rs0,
	turbo_cut_elimination_left(R0, RightProof, Delta1, Delta2, A, CL, CR, R)
    ;		     
        R = R0,
        turbo_cut_elimination_left1(Rs0, RightProof, Delta1, Delta2, A, CL0, CR, Rs)
    ).

%     CR |- CR                               Delta, CL |- A
%        .                                             .
%        .                                             .
%  Gamma |- CR  Delta, CL |- A            Delta, Gamma |- A
%
% the proof on the left transforms CR to Gamma without changing the succedent; we can
% use this same sequence of rules on the right to transform (replace) CL by the
% successive antecedents of the left proof

turbo_cut_elimination_right(rule(Nm, Delta, A, Rs0), LeftProof, Gamma, CL, CR, Proof) :-
    (
        Delta = [_-CL0], same_formula1(CL0, CL), Rs0 = []
    ->
	/* reached axiom */     
	Proof = LeftProof    
    ;				      		
        /* replace CL in the antecedent by Gamma */
        split_antecedent(Delta, _-CL, Delta1, Delta2),
	%append(Delta1, [_-CL|Delta2], Delta),
	append(Delta1, Gamma, GammaDelta1),
	append(GammaDelta1, Delta2, GammaDelta),
	Proof = rule(Nm, GammaDelta, A, Rs),
	turbo_cut_elimination_right1(Rs0, LeftProof, Gamma, CL, CR, Rs)
    ).

% = proceed to the subproof containing CR
turbo_cut_elimination_right1([R0|Rs0], LeftProof, Gamma, CL0, CR, [R|Rs]) :-
    (
	antecedent_member(CL0, CL, R0)
    ->
	Rs = Rs0,
	turbo_cut_elimination_right(R0, LeftProof, Gamma, CL, CR, R)
    ;		     
        R = R0,
        turbo_cut_elimination_right1(Rs0, LeftProof, Gamma, CL0, CR, Rs)
    ).

antecedent_member(F, rule(_, Gamma, _, _)) :-
	antecedent_member(F, _, Gamma).

antecedent_member(F0, F, rule(_, Gamma, _, _)) :-
	antecedent_member1(Gamma, F0, F).

antecedent_member1([_-G|Gs], F0, F) :-
   (
	same_formula1(F0, G)
   ->
        F = G
   ;		       
        antecedent_member1(Gs, F0, F)
   ).

% = combine_univ(+Proof1, +Proof2, +Node1, +Node2, +VariableNumber, -Proof)
%
% combine Proof1 and Proof2 into Proof using a unary par contraction (with eigenvariable
% VariableNumber) which links Node1 to Node2

% = left rule for existential quantifier
combine_univ(P1, P2, N0, N1, V, N1-Rule) :-
        P1 = rule(_, Gamma, N0-exists(var(V),N1-A), _),
	P2 = rule(_, Delta0, C, _),
	!,
	/* TODO: guarantee this is the same formula occurrence, split_antecedent is too strict of a condition */
	/* Q: are the node numbers enough to guarantee this? Verify! */
	append(Delta1, [N1-A|Delta2], Delta0),
	%split_antecedent(Delta0, _-A, Delta1, Delta2), 
	append(Delta1, [N1-exists(var(V),N1-A)|Delta2], Delta),
	append(Delta1, Gamma, GD1),
	append(GD1, Delta2, GD),
	/* try to create a cut-free proof */
	try_cut_elimination_left(P1, rule(el, Delta, C, [P2]), GD, Delta1, Delta2, C, N0-exists(var(V),N1-A), N0-exists(var(V),N1-A), Rule).

% = right rule for universal quantifier
combine_univ(P1, P2, N0, N1, V, N1-Rule) :-
        P2 = rule(_, Gamma, N1-A, _),
	P1 = rule(_, Delta, C, _),
	/* TODO: guarantee this is the same formula occurrence, split_antecedent is too strict of a condition */
	/* Q: are the node numbers enough to guarantee this? Verify! */
	append(Delta0, [N0-forall(var(V),N1-A)|Delta1], Delta),
	%split_antecedent(Delta, _-forall(var(V),N1-A), Delta0, Delta1),
	append(Delta0, Gamma, GD0),
	append(GD0, Delta1, GD),
	/* try to create a cut-free proof */
	try_cut_elimination_right(rule(fr,Gamma,N1-forall(var(V),N1-A), [P2]), P1, GD, C, Gamma, N0-forall(var(V),N1-A), N0-forall(var(V),N1-A), Rule).

% = combine(+Proof1, +Proof2, +Node1, +Node2, -Proof)
%
% combine Proof1 and Proof2 into Proof using a binary par contraction which links Node1
% to Node2 (since this is a valid contraction, the two edges leaving Node1 must arrive
% in the same node Node2)

% = left rule for product
combine(P1, P2, N0, N1, N1-Rule) :-
	P1 = rule(_, Gamma, N0-p(N1-A, N1-B), _),
        P2 = rule(_, Delta0, C, _),
	!,
	/* TODO: guarantee this is the same formula occurrence, split_antecedent is too strict of a condition */
	/* Q: are the node numbers enough to guarantee this? Verify! */
	select_same_formula(N1, B, Delta0, Delta1),
	select_same_formula(N1, A, Delta1, Delta),
	replace_formula(A, N1, N1-p(N1-A,N1-B), Delta1, Delta2),
	append(Delta3, [N1-p(N1-A,N1-B)|Delta4], Delta2),
	append(Gamma, Delta, GD),		  
	/* try to create a cut-free proof */
	try_cut_elimination_left(P1, rule(pl, Delta2, C, [P2]), GD, Delta3, Delta4, C, N0-p(N1-A, N1-B), N0-p(N1-A, N1-B), Rule).

% = right rule for implication
combine(P1, P2, N0, N1, N1-Rule) :-
	P1 = rule(_, Gamma, A, _),
	P2 = rule(_, Delta0, N1-D, _),	
	/* TODO: guarantee this is the same formula occurrence, split_antecedent is too strict of a condition */
	/* Q: are the node numbers enough to guarantee this? Verify! */
	append(Gamma0, [N0-impl(N1-C,N1-D)|Gamma1], Gamma),
%	split_antecedent(Gamma, N0-impl(N1-C,N1-D), Gamma0, Gamma1),
	select_same_formula(N1, C, Delta0, Delta),
	append(Gamma0, Delta, GD0),
	append(GD0, Gamma1, GD),
	/* try to create a cut-free proof */
	try_cut_elimination_right(rule(ir, Delta, N0-impl(N1-C,N1-D), [P2]), P1, GD, A, Delta, N0-impl(N1-C,N1-D), N0-impl(N1-C,N1-D), Rule).

% = unify_atoms(+Atom1, +Atom2)
%
% true if Atom1 and Atom2 unify when disregarding the unique two-integer
% identifiers

unify_atoms(_-at(A, _, _, Vs), _-at(A, _, _, Vs)).

% = same_formula(+Formula1, +Formula2)
%
% true if Formula1 and Formula2 are the same when disregarding the subformula
% numbering

same_formula(_-F0, _-F) :-
	same_formula1(F0, F).

same_formula1(F0, F) :-
	/* variable subformulas unify */
	var(F0),
        !,
	F0 = F.
same_formula1(F0, F) :-
	var(F),
	!,
	F = F0.
same_formula1(at(A,Id1,Id2,_), at(A,Id3,Id4,_)) :-
	/* demand strict identity of atoms */
	Id1 == Id3,
	Id2 == Id4.
same_formula1(forall(X,F0), forall(Y,F)) :-
	X == Y,
	same_formula(F0, F).
same_formula1(exists(X,F0), exists(Y,F)) :-
	X == Y,
	same_formula(F0, F).
same_formula1(impl(A0,B0), impl(A,B)) :-
	same_formula(A0, A),
	same_formula(B0, B).
same_formula1(p(A0,B0), p(A,B)) :-
	same_formula(A0, A),
	same_formula(B0, B).

%

same_formula2(F0, F) :-
	/* variable subformulas unify */
	var(F0),
        !,
	F0 = F.
same_formula2(F0, F) :-
	var(F),
	!,
	F = F0.
same_formula2(at(A,Id1,Id2,Vs), at(A,Id3,Id4,Vs)) :-
	/* demand strict identity of atoms and unify variables */
	Id1 == Id3,
	Id2 == Id4.
same_formula2(forall(X,_-F0), forall(Y,_-F)) :-
	X = Y,
	same_formula2(F0, F).
same_formula2(exists(X,_-F0), exists(Y,_-F)) :-
	X = Y,
	same_formula2(F0, F).
same_formula2(impl(_-A0,_-B0), impl(_-A,_-B)) :-
	same_formula2(A0, A),
	same_formula2(B0, B).
same_formula2(p(_-A0,_-B0), p(_-A,_-B)) :-
	same_formula2(A0, A),
	same_formula2(B0, B).


%

select_same_formula(N, F, L0, L) :-
	select(N-F0, L0, L),
	same_formula2(F0, F),
	!.

% = select_neg_axiom(+InProofs, -OutProofs, +Vertex, +Order, -C, -A, -Proof)
%
% select the negative atomic formula indicated by the unique identifier Vertex-Order from InProofs, that is
% we are seeking a Proof with axiom
%
%   C |- A
%
% such that C is the formula indicated by Vertex-Order and OutProofs are the other proofs.

select_neg_axiom([Proof|Ps], Ps, V, O, C, A, Proof) :-
	select_neg_axiom1(Proof, V, O, C, A),
	!.
select_neg_axiom([P|Ps], [P|Rs], V, O, C, A, Proof) :-
	select_neg_axiom(Ps, Rs, V, O, C, A, Proof).

select_neg_axiom1(_-P, V, O, C, A) :-
	select_neg_axiom1(P, V, O, C, A).
select_neg_axiom1(rule(ax, [M-at(At,V2,O2,Vars)], N-at(At,V1,O1,Vars), []), V, O, M-at(At,V2,O2,Vars), N-at(At,V1,O1,Vars)) :-
	V2 == V,
	O2 == O,
	!.
select_neg_axiom1(rule(_, _, _, Rs), V, O, C, A) :-
	select_neg_axiom_list(Rs, V, O, C, A).

select_neg_axiom_list([R|_], V, O, C, A) :-
	select_neg_axiom1(R, V, O, C, A),
	!.
select_neg_axiom_list([_|Rs], V, O, C, A) :-
	select_neg_axiom_list(Rs, V, O, C, A).

% = select_pos_axiom(+InProofs, -Outproof, +Vertex, +Order, -Delta, -A, -Proof)
%
% select the positive atomic indicated by the unique identifier Vertex-Order from InProofs, that is
% we are seeking a Proof with conclusion
%
%   Delta |- A
%
% such that A is the formula indicated by Vertex-Order and OutProofs are the other proofs.

select_pos_axiom([Proof|Ps], Ps, V, O, Delta, A, Proof) :-
	select_pos_axiom1(Proof, V, O, Delta, A),
	!.
select_pos_axiom([P|Ps], [P|Rs], V, O, Delta, A, Proof) :-
	select_pos_axiom(Ps, Rs, V, O, Delta, A, Proof).

select_pos_axiom1(_-P, V, O, Delta, A) :-
	select_pos_axiom1(P, V, O, Delta, A).
select_pos_axiom1(rule(ax, [M-at(At,V2,O2,Vars)], N-at(At,V1,O1,Vars), []), V, O, [M-at(At,V2,O2,Vars)], N-at(At,V1,O1,Vars)) :-
	V1 == V,
	O1 == O,
	!.
select_pos_axiom1(rule(_, _, _, Rs), V, O, Delta, A) :-
	select_pos_axiom_list(Rs, V, O, Delta, A).

select_pos_axiom_list([R|_], V, O, Delta, A) :-
	select_pos_axiom1(R, V, O, Delta, A),
	!.
select_pos_axiom_list([_|Rs], V, O, Delta, A) :-
	select_pos_axiom_list(Rs, V, O, Delta, A).

% = select_ant_formula(+Antecedent, +Vertex, +Order, -Gamma, -A, -Delta)
%
% select the (negative) atomic formula indicated by Vertex-Order from the given Antecedent,
% dividing the antecedent into Gamma, A, Delta (Gamma is represented as a difference list)

select_ant_formula([N-F|Delta], V, O, [], N-at(At,V,O,Vars), Delta) :-
	max_neg(F, at(At,V1,O1,Vars)),
	V1 == V,
	O1 == O,
	!.
select_ant_formula([G|Gs], V, O, [G|Gamma], A, Delta) :-
	select_ant_formula(Gs, V, O, Gamma, A, Delta).

% = node_proofs(+Graph, -Proofs)
%
% for each of the nodes in Graph, retrieve the stored formula and polarity of the node and
% compute the corresponding sequent proof

node_proofs([V|Vs], [P|Ps]) :-
        node_proof1(V, P),
        node_proofs(Vs, Ps).
node_proofs([], []) :-
	diagnostics_rule.

node_proof1(vertex(N0, As, _, _), N0-Proof) :-
        node_formula(N0, Pol, F),
        node_proof2(As, F, N0, Pol, Proof),
	proof_diagnostics('~w. ', [N0], Proof),	
	!.

% = node_proof2(+Atoms, +Formula, +NodeNumber, +Polarity, -Proof)
%
% create a Proof using matching Formula to its Atoms

node_proof2([], F, N, _, rule(ax, [N-F], N-F, [])).
node_proof2([A|As], F, N, Pol, Proof) :-
        node_proof3(Pol, [A|As], F, N, Proof).

node_proof3(pos, L, F, N, Proof) :-
        create_pos_proof(F, N, L, [], Proof).
node_proof3(neg, L, F, N, Proof) :-
        max_neg_noid(F, MN0),
	rename_bound_variables(MN0, MN),
        create_neg_proof(F, N, L, [], MN, Proof).

% = max_neg(+Formula, -Conclusion)
%
% given a negative (antecedent) formula, compute the Conclusion of the proof we are computing for it;
% this is (maximal) subformula we reach following a path of negative subformulas.

max_neg(impl(_,_-F0), F) :-
	!,
	max_neg(F0, F).
max_neg(forall(_,_-F0), F) :-
	!,
	max_neg(F0, F).
max_neg(F, F).

% = max_neg(+Formula, -Conclusion)
%
% as max_neg, but erases the node id if the maximal negative formula is an atom

max_neg_noid(at(At, _, _, FVs), at(At, _, _, FVs)) :-
	!.
max_neg_noid(impl(_,_-F0), F) :-
	!,
	max_neg_noid(F0, F).
max_neg_noid(forall(_,_-F0), F) :-
	!,
	max_neg_noid(F0, F).
max_neg_noid(F, F).

% = create_pos_proof(+NumberedPositiveFormula, +/-AtomsDL, -Proof)

create_pos_proof(N-A, L0, L, Proof) :-
	create_pos_proof(A, N, L0, L, Proof).

% = create_pos_proof(+PositiveFormula, +NodeNumber, +/-AtomsDL, -Proof)

create_pos_proof(at(A,C,N,Vars), M, [pos(A,C,N,_,Vars)|L], L, rule(ax,[M-at(A,_C,_N,Vars)], M-at(A,C,N,Vars), [])) :-
	!.
create_pos_proof(exists(X,N-A), N, L0, L, rule(er, Gamma, N-exists(Y,N-A3), [ProofA])) :-
        !,
	/* rename to make sure bound variable isn't unified */
	rename_bound_variables(A, A2),
        create_pos_proof(A, N, L0, L, ProofA),
        ProofA = rule(_, Gamma, N-A2, _),
	rename_bound_variable(exists(X,N-A2), X, Y, exists(Y,N-A3)).
create_pos_proof(p(N-A,N-B), N, L0, L, rule(pr, GD, N-p(N-A2,N-B2), [P1,P2])) :-
        !,
        create_pos_proof(A, N, L0, L1, P1),
        create_pos_proof(B, N, L1, L, P2),
        P1 = rule(_, Gamma, N-A2, _),
        P2 = rule(_, Delta, N-B2, _),
        append(Gamma, Delta, GD).
% complex (negative) subformula
create_pos_proof(F, N, L, L, rule(ax, [N-F], N-F, [])).

% = create_neg_proof(+NumberedNegativeFormula, +/-AtomsDL, +Goal, -Proof)

create_neg_proof(N-A, L0, L, Neg, Proof) :-
	create_neg_proof(A, N, L0, L, Neg, Proof).

% = create_neg_proof(+NegativeFormula, +NodeNumber, +/-AtomsDL, +Goal, -Proof)

create_neg_proof(at(A,C,N,Vars), M, [neg(A,C,N,_,Vars)|L], L, at(A,C1,N1,Vars), rule(ax, [M-at(A,C,N,Vars)], M-at(A,C1,N1,Vars), [])) :-
        !.
create_neg_proof(impl(N-A0,N-B), N, L0, L, Neg, rule(il, GD, N-Neg, [ProofA,ProofB])) :-
        !,
	remove_formula_indices(A0, A),
	rename_bound_variables(A, AA),
        create_pos_proof(AA, N, L0, L1, rule(Nm, Gamma, N-A0, Rs)),
	create_neg_proof(B, N, L1, L, Neg, ProofB),
	rename_bound_variables(B, B2),
	/* put back the formula indices for the conclusion */
	ProofA = rule(Nm, Gamma, N-A0, Rs),
	ProofB = rule(_, Delta, _, _),
	select_formula(B2, N, Delta, Delta_B),
	append(Gamma, [N-impl(N-A0,N-B2)|Delta_B], GD).
create_neg_proof(forall(X,N-A), N, L0, L, Neg, rule(fl, GammaP, N-Neg, [ProofA])) :-
        !,
	rename_bound_variables(A, A2),
        create_neg_proof(A, N, L0, L, Neg, ProofA),
        ProofA = rule(_, Gamma, _C, _),
	/* rename to make sure bound variables aren't unified */
	replace_formula(A2, N, N-forall(Y,N-A3), Gamma, GammaP),
	rename_bound_variable(forall(X,N-A2), X, Y, forall(Y,N-A3)).
% complex (positive) subformula
create_neg_proof(F, N, L, L, _, rule(ax, [N-F], N-F, [])).

% = remove_formula_indices(+FormulaId, -FormulaNoId)
%
% remove node identifier information on the atomic subformulas of FormulaId
% to produce FormulaNoId

remove_formula_indices(N-F0, N-F) :-
	remove_formula_indices(F0, F).
remove_formula_indices(at(A,_,_,Vars), at(A,_,_,Vars)).
remove_formula_indices(forall(X, A0), forall(X, A)) :-
	remove_formula_indices(A0, A).
remove_formula_indices(exists(X, A0), exists(X, A)) :-
	remove_formula_indices(A0, A).
remove_formula_indices(impl(A0, B0), impl(A, B)) :-
	remove_formula_indices(A0, A),
	remove_formula_indices(B0, B).
remove_formula_indices(p(A0, B0), p(A, B)) :-
	remove_formula_indices(A0, A),
	remove_formula_indices(B0, B).

remove_formula_nodes(_-F0, F) :-
	remove_formula_nodes(F0, F).
remove_formula_nodes(at(A,_,_,Vars), at(A,Vars)).
remove_formula_nodes(forall(X,A0), forall(X, A)) :-
	remove_formula_nodes(A0, A).
remove_formula_nodes(exists(X,A0), exists(X, A)) :-
	remove_formula_nodes(A0, A).
remove_formula_nodes(impl(A0,B0), impl(A,B)) :-
	remove_formula_nodes(A0, A),
	remove_formula_nodes(B0, B).
remove_formula_nodes(p(A0,B0), p(A,B)) :-
	remove_formula_nodes(A0, A),
	remove_formula_nodes(B0, B).


% = sequent_to_nd(+SequentProof, -NaturalDeductionProof)
%
% translate a sequent proof to a natural deduction proof

sequent_to_nd(SequentProof, NDproof) :-
	sequent_to_nd(SequentProof, NDproof, 1, _NewIndex).

sequent_to_nd(_-R0, R, I0, I) :-
	sequent_to_nd(R0, R, I0, I).
sequent_to_nd(rule(ax, [M-A1], N-A2, []), rule(ax, [M-A1], N-A2, []), I, I).

sequent_to_nd(rule(el, Gamma, C, [R]), rule(ee(I1), Gamma, C, [rule(ax,[N1-exists(X,N0-B0)], N1-exists(X,N0-B0), []), Proof]), I0, I) :-
	member(N1-exists(X,N0-B0), Gamma),
	antecedent_member(B0, _B1, R),
	!,
	sequent_to_nd(R, Proof0, I0, I1),
	I is I1 + 1,
	withdraw_hypothesis(Proof0, I1, B0, Proof).

sequent_to_nd(rule(er, Gamma, N-A, [R0]), rule(ei, Gamma, N-A, [R]), I0, I) :-
	sequent_to_nd(R0, R, I0, I).

%             R                   forall(X,B) |- forall(X,B)       Proof0
%                                 --------------------------
%        Gamma, B |- C            forall(X,B) |- B              Gamma, B |- C
%   -----------------------       -------------------------------------------
%   Gamma, forall(X,B) |- C            Gamma, forall(X,B) |- C
%

sequent_to_nd(rule(fl, Gamma0, C, [R]), Proof, I0, I) :-
	% find a formula which is of the form forall(X,B) in the conclusion of the rule
	% and B in the premiss of the rule.
	member(N1-forall(X,N0-B0), Gamma0),
	antecedent_member(B0, B1, R),	
	!,
	sequent_to_nd(R, Proof0, I0, I),
	antecedent(Proof0, GammaB),
	select_same_formula(_NB, B1, GammaB, Gamma),
	GammaDelta = [N1-forall(X,N0-B0)|Gamma],
	try_cut_elimination_right(rule(fe, [N-forall(X,N0-B0)], N0-B1, [rule(ax, [N-forall(X,N-B0)], N1-forall(X,N0-B0), [])]),
				  Proof0, GammaDelta, C, [N-forall(X,N0-B0)], N-B0, N-B1, Proof).

sequent_to_nd(rule(fr, Gamma, N-A, [R0]), rule(fi, Gamma, N-A, [R]), I0, I) :-
	sequent_to_nd(R0, R, I0, I).

%                                         ProofA                        ProofC
%
%      R1              R2               Delta |- A   A -o B |- A -o B
%                                       -----------------------------
%  Delta |- A   Gamma, B |- C               Delta, A -o B |- B        Gamma, B |- C
%  --------------------------               ---------------------------------------
%   Gamma, Delta, A -o B |- C                      Gamma, Delta, A -o B |- C
%

sequent_to_nd(rule(il, Ant, C, [R1,R2]), Proof, I0, I) :-
	sequent_to_nd(R1, ProofA, I0, I1),
	sequent_to_nd(R2, ProofC, I1, I),
	ProofA = rule(_, Delta, _NA0, _),
	ProofC = rule(_, GammaB, _, _),
	member(M-impl(N-A,N-B0), Ant),
	antecedent_member(B0, B, R2),
	select_same_formula(_NB, B, GammaB, Gamma),
	append(Delta, [M-impl(N-A,N-B0)], DeltaAB),
	append(Gamma, DeltaAB, GammaDeltaAB),
	try_cut_elimination_right(rule(ie, DeltaAB, N-B0, [ProofA,rule(ax, [M-impl(N-A,N-B0)], M-impl(N-A,N-B0), [])]),
				  ProofC, GammaDeltaAB, C, DeltaAB, N-B0, N-B, Proof).

sequent_to_nd(rule(ir, Gamma, _-impl(_-A,_-B), [R0]), rule(ii(I1), Gamma, impl(A,B), [R]), I0, I) :-
	sequent_to_nd(R0, R1, I0, I1),
	I is I1 + 1,
        withdraw_hypothesis(R1, I1, A, R).

sequent_to_nd(rule(pl, Gamma, C, [R]), rule(pe(I1), Gamma, C, [rule(ax,[N0-p(N1-A0,N2-B0)], N0-p(N1-A,N2-B), []), Proof]), I0, I) :-
	member(N0-p(N1-A0,N2-B0), Gamma),
	antecedent_member(A0, A, R),
	antecedent_member(B0, B, R),
	!,
	sequent_to_nd(R, Proof0, I0, I1),
	I is I1 + 1,
        withdraw_hypothesis(Proof0, I1, A, Proof1),
	withdraw_hypothesis(Proof1, I1, B, Proof).

sequent_to_nd(rule(pr, Gamma, C, [R1,R2]), rule(pi, Gamma, C, [Proof1, Proof2]), I0, I) :-
	sequent_to_nd(R1, Proof1, I0, I1),
	sequent_to_nd(R2, Proof2, I1, I).


% = eta_reduce(+NDProof, -EtaShortNDProof)
%
% true if EtaShortNDProof is a eta normal form natural deduction proof of
% (first order linear logic) NDProof.

eta_reduce(InProof, OutProof) :-
   (	
	eta_short(true)
   ->
	eta_reduce_all(InProof, OutProof)
   ;
	OutProof = InProof
   ).

% iterate eta_reduce1/2 until the proof stays the same

eta_reduce_all(Proof0, Proof) :-
	eta_reduce1(Proof0, Proof1),
   (
        Proof1 == Proof0
   ->
        Proof = Proof1
   ;
        eta_reduce_all(Proof1, Proof)
   ).

eta_reduce1(N-Proof0, N-Proof) :-
	eta_reduce1(Proof0, Proof).
% TODO: generalize to other product/existential quantifier-like rules
eta_reduce1(rule(ee(I), _, _, [ProofE, ProofC]), Proof) :-
	replace_proof(ProofC, rule(ei, _, _, [rule(hyp(I),_,_, [])]), ProofE, Proof),
	!.
eta_reduce1(rule(pe(I),_, _, [ProofP, ProofC]), Proof) :-
	replace_proof(ProofC, rule(pi, _, _, [rule(hyp(I),_,_, []), rule(hyp(I),_,_,[])]), ProofP, Proof),
	!.
eta_reduce1(rule(NmI, _, _, [rule(NmE,_,_, [Rule])]), Rule) :-
	eta_pair_unary(NmI, NmE),
	!.
eta_reduce1(rule(NmI, _, _, [rule(NmE, _, _, [rule(hyp(I), _, _, []),Rule])]), Rule) :-
	eta_pair_binary(NmI, I, NmE),
	!.
eta_reduce1(rule(NmI, _, _, [rule(NmE, _, _, [Rule,rule(hyp(I), _, _, [])])]), Rule) :-
	eta_pair_binary(NmI, I, NmE),
	!.
eta_reduce1(rule(Nm, Gamma, C, Ps0), rule(Nm, Gamma, C, Ps)) :-
	eta_reduce_list(Ps0, Ps).

eta_reduce_list([], []).
eta_reduce_list([P0|Ps0], [P|Ps]) :-
	eta_reduce1(P0, P),
	eta_reduce_list(Ps0, Ps).

% = eta_pair_unary(?IntroductionRuleName, ?EliminationRuleName)
%
% pairs of introduction/elimination rules with a single premiss (for the elimination rule)

eta_pair_unary(fi, fe).

% = eta_pair_binary(?IntroductionRuleName, ?HypothesisIndex, ?EliminationRuleName)
%
% pairs of introduction/elimination rules with two premisses (for the elimination rule)
% HypothesisIndex is the unique integer corresponding to the withdrawn hypothesis

eta_pair_binary(ii(I), I, ie).
eta_pair_binary(dri(I), I, dre).
eta_pair_binary(dli(I), I, dle).
eta_pair_binary(dri(D,I), I, dre(D)).
eta_pair_binary(dli(D,I), I, dle(D)).
eta_pair_binary(hi(I), I, he).

% = replace_proof(+InProof, +ProofA, +ProofB, -OutProof)
%
% true if OutProof is InProof with the first occurrence of ProofA replaced by ProofB
% fails if there are no occurrences of ProofA in InProof.

replace_proof(Proof1, Proof1, Proof2, Proof2) :-
	!.
replace_proof(rule(Nm, Gamma, C, Rs0), Proof1, Proof2, rule(Nm, Gamma, C, Rs)) :-
	replace_proof_list(Rs0, Proof1, Proof2, Rs).

replace_proof_list([P0|Ps], A, B, [P|Ps]) :-
	replace_proof(P0, A, B, P),
	!.
replace_proof_list([P|Ps0], A, B, [P|Ps]) :-
	replace_proof_list(Ps0, A, B, Ps).

% = max_hypothesis(+Proof, +MaxIn, -MaxOut)
%
% find the hypothesis with the maximal integer index in Proof

max_hypothesis(rule(hyp(I), _, _, _), Max0, Max) :-
	!,
        Max is max(I,Max0).
max_hypothesis(rule(_, _, _, Rs), Max0, Max) :-
	max_hypothesis_list(Rs, Max0, Max).

max_hypothesis_list([], Max, Max).
max_hypothesis_list([R|Rs], Max0, Max) :-
	max_hypothesis(R, Max0, Max1),
	max_hypothesis_list(Rs, Max1, Max).

% = nd_to_hybrid(+NaturalDeductionProof, -HybridProof).
%
% translate a natural deduction first-order linear logic proof into a proof of
% hybrid type-logical grammars.

nd_to_hybrid(Proof0, Proof) :-
	max_hypothesis(Proof0, 0, Max0),
	Max1 is Max0 + 1,
	nd_to_hybrid(Proof0, Max1, Max, Proof),
	numbervars(Proof, Max, _).

nd_to_hybrid(rule(hyp(I), _, C0, []), Max, Max, rule(hyp(I), '$VAR'(I), HF, [])) :-
        /* withdrawn hypothesis with unique identifier I */
	remove_formula_nodes(C0, C),
	linear_to_hybrid(C, HF),
	/* compute the Church type of hybrid formula  HF */
	formula_type(HF, Type),
	retractall(free_var(I, _)),
	assert(free_var(I, Type)).
nd_to_hybrid(rule(ax, _, C0, []), Max0, Max, Rule) :-
        /* HTLG lexicon rule */ 
	remove_formula_nodes(C0, C),
	/* recover lexical lambda term here */
	linear_to_hybrid(C, VarList, _, HF),
	numbervars(VarList, 0, _),
	/* complex manipulations to allow HTLG grammars to use first-order */
	/* quantifiers for things like case */
        get_positions(VarList, N0, _R),
	lexicon:hybrid_lookup(N0, HF0, Lambda0),
	compute_pros_term(Lambda0, HF0, Lambda, Max0, Max),
	!,
    (
        HF0 == HF
    ->
	Rule = rule(ax, Lambda, HF, [])
    ;
        Rule = rule(fe, Lambda, HF, [rule(ax, Lambda, HF0, [])])
    ).
nd_to_hybrid(rule(ie, _, _, [P1,rule(fe, _, _, [P2])]), Max0, Max, Rule) :-
        !,
        /* Lambek calculus elimination rule */
	P2 = rule(_, _, C0, _),
	nd_to_hybrid(P1, Max0, Max1, Proof1),
	nd_to_hybrid(P2, Max1, Max2, Proof2),
	antecedent(Proof1, Term1),
	antecedent(Proof2, Term2),
	remove_formula_nodes(C0, C),
	/* compute the Lambek calculus formula LF corresponding to C */
	linear_to_lambek(C, [_, _], LF),
	/* use this formula LF to infer the relevant rule application */
	lambek_rule(LF, Term1, Term2, Max2, Max, Proof1, Proof2, Rule).
nd_to_hybrid(rule(fi, _, C0, [rule(ii(I), _, _, [P1])]), Max0, Max, rule(Nm, Term, LF, [Proof1])) :-
        /* Lambek calculus introduction rule */
	remove_formula_nodes(C0, C),
	linear_to_lambek(C, [_, _], LF),
	nd_to_hybrid(P1, Max0, Max1, Proof1),
	antecedent(Proof1, Term1),
	retractall(free_var(I, _)),
	compute_pros_term(appl(lambda('$VAR'(I),Term1),epsilon), LF, Term, Max1, Max),
	/* use the main connective of the Lambek calculus formula LF to */
	/* determine the rule name */
        lambek_rule_name(LF, I, Nm).
nd_to_hybrid(rule(ie, _, C0, [P1,P2]), Max0, Max, rule(he, Term, HF, [Proof1,Proof2])) :-
	remove_formula_nodes(C0, C),
	linear_to_hybrid(C, HF),
	nd_to_hybrid(P1, Max0, Max1, Proof1),
	nd_to_hybrid(P2, Max1, Max2, Proof2),
	antecedent(Proof1, Term1),
	antecedent(Proof2, Term2),
	compute_pros_term(appl(Term2,Term1), HF, Term, Max2, Max).
nd_to_hybrid(rule(ii(I), _, C0, [P1]), Max0, Max, rule(hi(I), Term, HF, [Proof1])) :-
	remove_formula_nodes(C0, C),
	linear_to_hybrid(C, HF),
	nd_to_hybrid(P1, Max0, Max1, Proof1),
	antecedent(Proof1, Term0),
	retractall(free_var(I, _)),
	compute_pros_term(lambda('$VAR'(I),Term0), HF, Term, Max1, Max).

get_positions(VarList0, L, R) :-
	msort(VarList0, VarList),
	get_positions1(VarList, L, R).

get_positions1([A,A|Rest], L, R) :-
	!,
	get_positions1(Rest, L, R).
get_positions1([L|Rest], L, R) :-
	get_positions2(Rest, R).

get_positions2([A,A|Rest], R) :-
	!,
	get_positions2(Rest, R).
get_positions2([R|Rest], R) :-
	get_positions3(Rest).

get_positions3([]).
get_positions3([A,A|Rest]) :-
	get_positions3(Rest).

lambek_rule_name(dl(_,_), I, dli(I)).
lambek_rule_name(dr(_,_), I, dri(I)).

lambek_rule(dl(_,A), Term1, Term2, Max0, Max, Proof1, Proof2, rule(dle, Term, A, [Proof1, Proof2])) :-
	compute_pros_term(Term1+Term2, A, Term, Max0, Max).
lambek_rule(dr(A,_), Term1, Term2, Max0, Max, Proof1, Proof2, rule(dre, Term, A, [Proof2, Proof1])) :-
	compute_pros_term(Term2+Term1, A, Term, Max0, Max).

% = withdraw_hypothesis(+InProof, +Index, +Atom, -OutProof)
%
% replace the axiom rule of Atom by a hypothesis rule indexed with I; ensures that when
% the proof is output using latex_nd/1 (instead of latex_proof/1) it will be portrayed
% as
%
% [ Atom ]^{Index}
%
% where Index will be shared with the rule introducing the hypothesis.

withdraw_hypothesis(rule(ax, [N-A0], B, []), I, A, rule(hyp(I), [N-A0], B, [])) :-
	same_formula2(A0, A),
	!.
withdraw_hypothesis(rule(Nm, Ant, F, Rs0), I, A, rule(Nm, Ant, F, Rs)) :-
	withdraw_hypothesis_list(Rs0, I, A, Rs).

withdraw_hypothesis_list([R0|Rs0], I, A, [R|Rs]) :-
   (
	withdraw_hypothesis(R0, I, A, R)
   ->	
        Rs = Rs0
   ;			 
        R = R0,
        withdraw_hypothesis_list(Rs0, I, A, Rs)
   ).


% = nd_to_displacement(+NaturalDeductionProof, -DisplacementCalculusProof).
%
% translate a natural deduction first-order linear logic proof into a proof of
% the Displacement calculus

nd_to_displacement(Rule0, Rule) :-
	nd_to_displacement(Rule0, 0, _, Rule).

nd_to_displacement(rule(hyp(I), _, C0, []), Max0, Max, rule(hyp(I), Label, DF, [])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, Vars, DF),
	vars_to_d_label(Vars, Max0, Max, Label),
	!,
	retractall(d_hyp(I, _)),
	assert(d_hyp(I, Label)).

nd_to_displacement(rule(ax, _, C0, []), Max, Max, rule(ax, [[Word]], DF, [])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, [N0,_], DF),
	/* recover the corresponding word from lexical lookup here */
	lexicon:memo_lookup(N0, Word),
	!.
% Lambek product introduction
nd_to_displacement(rule(ei,_,C0, [rule(pi, _, _, [P1,P2])]), Max0, Max, rule(pi, Label, DF, [Proof1,Proof2])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, _, DF),
	nd_to_displacement(P1, Max0, Max1, Proof1),
	nd_to_displacement(P2, Max1, Max, Proof2),
	Proof1 = rule(_, Label1, _, _),
	Proof2 = rule(_, Label2, _, _),
	d_concat(Label1, Label2, Label),
	!.
% Displacement product introduction
nd_to_displacement(rule(ei,_,C0,[rule(ei,_,_,[rule(pi,_,_,[P1,P2])])]), Max0, Max, rule(pi(LR), Label, DF, [Proof1,Proof2])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, _, DF),
	/* look at the formula to determine whether we found a left-wrap or right-wrap product */
	DF = p(LR,_,_),
	nd_to_displacement(P1, Max0, Max1, Proof1),
	nd_to_displacement(P2, Max1, Max, Proof2),
	Proof1 = rule(_, Label1, _, _),
	Proof2 = rule(_, Label2, _, _),
        ((LR = >) -> d_lwrap(Label1, Label2, Label) ; d_rwrap(Label1, Label2, Label)), 
	!.
% generic elimination rule for the implications
nd_to_displacement(rule(ie,_,C0, [RuleA,RuleAB0]), Max0, Max, Rule) :-
	/* elimination rule for one of the implications */
	/* get the positions of the conclusion of the rule */
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, VarsC, DF),
	/* get the positions of the argument (minor premiss) of the rule */
	RuleA = rule(_, _, A0, _),
	remove_formula_nodes(A0, A),
	linear_to_displacement(A, VarsA, _DA),
	/* use both together to decide which forall elimination rules belong to the */
	/* connective of the main formula of the rule */
	d_implication_elim(RuleAB0, VarsA, VarsC, RuleAB),
	nd_to_displacement(RuleA, Max0, Max1, Proof1),
	nd_to_displacement(RuleAB, Max1, Max, Proof2),
	Proof2 = rule(_, _, MainF, _),
	d_combine_proofs(Proof1, Proof2, MainF, DF, Rule),
	!.
% generic introduction rule for the implications
nd_to_displacement(Rule0, Max0, Max, Rule) :-
	/* introduction rule for one of the implications */
	Rule0 = rule(_, _, C0, _),
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, _Vars, DF),
	mill1_argument(C, A),
	linear_to_displacement(A, VarsA, _DA),
	length(VarsA, Len),
	d_quantifier_number(DF, Len, QN),
	d_implication_intro(Rule0, QN, I, Rule1),
	nd_to_displacement(Rule1, Max0, Max, Proof1),
	d_hyp(I, HLabel),
	antecedent(Proof1, PLabel),
	d_withdraw_hypothesis(DF, I, HLabel, PLabel, Proof1, Rule),
	!.
% = bridge
nd_to_displacement(rule(ei,_,C0, [P1]), Max0, Max, rule(bridge_i, Label, DF, [Proof1])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, _, DF),
	nd_to_displacement(P1, Max0, Max, Proof1),
	antecedent(Proof1, ALabel),
	d_lwrap(ALabel, [[]], Label),
	!.
% = left/right projections
nd_to_displacement(rule(fe, _, C0, [P1]), Max0, Max, rule(Nm, Label, DF, [Proof1])) :-
	remove_formula_nodes(C0, C),
	linear_to_displacement(C, _, DF),
	nd_to_displacement(P1, Max0, Max, Proof1),
	Proof1 = rule(_, ALabel, AF, _),
	d_projection(AF, Nm, ALabel, Label),
	!.

% TODO
% - bridge_e
% - rproj_i
% - lproj_i
% - product elimination rules, Lambek and Displacement

% Lambek: the number of quantifiers is equal to 2*s(A)+1 (ie. the number of string positions of the
% argument minus one
% uparrow: the number of quantifiers is equal to to 2*s(B) (ie. the number of string positions of the
% argument minus two
% downarrow: the number of quantifiers is equal to the number of string positions of the argument
% minus two

d_quantifier_number(dl(_,_), N0, N) :-
	N is N0 - 1.
d_quantifier_number(dr(_,_), N0, N) :-
	N is N0 - 1.
d_quantifier_number(dl(_,_,_), N0, N) :-
	N is N0 - 2.
d_quantifier_number(dr(_,_,_), N0, N) :-
	N is N0 - 2.


mill1_argument(forall(_,A0), A) :-
	mill1_argument(A0, A).
mill1_argument(impl(A,_), A).



d_projection(rproj(_), rproj_e, Label, [[]|Label]).
d_projection(lproj(_), lproj_e, Label0, Label) :-
	append(Label0, [[]], Label).

% = vars_to_d_label(+VarsList, +MaxIn, -MaxOut, -Label)
%
% compute the prosodic label of a hypothesis using only prosodic variables (of the form '$VAR'(N))
% of sort 0, i.e. a hypothesis of sort 1 is assigned the prosodic label '$VAR'(N0),'$VAR'(N1).
% Remember that prosodic labels are represented as lists of lists, where each of the list members
% is a "string" (represented as a list) and with implicit separators between these lists.

vars_to_d_label([_, _], Max0, Max, [['$VAR'(Max0)]]) :-
	!,
	Max is Max0 + 1.
vars_to_d_label([_, _|Vars], Max0, Max, [['$VAR'(Max0)]|Rest0]) :-
	Max1 is Max0 + 1,
	vars_to_d_label(Vars, Max1, Max, Rest0).

% = d_withdraw_hypothesis(+MainFormula, +HypothesisIndex, +HypothesisLabel, +RulePremissLabel, +SubProof, -Proof)
%
% auxliary predicate for the different introduction rules for the implications; combine the labels assigned to the
% premiss of the rule and the withdrawn hypothesis to compute the label for the conclusion of the rule to produce
% the result proof.

d_withdraw_hypothesis(dl(A,B), I, HLabel, PLabel, P1, rule(dli(I), CLabel, dl(A,B), [P1])) :-
	d_concat(HLabel, CLabel, PLabel).
d_withdraw_hypothesis(dr(A,B), I, HLabel, PLabel, P1, rule(dri(I), CLabel, dr(A,B), [P1])) :-
	d_concat(CLabel, HLabel, PLabel).
d_withdraw_hypothesis(dr(>,A,B), I, [[C]], PLabel, P1, rule(dri(>,I), CLabel, dr(>,A,B), [P1])) :-
	/* special treatment here since d_lwrap is tricky when first argument is variable */
	/* TODO: treat cases where we don't have [[C]] but rather [[C],[D],[E],...] */
	PLabel = [PL1|PLs],
	split_list(PL1, C, C1, C2),
	CLabel = [C1,C2|PLs].
d_withdraw_hypothesis(dl(>,A,B), I, HLabel, PLabel, P1, rule(dli(>,I), CLabel, dl(>,A,B), [P1])) :-	
	d_lwrap(HLabel, CLabel, PLabel).
d_withdraw_hypothesis(dr(<,A,B), I, [[C]], PLabel, P1, rule(dri(>,I), CLabel, dr(<,A,B), [P1])) :-
	/* special treatment here since d_lwrap is tricky when first argument is variable */
	/* TODO: treat cases where we don't have [[C]] but rather [[C],[D],[E],...] */
	append(PLs, [PL1], PLabel),
	split_list(PL1, C, C1, C2),
	append(PLs, [C1,C2], CLabel).
d_withdraw_hypothesis(dl(<,A,B), I, HLabel, PLabel, P1, rule(dli(>,I), CLabel, dl(<,A,B), [P1])) :-
	d_rwrap(HLabel, CLabel, PLabel).

% = d_combine_proofs(+Proof1, +Proof2, +MainFormula, +ConclusionFormula, -Proof)
%
% auxiliary predicate for the implication elimination, combines Proof1 and Proof2
% into a Proof of ConclusionFormula using the connective of MainFormula to decide
% the rule and the operation on the strings assigned to the two subproofs.

d_combine_proofs(Proof1, Proof2, MainF, DF, rule(Nm, Label, DF, Ps)) :-
	Proof1 = rule(_, LLabel, _, _),
	Proof2 = rule(_, RLabel, _, _),
	d_combine_proofs(MainF, Nm, LLabel, RLabel, Label, Proof1, Proof2, Ps).

d_combine_proofs(dr(_,_), dre, L1, L2, L, Proof1, Proof2, [Proof2, Proof1]) :-
	d_concat(L2, L1, L).
d_combine_proofs(dl(_,_), dle, L1, L2, L, Proof1, Proof2, [Proof1, Proof2]) :-
	d_concat(L1, L2, L).
d_combine_proofs(dr(>,_,_), dre(>), L1, L2, L, Proof1, Proof2, [Proof2, Proof1]) :-
	d_lwrap(L2, L1, L).
d_combine_proofs(dl(>,_,_), dle(>), L1, L2, L, Proof1, Proof2, [Proof1, Proof2]) :-
	d_lwrap(L1, L2, L).
d_combine_proofs(dr(<,_,_), dre(<), L1, L2, L, Proof1, Proof2, [Proof2, Proof1]) :-
	d_rwrap(L2, L1, L).
d_combine_proofs(dl(<,_,_), dle(<), L1, L2, L, Proof1, Proof2, [Proof1, Proof2]) :-
	d_rwrap(L1, L2, L).

% = d_concat(+Label1, +Label2, -Label)
%
% this is the generalisation of the list concatenation to tuples of lists as
% discussed in Section 3.1 of my Moot (2013).
% The concatenation of 
%
% x_1, ..., x_n    and   y_1, ..., y_m
%
% is x_1, ...., x_ny_1, ...., y_m

d_concat(As, Bs0, Cs) :-
	var(As),
	!,
    (
        Bs0 == []
    ->
        As = Cs
    ;
        Bs0 = [B|Bs],	
	append(As0, [M|Bs], Cs),
	append(A, B, M),
        append(As0, [A], As)
    ).
% base case: append the last element of the first list to the head of the second
% to form Merged, then return the list containing the rest of the items of the
% first list, followed by Merge, followed by the tail of the second list.
d_concat(L1, [First|L2], L) :-
	append(Prefix, [Last], L1),
	append(Last, First, Merged),
	append(Prefix, [Merged|L2], L).

d_lwrap([A0,A|As], Bs, Cs) :-
   (
        var(Bs)
   ->
	d_concat(ABs0, [A|As], Cs),
        d_concat([A0], Bs, ABs0)
   ;
        d_concat([A0], Bs, ABs0),
        d_concat(ABs0, [A|As], Cs)
   ).
%	d_concat(ABs0, [A|As], Cs).
d_rwrap(As, Bs, Cs) :-
	append(Prefix, [Last], As),
	d_concat(Prefix, Bs, PBs0),
	d_concat(PBs0, [Last], Cs).


universal_quantifier_variable(_-F, Q) :-
	universal_quantifier_variable(F, Q).
universal_quantifier_variable(forall(X,_), X).


% recover the list of quantified variables

d_implication_elim(Rule0, ArgVars0, ResultVars, Rule) :-
	/* dl(A,B) */
	append(ArgVars, [_], ArgVars0),
	identical_prefix(ArgVars, _, ResultVars),
	d_implication_elim(ArgVars, Rule0, Rule),
	!.
d_implication_elim(Rule0, [_|ArgVars], ResultVars, Rule) :-
	/* dr(A,B) */
	identical_postfix(_, ArgVars, ResultVars),
	d_implication_elim(ArgVars, Rule0, Rule),
	!.
d_implication_elim(Rule0, [_|ArgVars0], [_|ResultVars], Rule) :-
	/* \uparrow_> */
	append(ArgVars, [_], ArgVars0),
	identical_prefix(ArgVars, _, ResultVars),
	d_implication_elim(ArgVars, Rule0, Rule),
	!.
d_implication_elim(Rule0, [A,_,_|ArgVars], [R|ResultVars], Rule) :-
	A == R,
	/* \downarrow_> */
	identical_postfix(_, ArgVars, ResultVars),
	d_implication_elim([A|ArgVars], Rule0, Rule),
	!.
d_implication_elim(Rule0, [_|ArgVars0], ResultVars0, Rule) :-
	/* \uparrow_< */
	append(ArgVars, [_], ArgVars0),
	append(ResultVars, [_], ResultVars0),
	identical_postfix(_, ArgVars, ResultVars),
	d_implication_elim(ArgVars, Rule0, Rule),
	!.
d_implication_elim(Rule0, ArgVars0, ResultVars0, Rule) :-
	/* \downarrow_< */
	append(ArgVars1, [_,_,Z], ArgVars0),
	append(ResultVars, [XNM], ResultVars0),
	XNM == Z,
	identical_prefix(ArgVars1, _, ResultVars),
	append(ArgVars1, [XNM], ArgVars),
	d_implication_elim(ArgVars, Rule0, Rule),
	!.

d_implication_elim([_|Vs], rule(fe, _, _, [Rule0]), Rule) :-
	!,
	d_implication_elim(Vs, Rule0, Rule).
d_implication_elim([], Rule, Rule).
	

d_implication_intro(rule(fi, _, _, [Rule0]), N0, I, Rule) :-
	N0 > 0,
	N is N0 - 1,
	!,
	d_implication_intro(Rule0, N, I, Rule).
d_implication_intro(rule(ii(I), _, _, [Rule]), 0, I, Rule).

% =======================================
% =             Input/output            =
% =======================================

print_unifier(at(At,_,_,Vs0), at(At,_,_,Vs)) :-
	unifiable(Vs0, Vs, Unifier0),
	copy_term(Unifier0, Unifier),
	numbervars(Unifier, 0, _),
	latex_list(Unifier).

latex_list([]) :-
	format(latex, '[]', []).
latex_list([A|As]) :-
	format(latex, '[', []),
	latex_list1(As, A).

latex_list1([], A) :-
	format(latex, '~p]', [A]).
latex_list1([A|As], B) :-
	format(latex, '~p,', [B]),
	latex_list1(As, A).

print_diagnostics(Msg) :-
	proof_diagnostics(Msg, []).
print_diagnostics(Msg, Vs) :-
   (
	generate_diagnostics(true)
    ->
	format(latex, Msg, Vs)
    ;
        true
    ).


proof_diagnostics(Msg, P) :-
	proof_diagnostics(Msg, [], P).
proof_diagnostics(Msg, Vs, P) :-
   (
	generate_diagnostics(true)
    ->
	format(latex, Msg, Vs),
	latex_proof(P)
    ;
        true
    ).

diagnostics_rule :-
   (
	generate_diagnostics(true)
    ->
	format(latex, '~2n\\hrule~n\\medskip~2n', [])
    ;
        true
    ).
	
back to top