View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  
16  package org.qedeq.kernel.bo.logic.common;
17  
18  import org.qedeq.base.utility.Enumerator;
19  import org.qedeq.base.utility.EqualsUtility;
20  import org.qedeq.kernel.se.base.list.Atom;
21  import org.qedeq.kernel.se.base.list.Element;
22  import org.qedeq.kernel.se.base.list.ElementList;
23  import org.qedeq.kernel.se.dto.list.DefaultAtom;
24  import org.qedeq.kernel.se.dto.list.DefaultElementList;
25  import org.qedeq.kernel.se.dto.list.ElementSet;
26  
27  
28  /**
29   * Some useful static methods for formulas and terms.
30   *
31   * @author  Michael Meyling
32   */
33  public final class FormulaUtility implements Operators {
34  
35      /**
36       * Constructor.
37       *
38       */
39      private FormulaUtility() {
40          // nothing to do here
41      }
42  
43      /**
44       * Is {@link Element} a subject variable?
45       *
46       * @param   element    Element to look onto.
47       * @return  Is it a subject variable?
48       */
49      public static final boolean isSubjectVariable(final Element element) {
50          if (element == null || !element.isList() || element.getList() == null) {
51              return false;
52          }
53          final ElementList list = element.getList();
54          if (list.getOperator().equals(SUBJECT_VARIABLE)) {
55              if (list.size() != 1) {
56                  return false;
57              }
58              final Element first = element.getList().getElement(0);
59              if (first == null || !first.isAtom() || first.getAtom() == null) {
60                  return false;
61              }
62              final Atom atom = first.getAtom();
63              if (atom.getString() == null || atom.getAtom().getString() == null
64                      || atom.getString().length() == 0) {
65                  return false;
66              }
67          } else {
68              return false;
69          }
70          return true;
71      }
72  
73      /**
74       * Is {@link Element} a predicate variable?
75       *
76       * @param   element    Element to look onto.
77       * @return  Is it a predicate variable?
78       */
79      public static final boolean isPredicateVariable(final Element element) {
80          return isOperator(PREDICATE_VARIABLE, element);
81      }
82  
83      /**
84       * Is {@link Element} a proposition variable?
85       *
86       * @param   element    Element to look onto.
87       * @return  Is it a proposition variable?
88       */
89      public static final boolean isPropositionVariable(final Element element) {
90          return isOperator(PREDICATE_VARIABLE, element) && element.getList().size() <= 1;
91      }
92  
93      /**
94       * Is {@link Element} a function variable?
95       *
96       * @param   element    Element to look onto.
97       * @return  Is it a function variable?
98       */
99      public static final boolean isFunctionVariable(final Element element) {
100         return isOperator(FUNCTION_VARIABLE, element);
101     }
102 
103     /**
104      * Is {@link Element} a predicate constant?
105      *
106      * @param   element    Element to look onto.
107      * @return  Is it a predicate constant?
108      */
109     public static final boolean isPredicateConstant(final Element element) {
110         return isOperator(PREDICATE_CONSTANT, element);
111     }
112 
113     /**
114      * Is {@link Element} a function constant?
115      *
116      * @param   element    Element to look onto.
117      * @return  Is it a function constant?
118      */
119     public static final boolean isFunctionConstant(final Element element) {
120         return isOperator(FUNCTION_CONSTANT, element);
121     }
122 
123     /**
124      * Is the given element an list with given operator and has as first element an non empty
125      * string atom?
126      *
127      * @param   operator    Operator.
128      * @param   element     Check this element.
129      * @return  Check successful?
130      */
131     public static boolean isOperator(final String operator,
132             final Element element) {
133         return isOperator(operator, element, 0);
134     }
135 
136     /**
137      * Is the given element an list with given operator and has as first element an non empty
138      * string atom?
139      *
140      * @param   operator        Operator.
141      * @param   element         Check this element.
142      * @param   minArguments    Minimum number of arguments (beside the first string atom).
143      * @return  Check successful?
144      */
145     private static boolean isOperator(final String operator,
146             final Element element, final int minArguments) {
147         if (element == null || !element.isList() || element.getList() == null) {
148             return false;
149         }
150         final ElementList list = element.getList();
151         if (list.getOperator().equals(operator)) {
152             if (list.size() < 1 + minArguments) {
153                 return false;
154             }
155             final Element first = element.getList().getElement(0);
156             if (first == null || !first.isAtom() || first.getAtom() == null) {
157                 return false;
158             }
159             final Atom atom = first.getAtom();
160             if (atom.getString() == null || atom.getAtom().getString() == null
161                     || atom.getString().length() == 0) {
162                 return false;
163             }
164         } else {
165             return false;
166         }
167         return true;
168     }
169 
170     /**
171      * Return all free subject variables of an element.
172      *
173      * @param   element    Work on this element.
174      * @return  All free subject variables.
175      */
176     public static final ElementSet getFreeSubjectVariables(final Element element) {
177         final ElementSet free = new ElementSet();
178         if (isSubjectVariable(element)) {
179             free.add(element);
180         } else if (element.isList()) {
181             final ElementList list = element.getList();
182             if (isBindingOperator(list)) {
183                 for (int i = 1; i < list.size(); i++) {
184                     free.union(getFreeSubjectVariables(list.getElement(i)));
185                 }
186                 free.remove(list.getElement(0));
187             } else {
188                 for (int i = 0; i < list.size(); i++) {
189                     free.union(getFreeSubjectVariables(list.getElement(i)));
190                 }
191             }
192         }
193         return free;
194     }
195 
196     /**
197      * Return all bound subject variables of an element.
198      *
199      * @param   element    Work on this element.
200      * @return  All bound subject variables.
201      */
202     public static final ElementSet getBoundSubjectVariables(final Element element) {
203         final ElementSet bound = new ElementSet();
204         if (element.isList()) {
205             ElementList list = element.getList();
206             // if operator is quantifier or class term
207             if (isBindingOperator(list)) {
208                 // add subject variable to bound list
209                 bound.add(list.getElement(0));
210                 // add all bound variables of sub-elements
211                 for (int i = 1; i < list.size(); i++) {
212                     bound.union(getBoundSubjectVariables(
213                         list.getElement(i)));
214                 }
215             } else {
216                 // add all bound variables of sub-elements
217                 for (int i = 0; i < list.size(); i++) {
218                     bound.union(getBoundSubjectVariables(list.getElement(i)));
219                 }
220             }
221         }
222         return bound;
223     }
224 
225     /**
226      * Return all subject variables of an element.
227      *
228      * @param   element    Work on this element.
229      * @return  All subject variables.
230      */
231     public static final ElementSet getSubjectVariables(final Element element) {
232         final ElementSet all = new ElementSet();
233         if (isSubjectVariable(element)) {
234             all.add(element);
235         } else if (element.isList()) {
236             final ElementList list = element.getList();
237             for (int i = 1; i < list.size(); i++) {
238                 all.union(getSubjectVariables(list.getElement(i)));
239             }
240         }
241         return all;
242     }
243 
244     /**
245      * Return all predicate variables of an element. The arguments are normalized to subject
246      * variables "x_1", "x_2" and so on.
247      *
248      * @param   element    Work on this element.
249      * @return  All predicate variables of that formula.
250      */
251     public static final ElementSet getPredicateVariables(final Element element) {
252         final ElementSet all = new ElementSet();
253         if (isPredicateVariable(element)) {
254             final ElementList pred = element.getList();
255             final DefaultElementList normalized = new DefaultElementList(pred.getOperator());
256             normalized.add(pred.getElement(0));
257             for (int i = 1; i < pred.size(); i++) {
258                 normalized.add(createSubjectVariable("x_" + i));
259             }
260             all.add(normalized);
261         } else if (element.isList()) {
262             final ElementList list = element.getList();
263             for (int i = 0; i < list.size(); i++) {
264                 all.union(getPredicateVariables(list.getElement(i)));
265             }
266         }
267         return all;
268     }
269 
270     /**
271      * Return all proposition variables of an element. That are predicate variables with
272      * arity zero.
273      *
274      * @param   element    Work on this element.
275      * @return  All proposition variables of that formula.
276      */
277     public static final ElementSet getPropositionVariables(final Element element) {
278         final ElementSet all = new ElementSet();
279         if (isPropositionVariable(element)) {
280             all.add(element);
281         } else if (element.isList()) {
282             final ElementList list = element.getList();
283             for (int i = 0; i < list.size(); i++) {
284                 all.union(getPropositionVariables(list.getElement(i)));
285             }
286         }
287         return all;
288     }
289 
290     /**
291      * Return all part formulas of an element.
292      *
293      * @param   element    Work on this element.
294      * @return  All part formulas of that element.
295      */
296     public static final ElementSet getPartFormulas(final Element element) {
297         final ElementSet all = new ElementSet();
298         if (element == null || element.isAtom()) {
299             return all;
300         }
301         final ElementList list = element.getList();
302         if (isPredicateVariable(list)) {
303             all.add(element);
304         } else if (isPredicateConstant(list)) {
305             all.add(list);
306         } else if (Operators.CONJUNCTION_OPERATOR.equals(list.getOperator())
307                 || Operators.DISJUNCTION_OPERATOR.equals(list.getOperator())
308                 || Operators.NEGATION_OPERATOR.equals(list.getOperator())
309                 || Operators.IMPLICATION_OPERATOR.equals(list.getOperator())
310                 || Operators.EQUIVALENCE_OPERATOR.equals(list.getOperator())
311                 ) {
312             all.add(list);
313             for (int i = 0; i < list.size(); i++) {
314                 all.union(getPartFormulas(list.getElement(i)));
315             }
316         } else if (isBindingOperator(list)) {
317             all.add(list);
318             for (int i = 0; i < list.size(); i++) {
319                 all.union(getPartFormulas(list.getElement(i)));
320             }
321         } else if (isSubjectVariable(list)) {
322             // this is no formula
323         } else if (isFunctionVariable(list)) {
324             // this is no formula
325         } else if (isFunctionConstant(list)) {
326             // this is no formula
327         } else {
328             for (int i = 0; i < list.size(); i++) {
329                 all.union(getPartFormulas(list.getElement(i)));
330             }
331         }
332         return all;
333     }
334 
335     /**
336      * Has the given list an operator that binds a subject variable?
337      *
338      * @param   list    Check for this operator.
339      * @return  Has it an binding operator with subject variable?
340      */
341     public static boolean isBindingOperator(final ElementList list) {
342         final String operator = list.getOperator();
343         if (operator == null || list.size() <= 0 || !isSubjectVariable(list.getElement(0))) {
344             return false;
345         }
346         return operator.equals(EXISTENTIAL_QUANTIFIER_OPERATOR)
347                 || operator.equals(UNIQUE_EXISTENTIAL_QUANTIFIER_OPERATOR)
348                 || operator.equals(UNIVERSAL_QUANTIFIER_OPERATOR)
349                 || operator.equals(CLASS_OP);
350     }
351 
352     /**
353      * Get relative context of the first difference between the given elements.
354      *
355      * @param   first   First element.
356      * @param   second  Second element.
357      * @return  Relative path (beginning with ".") of first difference.
358      */
359     public static String getDifferenceLocation(final Element first, final Element second) {
360         final StringBuffer diff = new StringBuffer();
361         equal(diff, first, second);
362         return diff.toString();
363     }
364 
365     /**
366      * Is there any difference between the two elements? If so, the context has the difference
367      * position.
368      *
369      * @param   firstContext    Context (might be relative) for first element.
370      * @param   first           First element.
371      * @param   second          Second element.
372      * @return  Are both elements equal?
373      */
374     private static boolean equal(final StringBuffer firstContext,
375             final Element first, final Element second) {
376         if (first == null) {
377             return second == null;
378         }
379         if (second == null) {
380             return false;
381         }
382         if (first.isAtom()) {
383             if (!second.isAtom()) {
384                 return false;
385             }
386             return EqualsUtility.equals(first.getAtom().getString(), second.getAtom().getString());
387         }
388         if (second.isAtom()) {
389             return false;
390         }
391         if (!EqualsUtility.equals(first.getList().getOperator(), second.getList().getOperator())) {
392             return false;
393         }
394         if (first.getList().size() != second.getList().size()) {
395             return false;
396         }
397         for (int i = 0; i < first.getList().size(); i++) {
398             final int length = firstContext.length();
399             firstContext.append(".getList().getElement(" + i + ")");
400             if (!equal(firstContext, first.getList().getElement(i),
401                     second.getList().getElement(i))) {
402                 return false;
403             }
404             firstContext.setLength(length);
405         }
406         return true;
407     }
408 
409     /**
410      * Replace bound subject variables at given occurrence by another one.
411      * If goal occurrence number is below number of occurrences within the formula nothing is
412      * changed. Original parts are used, so don't modify the formulas directly.
413      *
414      * @param   originalSubjectVariable     Replace this subject variable.
415      * @param   replacementSubjectVariable  By this subject variable.
416      * @param   formulaElement              This is the formula we work on.
417      * @param   occurrenceGoal              This is the binding occurrence number.
418      * @param   occurreneCurrent            Counter how much occurrences we already had.
419      * @return  Formula with replaced subject variables.
420      */
421     public static Element replaceSubjectVariableQuantifier(final Element originalSubjectVariable,
422             final Element replacementSubjectVariable, final Element formulaElement,
423             final int occurrenceGoal, final Enumerator occurreneCurrent) {
424         if (formulaElement.isAtom()) {
425             return formulaElement;
426         }
427         final ElementList formula = formulaElement.getList();
428         if (occurreneCurrent.getNumber() > occurrenceGoal) {
429             return formula.copy();
430         }
431         final ElementList result = new DefaultElementList(formula.getOperator());
432         if (isBindingOperator(formula)
433                 && formula.getElement(0).equals(originalSubjectVariable)) {
434             occurreneCurrent.increaseNumber();
435 //            System.out.println("found: " + occurreneCurrent);
436             if (occurrenceGoal == occurreneCurrent.getNumber()) {
437 //                System.out.println("match: " + occurrenceGoal);
438                 return formula.replace(originalSubjectVariable,
439                     replacementSubjectVariable);
440             }
441         }
442         for (int i = 0; i < formula.size(); i++) {
443             result.add(replaceSubjectVariableQuantifier(originalSubjectVariable,
444                 replacementSubjectVariable, formula.getElement(i), occurrenceGoal,
445                 occurreneCurrent));
446         }
447         return result;
448     }
449 
450     /**
451      * Replace function or predicate variable by given term or formula. Old parts are not copied
452      * but taken directly - so don't modify the formulas!
453      *
454      * @param   formula             Formula we want the replacement take place.
455      * @param   operatorVariable    Predicate variable or function variable with only subject
456      *                              variables as arguments.
457      * @param   replacement         Replacement formula or term with matching subject variables.
458      *                              <code>operatorVariable</code> might have some subject variables
459      *                              that are not in <code>replacement</code> and vice versa.
460      * @return  Formula where operatorVariable was replaced.
461      */
462     public static Element replaceOperatorVariable(final Element formula,
463             final Element operatorVariable, final Element replacement) {
464         if (formula == null || operatorVariable == null || replacement == null
465                 || formula.isAtom() || operatorVariable.isAtom() || replacement.isAtom()) {
466             return formula;
467         }
468         final ElementList f = formula.getList();
469         final ElementList ov = operatorVariable.getList();
470         final ElementList r = replacement.getList();
471         // check elementary preconditions
472 //        System.out.println("ov.size=" + ov.size());
473         if (f.size() < 1 || ov.size() < 1) {
474 //            System.out.println("failed elementary conditions");
475             return formula;
476         }
477         // construct replacement formula with meta variables
478         Element rn = r;
479         for (int i = 1; i < ov.size(); i++) {
480             rn = rn.replace(ov.getElement(i), createMeta(ov.getElement(i)));
481         }
482         return replaceOperatorVariableMeta(formula, operatorVariable, rn);
483     }
484 
485     /**
486      * Replace function or predicate variable by given term or formula. Original parts are used -
487      * so don't modify them!
488      *
489      * @param   formula             Formula we want the replacement take place.
490      * @param   operatorVariable    Predicate variable or function variable with only subject
491      *                              variables as arguments.
492      * @param   replacement         Replacement formula or term with meta variables instead of
493      *                              subject variables to replace (matching
494      *                              <code>operatorVariable</code>).
495      * @return  Formula where operatorVariable was replaced.
496      */
497     private static Element replaceOperatorVariableMeta(final Element formula,
498             final Element operatorVariable, final Element replacement) {
499         if (formula.isAtom() || operatorVariable.isAtom() || replacement.isAtom()) {
500             return formula;
501         }
502         final ElementList f = formula.getList();
503         final ElementList ov = operatorVariable.getList();
504         final ElementList r = replacement.getList();
505         if (f.size() < 1 || ov.size() < 1) {
506             return formula.copy();
507         }
508         ElementList result;
509         if (EqualsUtility.equals(f.getOperator(), ov.getOperator()) && f.size() == ov.size()
510                 && f.getElement(0).equals(ov.getElement(0))) {
511             // replace meta variables by matching entries
512             result = r;
513             for (int i = 1; i < ov.size(); i++) {
514                 result = (ElementList) result.replace(createMeta(ov.getElement(i)),
515                     replaceOperatorVariableMeta(f.getElement(i), operatorVariable, replacement));
516             }
517         } else {
518             result = new DefaultElementList(f.getOperator());
519             for (int i = 0; i < f.size(); i++) {
520                 result.add(replaceOperatorVariableMeta(f.getElement(i), operatorVariable,
521                     replacement));
522             }
523         }
524         return result;
525     }
526 
527     /**
528      * Checks if a given term or formula contains a given function or predicate variable with same
529      * size.
530      *
531      * @param   formula             Formula we want the replacement take place.
532      * @param   operatorVariable    Predicate variable or function variable with only subject
533      *                              variables as arguments.
534      * @return  Do we have any occurrence?
535      */
536     public static boolean containsOperatorVariable(final Element formula,
537             final Element operatorVariable) {
538         if (formula == null || formula.isAtom() || operatorVariable == null
539                 || operatorVariable.isAtom()) {
540             return false;
541         }
542         final ElementList f = formula.getList();
543         final ElementList ov = operatorVariable.getList();
544         if (f.size() < 1 || ov.size() < 1) {
545             return f.equals(ov);
546         }
547         if (EqualsUtility.equals(f.getOperator(), ov.getOperator()) && f.size() == ov.size()
548                 && f.getElement(0).equals(ov.getElement(0))) {
549             return true;
550         }
551         for (int i = 0; i < f.size(); i++) {
552             if (containsOperatorVariable(f.getElement(i), operatorVariable)) {
553                 return true;
554             }
555         }
556         return false;
557     }
558 
559     /**
560      * Test if operator occurrence in formula matches always to a formula that contains no subject
561      * variable that is in the given ElementSet of bound subject variables..
562      *
563      * @param   formula             Formula we want to test the condition.
564      * @param   operatorVariable    Predicate variable or function variable with only subject
565      *                              variables as arguments.
566      * @param   bound               Set of subject variables that are tabu.
567      * @return  Formula test was successful.
568      */
569     public static boolean testOperatorVariable(final Element formula,
570             final Element operatorVariable, final ElementSet bound) {
571         if (formula.isAtom() || operatorVariable.isAtom()) {
572             return true;
573         }
574         final ElementList f = formula.getList();
575         final ElementList ov = operatorVariable.getList();
576         if (f.size() < 1 || ov.size() < 1) {
577             return true;
578         }
579         boolean ok = true;
580         if (EqualsUtility.equals(f.getOperator(), ov.getOperator()) && f.size() == ov.size()
581                 && f.getElement(0).equals(ov.getElement(0))) {
582             if (!getSubjectVariables(f).intersection(bound).isEmpty()) {
583                 return false;
584             }
585         } else {
586             for (int i = 0; ok && i < f.size(); i++) {
587                 ok = testOperatorVariable(f.getElement(i), operatorVariable, bound);
588             }
589         }
590         return ok;
591     }
592 
593     /**
594      * Is the given formula a simple implication like A =&gt; B.
595      *
596      * @param   formula Check this formula.
597      * @return  Is the formula a simple implication?
598      */
599     public static boolean isImplication(final Element formula) {
600         if (formula.isAtom()) {
601             return false;
602         }
603         final ElementList f = formula.getList();
604         return Operators.IMPLICATION_OPERATOR.equals(f.getList().getOperator())
605             && f.getList().size() == 2;
606     }
607 
608     /**
609      * Create meta variable for subject variable.
610      *
611      * @param   subjectVariable Subject variable, we want to have a meta variable for.
612      * @return  Resulting meta variable. If we didn't got a subject variable we just
613      *          return the original.
614      */
615     public static Element createMeta(final Element subjectVariable) {
616         if (!isSubjectVariable(subjectVariable)) {
617             return subjectVariable;
618         }
619         final DefaultElementList result = new DefaultElementList(META_VARIABLE);
620         result.add(subjectVariable.getList().getElement(0));
621         return result;
622     }
623 
624     /**
625      * Create subject variable out of variable name.
626      *
627      * @param   subjectVariableName     Subject variable name.
628      * @return  Resulting subject variable.
629      */
630     public static Element createSubjectVariable(final String subjectVariableName) {
631         final DefaultElementList result = new DefaultElementList(SUBJECT_VARIABLE);
632         result.add(new DefaultAtom(subjectVariableName));
633         return result;
634     }
635 
636     /**
637      * Create predicate variable out of variable name with no further arguments.
638      *
639      * @param   predicateVariableName     Predicate variable name.
640      * @return  Resulting predicate variable.
641      */
642     public static Element createPredicateVariable(final String predicateVariableName) {
643         final DefaultElementList result = new DefaultElementList(PREDICATE_VARIABLE);
644         result.add(new DefaultAtom(predicateVariableName));
645         return result;
646     }
647 
648 }