BasicLibrary.java

/*
 * #%L
 * prolobjectlink-jpi-jtrolog
 * %%
 * Copyright (C) 2012 - 2018 WorkLogic Project
 * %%
 * This program 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 2.1 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
/*
 * tuProlog - Copyright (C) 2001-2007 aliCE team at deis.unibo.it
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jTrolog.lib;

import jTrolog.engine.BindingsTable;
import jTrolog.errors.InvalidLibraryException;
import jTrolog.errors.PrologException;
import jTrolog.parser.Parser;
import jTrolog.terms.EvaluableTerm;
import jTrolog.terms.Float;
import jTrolog.terms.Int;
import jTrolog.terms.IteratorAsTerm;
import jTrolog.terms.Long;
import jTrolog.terms.Number;
import jTrolog.terms.Struct;
import jTrolog.terms.StructAtom;
import jTrolog.terms.Term;
import jTrolog.terms.Var;
import jTrolog.terms.WrapStruct;
import jTrolog.terms.WrapVar;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * This class defines a set of basic built-in predicates for the tuProlog engine
 * 
 * Library/Theory dependency: none
 * 
 * 
 * 
 */
@SuppressWarnings({ "rawtypes", "unchecked","serial" })
public class BasicLibrary extends Library {

	//
	// meta-predicates
	//

	/**
	 * sets a new theory provided as a text
	 */
	public boolean set_theory_1(BindingsTable bt, Term th) {
		if (!atom_1(bt, th))
			return false;
		try {
			engine.clearTheory();
			engine.addTheory(((Struct) th).name);
			return true;
		} catch (PrologException e) {
			System.err.println("invalid theory:" + e.getMessage());
			return false;
		}
	}

	/**
	 * adds a new theory provided as a text
	 */
	public boolean add_theory_1(BindingsTable bt, Term th) throws PrologException {
		if (!atom_1(bt, th))
			return false;
		((Library) this).engine.addTheory(((Struct) th).name);
		return true;
	}

	/** gets current theory text */
	public boolean get_theory_1(BindingsTable bt, Term arg) {
		return bt.unify(arg, new StructAtom(engine.getTheory()));
	}

	public boolean load_library_2(BindingsTable bt, Struct className, Term libName) throws InvalidLibraryException {
		Library lib = engine.loadLibrary(Parser.removeApices(className.name));
		return bt.unify(libName, new StructAtom(lib.getName()));
	}

	public boolean get_operators_list_1(BindingsTable bt, Struct argument) {
		LinkedList result = new LinkedList();
		for (Iterator it = engine.getCurrentOperators(); it.hasNext();)
			result.add((it.next()));
		if (result.isEmpty())
			return bt.unify(argument, Term.emptyList);
		result.add(Term.emptyList);
		return bt.unify(argument, bt.createStructList(result));
	}

	public boolean warning_0(BindingsTable bt) {
		engine.resetWarningList();
		return true;
	}

	public boolean nowarning_0(BindingsTable bt) {
		engine.resetWarningList();
		return true;
	}

	//
	// term type inspection
	//

	public static boolean constant_1(BindingsTable bt, Term t) {
		return atomic_1(bt, t);
	}

	public static boolean number_1(BindingsTable bt, Term t) {
		return t instanceof Number;
	}

	public static boolean integer_1(BindingsTable bt, Term t) {
		return t instanceof Int;
	}

	public static boolean float_1(BindingsTable bt, Term t) {
		return t instanceof Float;
	}

	public static boolean atom_1(BindingsTable bt, Term t) {
		return t instanceof StructAtom;
	}

	public static boolean compound_1(BindingsTable bt, Term t) {
		return t instanceof Struct && !(t instanceof StructAtom);
	}

	public static boolean list_1(BindingsTable bt, Term t) throws PrologException {
		if (t instanceof Var)
			throw new PrologException("instantiation_error");
		if (t.equals(Term.emptyList))
			return true;
		if (!(t instanceof Struct))
			return false;
		final Struct s = (Struct) t;
		if (s.predicateIndicator != Parser.listSignature)
			return false;
		// iterate the list to find the last element and run isList on that last
		// element
		Term last = null;
		for (Iterator it = Struct.iterator(s); it.hasNext(); last = (Term) it.next())
			;
		return list_1(bt, bt.resolve(last));
	}

	public boolean var_1(BindingsTable bt, Term t) {
		return t instanceof Var;
	}

	public boolean nonvar_1(BindingsTable bt, Term t) {
		return !(t instanceof Var);
	}

	public static boolean atomic_1(BindingsTable bt, Term t) {
		return t instanceof Number || t instanceof StructAtom;
	}

	public static boolean ground_1(BindingsTable bt, Term t) {
		if (t instanceof Var)
			return false;
		if (t instanceof Number || t instanceof StructAtom)
			return true;
		// compound and thus wrapped struct
		WrapStruct wrapStruct = ((WrapStruct) t);
		Var[] childVars = wrapStruct.getVarList();
		if (childVars == null)
			return false; // todo no WrapStruct should have null as childVars.
							// Should find out when and why null is returned
							// here..
		int ctx = wrapStruct.getContext();
		for (int i = 0; i < childVars.length; i++) {
			if (!ground_1(bt, bt.resolve(new WrapVar(childVars[i], ctx))))
				return false;
		}
		return true;
	}

