public enum CommonConcepts {
	AND("and"),
	ARG1GENL("arg1Genl"),
	ARG1ISA("arg1Isa"),
	ARG1NOTGENL("arg1NotGenl"),
	ARG1NOTISA("arg1NotIsa"),
	ARG2GENL("arg2Genl"),
	ARG2ISA("arg2Isa"),
	ARG2NOTGENL("arg2NotGenl"),
	ARG2NOTISA("arg2NotIsa"),
	ARGGENL("argGenl"),
	ARGISA("argIsa"),
	ARGNOTGENL("argNotGenl"),
	ARGNOTISA("argNotIsa"),
	ARITY("arity"),
	ASSERTED_SENTENCE("assertedSentence"),
	BINARY_PREDICATE("BinaryPredicate"),
	BROADER_TERM("broaderTerm"),
	CHARACTER_STRING("CharacterString"),
	COLLECTION("Collection"),
	COLLECTION_ORDER("CollectionOrder"),
	COMMENT("comment"),
	DATE("Date"),
	DAYFN("DayFn"),
	DIFFERENT("different"),
	DISJOINTWITH("disjointWith"),
	EQUALS("equals"),
	EVALUATABLE_PREDICATE("EvaluatablePredicate"),
	FALSE("False"),
	FIRST_ORDER_COLLECTION("FirstOrderCollection"),
	FUNCTION("Function-Denotational"),
	GENLINVERSE("genlInverse"),
	GENLMT("genlMt"),
	GENLPREDS("genlPreds"),
	GENLS("genls"),
	GREATER_THAN("greaterThan"),
	GREATER_THAN_EQUAL("greaterThanOrEqualTo"),
	INDIVIDUAL("Individual"),
	INTERVAL_BOUND("TimeInterval"),
	INTERVAL_FUNCTION("TimeIntervalInclusiveFn"),
	ISA("isa"),
	LATER_PREDICATE("laterThan"),
	LESS_THAN("lessThan"),
	LESS_THAN_EQUAL("lessThanOrEqualTo"),
	MONOTONIC_PREDICATE("DefaultMonotonicPredicate"),
	MONTHFN("MonthFn"),
	NEGATIVE_INTEGER("NegativeInteger"),
	NEGATIVE_NUMBER("NegativeNumber"),
	NLP_PREDICATE_STRING("nlpPredicateString"),
	NOT("not"),
	NUMERICALLY_EQUAL("numericallyEquals"),
	OR("or"),
	PARTIALLY_TANGIBLE("PartiallyTangible"),
	POSITIVE_INTEGER("PositiveInteger"),
	POSITIVE_NUMBER("PositiveNumber"),
	PREDICATE("Predicate"),
	PRESERVES_GENLS_IN_ARG("preservesGenlsInArg"),
	PRESERVES_ISA_IN_ARG("preservesIsaInArg"),
	PRETTY_STRING("prettyString"),
	PRETTY_STRING_CANONICAL("prettyString-Canonical"),
	QUOTED_ISA("quotedIsa"),
	REFINABLE_PREDICATE("RefinablePredicate"),
	REIFIABLE_FUNCTION("ReifiableFunction"),
	RELATION("Relation"),
	REMOVED("removed"),
	RESULT_GENL("resultGenl"),
	RESULT_GENL_ARG("resultGenlArg"),
	RESULT_ISA("resultIsa"),
	RESULT_ISA_ARG("resultIsaArg"),
	REWRITE_OF("rewriteOf"),
	SECOND_ORDER_COLLECTION("SecondOrderCollection"),
	SIBLING_DISJOINT_COLLECTION_TYPE("SiblingDisjointCollectionType"),
	SIBLING_DISJOINT_EXCEPTION("siblingDisjointExceptions"),
	SPECIAL_RELATION("SpecialRelation"),
	STRICTLY_FUNCTIONAL_SLOT("StrictlyFunctionalSlot"),
	STRING("CharacterString"),
	SYMMETRIC_BINARY("SymmetricBinaryPredicate"),
	SYNONYMOUS_EXTERNAL_CONCEPT("synonymousExternalConcept"),
	TERM_STRING("termStrings"),
	THE_FN("TheFn"),
	THE_YEAR("TheYear-Indexical"),
	THING("Thing"),
	TIME_INTERVAL("TimeInterval"),
	TRUE("True"),
	UNARY_RELATION("UnaryRelation"),
	UNREIFIABLE_FUNCTION("UnreifiableFunction"),
	URLFN("URLFn"),
	YEARFN("YearFn"),
	ZERO("Zero");

	private static final StringNode _COMMON_CONCEPT = new StringNode(
			"CommonConcept");

	private DAGNode node_;

	private String nodeName_;

	private int setID_ = -1;

	private CommonConcepts(String nodeName) {
		nodeName_ = nodeName;
	}

	public void clearNode() {
		node_ = null;
	}

	public int getID() {
		if (node_ == null)
			return setID_;
		return node_.id_;
	}

	public DAGNode getNode(DirectedAcyclicGraph dag) {
		if (node_ == null)
			node_ = (DAGNode) dag.findOrCreateNode(nodeName_, _COMMON_CONCEPT,
					true, false, true);
		return node_;
	}

	public String getNodeName() {
		return nodeName_;
	}

	public void setID(int id) {
		if (node_ == null)
			setID_ = id;
	}

	@Override
	public String toString() {
		return getID() + "";
	}

	private static void nlpPredicates(CommonConcepts cc, String nlpPred,
			CycDAG dag) {
		dag.findOrCreateEdge(
				new Node[] { NLP_PREDICATE_STRING.getNode(dag),
						cc.getNode(dag), new StringNode(nlpPred) },
				_COMMON_CONCEPT, true);
	}

