% This file is part of the Attempto Parsing Engine (APE).
% Copyright 2008-2013, Attempto Group, University of Zurich (see http://attempto.ifi.uzh.ch).
%
% The Attempto Parsing Engine (APE) is free software: you can redistribute it and/or modify it
% under the terms of the GNU Lesser General Public License as published by the Free Software
% Foundation, either version 3 of the License, or (at your option) any later version.
%
% The Attempto Parsing Engine (APE) 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Lesser General Public License along with the Attempto
% Parsing Engine (APE). If not, see http://www.gnu.org/licenses/.


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  Resolution of anaphors in discourse representation structures
%
%  N. E. Fuchs, IFI University of Zurich
%
%  May 31, 2005
%
%  Version 110724
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  Resolution of Anaphors
%
%    Resolution is governed by accessibility, closest textual precedence, specificity, and reflexivity.
%
%    Accessibility rules: a discourse referent is accessible from a DRS D if the discourse referent is in D, in a DRS enclosing D, in the antecedent of an 
%    ifthen-DRS with D as consequent, or in a disjunct that precedes D in an or-DRS. 
%
%    If the anaphor is a non-reflexive personal pronoun or a non-reflexive possessive pronoun the resolution algorithm picks the closest preceding accessible 
%    noun phrase that agrees in genus, numerus and person with the anaphor, and that is not the subject of the sentence. If resolution fails, an error situation 
%    arises.
%
%    Reflexive personal and possessive pronouns are resolved by APE that picks the subject of the sentence in which the pronoun occurs if the subject agrees in 
%    genus, numerus and person with the anaphor. If resolution fails, APE generates an error message.
%
%    If the anaphor is a definite noun phrase the resolution algorithm picks the closest preceding accessible noun phrase with matching genus and person and matching 
%    numerus - where a singular anaphor matches a singular countable or a mass antecedent, and a plual anaphor matches a plural countable antecedent. Either all 
%    anaphor conditions are a subset of the antecedent conditions, or a part of the anaphor conditions including the condition of the main noun is a subset of 
%    the  antecedent conditions, while the remaining anaphor conditions match conditions found in the DRS. Complex conditions of the anaphor that lead to sub-DRSs 
%    have to exactly unify with respective sub-DRSs in the antecedent. If resolution fails, the anaphor is treated as a new indefinite noun phrase, and a warning 
%    is generated.
%
%    If the anaphor is a variable the resolution algorithm picks the closest preceding accessible identical bare variable, or the closest preceding accessible 
%    noun phrase that has the variable as opposition. If variables are redefined in the same accsibility range, or if resolution fails, an error situation arises.
%
%    Proper nouns are unique and accessible from everywhere.
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  Representation of Antecedents and Anaphors
%
%    A proper name is represented in the DRS by the single condition
%
%    anaphor(Anaphortype, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, SentenceID, TokenID, Tokens, SentenceSubject)
%
%    where
%
%    - Anaphortype is proper_name
%    - AnaphorID is the number of the noun phrase
%    - AnaphorReferent is named(proper name)
%    - AnaphorConditions is []
%    - AnaphorGenus is one of masc, fem, human, neutr, encoded in a way that supports the gender hierarchy
%    - AnaphorNumerus is one of sg, pl, mass, encoded in a way that supports the number hierarchy
%    - AnaphorPerson is one of second, third
%    - SentenceID is the number of the sentence in which the anaphor occurs
%    - TokenID is the number of the first token of the anaphor
%    - Tokens is the proper name without a possibly occurring definite determiner
%    - SentenceSubject is ''
%
%
%    An indefinite noun phrase is represented in the DRS by its DRS conditions plus one additional condition
%
%    antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AntecedentGenus, AntecedentNumerus, AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens)
%
%    if the indefinite noun phase has an attached variable then follows a further condition 
%
%    antecedent(AntecedentIDV, AntecedentReferent, AntecedentConditionsV, AntecedentGenus, AntecedentNumerus, AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokensV]
%
%    where
%
%    - AntecedentID is the number of the noun phrase
%    - AntecedentIDV is AntecedentID + 1
%    - AntecedentReferent is the main discourse referent of the noun phrase
%    - AntecedentConditions is the list of the conditions that the noun phrase contributes to the DRS; minus any conditions that 
%      refer to anaphors (e.g. as in 'a man who sees himself' where 'himself' is represented by a separate anaphor/10 condition)
%    - AntecedentConditionsV contains just the condition for the variable
%    - AntecedentGenus is one of masc, fem, human, neutr, encoded in a way that supports the gender hierarchy
%    - AntecedentNumerus is one of sg, pl, mass, encoded in a way that supports the number hierarchy
%    - AntecedentPerson is one of second, third
%    - AntecedentSID is the number of the sentence in which the antecedent occurs
%    - AntecedentTID is the number of the first token of the antecedent
%    - AntecedentTokens is the noun of the main condition of the antecedent 
%    - AntecedentTokensV is the name of the variable
%
%
%    Indefinite pronouns - 'someone', 'somebody', 'something' and their negated and universal forms - are treated like an indefinite noun phrase.
%
%
%    A definite noun phrase is represented in the DRS by the single condition
%
%    anaphor(Anaphortype, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
%
%    if the indefinite noun phase has an attached variable then follows a further condition 
%
%    antecedent(AntecedentIDV, AntecedentReferent, AntecedentConditionsV, AntecedentGenus, AntecedentNumerus, AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens]
%
%    where
%
%    - Anaphortype is definite_noun_phrase
%    - AnaphorID is the number of the noun phrase
%    - AntecedentIDV is AnaphorID + 1
%    - AnaphorReferent is the main discourse referent of the noun phrase
%    - AnaphorConditions is the list of the conditions that the noun phrase contributes to the DRS; minus any conditions that 
%      refer to other anaphors (e.g. as in 'a man who sees himself/the dog' where 'himself/the dog' is represented by a separate anaphor/10
%      condition)
%    - AntecedentConditionsV contains just the condition for the variable
%    - AnaphorGenus is one of masc, fem, human, neutr, encoded in a way that supports the gender hierarchy
%    - AnaphorNumerus is one of sg, pl, mass, encoded in a way that supports the number hierarchy
%    - AnaphorPerson is one of second, third
%    - AnaphorSID is the number of the sentence in which the anaphor occurs
%    - AnaphorTID is the number of the first token of the anaphor
%    - AnaphorTokens is the noun of the main condition of the anaphor 
%    - AntecedentTokensV is the name of the variable
%    - SentenceSubject is ''
%
%
%    A nonreflexive personal or possessive pronoun is represented in the DRS by the single condition
%
%    anaphor(Anaphortype, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
%
%    where
%
%    - Anaphortype is nonreflexive_pronoun
%    - AnaphorID is the number of the noun phrase
%    - AnaphorReferent is the main discourse referent of the noun phrase
%    - AnaphorConditions is the list of the conditions that the noun phrase would contribute to the DRS; for pronouns AnaphorConditions is empty; for
%      variables there is the single condition variable/2
%    - AnaphorGenus is one of masc, fem, human, neutr, encoded in a way that supports the gender hierarchy
%    - AnaphorNumerus is one of sg, pl, mass, encoded in a way that supports the number hierarchy
%    - AnaphorPerson is one of second, third
%    - AnaphorSID is the number of the sentence in which the anaphor occurs
%    - AnaphorTID is the number of the first token of the anaphor
%    - AnaphorTokens is the text token of the anaphor 
%    - SentenceSubject is "subj(discourse referent of the subject of the sentence)", or "nosubj" if there is no subject
%
%
%    A bare variable is represented in the DRS by the single condition
%
%    anaphor(Anaphortype, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
%
%    where
%
%    - Anaphortype is one of nonreflexive_pronoun, reflexive_pronoun, variable
%    - AnaphorID is the number of the noun phrase
%    - AnaphorReferent is the main discourse referent of the noun phrase
%    - AnaphorConditions is the list of the conditions that the noun phrase would contribute to the DRS; for pronouns AnaphorConditions is empty; for
%      variables there is the single condition variable/2
%    - AnaphorGenus is one of masc, fem, human, neutr, encoded in a way that supports the gender hierarchy
%    - AnaphorNumerus is one of sg, pl, mass, encoded in a way that supports the number hierarchy
%    - AnaphorPerson is one of second, third
%    - AnaphorSID is the number of the sentence in which the anaphor occurs
%    - AnaphorTID is the number of the first token of the anaphor
%    - AnaphorTokens is the text token of the anaphor 
%    - SentenceSubject is ''
%
%
%    Essential assumptions
%
%    - noun phrases are numbered by AntecedentID and AnaphorID in textual order
%    - textual order of antecedents and anaphors is preserved in DRS processed by refres
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  declarations
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

:- module(refres, [resolve_anaphors/2]).

%%%:- check.

:- dynamic(variable_defined/3).

:- dynamic(nesting_level/1).

:- op( 400,  xfx, :).           % label
:- op( 400,  fy, -).            % negation
:- op( 400,  fy, ~).            % negation as failure
:- op( 600, xfy, v).            % disjunction
:- op( 650, xfy, =>).           % implication 

:- use_module('../logger/error_logger', [add_error_message_once/4, add_warning_message_once/4]).
:- use_module('../lexicon/lexicon_interface', [noun_pl/3]).

%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  resolve_anaphors(+DRSIn, -DRSOut)
%
%    - resolve_anaphors/3 resolves all anaphors of DRSIn against antecedents within DRSIn
%
%    - DRSIn contains additional conditions for antecedents (antecedent/8) and for anaphors (anaphor/10)
%    - DRSOut is DRSIn 
%			without the conditions antecedent/8 and anaphor/10,
%			without the conditions variable/2,
%      		with resolved anaphors
%	 - if refres generates error messages then DRSOut should be discarded since it is possibly corrupted
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

resolve_anaphors(drs(ReferentsIn, ConditionsIn), DRSOut) :-
  catch(call(resolve_anaphors1(drs(ReferentsIn, ConditionsIn), DRSOut)), CatchType, add_error_message_once(anaphor, '', CatchType, 'Send screenshot to APE developers.')).

resolve_anaphors1(drs(ReferentsIn, ConditionsIn), drs(ReferentsOut, ConditionsOut)) :-
  initialise_DRS_nesting_level,
  % enforce ordering of this DRS nesting level
  enforce_order(ConditionsIn, ConditionsInUpdated, [], ConditionsAllOut, [], AntecedentsIn),
  % match anaphors to antecedents, and collect all proper names
  resolve_all_anaphors(drs(ReferentsIn, ConditionsInUpdated), ConditionsAllOut, AntecedentsIn, _AntecedentsOut, [], _ProperNamesOut, drs(ReferentsOut, ConditionsIntermediate)),
  % remove duplicate conditions that are artefacts of anaphors like in "There is a man. The cat of the man sleeps." 
  % where "the cat of the man" cannot be resolved, and thus leaves two conditions for "man"
  filter_conditions(ConditionsIntermediate, ConditionsOut),
  % clean up for next run
  cleanup.

  
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  resolve_all_anaphors(+DRSIn, ConditionsAll, +AntecedentsIn, -AntecedentsOut, +ProperNamesIn, -ProperNamesOut, -DRSOut)
%
%    - resolve_all_anaphors/7 processes all conditions of DRSIn in depth-first order, and identifies them as antecedents, anaphors, complex conditions, or simple 
%      conditions. Antecedents are collected in AntecedentsIn, anaphors are resolved in textual order calling resolve_one_anaphor/9, complex conditions lead to  
%      recursive calls of resolve_all_anaphors/7 taking into account the nesting of DRSs (respectively the accessibility of referents), while simple conditions ?   
%      with the exception  of variable/2 ? are just copied to DRSOut.
%
%    - DRSIn contains additional conditions for antecedents (antecedent/8) and for anaphors (anaphor/10)
%    - DRSOut is DRSIn without the additional conditions and without the conditions variable/2
%    - ConditionsAll contains all conditions of the current DRS nesting level; used in resolve_one_anaphor/9 to resolve pronouns
%    - AntecedentsIn/AntecedentsOut is a threaded pair of lists of antecedents
%    - ProperNamesIn/ProperNamesOut is a threaded pair of lists of proper names
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

resolve_all_anaphors(drs(Referents, []), _ConditionsAllIn, AntecedentsIn, AntecedentsOut, ProperNames, ProperNames, drs(Referents, [])) :-
  !,
  (
    var(AntecedentsOut)
    ->
    AntecedentsOut = AntecedentsIn
  ;
    true
  ).

resolve_all_anaphors(drs(ReferentsIn, [Condition|Conditions]), ConditionsAllIn, AntecedentsIn, AntecedentsOut, ProperNamesIn, ProperNamesOut, DRSOut) :-
  (
    % simple conditions _-_  with the exception of variable(_, _)-_ are just copied
    Condition = _-_
    ->
    (
      Condition = variable(_, _)-_
      ->
      % variables are checked for redefinition
      define_new_variable(Condition, apposition), 
      DRSOut = drs(ReferentsOut, ConditionsOut)
    ;
      DRSOut = drs(ReferentsOut, [Condition|ConditionsOut])
    ),
    resolve_all_anaphors(drs(ReferentsIn, Conditions), ConditionsAllIn, AntecedentsIn, AntecedentsOut, ProperNamesIn, ProperNamesOut, drs(ReferentsOut, ConditionsOut))
  ;
    % antecedent
    Condition = antecedent(_, _, _, _, _, _, _, _, _)
    ->
    % add antecedent in correct order
    insert_antecedent(Condition, AntecedentsIn, AntecedentsIM),
    % continue loop
    resolve_all_anaphors(drs(ReferentsIn, Conditions), ConditionsAllIn, AntecedentsIM, AntecedentsOut, ProperNamesIn, ProperNamesOut, DRSOut)
  ;
    % anaphoric reference
    Condition = anaphor(_, AnaphorID1, _, _, _, _, _, _, _, _, _)
    ->
    (
      % Conditions contains an anaphor with smaller ID
      append(Front, [anaphor(Anaphortype2, AnaphorID2, AnaphorReferent2, AnaphorConditions2, AnaphorGenus2, AnaphorNumerus2, AnaphorPerson2, SentenceID2, TokenID2, Tokens2, Subject2)|Rest], Conditions),
      AnaphorID2 < AnaphorID1
      ->
      % permute anaphors
      append(Front,[anaphor(Anaphortype2, AnaphorID2, AnaphorReferent2, AnaphorConditions2, AnaphorGenus2, AnaphorNumerus2, AnaphorPerson2, SentenceID2, TokenID2, Tokens2, Subject2), Condition|Rest], PermutedConditions),
      resolve_all_anaphors(drs(ReferentsIn, PermutedConditions), ConditionsAllIn, AntecedentsIn, AntecedentsOut, ProperNamesIn, ProperNamesOut, DRSOut)
    ;
      % anaphor is in textual order
      resolve_one_anaphor(drs(ReferentsIn, Conditions), ConditionsAllIn, ConditionsAllOut, Condition, AntecedentsIn, AntecedentsIntermediate, ProperNamesIn, ProperNamesIM, drs(ReferentsIM, ConditionsIM)),     
      % continue loop
      resolve_all_anaphors(drs(ReferentsIM, ConditionsIM), ConditionsAllOut, AntecedentsIntermediate, AntecedentsOut, ProperNamesIM, ProperNamesOut, DRSOut)
    )
  ;
    % nested list
    is_list(Condition)
    ->
    DRSOut = drs(ReferentsOut, [ConditionListOut|ConditionsOut]),
    resolve_all_anaphors(drs(ReferentsIn, Condition), ConditionsAllIn, AntecedentsIn, AntecedentsTemp, ProperNamesIn, ProperNamesTemp, drs(ReferentsTemp, ConditionListOut)),
    resolve_all_anaphors(drs(ReferentsTemp, Conditions), ConditionsAllIn, AntecedentsTemp, AntecedentsOut, ProperNamesTemp, ProperNamesOut, drs(ReferentsOut, ConditionsOut))
  ;
    % embedded DRSs 
    (
      % negation
      Condition = - drs(ReferentsNIn, ConditionsNIn)
    ;
      % negation as failure
      Condition = ~ drs(ReferentsNIn, ConditionsNIn)
    ;
      % possibility
      Condition = can(drs(ReferentsNIn, ConditionsNIn))
    ;
      % necessity
      Condition = must(drs(ReferentsNIn, ConditionsNIn))
    ;
      % recommendation
      Condition = should(drs(ReferentsNIn, ConditionsNIn))
    ;
      % admissibility
      Condition = may(drs(ReferentsNIn, ConditionsNIn))
    ;
      % single question
      Condition = question(drs(ReferentsNIn, ConditionsNIn))
    ;
      % single command
      Condition = command(drs(ReferentsNIn, ConditionsNIn))
    ;
      % sentence subordination
      Condition = _ : drs(ReferentsNIn, ConditionsNIn)
    )
    ->
    functor(Condition, Operator, _),
    arg(1, Condition, Label),
    increase_DRS_nesting_level,
    % enforce ordering of this DRS nesting level
    enforce_order(ConditionsNIn, ConditionsNInUpdated, ConditionsAllIn, ConditionsAllOut, AntecedentsIn, AntecedentsInUpdated),
    % resolve anaphors on the current DRS nesting level; do not export any antecedents
    resolve_all_anaphors(drs(ReferentsNIn, ConditionsNInUpdated), ConditionsAllOut, AntecedentsInUpdated, AntecedentsInUpdated, ProperNamesIn, ProperNamesIM, drs(ReferentsIntermediate, ConditionsIntermediate1)),
    % continue on the previous DRS nesting level with ConditionsAllIn and with AntecedentsIn extended by antecedents for proper names found on this level
    decrease_DRS_nesting_level,
    add_proper_name_antecedents(ProperNamesIn, ProperNamesIM, AntecedentsIn, AntecedentsInPlusProperNames),
    resolve_all_anaphors(drs(ReferentsIn, Conditions), ConditionsAllIn, AntecedentsInPlusProperNames, AntecedentsOut, ProperNamesIM, ProperNamesOut, drs(ReferentsOut, ConditionsOut)),
    % remove duplicate conditions that are artefacts of anaphors like "the cat of the man" that could not be resolved
    % and thus could leave two conditions for "man"
    filter_conditions(ConditionsIntermediate1, ConditionsIntermediate2),
    % build DRSOut
    build_drs(Operator, Label, drs(ReferentsIntermediate, ConditionsIntermediate2), NewDRS),
    DRSOut = drs(ReferentsOut, [NewDRS|ConditionsOut])
  ;
    % embedded DRSs: implication
    Condition = drs(Referents1In, Conditions1In) => drs(Referents2In, Conditions2In)
    ->
    increase_DRS_nesting_level,
    % enforce ordering and resolve anaphors on the current DRS nesting level
    enforce_order(Conditions1In, Conditions1InUpdated, ConditionsAllIn, ConditionsAllIM1, AntecedentsIn, AntecedentsIM1),
    % to resolve the anaphora in the precondition the conditions of the consequence may be needed (example: A clerk enters every card of himself. )
    append(ConditionsAllIM1, Conditions2In, ConditionsAllPlusConsequence),
    resolve_all_anaphors(drs(Referents1In, Conditions1InUpdated), ConditionsAllPlusConsequence, AntecedentsIM1, AntecedentsIM2, ProperNamesIn, ProperNamesIM1, drs(Referents1Out, Conditions1Intermediate)), 
    enforce_order(Conditions2In, Conditions2InUpdated, ConditionsAllIM1, ConditionsAllIM2, AntecedentsIM2, AntecedentsIM3),
    resolve_all_anaphors(drs(Referents2In, Conditions2InUpdated), ConditionsAllIM2, AntecedentsIM3, _AntecedentsOut, ProperNamesIM1, ProperNamesIM2, drs(Referents2Out, Conditions2Intermediate1)),
    % continue on the previous DRS nesting level with ConditionsAllIn and with AntecedentsIn extended by antecedents for proper names found on this level
    decrease_DRS_nesting_level,
    add_proper_name_antecedents(ProperNamesIn, ProperNamesIM2, AntecedentsIn, AntecedentsInPlusProperNames),
    resolve_all_anaphors(drs(ReferentsIn, Conditions), ConditionsAllIn, AntecedentsInPlusProperNames, AntecedentsOut, ProperNamesIM2, ProperNamesOut, drs(ReferentsOut, ConditionsOut)),
    % remove duplicate conditions that are artefacts of anaphors like "the cat of the man" that could not be resolved
    % and thus could leave two conditions for "man"
    filter_conditions(Conditions1Intermediate, Conditions1Out),
    filter_conditions(Conditions2Intermediate1, Conditions2Intermediate2),
    % remove from the consequence of the implication any conditions that already occur in the precondition - with the exception of formulas
    subtract_conditions(Conditions2Intermediate2, Conditions1Out, Conditions2Out),
    % build DRSOut
    DRSOut = drs(ReferentsOut, [drs(Referents1Out, Conditions1Out) => drs(Referents2Out, Conditions2Out)|ConditionsOut])    
  ;
    % embedded DRSs: disjunction
    % 'v' is interpreted as a right-associative binary operator
    % for instance, a disjunction of three disjuncts is represented as D1 v drs([], [D2 v D3]) where each disjunct D is a DRS
    Condition = drs(Referents1In, Conditions1In) v DisjunctRestIn
    ->
    increase_DRS_nesting_level,
    % enforce ordering for first disjunct
    enforce_order(Conditions1In, Conditions1InUpdated, ConditionsAllIn, ConditionsAllIM, AntecedentsIn, AntecedentsInUpdated),
    % resolve anaphors of first disjunct
    resolve_all_anaphors(drs(Referents1In, Conditions1InUpdated), ConditionsAllIM, AntecedentsInUpdated, AntecedentsIM, ProperNamesIn, ProperNamesIM1, drs(Referents1Out, Conditions1Intermediate)),
    % process rest of disjuncts
    DisjunctRestIn = drs(_ReferentsDisjunctRestIn, ConditionsDisjunctRestIn),
    enforce_order(ConditionsDisjunctRestIn, _ConditionsDisjunctRestInUpdated, ConditionsAllIM, ConditionsAllOut, AntecedentsIM, AntecedentsIMUpdated),
    resolve_all_anaphors(DisjunctRestIn, ConditionsAllOut, AntecedentsIMUpdated, _AntecedentsOut, ProperNamesIM1, ProperNamesIM2, drs(ReferentsRestOut, ConditionsRestIntermediate)),
    % continue on the previous DRS nesting level with ConditionsAllIn and with AntecedentsIn extended by antecedents for proper names found on this level
    decrease_DRS_nesting_level,
    add_proper_name_antecedents(ProperNamesIn, ProperNamesIM2, AntecedentsIn, AntecedentsInPlusProperNames),
    resolve_all_anaphors(drs(ReferentsIn, Conditions), ConditionsAllIn, AntecedentsInPlusProperNames, AntecedentsOut, ProperNamesIM2, ProperNamesOut, drs(ReferentsOut, ConditionsOut)),
    % remove duplicate conditions that are artefacts of anaphors like "the cat of the man" that could not be resolved
    % and thus could leave two conditions for "man"
    filter_conditions(Conditions1Intermediate, Conditions1Out),
    filter_conditions(ConditionsRestIntermediate, ConditionsRestOut),
    % build DRSOut
    DRSOut = drs(ReferentsOut, [drs(Referents1Out, Conditions1Out) v drs(ReferentsRestOut, ConditionsRestOut)|ConditionsOut])
  ).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  resolve_one_anaphor(+drs(ReferentsIn, ConditionsIn), ConditionsAllIn, ConditionsAllOut, +Anaphor, +AntecedentsIn, -AntecedentsOut, +ProperNamesIn, -ProperNamesOut, -drs(ReferentsOut, ConditionsOut))
%
%    - resolve_one_anaphor/9 matches an Anaphor against the Antecedents, and then adapts the ReferentsIn and ConditionsIn of the DRS containing Anaphor generating 
%      ReferentsOut and ConditionsOut; furthermore it collects the proper names in the threaded pair of lists ProperNamesIn/ProperNamesOut
%
%    - ConditionsAllIn/ConditionsAllOut is a threaded pair of all conditions of the current DRS level
%    - Anaphor is the anaphor being processed
%    - AntecedentsIn/AntecedentsOut  is a threaded pair of lists of antecedents
%    - ProperNamesIn/ProperNamesOut is a threaded pair of lists of proper names
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

resolve_one_anaphor(drs(ReferentsIn, ConditionsIn), ConditionsAllIn, ConditionsAllOut, Anaphor, AntecedentsIn, AntecedentsOut, ProperNamesIn, ProperNamesOut, drs(ReferentsOut, ConditionsOut)) :-
  (
    % non-reflexive personal pronoun or non-reflexive possessive pronoun
    Anaphor = anaphor(nonreflexive_pronoun, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
    ->
    (
      % non-reflexive personal pronoun or non-reflexive possessive pronoun can be resolved 
      % constraints: AnaphorID > AntecedentID, AnaphorGenus = AntecedentGenus, AnaphorNumerus = AntecedentNumerus, AnaphorPerson = AntecedentPerson; a non-reflexive pronoun can occur as subject, 
      % as object or in a prepositional phrase, but it must not refer to the subject of the sentence in which it occurs
      member(antecedent(AntecedentID,  AntecedentReferent, _AntecedentConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIn),
      AnaphorID > AntecedentID,
      SentenceSubject \== subj(AntecedentReferent)
      ->
      delete_all_occurrences_of_one_discourse_referent(ReferentsIn, AnaphorReferent, ReferentsOut),
      (
        % add anaphors "he", "she", "he/she", "it", "they" to antecedents to handle cases like 'He sees himself.' ...
        (AnaphorTokens = 'he' ; AnaphorTokens = 'she' ; AnaphorTokens = 'he/she' ; AnaphorTokens = 'it' ; AnaphorTokens = 'they')
        ->
        ConditionsOut = [antecedent(AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens)|ConditionsIn]
      ;
        % ... but not the anaphors "him", "her", "him/her", "them", "his", "her", "his/her", "its", "their"
        ConditionsOut = ConditionsIn
      ),
      ConditionsAllOut = ConditionsAllIn,
      AnaphorReferent = AntecedentReferent,
      AntecedentsOut = AntecedentsIn,
      ProperNamesOut = ProperNamesIn
    ;
      % non-reflexive personal pronoun or non-reflexive possessive pronoun could not be resolved
      % create error message and continue resolution
      ReferentsOut = ReferentsIn,
      ConditionsOut = ConditionsIn,
      ConditionsAllOut = ConditionsAllIn,
      ProperNamesOut = ProperNamesIn,
      AntecedentsOut = AntecedentsIn,
      atom_concat('Unresolved anaphor: ', AnaphorTokens, ErrorText),
	  add_error_message_once(anaphor, AnaphorSID-AnaphorTID, ErrorText, 'Identify correct accessible antecedent.')
    )
  ;
    % definite noun phrase
    Anaphor = anaphor(definite_noun_phrase, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
    ->
    % if AnaphorConditions contains a condition for a variable then check for variable redefinition
    (
      member(variable(VariableReferent, VariableName)-AnaphorSID/_, AnaphorConditions),
      VariableReferent == AnaphorReferent
      ->
      (
        % variable is redefined on the same or a higher nesting level
        variable_defined(VariableName, DRSNestingLevel, bare),
        nesting_level(CurrentNestingLevel),
        CurrentNestingLevel >= DRSNestingLevel
        ->
        atom_concat('Redefined variable: ', VariableName, ErrorText),
        add_error_message_once(anaphor, AnaphorSID - AnaphorTID, ErrorText, 'Assign unique variables.')
      ;
        true
      )
    ;
      true
    ),
    (
      % definite noun phrase can be completely resolved against one antecedent
      % examples: "a red man who owns a dog" -> "the red man who owns a dog", "a who man does not work patiently" -> "the man who does not work patiently"
      % constraints: AnaphorID > AntecedentID, AnaphorGenus = AntecedentGenus, AnaphorPerson = AntecedentPerson, and ...   
      member(antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AnaphorGenus, _AntecedentNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIn),
      AnaphorID > AntecedentID,
      % ... main noun of anaphor matches main noun of antecedent and ...
      AnaphorTokens = AntecedentTokens,
      % ... anaphor is not part of antecedent - as in "a card of the card X1" - and ...
      \+ (member(relation(ReferentX, of, ReferentY)-AntecedentSID/_, AntecedentConditions), ReferentX == AntecedentReferent, ReferentY == AnaphorReferent), 
      % ... does not wrongly refer to a variable - as the last "the card X1" in "If a card X1 is a card of the card X1 then a man enters the card X1." - and ...
      (
        member(variable(ReferentZ, _X1)-AntecedentSID/_, AntecedentConditions)
        ->
        ReferentZ == AntecedentReferent
      ;
        true
      ),
      % ... AnaphorConditions are a subset of the AntecedentConditions
      \+ \+ (
              % decouple variables of antecedent and anaphor and ...
              Antecedent = antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AnaphorGenus, AntecedentNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens),
              copy_term(Antecedent, CopiedAntecedent),
              % ... ground variables of CopiedAntecedent to preserve their correct syntactic relations and ...
              numbervars(CopiedAntecedent, 1, _),
			  % ... unify AnaphorReferent with ground CopiedAntecedentReferent and ...
              CopiedAntecedent = antecedent(_, CopiedAntecedentReferent, CopiedAntecedentConditions, _, _, _, _, _, _),
			  AnaphorReferent = CopiedAntecedentReferent, 
			  % ... match all AnaphorConditions with CopiedAntecedentConditions
              match_elements(AnaphorConditions, CopiedAntecedentConditions)
            )
      ->
      % Before eliminating the referents of AnaphorConditions from the domain make sure that there is not the case "the card of who" that leads
      % to the anaphor conditions [relation(E, of, B)-1, object(E, card, countable, na, eq, 1)-1]. Removing all referents of AnaphorConditions would
      % also incorrectly remove the referent B that is defined elsewhere.
      (
        \+ \+ member(relation(AnaphorReferent, of, Owner1)-AnaphorSID/_, AnaphorConditions),
        \+ (member(object(Owner2, _, _, _, _, _)-AnaphorSID/_, AnaphorConditions), Owner1 == Owner2)
        ->
        select(relation(_SomeObject, of, _Owner)-AnaphorSID/_, AnaphorConditions, RestAnaphorConditions)
      ;
        RestAnaphorConditions = AnaphorConditions
      ), 
      % eliminate referents of RestAnaphorConditions
      term_variables(RestAnaphorConditions, AnaphorReferents),
      delete_all_occurrences_of_all_discourse_referents(ReferentsIn, AnaphorReferents, ReferentsOut),
      % eliminate from AntecedentsIn all antecedents following Anaphor whose conditions are a subset of AnaphorConditions
      eliminate_spurious_antecedents(AntecedentsIn, AnaphorID, AnaphorConditions, AntecedentsOut),
      ConditionsOut = ConditionsIn,
      ConditionsAllOut = ConditionsAllIn,
      AnaphorReferent = AntecedentReferent,
      ProperNamesOut = ProperNamesIn
    ;
	  % definite noun phrase can be resolved  partially against an antecedent and partially against other DRS conditions
      % examples: "a red man owns a dog" -> "the red man who owns a dog", "a man does not work patiently" -> "the man who does not work patiently"
      % constraints: AnaphorID > AntecedentID, AnaphorGenus = AntecedentGenus, AnaphorPerson = AntecedentPerson, and ...   
      member(antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AnaphorGenus, _AntecedentNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIn),
      AnaphorID > AntecedentID,
      % ... main noun of anaphor matches main noun of antecedent and ...
      AnaphorTokens = AntecedentTokens,
      % ... AnaphorConditions are a subset of the AntecedentConditions and ConditionsAllIn
      \+ \+ (
              % decouple variables of Antecedent and ConditionsAllIn from those of Anaphor and ...
              Antecedent = antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AnaphorGenus, AntecedentNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens),
              copy_term((Antecedent, ConditionsAllIn), (CopiedAntecedent, CopiedConditionsAllIn)),
              % ... ground variables of CopiedAntecedent and CopiedConditionsAllIn to preserve their correct syntactic relations and ...
              numbervars((CopiedAntecedent, CopiedConditionsAllIn), 1, _),
			  % ... unify AnaphorReferent with ground CopiedAntecedentReferent and ...
              CopiedAntecedent = antecedent(_, CopiedAntecedentReferent, CopiedAntecedentConditions, _, _, _, _, _, _),
			  AnaphorReferent = CopiedAntecedentReferent, 
              % ... identify main noun of anaphor conditions and ...
              once(append(Front, [object(AnaphorReferent, AnaphorTokens, Quant, Unit, Op, Count)-AnaphorSID/TID|Tail], AnaphorConditions)), 
              once(append(Front, Tail, RemainingAnaphorConditions)),
              MainAnaphorCondition = object(AnaphorReferent, AnaphorTokens, Quant, Unit, Op, Count)-AnaphorSID/TID,
              % ... match MainAnaphorCondition with AntecedentConditions and ...
              match_elements([MainAnaphorCondition], AntecedentConditions),
			  % ... match RemainingAnaphorConditions with concatenation of CopiedAntecedentConditions and CopiedConditionsAllIn
              append(CopiedAntecedentConditions, CopiedConditionsAllIn, CopiedAntecedentConditionsAndCopiedConditionsAllIn),
              match_elements(RemainingAnaphorConditions, CopiedAntecedentConditionsAndCopiedConditionsAllIn)
            )
      ->
      term_variables(AnaphorConditions, AnaphorReferents),
      delete_all_occurrences_of_all_discourse_referents(ReferentsIn, AnaphorReferents, ReferentsOut),
      % eliminate from AntecedentsIn all antecedents following Anaphor whose conditions are a subset of AnaphorConditions
      eliminate_spurious_antecedents(AntecedentsIn, AnaphorID, AnaphorConditions, AntecedentsOut),
      ConditionsOut = ConditionsIn,
      ConditionsAllOut = ConditionsAllIn,
      AnaphorReferent = AntecedentReferent,
      ProperNamesOut = ProperNamesIn
   ;
      % definite noun phrase could not be resolved
      % treat it as indefinite noun phrase
      ReferentsOut = ReferentsIn,
      ProperNamesOut = ProperNamesIn,
      AntecedentsOut = AntecedentsIn,
      % add its conditions without variable conditions to the DRS
      delete_all_occurrences_of_all_elements(AnaphorConditions, [variable(_, _)-_], AnaphorConditionsWithoutVariables),
      append(AnaphorConditionsWithoutVariables, ConditionsIn, ConditionsIM),
      % add anaphor condition as antecedent condition to ConditionsOut so that it will be added to antecedents in the next round
      % necessary to handle cases like "The man sees himself."
      ConditionsOut = [antecedent(AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens)|ConditionsIM],
      % add  the anaphor conditions to ConditionsAll
      append(AnaphorConditions, ConditionsAllIn, ConditionsAllOut),
      % create a warning message 
      (
        % countable anaphor: distinguish between singular or plural
        \+ \+ member(object(AnaphorReferent, AnaphorTokens, countable, na, _Op, AnaphorCount)-AnaphorSID/_, AnaphorConditions)
        ->
        (
          % singular anaphor
          AnaphorCount = 1
          ->
          AnaphorNoun = AnaphorTokens
        ;
          % plural anaphor
          % AnaphorCount > 1
          lexicon_interface:noun_pl(AnaphorNoun, AnaphorTokens, _Gender)
          ->
          true
        ;
          % anaphor not in any lexicon
          AnaphorNoun = AnaphorTokens
        ),
        concat_atom(['The definite noun phrase ''the ', AnaphorNoun, ''' does not have an antecedent and thus is not interpreted as anaphoric reference, but as a new indefinite noun phrase.'], ErrorText1),
        concat_atom(['If the definite noun phrase ''the ', AnaphorNoun, ''' should be an anaphoric reference then you must introduce an appropriate antecedent.'], ErrorText2),
        add_warning_message_once(anaphor, AnaphorSID-AnaphorTID, ErrorText1, ErrorText2)
      ;
        % mass anaphor
        member(object(AnaphorReferent, AnaphorTokens, mass, na, na, na)-AnaphorSID/_, AnaphorConditions)
        ->
        concat_atom(['The definite noun phrase ''the ', AnaphorTokens, ''' does not have an antecedent and thus is not interpreted as anaphoric reference, but as a new indefinite noun phrase.'], ErrorText1),
        concat_atom(['If the definite noun phrase ''the ', AnaphorTokens, ''' should be an anaphoric reference then you must introduce an appropriate antecedent.'], ErrorText2),
        add_warning_message_once(anaphor, AnaphorSID-AnaphorTID, ErrorText1, ErrorText2)
      )
    )
  ;
    % variable
    Anaphor = anaphor(variable, AnaphorID, AnaphorReferent, AnaphorConditions, _AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
    ->
    (
      % variable can be resolved
      % constraints: AnaphorID > AntecedentID, AnaphorNumerus = AntecedentNumerus, AnaphorPerson = AntecedentPerson, and AnaphorConditions are identical to the AntecedentConditions
      member(antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, _AntecedentGenus, AnaphorNumerus, AnaphorPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIn),
      AnaphorID > AntecedentID,
      % AnaphorGenus = AntecedentGenus, % variables are assigned a numerus but not a genus
      AntecedentConditions = [variable(AntecedentReferent, AntecedentVariable)-_AntecedentIndex],
      AnaphorConditions = [variable(AnaphorReferent, AnaphorVariable)-_AnaphorIndex],
      AntecedentVariable == AnaphorVariable
      ->
      delete_all_occurrences_of_one_discourse_referent(ReferentsIn, AnaphorReferent, ReferentsOut),
      ConditionsOut = ConditionsIn,
      ConditionsAllOut = ConditionsAllIn,
      AntecedentsOut = AntecedentsIn,
      AnaphorReferent = AntecedentReferent,
      ProperNamesOut = ProperNamesIn      
    ;
      % variable could not be resolved
      % treat variable as new bare variable
      % store variable for later check of possible redefinition (as in "X sees a man X")
      AnaphorConditions = [Variable],
	  define_new_variable(Variable, bare),
      % insert "something" and an antecedent to allow anaphoric references to this new bare variable
      ConditionsOut = [antecedent(AnaphorID, AnaphorReferent, AnaphorConditions, _AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens), 
                       object(AnaphorReferent,something,dom,na,na,na)-AnaphorSID/AnaphorTID|ConditionsIn],
      ReferentsOut = ReferentsIn,
      ConditionsAllOut = ConditionsAllIn,
      AntecedentsOut = AntecedentsIn,
      ProperNamesOut = ProperNamesIn      
    )
  ;
    % proper name
    Anaphor = anaphor(proper_name, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject)
    ->
    (
      % proper name occurred previously
      % constraints: ProperNamesIn contains an entry matching AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson and AnaphorTokens
      member(proper_name(_ID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, _SID, _TID, AnaphorTokens), ProperNamesIn) 
      -> 
      ReferentsOut = ReferentsIn,
      ConditionsOut = ConditionsIn,
      ConditionsAllOut = ConditionsAllIn,
      AntecedentsOut = AntecedentsIn,
      ProperNamesOut = ProperNamesIn
    ;
      % new proper name
      ReferentsOut = ReferentsIn,
      % an appropriate antecedent condition is added to allow references to the new proper name by pronouns 
      ConditionsOut = [antecedent(AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens)|ConditionsIn],
      ConditionsAllOut = ConditionsAllIn,
      AntecedentsOut = AntecedentsIn,
      % store proper name
      ProperNamesOut = [proper_name(AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens)|ProperNamesIn]
    )
 ;
    % unrecognised anaphor
    Anaphor = anaphor(_UnrecognizedAnaphorType, AnaphorID, AnaphorReferent, AnaphorConditions, AnaphorGenus, AnaphorNumerus, AnaphorPerson, AnaphorSID, AnaphorTID, AnaphorTokens, SentenceSubject) 
    ->
    % create error message and continue resolution
    ReferentsOut = ReferentsIn,
    ConditionsOut = [unresolved(Anaphor)|ConditionsIn],
    ConditionsAllOut = ConditionsAllIn,
    AntecedentsOut = AntecedentsIn,
    ProperNamesOut = ProperNamesIn,
    add_error_message_once(anaphor, AnaphorSID-AnaphorTID, 'Unrecognised anaphor.', 'Send screenshot to APE developers.')
  ).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  Groups of Supporting Predicates in Approximately Alphabetical Order
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  add_proper_name_antecedents(+ProperNamesThisLevel, +ProperNamesNestedLevel, +AntecedentsThisLevelIn, -AntecedentsThisLevelOut) 
%
%    - a nested level can introduce proper name anaphors, i.e. ProperNamesNestedLevel contains new elements compared to ProperNamesThisLevel
%    - these proper name anaphors are inserted as antecedents into AntecedentsThisLevelIn resulting in AntecedentsThisLevelOut
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

add_proper_name_antecedents(ProperNamesThisLevel, ProperNamesNestedLevel, AntecedentsThisLevelIn, AntecedentsThisLevelOut) :-
  % new proper names encountered on nested level?
  delete_all_occurrences_of_all_elements(ProperNamesNestedLevel, ProperNamesThisLevel, NewProperNames),
  (
    % no
    NewProperNames = []
    ->
    AntecedentsThisLevelOut = AntecedentsThisLevelIn
  ;
    % yes
    prepend_proper_name_antecedents(NewProperNames, AntecedentsThisLevelIn, AntecedentsThisLevelIntermediate1),
    sort(AntecedentsThisLevelIntermediate1, AntecedentsThisLevelIntermediate2),
    reverse(AntecedentsThisLevelIntermediate2, AntecedentsThisLevelOut)
  ).
  
prepend_proper_name_antecedents([], Antecedents, Antecedents).

prepend_proper_name_antecedents([proper_name(ID, Referent, Conditions, Genus, Numerus, Person, SID, TID, Tokens)|MoreProperNames], Antecedents, NewAntecedents) :-
  prepend_proper_name_antecedents(MoreProperNames, [antecedent(ID, Referent, Conditions, Genus, Numerus, Person, SID, TID, Tokens)|Antecedents], NewAntecedents).
  
  
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  build_drs(Operator, Label, DRS, NewDRS)
%
%    - for negation, negation as failure, possibility, necessity: NewDRS = Operator(DRS)
%    - for sentence subordination: NewDRS = Label : DRS
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

build_drs(Operator, Label, DRS, NewDRS) :-
  (
    var(Label)
	->
	NewDRS =.. [Operator, Label, DRS]
  ;
    % nonvar(Label)
	NewDRS =.. [Operator, DRS]
  ).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  check_for_redefined_variables(+Antecedents)
%
%    - checks whether the list of Antecedents contains redefined variables
%    - redefined variables can occur in antecedents that stand for indefinte noun phrases, or in antecedents that stand just for variables
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

check_for_redefined_variables([]).

check_for_redefined_variables([_Antecedent]).

% skip divider between DRS nesting levels
check_for_redefined_variables([divider_between_DRS_nesting_levels|Antecedents]) :-
  check_for_redefined_variables(Antecedents).

check_for_redefined_variables([Antecedent|Antecedents]) :-
  Antecedent = antecedent(AntecedentID1, AntecedentReferent1, AntecedentConditions1, _AnaphorGenus1, _AntecedentNumerus1, _AntecedentPerson1, AntecedentSID1, AntecedentTID1, _AntecedentTokens1), 
  (
    % variable is already defined in previous indefinite NP antecedent
    select(variable(Referent1, VariableName)-AntecedentSID1/_, AntecedentConditions1, [_|_]),
    Referent1 == AntecedentReferent1,
    member(antecedent(AntecedentID2, AntecedentReferent2, AntecedentConditions2, _AnaphorGenus2, _AntecedentNumerus2, _AntecedentPerson2, AntecedentSID2, _AntecedentTID2, _AntecedentTokens2), Antecedents),
    AntecedentID2 < AntecedentID1,
    select(variable(Referent2, VariableName)-AntecedentSID2/_, AntecedentConditions2, [_|_]),
    Referent2 == AntecedentReferent2
    ->
    atom_concat('Redefined variable: ', VariableName, ErrorText),
    add_error_message_once(anaphor, AntecedentSID1 - AntecedentTID1, ErrorText, 'Assign unique variables.')
  ;
    % variable is not redefined
    true
  ),
  % check remaining antecedents for redefinition
  check_for_redefined_variables(Antecedents).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  cleanup/0
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
    
cleanup :-
  retractall(variable_defined(_, _, _)).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  define_new_variable(+VariableCondition, +VariableType)
%
%    - each newly defined variable is asserted as variable_defined(VariableName, DRSNestingLevel, VariableType)
%    - VariableType is "bare" or "apposition"
%    - create an error message if a variable is redefined within its range of accessibility
%    - when the DRS nesting level is decreased all facts variable_defined/3 asserted for that level are retracted (cf. decrease_DRS_nesting_level)
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

define_new_variable(variable(_Referent, VariableName)-SentenceIndex/TokenIndex, VariableType) :-
  nesting_level(CurrentNestingLevel),
  (
    % variable is redefined on the same or a higher nesting level
    variable_defined(VariableName, DRSNestingLevel, _VariableType),
    CurrentNestingLevel >= DRSNestingLevel
    ->
    atom_concat('Redefined variable: ', VariableName, ErrorText),
    add_error_message_once(anaphor, SentenceIndex-TokenIndex, ErrorText, 'Assign unique variables.')
  ;
    true
  ),
  assert(variable_defined(VariableName, CurrentNestingLevel, VariableType)).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  delete_all_occurrences_of_all_elements(+ListIn, +ElementsToDelete, -ListOut)
%
%    - ListOut is ListIn without all occurrences of all the members of ElementsToDelete
%
%
%  delete_all(+ListIn, +ElementToDelete, -ListOut)
%
%    - ListOut is ListIn without all elements that unify with ElementsToDelete; i.e. ElementsToDelete works as a pattern against which the elements are matched
%    - notice that delete_all/3 differs from SWI Prologs built-in delete/3 that deletes all elements of ListIn that *simultaneously* unify with ElementToDelete
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
		
delete_all_occurrences_of_all_elements(ListIn, [], ListIn) :-
  !.

delete_all_occurrences_of_all_elements(ListIn, [ElementToDelete|ElementsToDelete], ListOut) :-
  delete_all(ListIn, ElementToDelete, ListIM),
  !,
  delete_all_occurrences_of_all_elements(ListIM, ElementsToDelete, ListOut).
  

delete_all([X|Xs],Z,Ys) :-
  \+ \+ (X = Z), 
  delete_all(Xs,Z,Ys).

delete_all([X|Xs],Z,[X|Ys]) :- 
  \+ \+ (X \= Z), 
  delete_all(Xs,Z,Ys).

delete_all([],_X,[]).
  
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  delete_all_occurrences_of_all_discourse_referents(+ReferentsIn, +ReferentsToDelete, -ReferentsOut)
%
%    - ReferentsOut is the list ReferentsIn without all occurrences of the elements of the list ReferentsToDelete
%
%
%  delete_all_occurrences_of_one_discourse_referent(+ReferentsIn, +ReferentToDelete, -ReferentsOut)
%
%    - ReferentsOut is the list ReferentsIn without all occurrences of ReferentToDelete
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

delete_all_occurrences_of_all_discourse_referents(Referents, [], Referents) :-
  !.

delete_all_occurrences_of_all_discourse_referents(ReferentsIn, [ReferentToDelete|ReferentsToDelete], ReferentsOut) :-
  delete_all_occurrences_of_one_discourse_referent(ReferentsIn, ReferentToDelete, ReferentsIM),
  delete_all_occurrences_of_all_discourse_referents(ReferentsIM, ReferentsToDelete, ReferentsOut).


delete_all_occurrences_of_one_discourse_referent([], _ReferentToDelete, []).

delete_all_occurrences_of_one_discourse_referent([Referent|Referents], ReferentToDelete, ReferentsOut) :-
  (
    Referent == ReferentToDelete
    -> 
    delete_all_occurrences_of_one_discourse_referent(Referents, ReferentToDelete, ReferentsOut)
  ;
    delete_all_occurrences_of_one_discourse_referent(Referents, ReferentToDelete, ReferentsRest),
    ReferentsOut = [Referent|ReferentsRest]
  ).

%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  eliminate_spurious_antecedents(+AntecedentsIn, +AnaphorID, +AnaphorConditions, -AntecedentsOut)
%
%    - AntecedentsOut is AntecedentsIn without spurious antecedents
%    - if a noun phrase like "the price of a resource" generates the anaphor "the price of a resource" and the antecedent "a resource", and the anaphor 
%      can be resolved then the antecedent is spurious
%    - spurious antecedents have an ID that is larger than AnaphorID and non-empty conditions that are a subset of AnaphorConditions 
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

eliminate_spurious_antecedents(AntecedentsIn, AnaphorID, AnaphorConditions, AntecedentsOut) :-
  (
    member(antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AntecedentGenus, AntecedentNumerus, AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIn),
    AnaphorID < AntecedentID, 
    \+ AntecedentConditions = [],
    \+ \+ (numbervars((AnaphorConditions, AntecedentConditions), 1, _), forall(member(Condition, AntecedentConditions), member(Condition, AnaphorConditions)))
    ->
    delete(AntecedentsIn, antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AntecedentGenus, AntecedentNumerus, AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens), AntecedentsIntermediate),
    eliminate_spurious_antecedents(AntecedentsIntermediate, AnaphorID, AnaphorConditions, AntecedentsOut)
  ;
    AntecedentsOut = AntecedentsIn
  ).

%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  DRS nesting level: initialise_DRS_nesting_level/0, increase_DRS_nesting_level/0, decrease_DRS_nesting_level/0
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

initialise_DRS_nesting_level :-
  retractall(nesting_level(_)),
  assert(nesting_level(1)).
  

increase_DRS_nesting_level :-
  retract(nesting_level(CurrentNestingLevel)),
  NewNestingLevel is CurrentNestingLevel + 1,
  assert(nesting_level(NewNestingLevel)).
  

decrease_DRS_nesting_level :-
  retract(nesting_level(CurrentNestingLevel)),
  NewNestingLevel is CurrentNestingLevel - 1,
  assert(nesting_level(NewNestingLevel)),
  retractall(variable_defined(_VariableName, CurrentNestingLevel, _VariableType)).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  enforce_order(+ConditionsIn, -ConditionsOut, +ConditionsAllIn, -ConditionsAllOut, +AntecedentsIn, -AntecedentsOut)
%
%    - enforce_order/6 
%        removes all antecedent/8 conditions from ConditionsIn giving ConditionsOut, and
%        prepends them in reverse textual order to AntecedentsIn resulting in AntecedentsOut (this guarantees the selection of the closest preceding antecedent)  
%        collects all simple conditions of ConditionsIn and adds them to the front of ConditionsAllIn resulting in ConditionsAllOut
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

enforce_order(ConditionsIn, ConditionsOut, ConditionsAllIn, ConditionsAllOut, AntecedentsIn, AntecedentsOut) :-
  % remove all antecedents from ConditionsIn and ...
  remove_and_collect_antecedents(ConditionsIn, [], AllAntecedents, [], ConditionsRest),
  % ... prepend them in inverse order to AntecedentsIn to get AntecedentsOut
  sort(AllAntecedents, AllAntecedentsSorted),
  reverse(AllAntecedentsSorted, AllAntecedentsSortedReversed),
  (
    AntecedentsIn = []
    ->
    AntecedentsOut = AllAntecedentsSortedReversed
  ;
    % for non-empty AntecedentsIn add divider between DRS nesting levels
    append(AllAntecedentsSortedReversed, [divider_between_DRS_nesting_levels|AntecedentsIn], AntecedentsOut)
  ),
  check_for_redefined_variables(AntecedentsOut),
  % reverse remaining conditions
  reverse(ConditionsRest, ConditionsOut),
  % prepend the remaining DRS conditions in reverse order to ConditionsAllIn to get ConditionsAllOut
  append(ConditionsOut, ConditionsAllIn, ConditionsAllOut).
  

remove_and_collect_antecedents([], Antecedents, Antecedents, ConditionsOut, ConditionsOut).

remove_and_collect_antecedents([ConditionIn|ConditionsIn], AntecedentsSofar, Antecedents, ConditionsOutSofor, ConditionsOut) :-
  (
    ConditionIn = antecedent(_ID, _Referent, _Conditions, _Genus, _Numerus, _Person, _SentenceID, _TokenID, _Tokens)
    -> 
    transform_allquantified_nouns(ConditionIn, TransformedConditionIn),
    remove_and_collect_antecedents(ConditionsIn, [TransformedConditionIn|AntecedentsSofar], Antecedents, ConditionsOutSofor, ConditionsOut)
  ;
    % generalised quantors 'exactly', 'less than' and 'at most' have additional bracketing that must be ...
    is_list(ConditionIn)
    -> 
    % ... processed recursively
    remove_and_collect_antecedents(ConditionIn, [], NewAntecedents, [], NewConditions),
    append(NewAntecedents, AntecedentsSofar, NewAntecedentsSofar),
    remove_and_collect_antecedents(ConditionsIn, NewAntecedentsSofar, Antecedents, [NewConditions|ConditionsOutSofor], ConditionsOut)
  ;
    remove_and_collect_antecedents(ConditionsIn, AntecedentsSofar, Antecedents, [ConditionIn|ConditionsOutSofor], ConditionsOut)
  ).
  

transform_allquantified_nouns(Condition, TransformedCondition) :-
  % phrases like "all men" generate an "antecedent(1, A, [object(A, man, countable, na, eq, 1)-1], $gen(B, $human(C)), $num(D, $pl), 1,'' , man)" that
  % on the one side is plural and on the other side has the count "eq 1"
  % to allow anaphoric reference by "the men" and "they" the count is replaced by "geq 2"
  (
    Condition = antecedent(AntecedentID, AntecedentReferent, AntecedentConditions, AntecedentGenus, '$num'(SP, '$pl'), AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens),
    select(object(Referent, Lemma, countable, na, eq, 1)-AntecedentSID/_, AntecedentConditions, RestAntecedentConditions),
    Referent == AntecedentReferent
    ->
    NewAntecedentConditions = [object(Referent, Lemma, countable, na, geq, 2)-AntecedentSID/_|RestAntecedentConditions],
    TransformedCondition = antecedent(AntecedentID, AntecedentReferent, NewAntecedentConditions, AntecedentGenus, '$num'(SP, '$pl'), AntecedentPerson, AntecedentSID, AntecedentTID, AntecedentTokens)
  ;
    TransformedCondition = Condition
  ).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  filter_conditions(+(ConditionsIn, -ConditionsOut)
%
%    - ConditionsOut is ConditionsIn without duplicate conditions
%    - create an error message if there are inconsistent noun phrase conjunctions, i.e. if they contain fewer elements than the plural object states
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

filter_conditions(ConditionsIn, ConditionsOut) :-
  % remove duplicate conditions
  remove_duplicate_conditions(ConditionsIn, ConditionsWithoutDuplicates),
  % check for noun phrase conjunctions that refer anaphorically to themselves
  noun_phrase_conjunction_refers_to_itself(ConditionsWithoutDuplicates),
  % establish original order of conditions
  reverse(ConditionsWithoutDuplicates, ConditionsOut).


remove_duplicate_conditions(Conditions, ConditionsWithoutDuplicates) :-
  remove_duplicate_conditions(Conditions, [], ConditionsWithoutDuplicates).

remove_duplicate_conditions([], ConditionsWithoutDuplicates, ConditionsWithoutDuplicates).

remove_duplicate_conditions([Condition|RestConditions], Singles, ConditionsWithoutDuplicates) :-
  (
    % simple Condition occurs identically – but possibly with different sentence index and different token index – in RestConditions
    Condition = ConditionProper - _/_,
    member(DuplicateSimpleCondition, RestConditions),
    DuplicateSimpleCondition = DuplicateSimpleConditionProper - _/_,
    ConditionProper == DuplicateSimpleConditionProper
    ->
    remove_duplicate_conditions(RestConditions, Singles, ConditionsWithoutDuplicates)
  ;
    % complex Condition occurs identically in RestConditions
    member(DuplicateComplexCondition, RestConditions),
    Condition == DuplicateComplexCondition
    ->
    remove_duplicate_conditions(RestConditions, Singles, ConditionsWithoutDuplicates)
  ;
    % Condition does not occur in RestConditions
    remove_duplicate_conditions(RestConditions, [Condition|Singles], ConditionsWithoutDuplicates)
  ).

   
noun_phrase_conjunction_refers_to_itself(Conditions) :-
  (
    % get head of noun phrase conjunction
    select(object(Whole,na,countable,na,eq,N)-Sentence/'', Conditions, RestConditions)
    ->
    % count the has_part/2 branches of the noun phrase conjunction 
    % use subterm/2 instead of member/2 since RestConditions can contain a list derived from a generalised quantifier
    findall(1, (subterm(has_part(Whole1,_Part)-Sentence/'', RestConditions), Whole1 == Whole), Branches),
    (
      % all N has_part/2 branches exist
      length(Branches, N)
      ->
      true
    ;
      % there are less than N has_part/2 branches meaning that there are anaphoric references within the noun phrase conjunction
      add_error_message_once(anaphor, Sentence - '', 'Noun phrase conjunction refers anaphorically to itself.', 'Remove anaphoric references from noun phrase conjunction.')
    ),
    noun_phrase_conjunction_refers_to_itself(RestConditions)
  ;
   % there are no (further) noun phrase conjunctions
   true
  ).


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  insert_antecedent(+Antecedent, +AntecedentsIn, -AntecedentsOut)
%
%    - insert_antecedent/3 inserts Antecedent into the list AntecedentsIn generating AntecedentOut while the orderedness of the antecedents is preserved
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

insert_antecedent(Antecedent, AntecedentsIn, AntecedentsOut) :-
  (
    % insert Antecedent before divider between DRS nesting levels
    member(divider_between_DRS_nesting_levels, AntecedentsIn) 
    ->
    append(Front, [divider_between_DRS_nesting_levels|Tail], AntecedentsIn),
    insert_antecedent1(Antecedent, Front, FrontOut),
    append(FrontOut, [divider_between_DRS_nesting_levels|Tail], AntecedentsOut)
  ;
    % no divider
    insert_antecedent1(Antecedent, AntecedentsIn, AntecedentsOut)
  ),
  check_for_redefined_variables(AntecedentsOut).

  
insert_antecedent1(Antecedent, [], [Antecedent]) :-
  !.			

insert_antecedent1(antecedent(ID1, R1, C1, G1, N1, P1, SID1, TID1, T1), [antecedent(ID2, R2, C2, G2, N2, P2, SID2, TID2, T2)|Antecedents], [antecedent(ID2, R2, C2, G2, N2, P2, SID2, TID2, T2)|AntecedentsIM]) :-	
  ID1 < ID2,
  !,
  insert_antecedent1(antecedent(ID1, R1, C1, G1, N1, P1, SID1, TID1, T1), Antecedents, AntecedentsIM).		

insert_antecedent1(antecedent(ID1, R1, C1, G1, N1, P1, SID1, TID1, T1),[antecedent(ID2, R2, C2, G2, N2, P2, SID2, TID2, T2)|Antecedents], [antecedent(ID1, R1, C1, G1, N1, P1, SID1, TID1, T1), antecedent(ID2, R2, C2, G2, N2, P2, SID2, TID2, T2)|Antecedents]) :-	
  ID1 >= ID2.				


%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  match_elements(+Subset, +Set)
%
%    - match_elements(Subset, Set) succeeds if all elements of Subset  - with the exception of antecedents and anaphors - occur in Set that is ground
%    - complex elements - represented by sub-DRSs - must occur identically in both sets
%
%  match_element_against_disjunction(+Element, +Disjunction)
%
%    - match_element_against_disjunction(Element, Disjunction) succeeds if Element matches one of the disjuncts of Disjunction
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

match_elements([], _Set).

match_elements([Element|Elements], Set) :-
  (
    % singular noun anaphor 'the object' refers to ...
    Element = object(R1, Noun, countable, na, eq, 1) - _Index1
    ->
    (
      % ... singular countable noun antecedent 'an/1 object', 'at least/more than/exactly 1 object', or to ...
      member(object(R2, Noun, countable, na, _Op, 1) - _Index2, Set),  
      R1 = R2
    ;
      % ... mass noun antecedent occurring by itself (e.g. some water) or with measurement noun (e.g. 2 kg of water)
      member(object(R2, Noun, mass, _Unit, _Op, _Count) - _Index2, Set), 
      R1 = R2
    )
  ;
	% plural noun anaphor 'the objects' represented as 'at least 2 objects' refers to ...
    Element = object(R1, Noun, countable, na, geq, 2) - _Index1
    ->
    (
      % ... plural countable noun antecedent antecedent 'N2 objects', 'at least/more than/exactly N2 objects', where N2>=2, or to ...
      member(object(R2, Noun, countable, na, _Op, N2) - _Index2, Set), 
      N2 >= 2, 
      R1 = R2
    ;
      % ... antecedent 'measurement noun of objects'
      member(object(R2, Noun, countable, Unit, _Op, _Count) - _Index2, Set),	
      \+ Unit = na, 
      R1 = R2
    )
  ;
    % modifier/4
    Element = modifier_pp(Arg11, Arg12, Arg13) - _Index1
    ->
    member(modifier_pp(Arg21, Arg22, Arg23) - _Index2, Set),
    % must allow for backtracking in member/2 since groups of modifier/4 conditions must be matched in any order
    Arg11 = Arg21,
    Arg12 = Arg22,
    Arg13 = Arg23
  ;
    % has_part/2
    Element = has_part(Whole1, Part1) - _Index1
    ->
    member(has_part(Whole2, Part2) - _Index2, Set),
    % must allow for backtracking in member/2 since groups of has_part/2 conditions must be matched in any order
    Whole1 = Whole2,
    Part1 = Part2
  ;
    % other simple conditions
    Element = Condition1 - _Index1
    -> 
    member(Condition2 - _Index2, Set),
    Condition1 = Condition2
  ;
    % antecedents
    Element = antecedent(_, _, _ ,_, _, _, _, _)
    ->
    true
  ;
    % anaphors
    Element = anaphor(_, _, _ ,_, _, _, _, _, _, _)
    ->
    true
  ;
    % nested list
    is_list(Element)
    ->
    member(List, Set),
    is_list(List),
    match_elements(Element, List),
    match_elements(List, Element)
  ;
    % complex condition: drs
    Element = drs(_Referents1, Conditions1)
    ->
    member(drs(_Referents2, Conditions2), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: negation
    Element = - drs(_Referents1, Conditions1)
    ->
    member(- drs(_Referents2, Conditions2), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: negation as failure
    Element = ~ drs(_Referents1, Conditions1)
    ->
    member(~ drs(_Referents2, Conditions2), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: possibility
    Element = can(drs(_Referents1, Conditions1))
    ->
    member(can(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: necessity
    Element = must(drs(_Referents1, Conditions1))
    ->
    member(must(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: recommendation
    Element = should(drs(_Referents1, Conditions1))
    ->
    member(should(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: admissibility
    Element = may(drs(_Referents1, Conditions1))
    ->
    member(may(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    Element = question(drs(_Referents1, Conditions1))
    ->
    member(question(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    Element = command(drs(_Referents1, Conditions1))
    ->
    member(command(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: sentence subordination
    Element = _ : drs(_Referents1, Conditions1)
    ->
    member( _ :(drs(_Referents2, Conditions2)), Set),
    match_elements(Conditions1, Conditions2),
    match_elements(Conditions2, Conditions1)
  ;
    % complex condition: implication
    Element = drs(_ReferentsP1, ConditionsP1) => drs(_ReferentsC1, ConditionsC1)
    ->
    member(drs(_ReferentsP2, ConditionsP2) => drs(_ReferentsC2, ConditionsC2), Set),
 	match_elements(ConditionsP1, ConditionsP2),
    match_elements(ConditionsP2, ConditionsP1),
 	match_elements(ConditionsC1, ConditionsC2),
    match_elements(ConditionsC2, ConditionsC1)
  ;
    % complex condition: disjunction
    % identity of Element and its counterpart in Set is established here
    Element = drs(_ReferentsD1, ConditionsD1) v DisjunctsRest1
    ->
    member(drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2, Set),
    % first disjunct drs(_ReferentsD1, ConditionsD1) of Element can match any of the disjuncts of drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2
    match_element_against_disjunction(drs(_ReferentsD1, ConditionsD1), drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2),
    % match remaining disjuncts DisjunctsRest1 of Element against drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2
    (
      % DisjunctsRest1 consists of exctly one disjunct
      \+ DisjunctsRest1 = drs([], [_ v _])
      ->
      match_element_against_disjunction(DisjunctsRest1, drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2)
    ;
      % DisjunctsRest1 consists of more than one disjunct
      DisjunctsRest1 = drs([], [DisjunctsRest11 v DisjunctsRest12])
      ->
      match_elements([DisjunctsRest11 v DisjunctsRest12], [drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2])
    )
  ),
  match_elements(Elements, Set).


match_element_against_disjunction(drs(_ReferentsD1, ConditionsD1), drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2) :-
  % Element drs(_ReferentsD1, ConditionsD1) matches one of the disjuncts of the Disjunction drs(_ReferentsD2, ConditionsD2) v DisjunctsRest2
  (
    % Element matches first disjunct of Disjunction
    match_elements(ConditionsD1, ConditionsD2)
    ->
    true
  ;
    % Element does not match first disjunct of Disjunction
    % there is exactly one further disjunct; try it
    \+ DisjunctsRest2 = drs([], [_ v _])
    ->
    match_elements([drs(_ReferentsD1, ConditionsD1)], [DisjunctsRest2]) 
  ;
    % Element does not match first disjunct of Disjunction
    % there are at least two further disjuncts; try them in order
    DisjunctsRest2 = drs([], [DisjunctsRest21 v DisjunctsRest22])
    ->
    match_element_against_disjunction(drs(_ReferentsD1, ConditionsD1), DisjunctsRest21 v DisjunctsRest22) 
  ).


%---------------------------------------------------------------------------------------------------------
%
%  subterm(+Sub,+Term) 
%
%  	Sub is a subterm of Term
%
%---------------------------------------------------------------------------------------------------------

subterm(Term, Term) :-
  nonvar(Term).

subterm(Sub,Term):-
  nonvar(Term),
  functor(Term,_F,N),
  N > 0,
  subterm(N,Sub,Term).


subterm(N,Sub,Term):-
  arg(N,Term,Arg),       
  subterm(Sub,Arg).

subterm(N,Sub,Term):-
  N>1,
  N1 is N-1,
  subterm(N1,Sub,Term).
   
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
%
%  subtract_conditions(+Conditions1, +Conditions2, -Difference)
%
%    - Difference contains all the conditions of Conditions1 that are not contained in Conditions2 - with the exception of formulas that are not affected
%    - indices of conditions are ignored
%    - no unification of conditions takes place
%
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------

subtract_conditions(Difference, [], Difference).

subtract_conditions(Conditions1, [Condition2|Conditions2], Difference) :-
  (
    Condition2 = Condition2C - _,
    Condition2C \= formula(_, _, _),
    select(Condition - _, Conditions1, RestConditions1),
    Condition == Condition2C
    ->
    subtract_conditions(RestConditions1, Conditions2, Difference)
  ;
    subtract_conditions(Conditions1, Conditions2, Difference)
  ).
  
  
%-----------------------------------------------------------------------------------------------------------------------------------------------------------------