	public boolean $arg_3(BindingsTable bt, Int n, Struct term, Term arg) throws PrologException {
		if (!BasicLibrary.compound_1(bt, term))
			throw new PrologException("type_error(compound, " + term + ")");
		if (n.intValue() < 0)
			throw new PrologException("domain_error(not_less_than_zero, " + n + ")");

		if (n.intValue() == 0 || n.intValue() > term.arity)
			return false;

		Term nthArg = term.getArg(n.intValue() - 1);
		return bt.unify(arg, nthArg);
	}

	public boolean $functor_3(BindingsTable bt, Term term, Term name, Term arity) throws PrologException {
		Int maxArity = (Int) engine.getFlagValue("max_arity");

		if (term instanceof Var) {
			if (name instanceof Var || arity instanceof Var)
				throw new PrologException("instantiation_error");

			if (!BasicLibrary.atomic_1(bt, name))
				throw new PrologException("type_error(atomic, " + name + ")");

			if (!(arity instanceof Int))
				throw new PrologException("type_error(integer, " + arity + ")");

			if (((Int) arity).longValue() > maxArity.intValue())
				throw new PrologException("representation_error(max_arity)");

			if (((Int) arity).intValue() < 0)
				throw new PrologException("domain_error(not_less_than_zero, " + arity + ")");

			if (!BasicLibrary.atom_1(bt, name) && ((Int) arity).intValue() > 0)
				throw new PrologException("type_error(atom, " + name + ")");

			if (BasicLibrary.atomic_1(bt, name) && ((Int) arity).intValue() == 0)
				return bt.unify(term, name);

			Struct newList = (WrapStruct) bt.wrapWithID(Parser.createListContainingAnyVars(((Int) arity).intValue() + 1));
			bt.unify(newList.getArg(0), name);
			return $tofromlist_2(bt, term, newList);
		}

		if (BasicLibrary.atomic_1(bt, term))
			return bt.unify(term, name) && bt.unify(arity, new Int(0));

		if (BasicLibrary.compound_1(bt, term)) {
			StructAtom a = new StructAtom(((Struct) term).name);
			return bt.unify(name, a) && bt.unify(arity, new Int(((Struct) term).arity));
		}
		return false;
	}

	public boolean $tofromlist_2(BindingsTable bt, Term structIn, Term listIn) throws PrologException {
		if (structIn instanceof Number || structIn instanceof StructAtom) {
			Struct newList = (WrapStruct) bt.wrapWithID(Parser.createListContainingAnyVars(1));
			bt.unify(newList.getArg(0), structIn);
			return bt.unify(newList, listIn);
		}
		if (structIn instanceof Struct) {
			Struct struct = (Struct) structIn;
			WrapStruct medium = (WrapStruct) bt.wrapWithID(Parser.createListContainingAnyVars(struct.arity + 1));
			Iterator it = bt.structListIterator(medium, false);
			// unify the name of the struct with the first in the medium list
			bt.unify((Term) it.next(), new StructAtom(struct.name));
			// unify the children of the Struct with the rest of the medium list
			for (int i = 0; i < struct.arity; i++)
				bt.unify((Var) it.next(), struct.getArg(i));
			// try to unify the medium list generated from the struct with
			// listIn
			return bt.unify(medium, listIn);
		}
		if (structIn instanceof Var) {
			if (listIn instanceof Var)
				throw new PrologException("instantiation_error");
			if (!list_1(bt, listIn))
				throw new PrologException("type_error(list, " + listIn + ")");
			if (listIn.equals(Term.emptyList))
				throw new PrologException("domain_error(non_empty_list, " + listIn + ")");
			Struct list = (Struct) listIn;

			Term head = bt.resolve(list.getArg(0));
			if (head instanceof Var)
				throw new PrologException("instantiation_error");
			if (!BasicLibrary.atom_1(bt, head))
				throw new PrologException("type_error(atom, " + head + ")");

			Term tail = bt.resolve(list.getArg(1));
			if (tail instanceof Struct) {
				LinkedList terms = new LinkedList();
				for (Iterator it = bt.structListIterator((Struct) tail, true); it.hasNext();)
					terms.add(it.next());
				if (terms.isEmpty())
					return bt.unify(structIn, head);
				if (terms.size() > ((Int) engine.getFlagValue("max_arity")).intValue())
					throw new PrologException("representation_error(max_arity)");

				Term[] argValuesToBeLinked = (Term[]) terms.toArray(new Term[0]);
				Term[] variableArgs = new Term[argValuesToBeLinked.length];
				for (int i = 0; i < argValuesToBeLinked.length; i++)
					variableArgs[i] = new Var("_", i + 1);
				WrapStruct wrapStructRes = (WrapStruct) bt.wrapWithID(new Struct(head.toString(), variableArgs));
				for (int i = 0; i < argValuesToBeLinked.length; i++)
					bt.unify(wrapStructRes.getArg(i), argValuesToBeLinked[i]);
				return bt.unify(structIn, wrapStructRes);
			} else {
				Struct newStruct = new Struct(head.toString(), new Term[] { new Var("_", 1) });
				WrapStruct wrapStructRes = (WrapStruct) bt.wrapWithID(newStruct);
				bt.unify(wrapStructRes.getArg(0), tail);
				return bt.unify(structIn, wrapStructRes);
			}
		}
		return false;
	}

	public boolean current_time_1(BindingsTable bt, Term time) throws Throwable {
		return bt.unify(time, new Long(System.currentTimeMillis()));
	}

	//
	// term/espression comparison
	//

	public Term eval_1(BindingsTable bt, EvaluableTerm structIn) throws Throwable {
		return bt.evalExpression(engine, structIn);
	}