	public static void createCommonAssertions(CycDAG dag) {
		boolean checks = dag.noChecks_;
		dag.noChecks_ = true;
		// isa, genls
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), ISA.getNode(dag),
				BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), ISA.getNode(dag),
				MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), ISA.getNode(dag),
						PrimitiveNode.parseNode("2"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), GENLS.getNode(dag),
				BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), GENLS.getNode(dag),
				MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), GENLS.getNode(dag),
						PrimitiveNode.parseNode("1"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), GENLS.getNode(dag),
						PrimitiveNode.parseNode("2"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), BINARY_PREDICATE.getNode(dag),
						PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);

		// disjointWith
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), DISJOINTWITH.getNode(dag),
						SYMMETRIC_BINARY.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), DISJOINTWITH.getNode(dag),
						MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), DISJOINTWITH.getNode(dag),
						PrimitiveNode.parseNode("1"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), DISJOINTWITH.getNode(dag),
						PrimitiveNode.parseNode("2"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);

		// Collection order
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), COLLECTION_ORDER.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);

		// Individual
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), INDIVIDUAL.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { DISJOINTWITH.getNode(dag),
						INDIVIDUAL.getNode(dag), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);

		// Collection
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), COLLECTION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), THING.getNode(dag),
				COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), COLLECTION.getNode(dag),
						THING.getNode(dag) }, _COMMON_CONCEPT, true);

		// Predicate
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RELATION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), UNARY_RELATION.getNode(dag),
						RELATION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), UNARY_RELATION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), PREDICATE.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), PREDICATE.getNode(dag),
						RELATION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), SPECIAL_RELATION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), SPECIAL_RELATION.getNode(dag),
						UNARY_RELATION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] {
						COMMENT.getNode(dag),
						SPECIAL_RELATION.getNode(dag),
						new StringNode(
								"The Special Relation collection is a collection of "
										+ "all relations that are treated in a special manner "
										+ "by CycDAG. When an assertion is wrapped by a special "
										+ "relation, CycDAG treats the assertions in a special "
										+ "manner, indexing them as they would normally be "
										+ "indexed without the special relation wrapper. The "
										+ "two primary members are [[not]] and [[removed]].") },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), BINARY_PREDICATE.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), BINARY_PREDICATE.getNode(dag),
						PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);

		// Function
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), FUNCTION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag),
				THE_FN.getNode(dag), FUNCTION.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { RESULT_ISA_ARG.getNode(dag), THE_FN.getNode(dag),
						PrimitiveNode.parseNode("1") }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), FUNCTION.getNode(dag),
						RELATION.getNode(dag) }, _COMMON_CONCEPT, true);

		// resultIsa/genls (+arg)
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_GENL.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_GENL.getNode(dag),
						MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_GENL.getNode(dag),
						PrimitiveNode.parseNode("1"), FUNCTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_GENL.getNode(dag),
						PrimitiveNode.parseNode("2"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_ISA.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_ISA.getNode(dag),
						MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_ISA.getNode(dag),
						PrimitiveNode.parseNode("1"), FUNCTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_ISA.getNode(dag),
						PrimitiveNode.parseNode("2"), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_GENL_ARG.getNode(dag),
						PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_GENL_ARG.getNode(dag),
						MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_GENL_ARG.getNode(dag),
						PrimitiveNode.parseNode("1"), FUNCTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_GENL_ARG.getNode(dag),
						PrimitiveNode.parseNode("2"),
						POSITIVE_INTEGER.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_ISA_ARG.getNode(dag),
						PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), RESULT_ISA_ARG.getNode(dag),
						MONOTONIC_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_ISA_ARG.getNode(dag),
						PrimitiveNode.parseNode("1"), FUNCTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), RESULT_ISA_ARG.getNode(dag),
						PrimitiveNode.parseNode("2"),
						POSITIVE_INTEGER.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						PRESERVES_GENLS_IN_ARG.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag),
						PRESERVES_GENLS_IN_ARG.getNode(dag),
						PrimitiveNode.parseNode("2"),
						POSITIVE_INTEGER.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						PRESERVES_ISA_IN_ARG.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ARGISA.getNode(dag),
				PRESERVES_ISA_IN_ARG.getNode(dag),
				PrimitiveNode.parseNode("2"), POSITIVE_INTEGER.getNode(dag) },
				_COMMON_CONCEPT, true);

		// Strictly Functional
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						STRICTLY_FUNCTIONAL_SLOT.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), COMMENT.getNode(dag),
						STRICTLY_FUNCTIONAL_SLOT.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), ARITY.getNode(dag),
				STRICTLY_FUNCTIONAL_SLOT.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						NLP_PREDICATE_STRING.getNode(dag),
						STRICTLY_FUNCTIONAL_SLOT.getNode(dag) },
				_COMMON_CONCEPT, true);

		// NLP Predicates
		dag.findOrCreateEdge(
				new Node[] { GENLPREDS.getNode(dag),
						PRETTY_STRING.getNode(dag), TERM_STRING.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(

				new Node[] { GENLPREDS.getNode(dag),
						PRETTY_STRING_CANONICAL.getNode(dag),
						PRETTY_STRING.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { PRETTY_STRING_CANONICAL.getNode(dag),
				PRETTY_STRING_CANONICAL.getNode(dag),
				new StringNode("commonly known as") }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(

				new Node[] { PRETTY_STRING_CANONICAL.getNode(dag),
						PRETTY_STRING.getNode(dag), new StringNode("known as") },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), CHARACTER_STRING.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag), STRING.getNode(dag),
						CHARACTER_STRING.getNode(dag) }, _COMMON_CONCEPT, true);
		Node theString = dag.findOrCreateFunctionNode(true, false,
				_COMMON_CONCEPT, THE_FN.getNode(dag), STRING.getNode(dag));
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), theString, STRING.getNode(dag) },
				_COMMON_CONCEPT, true);

		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						NLP_PREDICATE_STRING.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARITY.getNode(dag),
						NLP_PREDICATE_STRING.getNode(dag),
						PrimitiveNode.parseNode("2") }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ARGISA.getNode(dag),
				NLP_PREDICATE_STRING.getNode(dag),
				PrimitiveNode.parseNode("1"), PREDICATE.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ARGISA.getNode(dag),
				NLP_PREDICATE_STRING.getNode(dag),
				PrimitiveNode.parseNode("2"), CHARACTER_STRING.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] {
				COMMENT.getNode(dag),
				NLP_PREDICATE_STRING.getNode(dag),
				new StringNode(
						"A predicate for converting binary assertions into "
								+ "a natural language phrase. ("
								+ NLP_PREDICATE_STRING.nodeName_
								+ " PRED NLPSTR) means that PRED can be "
								+ "described using NLPSTR in the context of "
								+ "an adverb (e.g. 'an instance of', 'broader "
								+ "than', etc).") }, _COMMON_CONCEPT, true);
		Edge e = dag.findEdge(ISA.getNode(dag), URLFN.getNode(dag),
				REIFIABLE_FUNCTION.getNode(dag));
		dag.removeEdge(e, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), URLFN.getNode(dag),
				UNREIFIABLE_FUNCTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { PRETTY_STRING_CANONICAL.getNode(dag),
						URLFN.getNode(dag),
						new StringNode("<a href=______>______</a>") },
				_COMMON_CONCEPT, true);

		// Ordered Collections
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), INDIVIDUAL.getNode(dag),
						COLLECTION_ORDER.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), INDIVIDUAL.getNode(dag),
						FIRST_ORDER_COLLECTION.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(new Node[] { GENLS.getNode(dag),
				FIRST_ORDER_COLLECTION.getNode(dag), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						FIRST_ORDER_COLLECTION.getNode(dag),
						COLLECTION_ORDER.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag),
						SECOND_ORDER_COLLECTION.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						FIRST_ORDER_COLLECTION.getNode(dag),
						SECOND_ORDER_COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						SECOND_ORDER_COLLECTION.getNode(dag),
						COLLECTION_ORDER.getNode(dag) }, _COMMON_CONCEPT, true);

		// Primitives
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag),
				STRING.getNode(dag), COLLECTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), POSITIVE_INTEGER.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), NEGATIVE_INTEGER.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), ZERO.getNode(dag),
				INDIVIDUAL.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), POSITIVE_NUMBER.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), NEGATIVE_NUMBER.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), TRUE.getNode(dag),
				INDIVIDUAL.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), FALSE.getNode(dag),
				INDIVIDUAL.getNode(dag) }, _COMMON_CONCEPT, true);

		// Time functions
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), DATE.getNode(dag),
				COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), DAYFN.getNode(dag),
				UNREIFIABLE_FUNCTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(

				new Node[] { RESULT_ISA.getNode(dag), DAYFN.getNode(dag),
						DATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), MONTHFN.getNode(dag),
						UNREIFIABLE_FUNCTION.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { RESULT_ISA.getNode(dag), MONTHFN.getNode(dag),
						DATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag),
				YEARFN.getNode(dag), UNREIFIABLE_FUNCTION.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(

				new Node[] { RESULT_ISA.getNode(dag), YEARFN.getNode(dag),
						DATE.getNode(dag) }, _COMMON_CONCEPT, true);

		// Temporal Concepts
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), LATER_PREDICATE.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), LATER_PREDICATE.getNode(dag),
						PrimitiveNode.parseNode("1"), DATE.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ARGISA.getNode(dag), LATER_PREDICATE.getNode(dag),
						PrimitiveNode.parseNode("2"), DATE.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), INTERVAL_BOUND.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), INTERVAL_FUNCTION.getNode(dag),
						UNREIFIABLE_FUNCTION.getNode(dag) }, _COMMON_CONCEPT,
				true);

		// Evaluatable Predicates
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), DIFFERENT.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag),
				EQUALS.getNode(dag), EVALUATABLE_PREDICATE.getNode(dag) },
				_COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), GREATER_THAN.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), GREATER_THAN_EQUAL.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), LESS_THAN.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), LESS_THAN_EQUAL.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), LATER_PREDICATE.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), NUMERICALLY_EQUAL.getNode(dag),
						EVALUATABLE_PREDICATE.getNode(dag) }, _COMMON_CONCEPT,
				true);

		// Special predicates
		dag.findOrCreateEdge(new Node[] { ISA.getNode(dag), NOT.getNode(dag),
				SPECIAL_RELATION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag), REMOVED.getNode(dag),
						SPECIAL_RELATION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] {
						COMMENT.getNode(dag),
						REMOVED.getNode(dag),
						new StringNode(
								"The removed relation is a special relation for "
										+ "functionally removing assertions from the DAG "
										+ "without removing them completely. When an "
										+ "assertion is wrapped with a removed relation, "
										+ "it is treated as non-existent for most purposes, "
										+ "except indexing operations.") },
				_COMMON_CONCEPT, true);

		// Refinable predicate
		dag.findOrCreateEdge(
				new Node[] { ISA.getNode(dag),
						REFINABLE_PREDICATE.getNode(dag),
						COLLECTION.getNode(dag) }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(new Node[] {
				COMMENT.getNode(dag),
				REFINABLE_PREDICATE.getNode(dag),
				new StringNode("A RefinablePredicate is a predicate which "
						+ "inductively defines its semantic argument "
						+ "constraints based on the assertions that "
						+ "employ the predicate. Once a certain number "
						+ "of assertions are created that use a "
						+ "refinable predicate, the semantic argument "
						+ "constraints are inductively calculated and "
						+ "asserted, rejecting any evidence that no "
						+ "longer fits the constraints. Refinable "
						+ "predicates are also transient, in that "
						+ "they may be completely removed if they are "
						+ "found to be unused.") }, _COMMON_CONCEPT, true);
		dag.findOrCreateEdge(
				new Node[] { GENLS.getNode(dag),
						REFINABLE_PREDICATE.getNode(dag),
						BINARY_PREDICATE.getNode(dag) }, _COMMON_CONCEPT, true);

		// $1 /1(is)/(are)/ <text string> $2
		nlpPredicates(ARGGENL,
				"/2(argument $2)/(what arguments)/ of $1 is a type of $3", dag);
		nlpPredicates(ARG1GENL, "argument 1 of $1 is a type of $2", dag);
		nlpPredicates(ARG2GENL, "argument 2 of $1 is a type of $2", dag);
		nlpPredicates(ARGISA,
				"/2(argument $2)/(what arguments)/ of $1 is an instance of $3",
				dag);
		nlpPredicates(ARG1ISA, "argument 1 of $1 is an instance of $2", dag);
		nlpPredicates(ARG2ISA, "argument 2 of $1 is an instance of $2", dag);
		nlpPredicates(
				ARGNOTGENL,
				"/2(argument $2)/(what arguments)/ of $1 cannot be a type of $3",
				dag);
		nlpPredicates(ARG1NOTGENL, "argument 1 of $1 cannot be a type of $2",
				dag);
		nlpPredicates(ARG2NOTGENL, "argument 2 of $1 cannot be a type of $2",
				dag);
		nlpPredicates(
				ARGNOTISA,
				"/2(argument $2)/(what arguments)/ of $1 cannot be an instance of $3",
				dag);
		nlpPredicates(ARG1NOTISA,
				"argument 1 of $1 cannot be an instance of $2", dag);
		nlpPredicates(ARG2NOTISA,
				"argument 2 of $1 cannot be an instance of $2", dag);
		nlpPredicates(ARITY, "$1 /1(has)/(have)/ arity /2($2)/(what)/", dag);
		nlpPredicates(ASSERTED_SENTENCE, "$1 /1(is)/(are)/ asserted directly",
				dag);
		nlpPredicates(BROADER_TERM, "$2 /2(is)/(are)/ broader than $1", dag);
		nlpPredicates(COMMENT,
				"$1 /1(has)/(have)/ /2(comment $2)/(what comment)/", dag);
		nlpPredicates(DIFFERENT, "$1 /1(is)/(are)/ different to $2", dag);
		nlpPredicates(DISJOINTWITH, "$1 /1(is)/(are)/ disjoint with $2", dag);
		nlpPredicates(GENLINVERSE, "$1 /1(is)/(are)/ a supertype of $2", dag);
		nlpPredicates(GENLMT, "$1 /1(is)/(are)/ a sub-microtheory of $2", dag);
		nlpPredicates(GENLPREDS, "$1 /1(is)/(are)/ a sub-predicate of $2", dag);
		nlpPredicates(GENLS, "$1 /1(is)/(are)/ a kind of $2", dag);
		nlpPredicates(ISA, "$1 /1(is)/(are)/ an instance of $2", dag);
		nlpPredicates(LATER_PREDICATE, "$1 /1(is)/(are)/ later than $2", dag);
		nlpPredicates(NLP_PREDICATE_STRING,
				"$2 /2(is)/(are)/ the NL predicate string for $1", dag);
		nlpPredicates(PRETTY_STRING, "$1 /1(is)/(are)/ known as $2", dag);
		nlpPredicates(PRETTY_STRING_CANONICAL,
				"$1 /1(is)/(are)/ commonly known as $2", dag);
		nlpPredicates(QUOTED_ISA,
				"$1 /1(is)/(are)/ implicitly an instance of $2", dag);
		nlpPredicates(RESULT_GENL, "$2 /2(is)/(are)/ the type produced by $1",
				dag);
		nlpPredicates(RESULT_ISA,
				"$2 /2(is)/(are)/ the instance produced by $1", dag);
		nlpPredicates(
				RESULT_GENL_ARG,
				"the type produced by $1 /1(is)/(are)/ the same as /2(argument $2)/(what arguments)/",
				dag);
		nlpPredicates(
				RESULT_ISA_ARG,
				"the instance produced by $1 /1(is)/(are)/ the same as /2(argument $2)/(what arguments)/",
				dag);
		nlpPredicates(TERM_STRING, "$1 /1(is)/(are)/ also known as $2", dag);
		nlpPredicates(SIBLING_DISJOINT_EXCEPTION,
				"$1 /1(is)/(are)/ exempt from disjointness with $2", dag);
		nlpPredicates(REWRITE_OF,
				"$1 /1(is)/(are)/ the favoured rewrite of $2", dag);
		dag.noChecks_ = checks;
	}

	public static void initialise(CycDAG dag) {
		for (CommonConcepts cc : CommonConcepts.values()) {
			cc.clearNode();
			cc.getNode(dag);
		}
	}
}
 public class EdgeModifier {
	/**
	 * Returns the encapsulated nodes in an array of nodes if they are modified.
	 *
	 * @param nodes
	 *            The possibly encapsulated nodes to return (without modifier).
	 * @param dag
	 *            The DAG access.
	 * @return Either the same nodes if no modifier, or the nodes in the 2nd
	 *         argument position of the modified node array..
	 */
	public static Node[] getUnmodNodes(Node[] nodes, DirectedAcyclicGraph dag) {
		if (nodes.length == 2 && nodes[1] instanceof OntologyFunction
				&& isModifier(nodes[0], dag))
			return getUnmodNodes(((OntologyFunction) nodes[1]).getNodes(), dag);
		return nodes;
	}

	/**
	 * If the given node is a modifier node.
	 *
	 * @param node
	 *            The node to check.
	 * @param dag
	 *            The DAG access.
	 * @return True if it is a modifier node (NOT or REMOVED)
	 */
	private static boolean isModifier(Node node, DirectedAcyclicGraph dag) {
		if (isNegated(node, dag))
			return true;
		if (isRemoved(node, dag))
			return true;
		return false;
	}

	/**
	 * Returns the unmodified nodes of an edge.
	 *
	 * @param edge
	 *            The edge to extract nodes for.
	 * @param dag
	 *            The DAG access.
	 * @return The nodes of the edge without modifiers.
	 */
	public static Node[] getUnmodNodes(Edge edge, DirectedAcyclicGraph dag) {
		return getUnmodNodes(edge.getNodes(), dag);
	}

	public static boolean isNegated(Node node, DirectedAcyclicGraph dag) {
		return node.equals(CommonConcepts.NOT.getNode(dag));
	}

	public static boolean isNegated(Edge edge, DirectedAcyclicGraph dag) {
		return isNegated(edge.getNodes()[0], dag);
	}

	public static boolean isRemoved(Node node, DirectedAcyclicGraph dag) {
		return node.equals(CommonConcepts.REMOVED.getNode(dag));
	}

	public static boolean isRemoved(Edge edge, DirectedAcyclicGraph dag) {
		return isRemoved(edge.getNodes()[0], dag);
	}

	public static boolean isSpecial(Edge edge, DirectedAcyclicGraph dag) {
		if (isNegated(edge, dag))
			return true;
		if (isRemoved(edge, dag))
			return true;
		return false;
	}

	public static boolean isSpecial(Node[] edgeNodes, DirectedAcyclicGraph dag) {
		if (isNegated(edgeNodes[0], dag))
			return true;
		if (isRemoved(edgeNodes[0], dag))
			return true;
		return false;
	}
}
 public class OntologyFunction extends DAGNode implements Edge {
	private static final long serialVersionUID = 473544398260462641L;
	private boolean notAnonymous_ = true;
	protected Node[] nodes_;

	public OntologyFunction() {
		super();
	}

	public OntologyFunction(Node... nodes) {
		super();
		nodes_ = nodes;
		notAnonymous_ = false;
		id_ = -1;
	}

	protected OntologyFunction(DirectedAcyclicGraph dag, Node... nodes) {
		super();
		nodes_ = nodes;
		notAnonymous_ = !checkIfAnonymous(dag);
		id_ = -1;
	}

	@Override
	protected int requestID() {
		return -1;
	}

	public void reify(Node creator, CycDAG cycDAG) {
		if (id_ == -1 && notAnonymous_) {
			DAGNode existingNode = cycDAG.findDAGNode(getIdentifier(true));
			if (existingNode != null)
				id_ = existingNode.id_;
			else {
				id_ = super.requestID();
				if (creator != null)
					put(CREATOR, creator.getIdentifier(true));
			}
		}

		// Also reify the inner nodes if necessary
		for (Node n : nodes_) {
			if (n instanceof OntologyFunction)
				((OntologyFunction) n).reify(creator, cycDAG);
		}
	}

	private boolean checkIfAnonymous(DirectedAcyclicGraph dag) {
		// Check if function is unreifiable
		QueryModule queryModule = (QueryModule) dag
				.getModule(QueryModule.class);
		// If nodes[0] is unreifiable OR is not a function
		if (queryModule.prove(false, CommonConcepts.ISA.getNode(dag),
				nodes_[0], CommonConcepts.UNREIFIABLE_FUNCTION.getNode(dag)) == QueryResult.TRUE)
			return true;
		// If not a function
		// TODO Keep an eye on this
		// if (queryModule.prove(false, CommonConcepts.ISA.getNode(dag),
		// nodes_[0], CommonConcepts.FUNCTION.getNode(dag)) ==
		// QueryResult.FALSE)
		// return true;
		return false;
	}

	@Override
	public int compareTo(DAGObject o) {
		int result = super.compareTo(o);
		if (result != 0)
			return result;

		return toString().compareTo(o.toString());
	}

	@Override
	public boolean containsNode(Node node) {
		for (Node edgeNode : getNodes())
			if (edgeNode.equals(node))
				return true;
		return false;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null)
			return false;
		if (this == obj)
			return true;
		if (getClass() != obj.getClass())
			return false;
		OntologyFunction other = (OntologyFunction) obj;
		if (!Arrays.equals(nodes_, other.nodes_))
			return false;
		return true;
	}

	@Override
	public String getIdentifier() {
		return getIdentifier(false);
	}

	@Override
	public String getIdentifier(boolean useName) {
		if (id_ != -1 && !useName)
			return id_ + "";

		StringBuilder buffer = new StringBuilder("(");
		boolean first = true;
		for (Node n : nodes_) {
			if (!first)
				buffer.append(" ");
			buffer.append(n.getIdentifier(useName));
			first = false;
		}
		buffer.append(")");
		return buffer.toString();
	}

	@Override
	public String getName() {
		return "(" + StringUtils.join(nodes_, ' ') + ")";
	}

	@Override
	public Node[] getNodes() {
		return nodes_;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = prime + Arrays.hashCode(nodes_);
		return result;
	}

	@Override
	public boolean isAnonymous() {
		return !notAnonymous_;
	}

	@Override
	public String toString(boolean useIDs) {
		if (!useIDs)
			return toString();
		StringBuilder buffer = new StringBuilder("(");
		boolean first = true;
		for (Node n : nodes_) {
			if (!first)
				buffer.append(" ");
			buffer.append(n.getIdentifier());
			first = false;
		}
		buffer.append(")");
		return buffer.toString();
	}
}
 public enum CommonQuery {
	ALIAS("(termStrings ?0 ?X)"),
	ALLGENLS("(genls ?0 ?X)"),
	ALLISA("(isa ?0 ?X)"),
	ARGNGENL("(argGenl ?0 ?1 ?X)", true),
	ARGNISA("(argIsa ?0 ?1 ?X)", true),
	COMMENT("(comment ?0 ?X)"),
	COMMONGENLS("(and (genls ?0 ?X) (genls ?1 ?X))"),
	COMMONINSTANCES("(and (isa ?X ?0) (isa ?X ?1))"),
	COMMONISA("(and (isa ?0 ?X) (isa ?1 ?X))"),
	COMMONSPECS("(and (genls ?X ?0) (genls ?X ?1))"),
	DIRECTGENLS("(assertedSentence (genls ?0 ?X))", true),
	DIRECTINSTANCE("(assertedSentence (isa ?X ?0))", true),
	DIRECTISA("(assertedSentence (isa ?0 ?X))", true),
	DIRECTSPECS("(assertedSentence (genls ?X ?0))", true),
	GENLPREDS("(genlPreds ?0 ?X)"),
	GENLSIBLINGS("(assertedSentence (genls ?0 ?X))", true),
	INSTANCES("(isa ?X ?0)"),
	ISASIBLINGS("(assertedSentence (isa ?0 ?X))", true),
	MAXCOMMONINSTANCES("(and (isa ?X ?0) (isa ?X ?1))"),
	MAXCOMMONSPECS("(and (genls ?X ?0) (genls ?X ?1))"),
	MAXINSTANCES("(isa ?X ?0)", true),
	MAXSPECS("(assertedSentence (genls ?X ?0))", true),
	MINARGNGENL("(argGenl ?0 ?1 ?X)", true),
	MINARGNISA("(argIsa ?0 ?1 ?X)", true),
	MINCOMMONGENLS("(and (genls ?0 ?X) (genls ?1 ?X))", true),
	MINCOMMONISA("(and (isa ?0 ?X) (isa ?1 ?X))", true),
	MINGENLS("(assertedSentence (genls ?0 ?X))", true),
	MINISA("(assertedSentence (isa ?0 ?X))", true),
	SPECPREDS("(genlPreds ?X ?0)"),
	SPECS("(genls ?X ?0)");

	private QueryObject queryObject_;
	private String queryStr_;

	private boolean specialQuery_;

	private CommonQuery(String query) {
		this(query, false);
	}

	private CommonQuery(String query, boolean specialQuery) {
		queryStr_ = UtilityMethods.shrinkString(query, 1);
		specialQuery_ = specialQuery;
	}

	/**
	 * Converts the query string into a partially instantiated QueryObject.
	 *
	 * @param dag
	 *            The reference to the DAG.
	 */
	private void initialiseQueryObject(DirectedAcyclicGraph dag) {
		ArrayList<String> split = UtilityMethods.split(queryStr_, ' ');
		int i = 0;
		Node[] nodes = new Node[split.size()];
		for (String s : split)
			nodes[i++] = dag
					.findOrCreateNode(s, null, true, false, false, true);
		queryObject_ = new QueryObject(nodes);
	}

	/**
	 * Performs additional, query-specific, operations.
	 * 
	 * @param results
	 *            The results to alter.
	 * @param querier
	 *            The query module.
	 * @param dag
	 *            The DAG access.
	 * @param args
	 *            The args provided.
	 * @return Altered results, determined by special operations.
	 */
	private Collection<Node> runSpecial(Collection<Node> results,
			QueryModule querier, DirectedAcyclicGraph dag, Node[] args) {
		// Min args
		switch (this) {
		case ARGNISA:
		case MINARGNISA:
			// TODO Fix this.
			DAGNode argIsa = (DAGNode) dag.findOrCreateNode("arg"
					+ ((PrimitiveNode) args[1]).getPrimitive() + "Isa", null,
					false);
			QueryObject qo = new QueryObject(argIsa, args[0], new VariableNode(
					"?X"));
			for (Node n : QueryModule.parseResultsFromSubstitutions("?X",
					querier.executeQuery(qo)))
				if (!results.contains(n))
					results.add(n);
			break;
		case ARGNGENL:
		case MINARGNGENL:
			DAGNode argGenls = (DAGNode) dag.findOrCreateNode("arg"
					+ ((PrimitiveNode) args[1]).getPrimitive() + "Genl", null,
					false);
			qo = new QueryObject(argGenls, args[0], new VariableNode("?X"));
			for (Node n : QueryModule.parseResultsFromSubstitutions("?X",
					querier.executeQuery(qo)))
				if (!results.contains(n))
					results.add(n);
			break;
		default:
			break;
		}

		// Mins & maxes
		switch (this) {
		case GENLSIBLINGS:
		case MINGENLS:
			results.remove(args[0]);
		case ISASIBLINGS:
		case MINISA:
		case MINCOMMONISA:
		case MINCOMMONGENLS:
		case MINARGNISA:
		case MINARGNGENL:
			minGeneralFilter(results, dag);

			// Run the Siblings
			if (this == ISASIBLINGS || this == GENLSIBLINGS) {
				Collection<Node> parents = new ArrayList<>(results);
				results.clear();
				for (Node sibParent : parents) {
					CommonQuery downCQ = (this == ISASIBLINGS) ? CommonQuery.INSTANCES
							: CommonQuery.SPECS;
					results.addAll(downCQ.runQuery(dag, sibParent));
				}
				results.remove(args[0]);
			}
			break;
		case MAXSPECS:
			results.remove(args[0]);
		case MAXINSTANCES:
		case MAXCOMMONINSTANCES:
		case MAXCOMMONSPECS:
			if (this == MAXINSTANCES
					&& querier.prove(false, CommonConcepts.ISA.getNode(dag),
							args[0],
							CommonConcepts.FIRST_ORDER_COLLECTION.getNode(dag)) == QueryResult.TRUE)
				return results;
			maxSpecFilter(results, dag);
			break;
		case DIRECTISA:
			if (args[0] instanceof OntologyFunction) {
				for (Node n : querier.functionResults(
						(OntologyFunction) args[0], CommonConcepts.RESULT_ISA))
					if (!results.contains(n))
						results.add(n);
			}
			break;
		case DIRECTGENLS:
			if (args[0] instanceof OntologyFunction) {
				for (Node n : querier.functionResults(
						(OntologyFunction) args[0], CommonConcepts.RESULT_GENL))
					if (!results.contains(n))
						results.add(n);
			}
			break;
		case DIRECTINSTANCE:
			break;
		case DIRECTSPECS:
			break;
		default:
			break;
		}
		return results;
	}

	/**
	 * Runs this quick query with the given arguments. Returns the set of
	 * matching results, which is empty if no results found.
	 *
	 * @param dag
	 *            The dag access.
	 * @param args
	 *            The arguments of the query.
	 * @return The set of matching results, or empty if NIL.
	 */
	public Collection<Node> runQuery(DirectedAcyclicGraph dag, Node... args) {
		if (queryObject_ == null)
			initialiseQueryObject(dag);

		// Create a new QueryObject, replacing the placeholder variable
		Substitution sub = new Substitution();
		for (int j = 0; j < args.length; j++)
			sub.addSubstitution(new VariableNode("?" + j), args[j]);
		Node[] replacedArgs = sub.applySubstitution(queryObject_.getNodes());
		QueryObject qo = new QueryObject(false, false, QueryResult.TRUE,
				replacedArgs);

		QueryModule querier = (QueryModule) dag.getModule(QueryModule.class);

		Collection<Node> results = QueryModule.parseResultsFromSubstitutions(
				"?X", querier.executeQuery(qo));
		if (specialQuery_)
			results = runSpecial(results, querier, dag, args);
		return results;
	}

	/**
	 * Filter results to find the maximally general results
	 * 
	 * @param results
	 *            The results to filter.
	 * @param dag
	 *            The DAG access.
	 * @return The filtered input (same collection).
	 */
	public static Collection<Node> maxSpecFilter(Collection<Node> results,
			DirectedAcyclicGraph dag) {
		if (results.size() <= 1)
			return results;

		Set<Node> removed = new HashSet<>();
		for (Node node : results) {
			if (!removed.contains(node)) {
				// Get the genls
				Collection<Node> genls = CommonQuery.SPECS.runQuery(dag, node);
				genls.remove(node);

				// Remove any results that genls this node
				removed.addAll(genls);
			}
		}
		results.removeAll(removed);
		return results;
	}

	/**
	 * Filters results to find the minimally general results.
	 * 
	 * @param results
	 *            The results to filter.
	 * @param dag
	 *            The DAG access.
	 * @return The filtered input (same collection).
	 */
	public static Collection<? extends Node> minGeneralFilter(
			Collection<? extends Node> results, DirectedAcyclicGraph dag) {
		if (results.size() <= 1)
			return results;

		Set<Node> removed = new HashSet<>();
		for (Node node : results) {
			if (!removed.contains(node)) {
				// Get the genls
				Collection<Node> genls = CommonQuery.ALLGENLS.runQuery(dag,
						node);
				genls.remove(node);

				removed.addAll(genls);
			}
		}
		results.removeAll(removed);
		return results;
	}

	public static void reset() {
		for (CommonQuery cq : values())
			cq.queryObject_ = null;
	}
}
 public class QueryObject {
	/** The index of the first atomic argument. */
	private int atomicIndex_ = -1;
	/** All atomic arguments in the query. */
	private ArrayList<DAGNode> atomics_;
	/** All DAGNodes that have been processed. */
	private ArrayList<DAGNode> completed_;
	/** All DAGNodes that have been processed in set form. */
	private Set<DAGNode> completedSet_;
	/** The justification for the results. */
	private List<Node[]> justification_;
	/** If a justification is required. */
	private boolean needJustification_;
	/** The nodes of the query. */
	private Node[] nodes_;
	/** Any prior substitutions to apply to the nodes. */
	private Substitution priorSubstitution_;
	/** The substitutions for a result. */
	private SortedSet<Substitution> results_;
	/** The substitutions for a result in set form. */
	private Set<Substitution> resultsSet_;
	/** The substitutions yet to be matched. */
	private Set<Substitution> toComplete_;
	/** The index of the first variable argument. */
	private int variableIndex_ = -1;
	/** All variable arguments in the query. */
	private ArrayList<VariableNode> variables_;
	/** The state of the results - if TRUE, FALSE, or NIL. */
	private ObjectWrapper<QueryResult> resultState_ = new ObjectWrapper<>(
			QueryResult.NIL);
	/** The reason for a query's rejection (if rejected). */
	private ErrorEdge rejectionReason_;
	/** If the query has already been run. */
	private boolean hasRun_ = false;
	/** If the arguments should be verified. */
	private boolean verifyArguments_ = true;
	/** The query should only be run when the goal result can be attained. */
	private QueryResult goalResult_;

	// TODO Implement a 'cares about' variable so queries can be skipped if they
	// don't produce the cared about answer.

	/**
	 * Constructor for a new QueryObject. This QO is initialised with no
	 * justification.
	 * 
	 * @param nodes
	 *            The nodes of the query.
	 */
	public QueryObject(Node... nodes) {
		this(false, false, QueryResult.ALL, nodes);
	}

	/**
	 * Constructor for a new QueryObject with optional justification.
	 * 
	 * @param needJustification
	 *            If a justification is required.
	 * @param goalResult
	 *            The goal result for this query (i.e. what are we
	 *            interpreting?). Default to ALL.
	 * @param nodes
	 *            The nodes of the query.
	 */
	public QueryObject(boolean shouldVerify, boolean needJustification,
			QueryResult goalResult, Node... nodes) {
		verifyArguments_ = shouldVerify;
		nodes_ = nodes;
		buildAtomicVariableSets(nodes);
		results_ = new TreeSet<>();
		resultsSet_ = new HashSet<>();
		completed_ = new ArrayList<>();
		completedSet_ = new HashSet<>();
		needJustification_ = needJustification;
		justification_ = new ArrayList<>();
		goalResult_ = goalResult;
	}

	private void buildAtomicVariableSets(Node... nodes) {
		atomics_ = new ArrayList<>();
		variables_ = new ArrayList<>();
		for (int i = 1; i < nodes.length; i++) {
			if (nodes[i] instanceof VariableNode
					&& !variables_.contains(nodes[i])) {
				variables_.add((VariableNode) nodes[i]);
				variableIndex_ = i;
			} else if (nodes[i] instanceof OntologyFunction) {
				if (!processFunction((OntologyFunction) nodes[i])) {
					atomics_.add((DAGNode) nodes[i]);
					if (atomicIndex_ == -1)
						atomicIndex_ = i;
					else
						variableIndex_ = i;
				}
			} else if (nodes[i] instanceof DAGNode
					&& !atomics_.contains(nodes[i])) {
				atomics_.add((DAGNode) nodes[i]);
				if (atomicIndex_ == -1)
					atomicIndex_ = i;
				else
					variableIndex_ = i;
			}
		}
	}

	/**
	 * Processes a function and extracts variables. Returns true if any
	 * variables are found.
	 * 
	 * @param func
	 *            The function to process.
	 * @return True if the function contains any variables.
	 */
	private boolean processFunction(OntologyFunction func) {
		boolean isVariable = false;
		for (Node n : func.getNodes()) {
			if (n instanceof VariableNode && !variables_.contains(n)) {
				isVariable = true;
				variables_.add((VariableNode) n);
			} else if (n instanceof OntologyFunction)
				isVariable |= processFunction((OntologyFunction) n);
		}
		return isVariable;
	}

	public void addCompleted(DAGNode n) {
		if (completedSet_.add(n))
			completed_.add(n);
	}

	/**
	 * Adds a result and creates substitutions where necessary.
	 * 
	 * @param isTrueResult
	 *            If the result being added is a positive example (i.e. true),
	 *            or a negative example (i.e. proof against).
	 * @param nodes
	 *            The nodes being recorded and substituted.
	 * @return True if the inference can stop (either proof found, or stopping
	 *         parameter met).
	 */
	public boolean addResult(boolean isTrueResult, Node... nodes) {
		Node[] nonNegNodes = EdgeModifier.getUnmodNodes(nodes, CycDAG.selfRef_);
		if (nonNegNodes.length != nodes_.length)
			return false;

		// Note the justification chain
		if (needJustification_)
			justification_.add(nodes);

		// Create a substitution
		Substitution s = new Substitution();
		for (int i = 0; i < nodes_.length; i++) {
			if (nodes_[i] instanceof VariableNode) {
				String varStr = nodes_[i].toString();
				if (s.containsVariable(varStr)
						&& s.getSubstitution(varStr) != nonNegNodes[i])
					return false;
				s.addSubstitution(varStr, nonNegNodes[i]);
			} else if (isProof() && !nonNegNodes[i].equals(nodes_[i]))
				return false;
		}
		return addResult(isTrueResult, s);
	}

	/**
	 * Adds a result to the query. Returns true if the inference can stop.
	 * 
	 * @param isTrueResult
	 *            If the result being added is a positive example (i.e. true),
	 *            or a negative example (i.e. proof against).
	 * @param substitution
	 *            The result to add.
	 * @param justificationNodes
	 *            The justification to note for the result.
	 * @return True if the inference can stop (either proof found, or stopping
	 *         parameter met).
	 */
	public boolean addResult(boolean isTrueResult, Substitution substitution,
			Node... justificationNodes) {
		if (needJustification_ && justificationNodes != null
				&& justificationNodes.length > 0)
			justification_.add(justificationNodes);

		// OR based truth
		if (isTrueResult)
			resultState_.object_ = QueryResult.TRUE;
		else if (resultState_.object_ == QueryResult.NIL)
			resultState_.object_ = QueryResult.FALSE;
		if (isProof()) {
			if (isTrueResult) {
				// Check proof.
				if (!substitution.isEmpty())
					substitution = new Substitution();
				if (priorSubstitution_ != null)
					substitution = priorSubstitution_;
				results_.add(substitution);
				resultsSet_.add(substitution);
			}
			return true;
		} else {
			if (priorSubstitution_ != null && substitution != null)
				substitution.getSubstitutionMap().putAll(
						priorSubstitution_.getSubstitutionMap());

			if (isTrueResult && !resultsSet_.contains(substitution)) {
				results_.add(substitution);
				resultsSet_.add(substitution);
			}

			if (toComplete_ != null) {
				toComplete_.remove(substitution);
				return toComplete_.isEmpty();
			}
			return false;
		}
	}

	/**
	 * Adds a result to the query with a full justification provided.
	 *
	 * @param isTrueResult
	 *            If the result being added is a positive example (i.e. true),
	 *            or a negative example (i.e. proof against).
	 * @param substitution
	 *            The result to add.
	 * @param justification
	 *            The justification list to add to the justification.
	 */
	public void addResult(boolean isTrueResult, Substitution substitution,
			List<Node[]> justification) {
		addResult(isTrueResult, substitution);
		if (needJustification_ && justification != null
				&& justification.size() > 0)
			justification_.addAll(justification);
	}

	public void addResults(boolean isTrueResults,
			Collection<Substitution> results) {
		if (results == null)
			return;
		for (Substitution sub : results)
			if (addResult(isTrueResults, sub))
				return;
	}

	public void cleanTransitiveJustification(List<Node[]> justifications,
			DirectedAcyclicGraph dag) {
		if (!needJustification_)
			return;
		List<Node[]> transitiveJustification = new ArrayList<>();
		Node transitiveNode = null;
		for (int i = justifications.size() - 1; i >= 0; i--) {
			Node[] justNodes = justifications.get(i);
			// Remove negation
			Node[] nonNegatedNodes = EdgeModifier.getUnmodNodes(justNodes, dag);
			if (transitiveNode == null
					|| nonNegatedNodes[2].equals(transitiveNode)
					|| (nonNegatedNodes[2] instanceof OntologyFunction && ((OntologyFunction) nonNegatedNodes[2])
							.getNodes()[0].equals(transitiveNode))) {
				transitiveNode = nonNegatedNodes[1];
				transitiveJustification.add(justNodes);
			}

			if (transitiveNode.equals(nodes_[1]))
				break;
		}

		Collections.reverse(transitiveJustification);
		justification_ = transitiveJustification;
	}

	public DAGNode getAtomic() {
		if (atomics_.size() == 0)
			return null;
		return atomics_.get(0);
	}

	public int getAtomicIndex() {
		return atomicIndex_;
	}

	public ArrayList<DAGNode> getCompleted() {
		return completed_;
	}

	public List<Node[]> getJustification() {
		return justification_;
	}

	public Node getNode(int i) {
		return nodes_[i];
	}

	public Node[] getNodes() {
		return nodes_;
	}

	public int getNumVariables() {
		return variables_.size();
	}

	public Collection<Substitution> getPriorSubstitutions() {
		return toComplete_;
	}

	public Collection<Substitution> getResults() {
		if (isProof()
				&& (results_.isEmpty() || getResultState() != QueryResult.TRUE))
			return null;
		else
			return results_;
	}

	public VariableNode getVariable() {
		return getVariable(0);
	}

	public VariableNode getVariable(int i) {
		if (variables_ == null || variables_.size() <= i)
			return null;
		return variables_.get(i);
	}

	public int getVariableIndex() {
		return variableIndex_;
	}

	public Collection<VariableNode> getVariables() {
		return variables_;
	}

	public boolean isCompleted(Node n) {
		return completedSet_.contains(n);
	}

	public boolean isProof() {
		return variables_.isEmpty();
	}

	/**
	 * Creates a new QueryObject with different nodes but the same result sets,
	 * etc. However, does not copy justification!
	 * 
	 * @param nodes
	 *            The new nodes.
	 * 
	 * @return The new QueryObject sharing the results.
	 */
	public QueryObject modifyNodes(Node... nodes) {
		QueryObject newObj = new QueryObject(verifyArguments_,
				needJustification_, goalResult_, nodes);
		newObj.completed_ = completed_;
		newObj.completedSet_ = completedSet_;
		newObj.results_ = results_;
		newObj.resultsSet_ = resultsSet_;
		newObj.toComplete_ = toComplete_;
		newObj.resultState_ = resultState_;
		newObj.priorSubstitution_ = priorSubstitution_;
		return newObj;
	}

	public QueryObject clone() {
		return modifyNodes(nodes_);
	}

	public QueryObject modifyNodes(Substitution baseSub, Node... nodes) {
		QueryObject newObj = new QueryObject(verifyArguments_,
				needJustification_, goalResult_, nodes);
		newObj.results_ = results_;
		newObj.resultsSet_ = resultsSet_;
		newObj.toComplete_ = toComplete_;
		newObj.priorSubstitution_ = baseSub;
		return newObj;
	}

	public void setToComplete(Collection<Substitution> intersect) {
		if (intersect != null) {
			toComplete_ = new HashSet<>(intersect);
			// results_.clear();
			// completed_.clear();
			// completedSet_.clear();
			// resultsSet_.clear();
			// resultState_.object_ = QueryResult.NIL;
			// justification_.clear();
		}
	}

	@Override
	public String toString() {
		return Arrays.toString(nodes_);
	}

	public boolean shouldJustify() {
		return needJustification_;
	}

	/**
	 * Gets the state of the executed query object - TRUE, FALSE, or NIL.
	 *
	 * @return The result of the executed query object.
	 */
	public QueryResult getResultState() {
		return resultState_.object_;
	}

	/**
	 * Sets the state of the executed query object - TRUE, FALSE, or NIL.
	 */
	public void setResultState(QueryResult result) {
		resultState_.object_ = result;
	}

	public void flipResultState() {
		if (resultState_.object_ == QueryResult.TRUE) {
			resultState_.object_ = QueryResult.FALSE;
			results_.clear();
			resultsSet_.clear();
		} else if (resultState_.object_ == QueryResult.FALSE) {
			resultState_.object_ = QueryResult.TRUE;
			Substitution s = new Substitution();
			results_.add(s);
			resultsSet_.add(s);
		}
		
		if (goalResult_ == QueryResult.TRUE)
			goalResult_ = QueryResult.FALSE;
		if (goalResult_ == QueryResult.FALSE)
			goalResult_ = QueryResult.TRUE;
	}

	public ErrorEdge getRejectionReason() {
		return rejectionReason_;
	}

	public void setRejectionReason(ErrorEdge errorEdge) {
		rejectionReason_ = errorEdge;
	}

	public void setJustification(List<Node[]> justification) {
		justification_ = justification;
	}

	public boolean isRun() {
		return hasRun_;
	}

	public void setRun(boolean b) {
		hasRun_ = b;
	}

	public boolean shouldVerify() {
		return verifyArguments_;
	}

	public void setVerify(boolean shouldVerify) {
		verifyArguments_ = shouldVerify;
	}

	/**
	 * If the goal of this query desires a particular result.
	 *
	 * @param result
	 *            The result that could be produced.
	 * @return True if the goal allows the result.
	 */
	public boolean desiresResult(QueryResult result) {
		// If all results, or goal is open, return true
		if (goalResult_ == QueryResult.ALL || result == QueryResult.ALL
				|| result == QueryResult.NIL)
			return true;
		// If after true and true, return true
		if (result == QueryResult.TRUE && goalResult_ == QueryResult.TRUE)
			return true;
		// If after false and false, return true
		if (result == QueryResult.FALSE && goalResult_ == QueryResult.FALSE)
			return true;
		return false;
	}

	public QueryResult getGoalResult() {
		return goalResult_;
	}

	public void setGoalResult(QueryResult newGoal) {
		goalResult_ = newGoal;
	}
}
 public enum QueryResult {
	TRUE, FALSE, NIL, ALL;
}
 public abstract class QueryWorker implements Serializable {
	private static final long serialVersionUID = -9204653084421990460L;

	protected QueryModule querier_;

	protected transient OntologyEdgeModule relatedModule_;

	protected transient DirectedAcyclicGraph dag_;

	public QueryWorker(QueryModule queryModule) {
		querier_ = queryModule;
	}

	public Collection<Substitution> query(Node... nodes)
			throws IllegalArgumentException {
		QueryObject queryObj = new QueryObject(nodes);
		queryInternal(queryObj);
		return queryObj.getResults();
	}

	/**
	 * Asks a query containing one or more variables and returns all possible
	 * substitutions for the variables.
	 * 
	 * @param queryObj
	 *            The necessary information and returned object for the query.
	 */
	public abstract void queryInternal(QueryObject queryObj)
			throws IllegalArgumentException;

	public void setDAG(DirectedAcyclicGraph dag) {
		dag_ = dag;
		relatedModule_ = (OntologyEdgeModule) dag
				.getModule(OntologyEdgeModule.class);
		if (relatedModule_ == null)
			throw new ModuleException(
					"Cannot perform query without related edge module.");
	}
}
 public class Substitution implements Comparable<Substitution> {
	private Map<String, Node> substitutionMap_;

	public Substitution() {
		substitutionMap_ = new HashMap<>();
	}

	public Substitution(Map<String, Node> substitutionMap) {
		substitutionMap_ = new HashMap<>(substitutionMap);
	}

	public Substitution(String var, Node node) {
		substitutionMap_ = new HashMap<>();
		substitutionMap_.put(var, node);
	}

	public Substitution(Object variable, Node otherNode) {
		this(variable.toString(), otherNode);
	}

	public void addSubstitution(String variable, Node substitution) {
		substitutionMap_.put(variable, substitution);
	}

	public void addSubstitution(Object variable, Node substitution) {
		addSubstitution(variable.toString(), substitution);
	}

	public Node getSubstitution(Object variable) {
		return substitutionMap_.get(variable.toString());
	}

	@Override
	public Substitution clone() {
		Substitution clone = new Substitution(substitutionMap_);
		return clone;
	}

	public Map<String, Node> getSubstitutionMap() {
		return substitutionMap_;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime
				* result
				+ ((substitutionMap_ == null) ? 0 : substitutionMap_.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Substitution other = (Substitution) obj;
		if (substitutionMap_ == null) {
			if (other.substitutionMap_ != null)
				return false;
		} else if (!substitutionMap_.equals(other.substitutionMap_))
			return false;
		return true;
	}

	@Override
	public int compareTo(Substitution o) {
		if (o == null)
			return -1;

		// Smaller sub first
		int result = Integer.compare(substitutionMap_.size(),
				o.substitutionMap_.size());
		if (result != 0)
			return result;

		// Otherwise, iterate through keys and values
		Iterator<Map.Entry<String, Node>> thisIter = substitutionMap_
				.entrySet().iterator();
		Iterator<Map.Entry<String, Node>> thatIter = o.substitutionMap_
				.entrySet().iterator();
		while (thisIter.hasNext()) {
			Map.Entry<String, Node> entryA = thisIter.next();
			Map.Entry<String, Node> entryB = thatIter.next();
			result = entryA.getKey().compareTo(entryB.getKey());
			if (result != 0)
				return result;

			result = entryA.getValue().getIdentifier()
					.compareTo(entryB.getValue().getIdentifier());
			if (result != 0)
				return result;
		}

		// Else, compare by hashcode
		return Integer.compare(hashCode(), o.hashCode());
	}

	public boolean containsVariable(String var) {
		return substitutionMap_.containsKey(var);
	}

	@Override
	public String toString() {
		return substitutionMap_.toString();
	}

	public Substitution inverseSub() {
		Substitution inverse = new Substitution();
		for (String key : substitutionMap_.keySet()) {
			inverse.addSubstitution(substitutionMap_.get(key).toString(),
					new VariableNode(key));
		}
		return inverse;
	}

	public Substitution applySubstitution(Substitution sub) {
		Substitution subbedSub = new Substitution();
		for (String key : substitutionMap_.keySet()) {
			Node value = substitutionMap_.get(key);
			key = (sub.containsVariable(key)) ? sub.getSubstitution(key)
					.toString() : key;
			subbedSub.addSubstitution(key, value);
		}
		return subbedSub;
	}

	public boolean isEmpty() {
		return substitutionMap_.isEmpty();
	}

	/**
	 * Applies a substitution to a set of nodes to produce a new set of nodes.
	 * 
	 * @param sub
	 *            The substitution to apply.
	 * @return A new array of nodes if the substitution changed anything,
	 *         otherwise return the same input set.
	 */
	public Node[] applySubstitution(Node[] nodes) {
		Node[] subbedNodes = new Node[nodes.length];
		boolean changed = false;
		for (int i = 0; i < subbedNodes.length; i++) {
			subbedNodes[i] = applySubstitution(nodes[i]);
			changed |= !subbedNodes[i].equals(nodes[i]);
		}
		if (changed)
			return subbedNodes;
		return nodes;
	}

	public Node applySubstitution(Node node) {
		if (node instanceof OntologyFunction) {
			return new OntologyFunction(
					applySubstitution(((OntologyFunction) node).getNodes()));
		} else if (substitutionMap_.containsKey(node.toString()))
			return substitutionMap_.get(node.toString());
		return node;
	}
}
 public class VariableNode extends StringNode {
	public VariableNode(String string) {
		super(string);
	}

	private static final long serialVersionUID = -918899352582359058L;
	public static final VariableNode DEFAULT = new VariableNode("?X");

	@Override
	public String toString() {
		return getName();
	}

}
 public class DisjointModule extends DAGModule<Collection<DAGNode>> {
	private static final long serialVersionUID = 1L;

	public static final String DISJOINT_ID = "disj";

	/** Maps which node(s) are disjoint to the key node (at most general level) */
	private MultiMap<DAGNode, DAGNode> disjointMap_;

	private MultiMap<DAGNode, DAGNode> notDisjointMap_;

	private QueryModule queryModule_;

	public DisjointModule() {
		disjointMap_ = MultiMap.createConcurrentHashSetMultiMap();
		notDisjointMap_ = MultiMap.createConcurrentHashSetMultiMap();
	}

	/**
	 * Adds parent identifier(s) to a node, keeping them in sorted order.
	 * 
	 * @param n
	 *            The node to add to.
	 * @param parentID
	 *            The parent(s) to add to the node.
	 */
	private void addDisjointParents(DAGNode n, String nodeParentStr,
			String... parentID) {
		String[] writable = parentID;
		if (nodeParentStr != null) {
			if (nodeParentStr.equals(StringUtils.join(writable, ',')))
				return;
			ArrayList<String> parents = UtilityMethods
					.split(nodeParentStr, ',');
			for (String pID : parentID) {
				if (!parents.contains(pID))
					parents.add(pID);
			}
			Collections.sort(parents);
			writable = parents.toArray(new String[parents.size()]);
		}

		// Write the parents
		dag_.addProperty(n, DISJOINT_ID, StringUtils.join(writable, ','));
	}

	/**
	 * Gets all collections disjoint to the given input collections (i.e. the
	 * disjoint collections on the other side of the relationship). These are
	 * stored as the keys for a map, where the values are the corresponding
	 * disjoint edge for the key.
	 * 
	 * @param parentCollectionIDs
	 *            The collections to find disjoint collections for.
	 * @return All direct disjoint collections to the input collections.
	 */
	private Map<DAGNode, DAGNode> getDisjoints(
			Collection<DAGNode> parentCollectionIDs,
			MultiMap<DAGNode, DAGNode> disjMap) {
		Map<DAGNode, DAGNode> subMap = new HashMap<>();
		for (DAGNode n : parentCollectionIDs) {
			Collection<DAGNode> disjointCollection = disjMap.get(n);
			if (disjointCollection != null) {
				for (DAGNode n2 : disjointCollection)
					subMap.put(n2, n);
			}
		}
		return subMap;
	}

	/**
	 * Gets all parent disjoint nodes associated with this node, as defined by
	 * the parameter encoded into the node. By 'parent disjoint collection', I
	 * mean the collection explicitly given in the disjointness assertion.
	 * 
	 * @param node
	 *            The node to get parent disjoint collections for.
	 * @return The parent node(s) for the current node.
	 * @throws IllegalClassFormatException
	 */
	private Collection<DAGNode> getParents(DAGNode node)
			throws IllegalClassFormatException {
		String disjStr = node.getProperty(DISJOINT_ID);
		Collection<DAGNode> parents = new HashSet<>();

		// If ontology function
		if (node instanceof OntologyFunction) {
			Collection<DAGNode> genlNodes = queryModule_.functionResults(
					(OntologyFunction) node, CommonConcepts.RESULT_GENL);
			for (DAGNode genlNode : genlNodes)
				parents.addAll(getParents(genlNode));
		}

		if (disjStr == null)
			return parents;
		// TODO If not collection

		ArrayList<String> disjStrs = UtilityMethods.split(disjStr, ',');
		for (String str : disjStrs) {
			if (str.startsWith("-"))
				continue;
			Node parentNode = dag_.findOrCreateNode(str, null, false);
			if (parentNode instanceof DAGNode)
				parents.add((DAGNode) parentNode);
			else
				throw new IllegalClassFormatException(
						"Disjoint node should be a DAGNode!");
		}
		return parents;
	}

	/**
	 * Checks if a currently added disjoint edge is legal based on the
	 * definition of disjointness.
	 * 
	 * @param edge
	 *            The disjoint edge being added.
	 * @return True if there are no concepts that are a member of both instances
	 *         of the edge's collections.
	 */
	private boolean isLegalDisjointEdge(Node[] nodes) {
		Collection<Substitution> genlQuery = queryModule_
				.executeQuery(new QueryObject(true, false, QueryResult.TRUE,
						CommonConcepts.AND.getNode(dag_), new OntologyFunction(
								CommonConcepts.GENLS.getNode(dag_),
								VariableNode.DEFAULT, nodes[1]),
						new OntologyFunction(
								CommonConcepts.GENLS.getNode(dag_),
								VariableNode.DEFAULT, nodes[2])));
		if (!genlQuery.isEmpty())
			return false;
		Collection<Substitution> isaQuery = queryModule_
				.executeQuery(new QueryObject(true, false, QueryResult.TRUE,
						CommonConcepts.AND.getNode(dag_), new OntologyFunction(
								CommonConcepts.ISA.getNode(dag_),
								VariableNode.DEFAULT, nodes[1]),
						new OntologyFunction(CommonConcepts.ISA.getNode(dag_),
								VariableNode.DEFAULT, nodes[2])));
		if (!isaQuery.isEmpty())
			return false;
		return true;
	}

	/**
	 * Recursively works down each set of children, adjusting the possible
	 * disjoints if necessary. Each step examines if the node is still disjoint
	 * to the other collection. If so, it is removed from the possibles for each
	 * of its children collections.
	 *
	 * @param node
	 *            The node to examine disjointness for.
	 * @param possibleDisjLosses
	 *            The collections to test disjointness.
	 * @param completed
	 *            The nodes that have been completed already.
	 */
	private void recurseRemoveDisjs(Node node,
			Collection<Node> possibleDisjLosses, HashSet<Node> completed) {
		// Stop checks
		if (completed.contains(node))
			return;
		completed.add(node);
		if (possibleDisjLosses.isEmpty())
			return;

		// Disjoint checks
		// Does this node genls the collection?
		Collection<Node> newDisjLosses = new HashSet<>();
		for (Node disjParent : possibleDisjLosses) {
			QueryObject qo = new QueryObject(false, false, QueryResult.TRUE,
					CommonConcepts.GENLS.getNode(dag_), node, disjParent);
			if (queryModule_.prove(qo) != QueryResult.TRUE) {
				// Remove from node's disjoint string
				removeDisjointParent((DAGNode) node, disjParent.getIdentifier());
				newDisjLosses.add(disjParent);
			}
		}

		// Recursion step
		Collection<Node> children = CommonQuery.DIRECTSPECS
				.runQuery(dag_, node);
		for (Node child : children) {
			recurseRemoveDisjs(child, newDisjLosses, completed);
		}
	}

	/**
	 * Removes a disjoint parent reference from a node's parent string.
	 *
	 * @param node
	 *            The node to remove the disjoint parent ID from.
	 * @param parentID
	 *            The disjoint parent id.
	 */
	private void removeDisjointParent(DAGNode node, String parentID) {
		String parentString = node.getProperty(DISJOINT_ID);
		if (parentString != null) {
			ArrayList<String> parents = UtilityMethods.split(parentString, ',');
			parents.remove(parentID);
			dag_.addProperty(node, DISJOINT_ID, StringUtils.join(parents, ','));
		}
	}

	protected void buildJustification(QueryObject queryObj,
			Map<DAGNode, DAGNode> disjMap,
			Collection<DAGNode> disjointCollections, int varIndex,
			boolean negated) {
		DAGNode key = disjointCollections.iterator().next();
		DAGNode genls = CommonConcepts.GENLS.getNode(dag_);
		List<Node[]> justification = queryObj.getJustification();

		// Add the first genls justification.
		QueryObject genlsA = new QueryObject(false, true, QueryResult.ALL,
				genls, queryObj.getAtomic(), disjMap.get(key));
		queryModule_.prove(genlsA);
		List<Node[]> justificationA = genlsA.getJustification();
		if (justificationA.size() != 1
				|| !Arrays.equals(justificationA.get(0), new Node[] { genls,
						disjMap.get(key), disjMap.get(key) }))
			justification.addAll(justificationA);

		// Add the disjoint edge.
		Node[] disjointEdge = new Node[] {
				CommonConcepts.DISJOINTWITH.getNode(dag_), disjMap.get(key),
				key };
		if (negated)
			disjointEdge = new Node[] { CommonConcepts.NOT.getNode(dag_),
					new OntologyFunction(disjointEdge) };
		if (dag_.findEdge(disjointEdge) == null) {
			disjointEdge = new Node[] {
					CommonConcepts.DISJOINTWITH.getNode(dag_), key,
					disjMap.get(key) };
			if (negated)
				disjointEdge = new Node[] { CommonConcepts.NOT.getNode(dag_),
						new OntologyFunction(disjointEdge) };
		}
		justification.add(disjointEdge);

		// Add the last genls justification (reversed).
		QueryObject genlsB = new QueryObject(false, true, QueryResult.ALL,
				genls, queryObj.getNode(varIndex), key);
		queryModule_.prove(genlsB);
		List<Node[]> justificationB = genlsB.getJustification();
		if (justificationB.size() != 1
				|| !Arrays.equals(justificationB.get(0), new Node[] { genls,
						key, key })) {
			Collections.reverse(justificationB);
			justification.addAll(justificationB);
		}
	}

	@SuppressWarnings("unchecked")
	protected Collection<DAGNode> calculateDisjoint(QueryObject queryObj,
			Collection<DAGNode> parentCollections,
			MultiMap<DAGNode, DAGNode> disjointMap, boolean negated)
			throws IllegalClassFormatException {
		// Get the disjoint collections for the parents
		Map<DAGNode, DAGNode> disjMap = getDisjoints(parentCollections,
				disjointMap);
		Collection<DAGNode> disjointCollections = disjMap.keySet();

		if (!queryObj.isProof()) {
			// If variable, add and return results.
			for (DAGNode n : disjointCollections)
				queryObj.addResult(!negated,
						new Substitution(queryObj.getVariable(), n),
						(Node[]) null);
			return disjointCollections;
		} else {
			// If proof, check if true and add justification.
			int varIndex = (queryObj.getAtomicIndex() == 1) ? 2 : 1;
			Collection<DAGNode> otherCollections = getParents((DAGNode) queryObj
					.getNode(varIndex));
			// TODO Seems dangerous here to be directly affecting the map set
			disjointCollections.retainAll(otherCollections);
			if (!disjointCollections.isEmpty()) {
				if (queryObj.shouldJustify()) {
					// Build the justification
					buildJustification(queryObj, disjMap, disjointCollections,
							varIndex, negated);
				}
				queryObj.addResult(!negated, new Substitution(), (Node[]) null);
				return Collections.EMPTY_LIST;
			}
		}
		return null;
	}

	@Override
	public boolean addEdge(DAGEdge edge) {
		// If the edge is a disjoint edge, record and propagate
		Node[] nodes = EdgeModifier.getUnmodNodes(edge, dag_);
		boolean negated = EdgeModifier.isNegated(edge, dag_);
		if (nodes[0].equals(CommonConcepts.DISJOINTWITH.getNode(dag_))) {
			// First check if the disjoint edge is valid
			if (!negated && !isLegalDisjointEdge(nodes))
				return false;

			// Add to map
			if (negated) {
				notDisjointMap_.put((DAGNode) nodes[1], (DAGNode) nodes[2]);
				notDisjointMap_.put((DAGNode) nodes[2], (DAGNode) nodes[1]);
			} else {
				disjointMap_.put((DAGNode) nodes[1], (DAGNode) nodes[2]);
				disjointMap_.put((DAGNode) nodes[2], (DAGNode) nodes[1]);
			}

			// Propagate down
			// Node 1
			Collection<Node> subCols = queryModule_.executeAndParseVar(
					new QueryObject(CommonConcepts.GENLS.getNode(dag_),
							VariableNode.DEFAULT, nodes[1]),
					VariableNode.DEFAULT.getName());
			subCols.add(nodes[1]);
			for (Node n : subCols) {
				if (n instanceof DAGNode) {
					addDisjointParents((DAGNode) n,
							((DAGNode) n).getProperty(DISJOINT_ID),
							nodes[1].getIdentifier());
				}
			}

			// Node 2
			subCols = queryModule_.executeAndParseVar(new QueryObject(
					CommonConcepts.GENLS.getNode(dag_), VariableNode.DEFAULT,
					nodes[2]), VariableNode.DEFAULT.getName());
			subCols.add(nodes[2]);
			for (Node n : subCols) {
				if (n instanceof DAGNode) {
					addDisjointParents((DAGNode) n,
							((DAGNode) n).getProperty(DISJOINT_ID),
							nodes[2].getIdentifier());
				}
			}
		}
		// If the edge is a genls, propagate disjoint relationships
		else if (nodes[0].equals(CommonConcepts.GENLS.getNode(dag_))
				&& !negated) {
			// Add any data from the parent
			if (nodes[1] instanceof DAGNode && nodes[2] instanceof DAGNode) {
				String parentStr = ((DAGNode) nodes[2])
						.getProperty(DISJOINT_ID);
				if (parentStr != null && !parentStr.isEmpty()) {
					String[] parentDisjs = UtilityMethods.splitToArray(
							parentStr, ',');
					Collection<Node> subCols = queryModule_.executeAndParseVar(
							new QueryObject(CommonConcepts.GENLS.getNode(dag_),
									VariableNode.DEFAULT, nodes[1]),
							VariableNode.DEFAULT.getName());
					subCols.add(nodes[1]);
					for (Node n : subCols) {
						if (n instanceof DAGNode)
							addDisjointParents((DAGNode) n,
									((DAGNode) n).getProperty(DISJOINT_ID),
									parentDisjs);
					}
				}
			}
		}

		return true;
	}

	@Override
	public Collection<DAGNode> execute(Object... args)
			throws IllegalArgumentException, ModuleException {
		if (args.length == 1 && args[0] instanceof QueryObject)
			return execute(((QueryObject) args[0]).shouldJustify(), args);
		return execute(new QueryObject((Node[]) args));
	}

	public Collection<DAGNode> execute(QueryObject queryObj)
			throws IllegalArgumentException, ModuleException {
		try {
			// Get the precomputed parent collections for the first arg
			Collection<DAGNode> parentCollections = getParents(queryObj
					.getAtomic());
			if (parentCollections.isEmpty())
				return null;

			// Compare using disjoint map
			Collection<DAGNode> result = calculateDisjoint(queryObj,
					parentCollections, disjointMap_, false);
			if (result != null)
				return result;

			// Compare using non-disjoint map
			result = calculateDisjoint(queryObj, parentCollections,
					notDisjointMap_, true);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public boolean initialisationComplete(TIndexedCollection<DAGNode> nodes,
			TIndexedCollection<DAGEdge> edges, boolean forceRebuild) {
		if (disjointMap_.isKeysEmpty()) {
			System.out.print("Building Disjoint Module map... ");
			defaultRebuild(nodes, false, edges, true);
			System.out.println("Done!");
			return true;
		}
		return false;
	}

	@Override
	public boolean removeEdge(DAGEdge edge) {
		Node[] nodes = EdgeModifier.getUnmodNodes(edge, dag_);
		boolean negated = EdgeModifier.isNegated(edge, dag_);
		if (nodes[0].equals(CommonConcepts.DISJOINTWITH.getNode(dag_))) {
			// Easy peasy, just remove the element from the map
			// Kinda messy, but it works.
			if (negated) {
				Collection<DAGNode> coll = notDisjointMap_.get(nodes[1]);
				if (coll != null)
					coll.remove(nodes[2]);
				coll = notDisjointMap_.get(nodes[2]);
				if (coll != null)
					coll.remove(nodes[1]);
			} else {
				Collection<DAGNode> coll = disjointMap_.get(nodes[1]);
				if (coll != null)
					coll.remove(nodes[2]);
				coll = disjointMap_.get(nodes[2]);
				if (coll != null)
					coll.remove(nodes[1]);
			}
		} else if (nodes[0].equals(CommonConcepts.GENLS.getNode(dag_))
				&& !negated) {
			// For each child of the arg1, recalculate disjoints
			if (nodes[1] instanceof DAGNode && nodes[2] instanceof DAGNode) {
				// Calculate what is lost from inheritance
				String parentStr = ((DAGNode) nodes[2])
						.getProperty(DISJOINT_ID);
				if (parentStr == null || parentStr.isEmpty())
					return true;
				Set<String> parentDisjs = new HashSet<>(UtilityMethods.split(
						parentStr, ','));
				String childStr = ((DAGNode) nodes[1]).getProperty(DISJOINT_ID);
				ArrayList<String> childDisjs = UtilityMethods.split(childStr,
						',');
				// The set of possible lost disjoints
				parentDisjs.retainAll(childDisjs);
				Collection<Node> possibleDisjs = new HashSet<>();
				for (String s : parentDisjs)
					possibleDisjs.add(dag_.findOrCreateNode(s, null, false));

				// Work down one node at a time
				recurseRemoveDisjs(nodes[1], possibleDisjs, new HashSet<Node>());
			}
		}
		return true;
	}

	@Override
	public void setDAG(DirectedAcyclicGraph directedAcyclicGraph) {
		super.setDAG(directedAcyclicGraph);
		queryModule_ = (QueryModule) dag_.getModule(QueryModule.class);
	}

	@Override
	public boolean supportsEdge(DAGEdge edge) {
		return !EdgeModifier.isRemoved(edge, dag_);
	}

	@Override
	public boolean supportsNode(DAGNode node) {
		return false;
	}

	@Override
	public String toString() {
		return "DisjointWith Module: " + (disjointMap_.size() / 2)
				+ " assertions & " + (notDisjointMap_.size() / 2)
				+ " negated assertions";
	}
}
 public class QueryModule extends DAGModule<Collection<Substitution>> {
	private static final long serialVersionUID = 1925714026500662430L;

	public static final String DEFAULT_WORKER = "_DEFAULT_";

	public static final String EVALUATABLE_WORKER = "_EVALUATABLE_";

	private transient Map<String, QueryWorker> inferenceModules_;

	public QueryModule() {
	}

	private void applyModule(String moduleName, QueryObject queryObj)
			throws IllegalArgumentException {
		initInferenceModules();
		inferenceModules_.get(moduleName).queryInternal(queryObj);
	}

	private void initInferenceModules() {
		if (inferenceModules_ != null)
			return;
		inferenceModules_ = new HashMap<>();
		inferenceModules_.put("assertedSentence", new AssertedSentenceWorker(
				this));
		inferenceModules_.put("genls", new TransitiveWorker(this));
		inferenceModules_.put("genlPreds", new TransitiveWorker(this));
		inferenceModules_.put("isa", new IsaWorker(this));
		inferenceModules_.put("disjointWith", new DisjointWithWorker(this));
		inferenceModules_.put("and", new AndWorker(this));
		inferenceModules_.put("or", new OrWorker(this));
		inferenceModules_.put("resultIsa", new IsaWorker(this));
		inferenceModules_.put("resultGenl", new IsaWorker(this));
		for (String eval : EvaluatablePredicateWorker.SUPPORTED_PREDICATES)
			inferenceModules_.put(eval, new EvaluatablePredicateWorker(this));
		inferenceModules_.put(DEFAULT_WORKER,
				new GenlPredTransitiveWorker(this));
	}

	private DAGNode integerToDAGNode(Number primitive) {
		if (primitive.intValue() == 0)
			return CommonConcepts.ZERO.getNode(dag_);
		else if (primitive.intValue() > 0)
			return new OntologyFunction(CommonConcepts.THE_FN.getNode(dag_),
					CommonConcepts.POSITIVE_INTEGER.getNode(dag_));
		else
			return new OntologyFunction(CommonConcepts.THE_FN.getNode(dag_),
					CommonConcepts.NEGATIVE_INTEGER.getNode(dag_));
	}

	private DAGNode rationalToDAGNode(Number primitive) {
		if (primitive.intValue() == 0)
			return CommonConcepts.ZERO.getNode(dag_);
		else if (primitive.intValue() > 0)
			return new OntologyFunction(CommonConcepts.THE_FN.getNode(dag_),
					CommonConcepts.POSITIVE_NUMBER.getNode(dag_));
		else
			return new OntologyFunction(CommonConcepts.THE_FN.getNode(dag_),
					CommonConcepts.NEGATIVE_NUMBER.getNode(dag_));
	}

	private DAGNode stringToDAGNode(String name) {
		return new OntologyFunction(CommonConcepts.THE_FN.getNode(dag_),
				CommonConcepts.STRING.getNode(dag_));
	}

	/**
	 * Parses the nodes from the variable substitutions.
	 * 
	 * @param qo
	 *            The query object to run and parse.
	 * @param var
	 *            The variable substitutions to extract.
	 * @return The parsed results.
	 */
	protected Collection<Node> executeAndParseVar(QueryObject qo, String var) {
		Collection<Substitution> subs = executeQuery(qo);
		if (qo.getResultState() == QueryResult.TRUE)
			return parseResultsFromSubstitutions(var, subs);
		else
			return new ArrayList<>();
	}

	/**
	 * Takes a set of Nodes and Strings (in the form ?X) as arguments. Returns
	 * all variable replacements that are valid.
	 */
	@Override
	public Collection<Substitution> execute(Object... args)
			throws IllegalArgumentException, ModuleException {
		Node[] asNodes = new Node[args.length];
		System.arraycopy(args, 0, asNodes, 0, args.length);

		QueryObject qo = new QueryObject(asNodes);
		qo.setVerify(true);
		return executeQuery(qo);
	}

	public Collection<Substitution> executeQuery(QueryObject queryObj) {
		// Remove negation when evaluating and flip result if negated
		boolean negated = queryObj.getNode(0).equals(
				CommonConcepts.NOT.getNode(dag_));
		QueryObject origQO = queryObj;
		if (negated) {
			queryObj = queryObj.modifyNodes(((OntologyFunction) queryObj
					.getNode(1)).getNodes());
			queryObj.flipResultState();
		}

		String module = DEFAULT_WORKER;
		if (inferenceModules_.containsKey(queryObj.getNode(0).toString()))
			module = queryObj.getNode(0).getName();

		// Dealing with multiple variables
		Collection<Substitution> priorSubs = queryObj.getPriorSubstitutions();
		if (queryObj.getNumVariables() >= 1 && priorSubs != null
				&& !priorSubs.isEmpty()) {
			// Iterate through prior variable matches (in toComplete) to
			// minimise query to 1 or fewer variables.
			Node[] nodes = queryObj.getNodes();
			boolean isComplete = false;
			for (Substitution variableMatch : priorSubs) {
				QueryObject instantiated = queryObj.modifyNodes(variableMatch,
						variableMatch.applySubstitution(nodes));
				applyModule(module, instantiated);
				isComplete |= instantiated.getResultState() == QueryResult.TRUE;
				if (instantiated.shouldJustify()) {
					List<Node[]> justification = instantiated
							.getJustification();
					if (!justification.isEmpty()) {
						for (Node[] step : justification) {
							if (!queryObj.getJustification().contains(step))
								queryObj.getJustification().add(step);
						}
					}
				}
			}

			if (isComplete)
				queryObj.setResultState(QueryResult.TRUE);
			if (origQO.shouldJustify())
				origQO.setJustification(queryObj.getJustification());
			queryObj.setRun(true);
			return queryObj.getResults();
		} else {
			applyModule(module, queryObj);

			// Verify arguments after executing module.
			if (queryObj.shouldVerify()
					&& queryObj.getResultState() == QueryResult.NIL) {
				ErrorEdge ee = ((CycDAG) dag_).verifyEdgeArguments(queryObj,
						negated, false, null, false);
				if (queryObj.isProof() || ee != null) {
					if (negated)
						queryObj.flipResultState();
					if (queryObj.getResultState() == QueryResult.FALSE)
						queryObj.setRejectionReason(ee);
					if (origQO.shouldJustify())
						origQO.setJustification(queryObj.getJustification());
					queryObj.setRun(true);
					return queryObj.getResults();
				}
			}

			if (negated)
				queryObj.flipResultState();
			if (origQO.shouldJustify())
				origQO.setJustification(queryObj.getJustification());
			queryObj.setRun(true);
			return queryObj.getResults();
		}
	}

	/**
	 * Returns all nodes that a function produces (either resultGenl or
	 * resultIsa, and their expanded <X>Arg variants). Other methods can use
	 * this in their calculations of a query result.
	 * 
	 * @param functionNode
	 *            The function to return nodes for.
	 * @param resultQuery
	 *            The type of results to look for (resultGenl/resultIsa).
	 * @return The collection of all results returned by the function.
	 */
	public Collection<DAGNode> functionResults(OntologyFunction functionNode,
			CommonConcepts resultQuery) {
		Node[] funcNodes = functionNode.getNodes();
		Collection<DAGNode> results = new ArrayList<>();
		RelatedEdgeModule relatedModule = (RelatedEdgeModule) dag_
				.getModule(RelatedEdgeModule.class);
		Collection<Edge> resultEdges = relatedModule.findEdgeByNodes(
				resultQuery.getNode(dag_), funcNodes[0]);
		for (Edge e : resultEdges) {
			if (!EdgeModifier.isNegated(e, dag_))
				results.add((DAGNode) e.getNodes()[2]);
			else
				System.out
						.println("I should be returning a negative result here.");
			// TODO Figure out how to return negative result.
		}

		// resultArgs
		CommonConcepts resultArgConcept = (resultQuery == CommonConcepts.RESULT_GENL) ? CommonConcepts.RESULT_GENL_ARG
				: (resultQuery == CommonConcepts.RESULT_ISA) ? CommonConcepts.RESULT_ISA_ARG
						: null;
		resultEdges = relatedModule.findEdgeByNodes(
				resultArgConcept.getNode(dag_), funcNodes[0]);
		for (Edge e : resultEdges) {
			Integer argIndex = Integer.parseInt(e.getNodes()[2].toString());
			Node n = funcNodes[argIndex];
			if (n instanceof DAGNode)
				results.add((DAGNode) n);
		}

		return results;
	}

	/**
	 * Gets the expanded set of nodes that all represent the input node. This is
	 * used for decoding functions, Strings and primitives.
	 * 
	 * @param node
	 *            The node to expand.
	 * @return The set of nodes representing the expanded node.
	 */
	public DAGNode getExpanded(Node node) {
		if (node == null)
			return null;
		if (node instanceof StringNode) {
			return stringToDAGNode(node.getName());
		} else if (node instanceof PrimitiveNode) {
			PrimitiveNode primNode = (PrimitiveNode) node;
			if (primNode.getPrimitive() instanceof Integer
					|| primNode.getPrimitive() instanceof Short
					|| primNode.getPrimitive() instanceof Long)
				return integerToDAGNode((Number) primNode.getPrimitive());
			else if (primNode.getPrimitive() instanceof Boolean) {
				if ((Boolean) primNode.getPrimitive())
					return CommonConcepts.TRUE.getNode(dag_);
				else
					return CommonConcepts.FALSE.getNode(dag_);
			} else if (primNode.getPrimitive() instanceof Double
					|| primNode.getPrimitive() instanceof Float)
				return rationalToDAGNode((Number) primNode.getPrimitive());
			else if (primNode.getPrimitive() instanceof Character)
				return stringToDAGNode(node.getName());
		}
		return (DAGNode) node;
	}

	public QueryResult prove(boolean checkValidity, Node... nodes) {
		QueryObject qo = new QueryObject(checkValidity, false, QueryResult.ALL,
				nodes);
		return prove(qo);
	}

	public QueryResult prove(QueryObject queryObject) {
		executeQuery(queryObject);
		return queryObject.getResultState();
	}

	@Override
	public void setDAG(DirectedAcyclicGraph directedAcyclicGraph) {
		super.setDAG(directedAcyclicGraph);

		initInferenceModules();
		for (QueryWorker qw : inferenceModules_.values())
			qw.setDAG(directedAcyclicGraph);
	}

	@Override
	public boolean supportsEdge(DAGEdge edge) {
		return false;
	}

	@Override
	public boolean supportsNode(DAGNode node) {
		return false;
	}

	/**
	 * Parses results from a collection of substitutions.
	 * 
	 * @param var
	 *            The variable to parse out.
	 * @param subs
	 *            The collection of substitutions.
	 * @return A collection of nodes representing the substitutions for the
	 *         variable.
	 */
	public static Collection<Node> parseResultsFromSubstitutions(Object var,
			Collection<Substitution> subs) {
		if (subs == null)
			return null;
		Collection<Node> results = new ArrayList<>(subs.size());
		for (Substitution s : subs) {
			Node res = s.getSubstitution(var);
			results.add(res);
		}
		return results;
	}
}
 public class TransitiveIntervalSchemaModule extends
		DAGModule<Collection<DAGNode>> {
	/** Serialisation. */
	private static final long serialVersionUID = 6562719667555853873L;

	/** Temp mark for node scanning. */
	private static final String TEMP_MARK = "tmp";

	/** Ancestor ID property. */
	public static final String ANCESTOR_ID = "ancsID";

	/** The interval size for the tree. */
	public static final int INITIAL_INTERVAL = 1024;

	/** The mark for topological scanning. */
	public static final String MARK = "topMark";

	/** A permanent mark for topological scanning. */
	public static final String PERMANENT_MARK = "prm";

	/** Predecessor ID property. */
	public static final String PREDECESSOR_ID = "predID";

	/** The ancestor map of transitive intervals. */
	private IntervalSchema ancestorMap_;

	/** All identified cycles from the last scan. */
	private transient Collection<Pair<DAGEdge, DAGEdge>> cycles_;

	/** The initial tree interval size (can be reduced if tree gets too big). */
	private int interval_ = INITIAL_INTERVAL;

	/** The predecessor map of transitive intervals. */
	private IntervalSchema predecessorMap_;

	/** A link to the query module. */
	private transient QueryModule queryModule_;

	/** A link to the related edge module. */
	private transient RelatedEdgeModule relEdgeModule_;

	/** If the maps require a rebuild. */
	private boolean requiresRebuild_ = true;

	/** A link to the virtual root node in predecessor and ancestor map. */
	private DAGNode virtualRoot_;

	/** The transitive predicate this module is for. */
	protected DAGNode transitiveNode_;

	/** If incremental updates are allowed. */
	public boolean incrementalSupported_ = true;

	/**
	 * Builds a spanning tree for a given topologically sorted collection of
	 * nodes.
	 * 
	 * @param topologicalList
	 *            The topologically sorted list of nodes.
	 * @param predecessorTree
	 *            If this tree is building a predecessor tree (alt. ancestor
	 *            tree).
	 * @return The spanning tree for the nodes.
	 */
	private Map<DAGNode, TreeNode> buildSpanningTree(
			List<DAGNode> topologicalList, boolean predecessorTree) {
		TreeNode virtualRootNode = new TreeNode();
		Map<DAGNode, TreeNode> spanningTree = new HashMap<>();
		spanningTree.put(virtualRoot_, virtualRootNode);

		// Iterate through the list (backwards if ancestors)
		ListIterator<DAGNode> iter = null;
		if (predecessorTree)
			iter = topologicalList.listIterator();
		else
			iter = topologicalList.listIterator(topologicalList.size());
		while ((predecessorTree && iter.hasNext())
				|| (!predecessorTree && iter.hasPrevious())) {
			DAGNode n = (predecessorTree) ? iter.next() : iter.previous();

			dag_.removeProperty(n, MARK);
			// For every incoming edge
			Collection<DAGNode> transitiveNodes = getTransitiveNodes(n,
					!predecessorTree);

			DAGNode currentNode = null;
			int currentSize = -1;
			for (DAGNode edgeNode : transitiveNodes) {
				if (edgeNode.equals(n))
					continue;
				if (!spanningTree.containsKey(edgeNode))
					continue;
				if (currentNode == null) {
					currentNode = edgeNode;
					currentSize = spanningTree.get(currentNode).trans_.size();
				} else {
					int edgeSize = spanningTree.get(edgeNode).trans_.size();
					if (currentSize < edgeSize) {
						currentNode = edgeNode;
						currentSize = edgeSize;
					}
				}
			}

			// Set predecessors as remaining incoming edges + their predecessors
			if (currentNode == null) {
				spanningTree.put(n, new TreeNode());
				virtualRootNode.children_.add(n);
			} else {
				// Predecessor info
				TreeNode thisTreeNode = new TreeNode();
				thisTreeNode.trans_.add(currentNode);
				TreeNode otherTreeNode = spanningTree.get(currentNode);
				thisTreeNode.trans_.addAll(otherTreeNode.trans_);
				spanningTree.put(n, thisTreeNode);

				// Ancestor info
				otherTreeNode.children_.add(n);
			}
		}
		return spanningTree;
	}

	private Collection<DAGNode> getTransitiveNodes(DAGNode node,
			boolean isUpwards) {
		int nIndex = (isUpwards) ? 2 : 3;
		Collection<Edge> edges = relEdgeModule_.execute(transitiveNode_, 1,
				node, nIndex);
		Collection<DAGNode> transitiveNodes = new HashSet<>(edges.size());
		int tIndex = (isUpwards) ? 2 : 1;
		for (Edge edge : edges) {
			// TODO How to deal with negated edges?
			if (!EdgeModifier.isSpecial(edge, dag_)
					&& tIndex < edge.getNodes().length)
				transitiveNodes.add((DAGNode) edge.getNodes()[tIndex]);
		}

		// Function chasing
		if (transitiveNode_.equals(CommonConcepts.GENLS.getNode(dag_))
				&& isUpwards && node instanceof OntologyFunction)
			transitiveNodes.addAll(queryModule_.functionResults(
					(OntologyFunction) node, CommonConcepts.RESULT_GENL));
		return transitiveNodes;
	}

	private List<Node[]> justifyTransitiveR(DAGNode baseNode,
			DAGNode transNode, List<Node[]> justification,
			Set<DAGNode> seenNodes) {
		if (baseNode.equals(transNode))
			return justification;

		for (DAGNode directNext : getTransitiveNodes(baseNode, true)) {
			try {
				if (!seenNodes.contains(directNext)
						&& predecessorMap_.isTransitive(directNext, transNode)) {
					seenNodes.add(directNext);
					if (justifyTransitiveR(directNext, transNode,
							justification, seenNodes) != null) {
						justification.add(new Node[] { transitiveNode_,
								baseNode, directNext });
						return justification;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	private boolean rebuildTrees() {
		ancestorMap_ = null;
		predecessorMap_ = null;
		initialisationComplete(dag_.getNodes(), dag_.getEdges(), true);
		return true;
	}

	/**
	 * Visits a node and follows it up to establish a topological sort.
	 * 
	 * @param n
	 *            The node to check.
	 * @param sortedNodes
	 *            The currently topologically sorted nodes
	 * @param markMap
	 *            A map to mark scanned nodes.
	 * @param cycle
	 *            Keeps track of cycles.
	 * @return A node if there is a cycle, otherwise null.
	 */
	protected DAGNode topologicalVisit(DAGNode n,
			LinkedList<DAGNode> sortedNodes, HashMap<DAGNode, String> markMap,
			ArrayList<DAGNode> cycle) {
		String permaMark = n.getProperty(MARK);
		if (permaMark != null && permaMark.equals(PERMANENT_MARK))
			return null;

		String mark = markMap.get(n);
		if (mark != null && mark.equals(TEMP_MARK)) {
			System.out.print("Cycle");
			// Does it have a rewrite of?
			if (!relEdgeModule_.execute(
					CommonConcepts.REWRITE_OF.getNode(dag_), "1", n, "-F")
					.isEmpty())
				System.out.print(" (has rewriteOf)");
			System.out.print(": " + n);
			cycle.add(n);
			return n;
		}
		DAGNode cycleNode = null;
		if (mark == null) {
			markMap.put(n, TEMP_MARK);
			Collection<DAGNode> transitiveNodes = getTransitiveNodes(n, true);
			for (DAGNode edgeNode : transitiveNodes) {
				if (!edgeNode.equals(n)) {
					DAGNode tempNode = topologicalVisit(edgeNode, sortedNodes,
							markMap, cycle);
					if (cycleNode == null)
						cycleNode = tempNode;
					if (tempNode != null) {
						cycle.add(n);
						System.out.print(" -> " + n);
						if (n.equals(tempNode)) {
							System.out.println();
							cycleNode = null;

							// Add a cycle to the cycles collection
							Edge firstEdge = dag_.findOrCreateEdge(
									new Node[] { transitiveNode_, cycle.get(1),
											cycle.get(0) }, null, false, false);
							Edge secondEdge = dag_.findOrCreateEdge(
									new Node[] { transitiveNode_,
											cycle.get(cycle.size() - 1),
											cycle.get(cycle.size() - 2) },
									null, false, false);
							if (firstEdge instanceof DAGEdge
									&& secondEdge instanceof DAGEdge) {
								cycles_.add(new Pair<DAGEdge, DAGEdge>(
										(DAGEdge) firstEdge,
										(DAGEdge) secondEdge));
							}
						}
					}
				}
			}
			dag_.addProperty(n, MARK, PERMANENT_MARK);
			markMap.put(n, PERMANENT_MARK);
			sortedNodes.push(n);
		}
		return cycleNode;
	}

	@Override
	public boolean addEdge(DAGEdge edge) {
		if (!isReady())
			return true;

		if (!incrementalSupported_) {
			requiresRebuild_ = true;
			return true;
		}

		if (edge.getNodes()[0].equals(transitiveNode_)) {
			DAGNode nodeSubj = (DAGNode) edge.getNodes()[1];
			String subjIntIDStr = nodeSubj.getProperty(PREDECESSOR_ID);
			DAGNode nodeObj = (DAGNode) edge.getNodes()[2];
			String objIntIDStr = nodeObj.getProperty(PREDECESSOR_ID);
			if (subjIntIDStr == null) {
				// genls NEW exist

				// Create new root node for predecessorTree
				if (!predecessorMap_.addNewRootedNode(nodeSubj))
					return rebuildTrees();

				// Attach to parent descendant node in ancestorTree
				if (!ancestorMap_.addNewLeafNode(nodeSubj,
						Integer.parseInt(nodeObj.getProperty(ANCESTOR_ID))))
					return rebuildTrees();
			}
			if (objIntIDStr == null) {
				// genls exist NEW

				// Create new root node for ancestorTree
				if (!ancestorMap_.addNewRootedNode(nodeObj))
					return rebuildTrees();
				// Attach to parent descendant node in predecessorTree
				if (!predecessorMap_.addNewLeafNode(nodeObj,
						Integer.parseInt(subjIntIDStr)))
					return rebuildTrees();
			}

			// Propagate the intervals
			predecessorMap_.propagateIntervalAddition(nodeSubj, nodeObj,
					ancestorMap_, false);
			ancestorMap_.propagateIntervalAddition(nodeObj, nodeSubj,
					predecessorMap_, false);
		}
		return true;
	}

	@Override
	public void disableCached() {
		predecessorMap_ = null;
		ancestorMap_ = null;
	}

	@Override
	public Collection<DAGNode> execute(Object... args)
			throws IllegalArgumentException, ModuleException {
		if (args.length < 2 || args[1] == null)
			return null;

		boolean upwards = (boolean) args[0];
		IntervalSchema schema = (upwards) ? predecessorMap_ : ancestorMap_;
		DAGNode node = (DAGNode) args[1];
		if (args.length >= 3) {
			DAGNode otherNode = (DAGNode) args[2];
			try {
				if (schema.isTransitive(node, otherNode))
					return new ArrayList<DAGNode>(0);
				else
					return null;
			} catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}
		return schema.getTransitivePredecessors(node, false);
	}

	@Override
	public Collection<String> getPertinentProperties() {
		Collection<String> props = new ArrayList<String>(2);
		props.add(ANCESTOR_ID);
		props.add(PREDECESSOR_ID);
		return props;
	}

	public DAGNode getTransitiveNode() {
		return transitiveNode_;
	}

	/**
	 * Identifies pairs of edges that create cycles, such that removing one of
	 * the edges should remove the cycle.
	 * 
	 * @return A collection pairs of edges, where each pair of edges create a
	 *         cycle.
	 */
	public Collection<Pair<DAGEdge, DAGEdge>> identifyCycles(
			TIndexedCollection<DAGNode> nodes) {
		topologicalList(nodes);
		TIntObjectIterator<DAGNode> iter = nodes.iterator();
		for (int i = nodes.size(); i-- > 0;) {
			iter.advance();
			dag_.removeProperty(iter.value(), MARK);
		}
		return cycles_;
	}

	@Override
	public boolean initialisationComplete(TIndexedCollection<DAGNode> nodes,
			TIndexedCollection<DAGEdge> edges, boolean forceRebuild) {
		if (!requiresRebuild_ && !forceRebuild)
			return false;
		virtualRoot_ = null;
		initMembers();
		long startTime = System.currentTimeMillis();
		if (requiresRebuild_)
			System.out.print("Recreating transitive interval schema for "
					+ transitiveNode_ + "... ");
		else
			System.out.print("Creating transitive interval schema for "
					+ transitiveNode_ + "... ");

		// Sort topologically
		List<DAGNode> topologicalList = topologicalList(nodes);

		// Build spanning tree
		Map<DAGNode, TreeNode> predecessors = buildSpanningTree(
				topologicalList, true);
		Map<DAGNode, TreeNode> ancestors = buildSpanningTree(topologicalList,
				false);

		// Assign numbers and intervals
		IntervalSchema predecessorMap = new IntervalSchema(predecessors.size(),
				PREDECESSOR_ID);
		predecessorMap.assignIntervals(predecessors);
		IntervalSchema ancestorMap = new IntervalSchema(ancestors.size(),
				ANCESTOR_ID);
		ancestorMap.assignIntervals(ancestors);

		// Link all nodes
		predecessorMap.linkIntervals(topologicalList, true);
		ancestorMap.linkIntervals(topologicalList, false);
		predecessorMap_ = predecessorMap;
		ancestorMap_ = ancestorMap;

		System.out.println("Done! (" + (System.currentTimeMillis() - startTime)
				/ 1000f + "s)");
		requiresRebuild_ = false;
		return true;
	}

	/**
	 * Checks if the members are initialised.
	 */
	public void initMembers() {
		if (transitiveNode_ == null)
			transitiveNode_ = CommonConcepts.GENLS.getNode(dag_);
		if (relEdgeModule_ == null)
			relEdgeModule_ = (RelatedEdgeModule) dag_
					.getModule(RelatedEdgeModule.class);
		if (queryModule_ == null)
			queryModule_ = (QueryModule) dag_.getModule(QueryModule.class);
		if (virtualRoot_ == null)
			virtualRoot_ = new DAGNode();
	}

	public boolean isReady() {
		return predecessorMap_ != null && ancestorMap_ != null;
	}

	public List<Node[]> justifyTransitive(DAGNode baseNode, DAGNode transNode) {
		List<Node[]> justification = new ArrayList<Node[]>();
		justifyTransitiveR(baseNode, transNode, justification,
				new HashSet<DAGNode>());
		Collections.reverse(justification);

		return justification;
	}

	@Override
	public boolean removeEdge(DAGEdge edge) {
		if (!isReady())
			return true;

		if (!incrementalSupported_) {
			requiresRebuild_ = true;
			return true;
		}

		if (edge.getNodes()[0].equals(transitiveNode_)) {
			DAGNode nodeSubj = (DAGNode) edge.getNodes()[1];
			DAGNode nodeObj = (DAGNode) edge.getNodes()[2];
			predecessorMap_.removeEdge(nodeSubj, nodeObj, ancestorMap_);
			ancestorMap_.removeEdge(nodeObj, nodeSubj, predecessorMap_);
		}
		return true;
	}

	@Override
	public void setDAG(DirectedAcyclicGraph directedAcyclicGraph) {
		super.setDAG(directedAcyclicGraph);
		initMembers();
	}

	@Override
	public boolean supportsEdge(DAGEdge edge) {
		// TODO Should need to handle negated edges.
		return !EdgeModifier.isSpecial(edge, dag_);
	}

	@Override
	public boolean supportsNode(DAGNode node) {
		return false;
	}

	/**
	 * Topologically sorts a collection of nodes.
	 * 
	 * @param nodes
	 *            The nodes to sort.
	 * @return The topologically sorted list of nodes.
	 */
	public List<DAGNode> topologicalList(TIndexedCollection<DAGNode> nodes) {
		cycles_ = new ArrayList<>();
		LinkedList<DAGNode> sortedNodes = new LinkedList<>();
		RewriteOfModule rewriteModule = (RewriteOfModule) dag_
				.getModule(RewriteOfModule.class);
		TIntObjectIterator<DAGNode> iter = nodes.iterator();
		for (int i = nodes.size(); i-- > 0;) {
			iter.advance();
			DAGNode n = iter.value();
			if (rewriteModule != null)
				n = rewriteModule.getRewrite(n);
			// Ignore non-collections
			if (queryModule_.prove(false, CommonConcepts.ISA.getNode(dag_), n,
					CommonConcepts.COLLECTION.getNode(dag_)) == QueryResult.TRUE)
				topologicalVisit(n, sortedNodes,
						new HashMap<DAGNode, String>(),
						new ArrayList<DAGNode>());
		}
		return sortedNodes;
	}

	@Override
	public String toString() {
		if (predecessorMap_ == null)
			return "Transitive Interval Module: <EMPTY>";
		return "Transitive Interval Module: "
				+ predecessorMap_.schemaMap_.size();
	}

	private class IDOrder implements Comparator<DAGNode> {
		private String intervalIDKey_;
		private int order_;

		public IDOrder(String intervalIDKey, boolean isReversed) {
			intervalIDKey_ = intervalIDKey;
			order_ = (isReversed) ? -1 : 1;
		}

		@Override
		public int compare(DAGNode arg0, DAGNode arg1) {

			return order_
					* Integer.compare(
							Integer.parseInt(arg0.getProperty(intervalIDKey_)),
							Integer.parseInt(arg1.getProperty(intervalIDKey_)));
		}

	}

	/**
	 * The interval schema that forms the lookup data structure.
	 * 
	 * @author Sam Sarjant
	 */
	private class IntervalSchema implements Serializable {
		private static final long serialVersionUID = -5807751667326507399L;
		private String intervalIDKey_;
		private SortedMap<Integer, DAGNode> inverseMap_;
		private int maxIndex_;
		private transient Lock schemaLock_;
		private Map<Integer, Collection<int[]>> schemaMap_;

		public IntervalSchema(int size, String intervalIDKey) {
			schemaMap_ = new ConcurrentHashMap<>(size);
			inverseMap_ = new TreeMap<>();
			intervalIDKey_ = intervalIDKey;
			schemaLock_ = new ReentrantLock();
		}

		/**
		 * Adds all intervals associated with n2 to n1, subsuming where
		 * appropriate.
		 * 
		 * @param n1
		 *            The node to add intervals to.
		 * @param n2
		 *            The node to add intervals from.
		 * @return True if the intervals were changed as a result of this call.
		 */
		private boolean addIntervals(DAGNode n1, DAGNode n2) {
			if (n1.getProperty(intervalIDKey_) == null)
				return false;
			Integer n1ID = Integer.parseInt(n1.getProperty(intervalIDKey_));
			if (n2.getProperty(intervalIDKey_) == null)
				return false;
			Integer n2ID = Integer.parseInt(n2.getProperty(intervalIDKey_));
			Collection<int[]> n1Intervals = schemaMap_.get(n1ID);
			Collection<int[]> n2Intervals = schemaMap_.get(n2ID);

			boolean changed = false;
			for (int[] intervalN2 : n2Intervals) {
				boolean addNew = true;
				// TODO Merge intervals when appropriate
				// Check for subsumption
				for (int[] intervalN1 : n1Intervals) {
					if (intervalN1[0] <= intervalN2[0]
							&& intervalN1[1] >= intervalN2[1]) {
						// 1_0, 2_0, 2_1, 1_1
						addNew = false;
					} else {
						if (intervalN2[0] < intervalN1[0]
								&& intervalN2[1] >= intervalN1[0]) {
							// 2_0, 1_0, 2_1
							intervalN1[0] = intervalN2[0];
							addNew = false;
							changed = true;
						}
						if (intervalN2[1] > intervalN1[1]
								&& intervalN2[0] <= intervalN1[1]) {
							// 2_0, 1_1, 2_1
							intervalN1[1] = intervalN2[1];
							addNew = false;
							changed = true;
						}
					}

					if (!addNew)
						break;
				}

				if (addNew)
					n1Intervals.add(Arrays.copyOf(intervalN2, 2));
				changed |= addNew;
			}
			return changed;
		}

		/**
		 * Traverse a spanning tree in post-order (LRC) fashion. Assign IDs to
		 * the nodes and record the intervals.
		 * 
		 * @param node
		 *            The current node.
		 * @param currentIndex
		 *            The current ID to assign.
		 * @param internalIDKey
		 *            The key under which the internals IDs are recorded on the
		 *            node.
		 * @param spanningTree
		 *            The spanning tree.
		 * @return The next ID after this one.
		 */
		private int postOrderTraverse(DAGNode node, int currentIndex,
				String internalIDKey, Map<DAGNode, TreeNode> spanningTree) {
			// Children first
			int prevIndex = currentIndex;
			TreeNode spanNode = spanningTree.get(node);
			for (DAGNode child : spanNode.children_) {
				currentIndex = postOrderTraverse(child, currentIndex,
						internalIDKey, spanningTree);
			}

			// Assign ID and interval
			currentIndex += interval_;
			dag_.addProperty(node, internalIDKey, currentIndex + "");
			recordID(node, currentIndex, new int[] { prevIndex + 1,
					currentIndex });

			return currentIndex;
		}

		/**
		 * Records ID details for a given node.
		 * 
		 * @param node
		 *            The node to record details for.
		 * @param id
		 *            The spanning tree ID of the node.
		 * @param interval
		 *            The interval of transitive values to record for the node.
		 */
		private void recordID(DAGNode node, int id, int[] interval) {
			if (schemaLock_ == null)
				schemaLock_ = new ReentrantLock();
			try {
				schemaLock_.lock();
				inverseMap_.put(id, node);
				Collection<int[]> intervals = new ArrayList<>(1);
				intervals.add(interval);
				schemaMap_.put(id, intervals);
			} finally {
				schemaLock_.unlock();
			}
		}

		/**
		 * Removes all intervals associated with n2 from n1, reducing interval
		 * scope where appropriate.
		 * 
		 * @param n1
		 *            The node to remove intervals from.
		 * @param n2
		 *            This node's intervals are removed from the other node.
		 * @return True if intervals were changed as a result of this call.
		 */
		private boolean removeIntervals(DAGNode n1, DAGNode n2) {
			if (n1.equals(n2))
				return false;
			Integer n1ID = Integer.parseInt(n1.getProperty(intervalIDKey_));
			Collection<int[]> n1Intervals = schemaMap_.get(n1ID);
			Integer n2ID = Integer.parseInt(n2.getProperty(intervalIDKey_));
			boolean changed = false;
			for (int[] intervalN2 : schemaMap_.get(n2ID)) {
				int[] splitInterval = null;
				// Check for subsumption
				for (Iterator<int[]> iter = n1Intervals.iterator(); iter
						.hasNext();) {
					int[] intervalN1 = iter.next();
					if (intervalN1[0] < intervalN2[0]
							&& intervalN1[1] > intervalN2[1]) {
						// 1_0, 2_0, 2_1, 1_1
						splitInterval = new int[] { intervalN2[1] + 1,
								intervalN1[1] };
						intervalN1[1] = intervalN2[0] - 1;
					} else {
						if (intervalN2[0] <= intervalN1[0]
								&& intervalN2[1] > intervalN1[0]) {
							// 2_0, 1_0, 2_1
							intervalN1[0] = intervalN2[1] + 1;
							changed = true;
						}
						if (intervalN2[1] >= intervalN1[1]
								&& intervalN2[0] < intervalN1[1]) {
							// 2_0, 1_1, 2_1
							intervalN1[1] = intervalN2[0] - 1;
							changed = true;
						}
						if (intervalN1[0] > intervalN1[1])
							iter.remove();
					}
				}

				if (splitInterval != null) {
					n1Intervals.add(splitInterval);
					changed = true;
				}
			}
			return changed;
		}

		/**
		 * Adds a new node as a child of another node.
		 * 
		 * @param node
		 *            The node to add.
		 * @param objKey
		 *            The key of the parent to add to.
		 * @return True if the node was added. False if the tree needs to be
		 *         rebuilt.
		 */
		public synchronized boolean addNewLeafNode(DAGNode node, int objKey) {
			SortedMap<Integer, DAGNode> headMap = inverseMap_.headMap(objKey);
			int prevIndex = (headMap != null && !headMap.isEmpty()) ? headMap
					.lastKey() : 0;
			// int childID = (int) Math.ceil(prevIndex * INTERVAL_SPLIT + objKey
			// * (1 - INTERVAL_SPLIT));
			int childID = prevIndex + 1;
			if (inverseMap_.containsKey(childID))
				return false;

			dag_.addProperty(node, intervalIDKey_, childID + "");
			recordID(node, childID, new int[] { prevIndex + 1, childID });
			return true;
		}

		/**
		 * Adds a new node to the virtual root of the tree.
		 * 
		 * @param node
		 *            The node being added.
		 * @return True if the node was added. False if the tree needs to be
		 *         rebuilt.
		 */
		public synchronized boolean addNewRootedNode(DAGNode node) {
			int prevIndex = maxIndex_;
			if (maxIndex_ > Integer.MAX_VALUE - interval_) {
				// Too many indices. Reduce interval and restructure tree
				interval_ /= 2;
				return false;
			}

			maxIndex_ += interval_;
			dag_.addProperty(node, intervalIDKey_, maxIndex_ + "");
			recordID(node, maxIndex_, new int[] { prevIndex + 1, maxIndex_ });
			return true;
		}

		/**
		 * Assigns initial IDs and intervals to the set of nodes in a spanning
		 * tree.
		 * 
		 * @param spanningTree
		 *            The tree of nodes to assign IDs to.
		 * @return The resulting IntervalSchema.
		 */
		public IntervalSchema assignIntervals(
				Map<DAGNode, TreeNode> spanningTree) {
			IntervalSchema intervalSchema = new IntervalSchema(
					spanningTree.size() * 2, intervalIDKey_);
			maxIndex_ = postOrderTraverse(virtualRoot_, 0, intervalIDKey_,
					spanningTree);
			recordID(virtualRoot_, maxIndex_,
					new int[] { 0, Integer.MAX_VALUE });
			return intervalSchema;
		}

		/**
		 * Gets all transitive predecessors for a given node, ordered by their
		 * post-order IDs.
		 * 
		 * @param node
		 *            The node to get the transitive predecessors for.
		 * @param reversed
		 *            If the order of elements should be reversed.
		 * @return All transitively accessed nodes.
		 */
		public Collection<DAGNode> getTransitivePredecessors(DAGNode node,
				boolean reversed) {
			String predIDStr = node.getProperty(intervalIDKey_);
			if (predIDStr == null)
				return null;
			int predID = Integer.parseInt(predIDStr);
			Collection<int[]> intervals = schemaMap_.get(predID);
			Collection<DAGNode> transitive = new TreeSet<>(new IDOrder(
					intervalIDKey_, reversed));

			if (schemaLock_ == null)
				schemaLock_ = new ReentrantLock();
			try {
				schemaLock_.lock();
				for (int[] interval : intervals)
					transitive.addAll(inverseMap_.subMap(interval[0],
							interval[1] + 1).values());
			} finally {
				schemaLock_.unlock();
			}

			return transitive;
		}

		/**
		 * Asks if a given node is transitively higher than another.
		 * 
		 * @param node
		 *            The base node.
		 * @param otherNode
		 *            The query node.
		 * @return True if the otherNode is transitively accessed from node.
		 * @throws Exception
		 *             If the node properties are misaligned to the transitive
		 *             properties.
		 */
		public boolean isTransitive(DAGNode node, DAGNode otherNode)
				throws Exception {
			String predIDStr = node.getProperty(intervalIDKey_);
			if (predIDStr == null)
				return false;
			int predID = Integer.parseInt(predIDStr);

			String otherIDStr = otherNode.getProperty(intervalIDKey_);
			if (otherIDStr == null)
				return false;
			int otherID = Integer.parseInt(otherIDStr);

			Collection<int[]> intervals = schemaMap_.get(predID);
			if (intervals == null)
				throw new Exception("Transitive properties misaligned! Please "
						+ "wipe properties and rebuild.");
			for (int[] interval : intervals)
				if (otherID >= interval[0] && otherID <= interval[1])
					return true;
			return false;
		}

		/**
		 * Links the nodes with non-spanning tree intervals.
		 * 
		 * @param topologicalList
		 *            The nodes sorted topologically.
		 * @param schema
		 *            The interval schema to add to.
		 * @param predecessorTree
		 *            If the interval schema is for predecessors (else
		 *            ancestors).
		 */
		public void linkIntervals(List<DAGNode> topologicalList,
				boolean predecessorTree) {
			ListIterator<DAGNode> iter = null;
			if (predecessorTree)
				iter = topologicalList.listIterator(topologicalList.size());
			else
				iter = topologicalList.listIterator();
			while ((predecessorTree && iter.hasPrevious())
					|| (!predecessorTree && iter.hasNext())) {
				DAGNode n = (predecessorTree) ? iter.previous() : iter.next();
				Collection<DAGNode> transitiveNodes = getTransitiveNodes(n,
						predecessorTree);
				for (DAGNode edgeNode : transitiveNodes) {
					if (edgeNode.equals(n) || edgeNode.isAnonymous())
						continue;
					addIntervals(n, edgeNode);
				}
			}
		}

		/**
		 * Adds all intervals associated with interval node to base node and
		 * propagates the changes to predecessors as much as necessary.
		 * 
		 * @param baseNode
		 *            The node to add the intervals to.
		 * @param intervalNode
		 *            The node from which the intervals are added.
		 */
		public void propagateIntervalAddition(DAGNode baseNode,
				DAGNode intervalNode, IntervalSchema mirrorSchema,
				boolean removeFirst) {
			Collection<DAGNode> predecessors = mirrorSchema
					.getTransitivePredecessors(baseNode, true);
			for (DAGNode predecessor : predecessors) {
				// TODO Can stop early if ordered correctly.
				addIntervals(predecessor, intervalNode);
			}
		}

		/**
		 * Removes an edge from the tree.
		 * 
		 * @param nodeSubj
		 *            The subject of the edge.
		 * @param nodeObj
		 *            The object of the edge.
		 * @param mirrorSchema
		 */
		public void removeEdge(DAGNode nodeSubj, DAGNode nodeObj,
				IntervalSchema mirrorSchema) {
			// Propagate the intervals
			Collection<DAGNode> predecessors = mirrorSchema
					.getTransitivePredecessors(nodeSubj, true);

			if (schemaLock_ == null)
				schemaLock_ = new ReentrantLock();
			try {
				schemaLock_.lock();
				for (DAGNode predecessor : predecessors) {
					// Remove the subjIntervals from the obj
					removeIntervals(predecessor, nodeObj);
					Collection<DAGNode> transitives = getTransitiveNodes(
							predecessor, intervalIDKey_.equals(PREDECESSOR_ID));
					for (DAGNode n : transitives)
						addIntervals(predecessor, n);
				}
			} finally {
				schemaLock_.unlock();
			}
		}
	}

	private class TreeNode {
		Collection<DAGNode> children_;
		Collection<DAGNode> trans_;

		public TreeNode() {
			trans_ = new ArrayList<>();
			children_ = new ArrayList<>();
		}
	}
}

import graph.core.CommonConcepts;
import graph.core.DAGNode;
import graph.core.DirectedAcyclicGraph;
import graph.core.Edge;
import graph.core.EdgeModifier;
import graph.core.Node;
import graph.core.OntologyFunction;
import graph.inference.CommonQuery;
import graph.inference.QueryObject;
import graph.inference.QueryResult;
import graph.inference.QueryWorker;
import graph.inference.Substitution;
import graph.inference.VariableNode;
import graph.module.DisjointModule;
import graph.module.QueryModule;
import graph.module.TransitiveIntervalSchemaModule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.apache.commons.collections4.CollectionUtils;

/*******************************************************************************
 * Copyright (C) 2013 University of Waikato, Hamilton, New Zealand.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Sam Sarjant - initial API and implementation
 ******************************************************************************/
package graph.inference.module;




public class DisjointWithWorker extends QueryWorker {
	private static final long serialVersionUID = -2014914913163958118L;
	private transient DisjointModule disjointModule_;

	public DisjointWithWorker(QueryModule queryModule) {
		super(queryModule);
	}

	private Collection<? extends Node[]> alterGenlJustification(

			QueryObject genlResults, DAGNode disjointNode, boolean isReversed) {