	public boolean is_2(BindingsTable bt, Term structIn, EvaluableTerm listIn) throws Throwable {
		return bt.unify(structIn, bt.evalExpression(engine, listIn));
	}

	public boolean expression_equality_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number val1 = bt.evalExpression(engine, structIn);
		Number val2 = bt.evalExpression(engine, listIn);
		if (val1 instanceof Float || val2 instanceof Float)
			return Number.compareDoubleValues(val1, val2) == 0;
		return val1.equals(val2);
	}

	public boolean expression_greater_than_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number num0 = bt.evalExpression(engine, structIn);
		Number num1 = bt.evalExpression(engine, listIn);
		if (num0 instanceof Float || num1 instanceof jTrolog.terms.Float)
			return Number.compareDoubleValues(num0, num1) > 0;
		return num0.intValue() > num1.intValue();
	}

	public boolean expression_less_or_equal_than_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return !expression_greater_than_2(bt, structIn, listIn);
	}

	public boolean expression_less_than_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return !expression_greater_or_equal_than_2(bt, structIn, listIn);
	}

	public boolean expression_greater_or_equal_than_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return expression_greater_than_2(bt, structIn, listIn) || expression_equality_2(bt, structIn, listIn);
	}

	public static boolean term_equality_2(BindingsTable bt, Term structIn, Term listIn) {
		if (structIn instanceof Var) {
			if (!(listIn instanceof Var))
				return false;
			Var v = (Var) listIn;
			if (((Var) structIn).isAnonymous() && v.isAnonymous())
				return false;
			return v.equals(structIn);
		}
		if (structIn instanceof Number) {
			if (!(listIn instanceof Number))
				return false;
			return number_equality_2((Number) structIn, (Number) listIn);
		}

		if (structIn instanceof Struct) {
			if (!(listIn instanceof Struct))
				return false;

			Struct ts = (Struct) listIn;
			if (((Struct) structIn).arity != ts.arity || !((Struct) structIn).name.equals(ts.name))
				return false;

			for (int i = 0; i < ((Struct) structIn).arity; i++) {
				if (!term_equality_2(null, ((Struct) structIn).getArg(i), ts.getArg(i)))
					return false;
			}
			return true;
		}
		return false;
	}

	public static boolean number_equality_2(Number listIn, Number structIn) {
		if (listIn instanceof Int && structIn instanceof Int)
			return structIn.longValue() == listIn.longValue();
		else if (listIn instanceof Float && structIn instanceof Float)
			return Number.compareDoubleValues(structIn, listIn) == 0;
		return false;
	}

	public static boolean term_greater_than_2(BindingsTable bt, Term structIn, Term listIn) {
		if (structIn instanceof Var) {
			if (!(listIn instanceof Var))
				return false;
			return structIn.toString().hashCode() > listIn.toString().hashCode();
		}

		if (structIn instanceof Struct) {
			if (listIn instanceof Struct) {
				Struct ts = (Struct) listIn;
				int tarity = ts.arity;
				if (((Struct) structIn).arity < tarity)
					return false;
				if (((Struct) structIn).arity == tarity) {
					if (((Struct) structIn).name.equals(ts.name)) {
						for (int i = 0; i < ((Struct) structIn).arity; i++) {
							if (term_greater_than_2(null, ((Struct) structIn).getArg(i), ts.getArg(i)))
								return true;
							if (!term_equality_2(null, ((Struct) structIn).getArg(i), ts.getArg(i)))
								return false;
						}
					}
					if (((Struct) structIn).name.compareTo(ts.name) <= 0)
						return false;
				}
			}
			return true;
		}
		if (structIn instanceof Number) {
			if (listIn instanceof Var)
				return true;
			if (listIn instanceof Struct)
				return false;
			if (!(listIn instanceof Number))
				return false;

			Number n2 = ((Number) listIn);
			Number n1 = ((Number) structIn);
			if (n1 instanceof Float || n2 instanceof Float)
				return Number.compareDoubleValues(n1, n2) > 0;
			return n1.longValue() > n2.longValue();
		}
		return false; // such as implementation specific NullTerm etc.
	}

	public static boolean term_less_than_2(BindingsTable bt, Term structIn, Term listIn) {
		return !term_greater_than_2(null, structIn, listIn) && !term_equality_2(null, structIn, listIn);
	}

	public Term expression_plus_1(BindingsTable bt, EvaluableTerm structIn) throws Throwable {
		return bt.evalExpression(engine, structIn);
	}

	public Term expression_minus_1(BindingsTable bt, EvaluableTerm listIn) throws Throwable {
		Number val0 = bt.evalExpression(engine, listIn);
		if (val0 instanceof jTrolog.terms.Long)
			return new jTrolog.terms.Long(val0.longValue() * -1);
		if (val0 instanceof Int)
			return new Int(val0.intValue() * -1);
		if (val0 instanceof jTrolog.terms.Double)
			return new jTrolog.terms.Double(val0.doubleValue() * -1);
		if (val0 instanceof jTrolog.terms.Float)
			return new jTrolog.terms.Float(val0.floatValue() * -1);
		return null;
	}

	public Term expression_bitwise_not_1(BindingsTable bt, EvaluableTerm structIn) throws Throwable {
		Number val = bt.evalExpression(engine, structIn);
		if (!(val instanceof Int))
			throw new PrologException("type_error(integer, " + val + ")");
		return new Int(~val.intValue());
	}

	public Term expression_plus_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number val0 = bt.evalExpression(engine, structIn);
		Number val1 = bt.evalExpression(engine, listIn);
		if (val0 instanceof Float || val1 instanceof Float)
			return new jTrolog.terms.Double(val0.doubleValue() + val1.doubleValue());
		return Number.getIntegerNumber(val0.longValue() + val1.longValue());
	}

	public Term expression_minus_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number val0 = bt.evalExpression(engine, structIn);
		Number val1 = bt.evalExpression(engine, listIn);
		if (val0 instanceof Float || val1 instanceof Float)
			return new jTrolog.terms.Double(val0.doubleValue() - val1.doubleValue());
		return Number.getIntegerNumber(val0.longValue() - val1.longValue());
	}

	public Term expression_multiply_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number val0 = bt.evalExpression(engine, structIn);
		Number val1 = bt.evalExpression(engine, listIn);
		if (val0 instanceof Float || val1 instanceof Float)
			return new jTrolog.terms.Double(val0.doubleValue() * val1.doubleValue());
		return Number.getIntegerNumber(val0.longValue() * val1.longValue());
	}

	public Term expression_div_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		Number val0 = bt.evalExpression(engine, structIn);
		Number val1 = bt.evalExpression(engine, listIn);
		if (val0 instanceof Float || val1 instanceof Float)
			return new jTrolog.terms.Double(val0.doubleValue() / val1.doubleValue());
		return Number.getIntegerNumber((long) (val0.doubleValue() / val1.doubleValue()));
	}

	public Term expression_integer_div_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return Number.getIntegerNumber(bt.evalExpression(engine, structIn).longValue() / bt.evalExpression(engine, listIn).longValue());
	}

	public Term expression_pow_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		double val = Math.pow(bt.evalExpression(engine, structIn).doubleValue(), bt.evalExpression(engine, listIn).doubleValue());
		return new jTrolog.terms.Double(val);
	}

	public Term expression_bitwise_shift_right_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return new Int(bt.evalExpression(engine, structIn).intValue() >> bt.evalExpression(engine, listIn).intValue());
	}

	public Term expression_bitwise_shift_left_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return new Int(bt.evalExpression(engine, structIn).intValue() << bt.evalExpression(engine, listIn).intValue());
	}

	public Term expression_bitwise_and_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return new Int(bt.evalExpression(engine, structIn).intValue() & bt.evalExpression(engine, listIn).intValue());
	}

	public Term expression_bitwise_or_2(BindingsTable bt, EvaluableTerm structIn, EvaluableTerm listIn) throws Throwable {
		return new Int(bt.evalExpression(engine, structIn).intValue() | bt.evalExpression(engine, listIn).intValue());
	}

	//
	// text/atom manipulation predicates
	//

	/**
	 * bidirectional text/term conversion.
	 */
	public boolean text_term_2(BindingsTable bt, Term structIn, Term listIn) {
		if (!ground_1(bt, structIn))
			return bt.unify(structIn, new StructAtom(listIn.toString()));
		Term result = new Parser(Parser.removeApices(structIn.toString()), engine).nextTerm(false);
		return bt.unify(listIn, result);
	}

	public boolean text_concat_3(BindingsTable bt, Term source1, Term source2, Term dest) {
		if (!atom_1(bt, source1) || !atom_1(bt, source2))
			return false;
		return bt.unify(dest, new StructAtom(((Struct) source1).name + ((Struct) source2).name));
	}

	public boolean num_atom_2(BindingsTable bt, Term structIn, Term listIn) {
		if (listIn instanceof Var) {
			if (structIn instanceof Number)
				return bt.unify(listIn, new StructAtom(structIn.toString()));
			return false;
		}
		if (!atom_1(bt, listIn))
			return false;
		Number num = Parser.parseNumber(Parser.removeApices(((Struct) listIn).name).trim());
		return bt.unify(structIn, num);
	}

	public String getTheory() {
		return
		//
		// operators defined by the BasicLibrary theory
		//
		"':-'(op( 1200, fx,   ':-')). \n"
				+ ":- op( 1200, xfx,  ':-'). \n"
				+ ":- op( 1200, fx,   '?-'). \n"
				+ ":- op( 1100, xfy,  ';'). \n"
				+ ":- op( 1050, xfy,  '->'). \n"
				+ ":- op( 1000, xfy,  ','). \n"
				+ ":- op(  900, fy,   '\\+'). \n"
				+ ":- op(  900, fy,   'not'). \n"
				+
				//
				":- op(  700, xfx,  '='). \n"
				+ ":- op(  700, xfx,  '\\='). \n"
				+ ":- op(  700, xfx,  '=='). \n"
				+ ":- op(  700, xfx,  '\\=='). \n"
				+
				//
				":- op(  700, xfx,  '@>'). \n"
				+ ":- op(  700, xfx,  '@<'). \n"
				+ ":- op(  700, xfx,  '@=<'). \n"
				+ ":- op(  700, xfx,  '@>='). \n"
				+ ":- op(  700, xfx,  '=:='). \n"
				+ ":- op(  700, xfx,  '=\\='). \n"
				+ ":- op(  700, xfx,  '>'). \n"
				+ ":- op(  700, xfx,  '<'). \n"
				+ ":- op(  700, xfx,  '=<'). \n"
				+ ":- op(  700, xfx,  '>='). \n"
				+
				//
				":- op(  700, xfx,  'is'). \n"
				+ ":- op(  700, xfx,  '=..'). \n"
				+ ":- op(  500, yfx,  '+'). \n"
				+ ":- op(  500, yfx,  '-'). \n"
				+ ":- op(  500, yfx,  '/\\'). \n"
				+ ":- op(  500, yfx,  '\\/'). \n"
				+ ":- op(  400, yfx,  '*'). \n"
				+ ":- op(  400, yfx,  '/'). \n"
				+ ":- op(  400, yfx,  '//'). \n"
				+ ":- op(  400, yfx,  '>>'). \n"
				+ ":- op(  400, yfx,  '<<'). \n"
				+ ":- op(  400, yfx,  'rem'). \n"
				+ ":- op(  400, yfx,  'mod'). \n"
				+ ":- op(  200, xfx,  '**'). \n"
				+ ":- op(  200, xfy,  '^'). \n"
				+ ":- op(  200, fy,   '\\'). \n"
				+ ":- op(  200, fy,   '-'). \n"
				+
				//
				// flag management
				//
				"current_prolog_flag(Name,Value) :- get_prolog_flag(Name,Value),!.\n"
				+ "current_prolog_flag(Name,Value) :- flag_list(L), member(flag(Name,Value),L).\n"
				+
				//
				// expression/term comparison
				//
				"'=\\='(X,Y):- not expression_equality(X,Y). \n"
				+ "'\\=='(X,Y):- not term_equality(X,Y).\n"
				+ "'@>='(X,Y):- not term_less_than(X,Y).\n"
				+ "'@=<'(X,Y):- not term_greater_than(X,Y).\n"
				+
				//
				// meta-predicates
				//
				"clause(H,B) :- var(H),!, '$instantiation_error'. "
				+ "clause(H,B) :- number(H),!, '$type_error'(callable, H). "
				+ "clause(H,B) :- number(B),!, '$type_error'(callable, B). "
				+ "clause(H,B) :- '$find'(H,L), member((':-'(H,B)),L). "
				+

				"current_predicate(PI) :- PI = Name/Arity, "
				+ "'$all_dynamic_predicate_indicators'(Iterator), "
				+ "has_next(Iterator), "
				+ "'$current_pred_impl'(Name, Arity, Iterator). "
				+

				"has_next(Iterator) :- not('$has_next'(Iterator)), !, fail. "
				+ "has_next(Iterator) :- true. "
				+ "has_next(Iterator) :- has_next(Iterator). "
				+
				//
				"C -> T ; B :- C, !, T. \n"
				+ "C -> T ; B :- !, B. \n"
				+ "C -> T :- C, !, T. \n"
				+ "A ; B :- A. \n                                                                                          "
				+ "A ; B :- B. \n                                                                                          "
				+ "unify_with_occurs_check(X,Y) :- X=Y.\n                                                                     "
				+ // todo, every check now has with_occurs_check, and
					// with_occurs must be shut down manually..
				"current_op(Pri,Type,Name):-get_operators_list(L),member(op(Pri,Type,Name),L).\n                          "
				+ "once(X) :- X.                                                                                  "
				+ "repeat. \n                                                                                              "
				+ "repeat        :- repeat. \n                                                                             "
				+ "'\\+'(P):- not(P). \n                                                                                             "
				+ "not(G) :- call(G),!,fail. \n                                                                     "
				+ "not(_). \n                                                                                              "
				+
				//
				// All solutions predicates
				//
				"findall(Template, Goal, Instances) :- \n"
				+ "new_record_key(Key), \n"
				+ "findall_impl(Template, Goal, Key, L), \n"
				+ "Instances = L. \n"
				+ "findall_impl(Template, Goal, Key, _) :- \n"
				+ "call(Goal), \n"
				+ "copy(Template, CL), \n"
				+ "record(Key, CL), \n"
				+ "fail. \n"
				+ "findall_impl(_, _, Key, Instances) :- "
				+ "recorded(Key, Instances), "
				+ "erase(Key). \n"
				+

				"bagof(Template, Goal, Instances) :- \n"
				+ "free_variables_set(Goal, Template, Set), \n"
				+ "Witness =.. [witness | Set], \n"
				+ "iterated_goal_term(Goal, G), \n"
				+ "findall(Witness + Template, G, S), \n"
				+ "'$bagof_impl_a'(Witness, S, Instances). \n"
				+

				"'$bagof_impl_a'(_, [], _) :- !, fail. \n"
				+ "'$bagof_impl_a'(WitnessIn, BigSet, Instances) :- "
				+ "'$stripBagList'(WitnessIn, BigSet, Matches, RemainderSet, Variant), "
				+ "'$bagof_impl_b'(WitnessIn, Instances, RemainderSet, Variant, Matches). "
				+

				"'$bagof_impl_b'(WitnessIn, Instances, _, Variant, Matches) :- WitnessIn = Variant, Instances = Matches. \n"
				+ // first success?
				"'$bagof_impl_b'(WitnessIn, Instances, RemainderSet, _, _) :- '$bagof_impl_a'(WitnessIn, RemainderSet, Instances). \n"
				+ // get the next from remainding bag
					//
					//
					//
					// "bagof(Template, Goal, Instances) :- " +
					// "free_variables_set(Goal, Template, Set), " +
					// "Witness =.. [witness | Set], " +
					// "iterated_goal_term(Goal, G), " +
					// "findall(Witness + Template, G, S), " +
					// "new_record_key(SetKey), " +
					// "record(SetKey, S), " +
					// "bagof_impl_x(Witness, SetKey, Instances). " +
					//
					// "bagof_impl_x(_, SetKey, _) :- " +
					// "recorded(SetKey, []), " +
					// "!, " +
					// "fail. \n" +
					// "bagof_impl_x(Witness, SetKey, Instances) :- " +
					// "recorded(SetKey, List), " +
					// "split_list(Witness, List, Instances, Rest), " +
					// "erase(SetKey), " +
					// "record(SetKey, Rest). \n" +
					// "bagof_impl_x(Witness, SetKey, Instances) :- bagof_impl_x(Witness, SetKey, Instances). "
					// +
					//
					// "split_list(Witness, [WW + TT | List], [TT | Instances], Rest) :- variant2(Witness, WW), !, split_list(Witness, List, Instances, Rest). "
					// +
					// "split_list(Witness, [H | List], Instances, [H | Rest]) :- split_list(Witness, List, Instances, Rest). "
					// +
					// "split_list(_, [], [], []). " +
					//
				"setof(Template, Goal, Instances) :- \n"
				+ "bagof(Template, Goal, List), \n"
				+ "quicksort(List, '@<', OrderedList), \n"
				+ "no_duplicates(OrderedList, Instances). \n"
				+

				// "free_variables_set(T, V, FV) :- \n" +
				// "variable_set(T, VST), \n" +
				// "existential_variables(T, EVST), \n" +
				// "variable_set(V, VSV), \n" +
				// "list_remove(VST, EVST, V1), " +
				// "list_remove(V1, VSV, V2), " +
				// "FV = V2. \n" +
				//
				// "list_remove([],L2,[]). \n                                                                                    "
				// +
				// "list_remove([E|T1], L2, L3):- member(E, L2), !, list_remove(T1,L2,L3). \n                                                         "
				// +
				// "list_remove([E|T1], L2, [E|T3]):- list_remove(T1,L2,T3). \n                                                         "
				// +
				//
				"free_variables_set(Term, WithRespectTo, Set) :- \n"
				+ "variable_set(Term, VS), \n"
				+ "variable_set(WithRespectTo, VS1), \n"
				+ "existential_variables(Term, EVS1), \n"
				+ "'$list_diff'(VS, VS1, T), \n"
				+ "'$list_diff'(T, EVS1, T2), \n"
				+ "Set =T2. \n"
				+

				"existential_variables(Term, []) :- var(Term), !. \n"
				+ "existential_variables(Term, []) :- atomic(Term), !. \n"
				+ "existential_variables(V ^ G, EVS) :- variable_set(V, VS), \n"
				+ "existential_variables(G, ExistentialVars), \n"
				+ "append(VS, ExistentialVars, EVS). \n"
				+ "existential_variables(_, []). \n"
				+

				"iterated_goal_term(_ ^ SubGoal, Goal) :- iterated_goal_term(SubGoal, Goal). \n"
				+ "iterated_goal_term(G, G). \n"
				+

				// intersect not tested
				"intersect([H|A],B,[H|C]) :- member(H,B), !, intersect(A,B,C). "
				+ "intersect([H|T],B,C) :- intersect(T,B,C). "
				+ "intersect([],[],[]). "
				+

				"diff(A,B,C) :- diff_impl(A,B,A2,C). "
				+ "diff_impl([H|A],B,A2,C) :- member(H,B), !, diff_impl(A,B,A2,C). "
				+ "diff_impl([H|A],B,A2,[H|C]) :- diff_impl(A,B,A2,C). "
				+ "diff_impl([],[H|B],A2,C) :- member(H,A2), !, diff_impl([],B,A2,C). "
				+ "diff_impl([],[H|B],A2,[H|C]) :- diff_impl([],B,A2,C). "
				+ "diff_impl([],[],[],[]). "
				+

				"no_duplicates([], []). \n"
				+ "no_duplicates([H | T], L) :- member(H, T), !, no_duplicates(T, L). \n"
				+ "no_duplicates([H | T], [H | L]) :- no_duplicates(T, L). \n"
				+
				//
				// theory management predicates
				//
				"retract(Rule) :- Rule = ':-'(Head, Body), !, clause(Head, Body), '$retract'(Rule). \n"
				+ "retract(Fact) :- clause(Fact, true), '$retract'(Fact). \n"
				+

				"retractall(Head) :- findall(Head, clause(Head, _), L), '$retract_clause_list'(L), !. \n"
				+ "'$retract_clause_list'([]). \n"
				+ "'$retract_clause_list'([E | T]) :- !, '$retract'(E), '$retract_clause_list'(T). \n"
				+
				//
				// auxiliary predicates
				//
				"member(E,[E|_]). \n                                                                                     "
				+ "member(E,[_|L]):- member(E,L). \n                                                                       " + "length(L, S) :- number(S), S >= 0, !, lengthN(L, S), !. \n"
				+ "length(L, S) :- var(S), lengthX(L, S). \n" + "lengthN([],0). \n" + "lengthN(_, N) :- nonvar(N), N < 0, !, fail. \n" + "lengthN([_|L], N) :- lengthN(L,M), N is M + 1. \n"
				+ "lengthX([],0). \n" + "lengthX([_|L], N) :- lengthX(L,M), N is M + 1. \n"
				+ "append([],L2,L2). \n                                                                                    "
				+ "append([E|T1],L2,[E|T2]):- append(T1,L2,T2). \n                                                         "
				+ "reverse(L1,L2):- reverse0(L1,[],L2). \n                                                                 "
				+ "reverse0([],Acc,Acc). \n                                                                                "
				+ "reverse0([H|T],Acc,Y):- reverse0(T,[H|Acc],Y). \n                                                       "
				+ "delete(E,[],[]). \n                                                                                     "
				+ "delete2(E,[E|T],L):- !,delete(E,T,L). \n                                                                 "
				+ "delete(E,[H|T],[H|L]):- delete2(E,T,L). \n                                                               "
				+ "element(1,[E|L],E):- !. \n                                                                              "
				+ "element(N,_,_):- N < 0, !, fail. \n                                                                     "
				+ "element(N,[_|L],E):- M is N - 1,element(M,L,E). \n                                                      " +

				"quicksort([],Pred,[]).                             \n" + "quicksort([X|Tail],Pred,Sorted):-                  \n" + "   split(X,Tail,Pred,Small,Big),                   \n"
				+ "   quicksort(Small,Pred,SortedSmall),              \n" + "   quicksort(Big,Pred,SortedBig),                  \n" + "   append(SortedSmall,[X|SortedBig],Sorted).       \n"
				+ "split(_,[],_,[],[]).                               \n" + "split(X,[Y|Tail],Pred,Small,[Y|Big]):-             \n" + "   Predicate =..[Pred,X,Y],                        \n"
				+ "   call(Predicate),!,                              \n" + "   split(X,Tail,Pred,Small,Big).                   \n" + "split(X,[Y|Tail],Pred,[Y|Small],Big):-             \n"
				+ "   split(X,Tail,Pred,Small,Big).                   \n";
	}

	// Internal Java predicates which are part of the bagof/3 and setof/3
	// algorithm

	public boolean $has_next_1(BindingsTable bt, IteratorAsTerm iterator) {
		return iterator.hasNext();
	}

	public boolean $all_dynamic_predicate_indicators_1(BindingsTable bt, Term iterator) throws PrologException {
		if (iterator instanceof Var) {
			Term iteratorTerm = new IteratorAsTerm(engine.dynamicPredicateIndicators());
			return bt.unify(iterator, iteratorTerm);
		}
		if (iterator instanceof IteratorAsTerm)
			return ((IteratorAsTerm) iterator).hasNext();
		throw new PrologException("$all_dynamic_predicate_indicators has a bug. contact ivar.");
	}

	public boolean $current_pred_impl_3(BindingsTable bt, Term name, Term arity, IteratorAsTerm allDynamicPIs) {
		String nextDynamicPI = (String) allDynamicPIs.next();
		String[] pred = nextDynamicPI.split("/");
		if (!bt.unify(name, new StructAtom(pred[0])))
			return false;
		return bt.unify(arity, new Int(pred[1]));
	}

	// "variable_set(T, [T]) :- var(T), !. \n" +
	// "variable_set(T, []) :- atomic(T), !. \n" +
	// "variable_set(T, List) :- T =.. [_|Args], variable_set_arguments(Args, List). \n"
	// +
	//
	// "variable_set_arguments([], []). \n" +
	// "variable_set_arguments([H|T], List) :- " +
	// "variable_set(H, SubList1), " +
	// "variable_set_arguments(T, SubList2), " +
	// "append(SubList1, SubList2, List). \n" +
	public boolean variable_set_2(BindingsTable bt, Term withVars, Term varList) {
		if (withVars instanceof Number || withVars instanceof StructAtom)
			return bt.unify(Term.emptyList, varList);
		LinkedList l = new LinkedList();
		if (withVars instanceof Var)
			l.add(withVars);
		else
			l.addAll(Arrays.asList(((Struct) withVars).getVarList()));
		l.add(Term.emptyList);
		return bt.unify(bt.createStructList(l), varList);
	}

	// internal variable / data base map
	private HashMap map = new HashMap();

	public boolean new_record_key_1(BindingsTable bt, Var key) {
		Long newKey = new Long(key.hashCode());
		return bt.unify(key, newKey);
	}

	public boolean record_2(BindingsTable bt, Number key, Term unit) {
		LinkedList appendStorage = (LinkedList) map.get(key.toString());
		if (appendStorage == null)
			appendStorage = new LinkedList();
		appendStorage.add(BindingsTable.unWrap(unit));
		map.put(key.toString(), appendStorage);
		return true;
	}

	public boolean erase_2(BindingsTable bt, Number key, Term list) {
		LinkedList appendStorage = (LinkedList) map.remove(key.toString());
		if (appendStorage == null)
			return bt.unify(list, Term.emptyList);
		appendStorage.add(Term.emptyList);
		return bt.unify(list, bt.createStructList(appendStorage));
	}

	public boolean recorded_2(BindingsTable bt, Number key, Term list) {
		LinkedList appendStorage = (LinkedList) map.get(key.toString());
		if (appendStorage == null)
			return bt.unify(list, Term.emptyList);
		appendStorage.add(Term.emptyList);
		return bt.unify(list, bt.createStructList(appendStorage));
	}

	public boolean erase_1(BindingsTable bt, Number key) {
		map.remove(key.toString());
		return true;
	}

	// internal variable / data base map

	public boolean $stripBagList_5(BindingsTable bt, Struct witnessIn, Struct a_and_bSet, Term aSet, Term bSet, Term variant) throws PrologException {
		LinkedList results = new LinkedList();
		LinkedList remains = new LinkedList();

		int wrapID = witnessIn instanceof WrapStruct ? ((WrapStruct) witnessIn).context : bt.getUniqueExecutionCtxID();
		witnessIn = (Struct) BindingsTable.wrapWithID(bt.variant(witnessIn), wrapID);

		// 1a. get a variant of the first witness+template from the inputList
		Iterator it = bt.structListIterator(a_and_bSet, true);
		Struct w_t = (Struct) it.next();

		// 1b. the witness branch of the first result will be used to match
		// other results with identical witnesses
		Struct witness1 = (Struct) variant(witnessIn, w_t.getArg(0));
		bt.unify(w_t.getArg(0), witness1);

		// 1c. unify first witness with Variant output and add to result
		bt.unify(variant, witness1);
		results.add(BindingsTable.unWrap(w_t.getArg(1)));

		// 2. iterate the rest of the input list and
		// put all matching witnesses from bigset into result and the rest in
		// remainder
		while (it.hasNext()) {
			w_t = (Struct) it.next();
			Struct witness2 = (Struct) variant(witness1, w_t.getArg(0));
			if (term_equality_2(null, witness1, witness2)) {
				bt.unify(w_t.getArg(0), witness1);
				results.add(BindingsTable.unWrap(w_t.getArg(1)));
			} else {
				remains.add(BindingsTable.unWrap(w_t));
			}
		}

		// 3. unify the results lists and remains list with aSet and bSet
		if (remains.isEmpty()) {
			bt.unify(bSet, Term.emptyList);
		} else {
			remains.add(Term.emptyList);
			bt.unify(bSet, BindingsTable.wrapWithID(Parser.createStructList(remains), wrapID));
		}
		results.add(Term.emptyList);
		return bt.unify(aSet, BindingsTable.wrapWithID(Parser.createStructList(results), wrapID));
	}

	// todo not finished.. Just a hack to see if things roughly work
	// todo bug in the bag_of predicate..
	// todo I think the fault lies with variant..
	// todo I think variant constructs a Struct with Variables from various
	// contexts..
	private static Term variant(Term former, Term latter) {
		if (BasicLibrary.atomic_1(null, latter))
			return latter;
		if (latter instanceof Var) {
			if (former instanceof Var)
				return former;
			throw new RuntimeException("h�");
		}
		if (latter instanceof Struct) {
			if (former instanceof Var)
				return latter;
			if (!(former instanceof Struct))
				throw new RuntimeException("h�2");
			final Struct sFormer = (Struct) former;
			final Struct sLatter = (Struct) latter;
			if (sFormer.predicateIndicator != sLatter.predicateIndicator)
				throw new RuntimeException("h�3");
			Term[] newChildren = new Term[sLatter.arity];
			for (int i = 0; i < sFormer.arity; i++)
				newChildren[i] = variant(BindingsTable.unWrap(sFormer.getArg(i)), BindingsTable.unWrap(sLatter.getArg(i))); // todo
																															// this
																															// is
																															// just
																															// a
																															// hack.
																															// I
																															// don't
																															// think
																															// it
																															// works
																															// at
																															// all,
																															// this
																															// needs
																															// to
																															// be
																															// fixed
			return BindingsTable.wrapWithID(new Struct(sLatter.name, newChildren), ((WrapStruct) former).context); // todo
																													// bug
																													// in
																													// context
																													// setting
																													// here..
		}
		throw new RuntimeException("h�4");
	}

	public boolean $list_diff_3(BindingsTable bt, Struct main, Struct retract, Var diff) throws PrologException {
		if (retract.equals(Term.emptyList))
			return bt.unify(diff, main);
		LinkedList resultList = new LinkedList();
		outer: for (Iterator it = bt.structListIterator(main, true); it.hasNext();) {
			Term child = (Term) it.next();
			for (Iterator it2 = bt.structListIterator(retract, true); it2.hasNext();) {
				Term child2 = (Term) it2.next();
				if (BasicLibrary.term_equality_2(null, child, child2))
					continue outer;
			}
			resultList.add(child);
		}
		if (resultList.isEmpty())
			return bt.unify(diff, Term.emptyList);
		resultList.add(Term.emptyList);
		return bt.unify(diff, bt.createStructList(resultList));
	}

	/**
	 * Defines a map for synonyms for primitives. String primitive name =
	 * String[]{synonym name, another synonym name, ..}.
	 */
	public String[] getSynonym(String primitive) {
		if (primitive.equals("expression_plus"))
			return new String[] { "+" };
		if (primitive.equals("expression_minus"))
			return new String[] { "-" };
		if (primitive.equals("expression_multiply"))
			return new String[] { "*" };
		if (primitive.equals("expression_div"))
			return new String[] { "/" };
		if (primitive.equals("expression_pow"))
			return new String[] { "**" };
		if (primitive.equals("expression_bitwise_shift_right"))
			return new String[] { ">>" };
		if (primitive.equals("expression_bitwise_shift_left"))
			return new String[] { "<<" };
		if (primitive.equals("expression_bitwise_and"))
			return new String[] { "/\\" };
		if (primitive.equals("expression_bitwise_or"))
			return new String[] { "\\/" };
		if (primitive.equals("expression_integer_div"))
			return new String[] { "//" };
		if (primitive.equals("expression_bitwise_not"))
			return new String[] { "\\" };
		if (primitive.equals("$functor"))
			return new String[] { "functor" };
		if (primitive.equals("$arg"))
			return new String[] { "arg" };
		if (primitive.equals("$tofromlist"))
			return new String[] { "=.." };
		if (primitive.equals("expression_equality"))
			return new String[] { "=:=" };
		if (primitive.equals("expression_greater_than"))
			return new String[] { ">" };
		if (primitive.equals("expression_less_than"))
			return new String[] { "<" };
		if (primitive.equals("expression_greater_or_equal_than"))
			return new String[] { ">=" };
		if (primitive.equals("expression_less_or_equal_than"))
			return new String[] { "=<" };
		if (primitive.equals("term_equality"))
			return new String[] { "==" };
		if (primitive.equals("term_greater_than"))
			return new String[] { "@>" };
		if (primitive.equals("term_less_than"))
			return new String[] { "@<" };
		return null;
	}

}