/******************************************************************************
 * Copyright (c) 2015 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *****************************************************************************/
 package com.ibm.research.owlql;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.Query;
import org.apache.jena.sparql.syntax.ElementTriplesBlock;
import org.apache.jena.sparql.syntax.ElementUnion;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.ClassExpressionType;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDataSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLDisjointDataPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLDisjointObjectPropertiesAxiom;
import org.semanticweb.owlapi.model.OWLIrreflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLNamedObject;
import org.semanticweb.owlapi.model.OWLObjectComplementOf;
import org.semanticweb.owlapi.model.OWLObjectInverseOf;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLProperty;
import org.semanticweb.owlapi.model.OWLPropertyExpression;
import org.semanticweb.owlapi.model.OWLPropertyRange;
import org.semanticweb.owlapi.model.OWLQuantifiedRestriction;
import org.semanticweb.owlapi.model.OWLReflexiveObjectPropertyAxiom;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.model.OWLSubPropertyAxiom;
import org.semanticweb.owlapi.rdf.util.RDFConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;

public class NormalizedOWLQLTbox {

	private static final Logger logger = LoggerFactory.getLogger(NormalizedOWLQLTbox.class);
	/**
	 * the original ontology
	 */
	//protected OWLOntology originalOnt;
	/**
	 * OWL factory
	 */
	protected OWLDataFactory fac;
	/**
	 * normalizer
	 */
	protected OWLQLNormalizer normalizer;
	/**
	 * normalized OWLQL axioms
	 */
	protected OWLOntology normalizedOntology;
	/**
	 * non OWL QL axioms
	 */
	protected Set<OWLAxiom> nonOWLQLAxioms;
	/**
	 * positive concept inclusion axioms in the tbox
	 */
	protected Set<OWLSubClassOfAxiom> posIncAxInTbox;
	/**
	 * positve role inclusion axioms in the tbox
	 */
	protected Set<OWLSubPropertyAxiom> posSubPropAxInTbox;
	/**
	 * negative concept inclusion in the tbox
	 */
	protected Set<OWLSubClassOfAxiom> negIncAxInTbox;
	
	protected Set<OWLObjectProperty> reflexiveProps;
	
	protected Set<OWLObjectProperty> irreflexiveProps;
	
	
	/**
	 * negative object property inclusion axioms in the tbox
	 */
	protected Set<OWLDisjointObjectPropertiesAxiom> negObjectSubPropAxInTbox;
	/**
	 * negative data property inclusion axioms in the tbox
	 */
	protected Set<OWLDisjointDataPropertiesAxiom> negDataSubPropAxInTbox;
	
	/**
	 * negative concept inclusion in the the negative closure
	 */
	protected Set<OWLSubClassOfAxiom> negIncAxInNegClos;
	/**
	 * negative object property inclusion axioms in the negative closure
	 */
	protected Set<OWLDisjointObjectPropertiesAxiom> negObjectSubPropAxInNegClos;
	/**
	 * negative data property inclusion axioms in the negative closure
	 */
	protected Set<OWLDisjointDataPropertiesAxiom> negDataSubPropAxInNegClos;
	/**
	 *  negative inclusion axiom closure
	 */
	protected Set<OWLAxiom> negativeInclClosure;
	/**mapping from key (ie, named property or concept) positive concept inclusion axioms.
	* used as an index to speed up the search for axioms to consider during rewrite.
	*/
	protected Map<String, Set<OWLSubClassOfAxiom>> iri2PosIncAxInTbox;
	/**mapping from key (ie, named property or concept) positive role inclusion axioms.
	* used as an index to speed up the search for axioms to consider during rewrite.
	*/
	protected Map<String, Set<OWLSubPropertyAxiom>> iri2PosSubPropAxInTbox;

	protected Set<OWLClass> unsatisfiableClasses;
	protected Set<OWLProperty> unsatisfiableProperties;
	
	
	public NormalizedOWLQLTbox(OWLOntology originalOntTbox) {
		super();
		fac = originalOntTbox.getOWLOntologyManager().getOWLDataFactory();
		init(originalOntTbox.getAxioms());
	}
	
	public NormalizedOWLQLTbox(Set<OWLAxiom> axioms) {
		super();
		fac = OWLDataFactoryImpl.getInstance();
		init(axioms);
	}
	
	
	
	public NormalizedOWLQLTbox(NormalizedOWLQLTbox tbox) {
		super();
		try {
			this.fac = tbox.fac;
			this.normalizer = tbox.normalizer.copy();
			this.normalizedOntology = OWLManager.createOWLOntologyManager().createOntology();
			this.normalizedOntology.getOWLOntologyManager().addAxioms(this.normalizedOntology, tbox.normalizedOntology.getAxioms());
			this.nonOWLQLAxioms = new HashSet<OWLAxiom>(tbox.nonOWLQLAxioms);
			this.posIncAxInTbox = new HashSet<OWLSubClassOfAxiom>(tbox.posIncAxInTbox);
			this.posSubPropAxInTbox = new HashSet<OWLSubPropertyAxiom>(tbox.posSubPropAxInTbox);
			this.negIncAxInTbox =  new HashSet<OWLSubClassOfAxiom>(tbox.negIncAxInTbox);
			this.negObjectSubPropAxInTbox =  new HashSet<OWLDisjointObjectPropertiesAxiom>(tbox.negObjectSubPropAxInTbox);
			this.negDataSubPropAxInTbox = new HashSet<OWLDisjointDataPropertiesAxiom>(tbox.negDataSubPropAxInTbox);
			this.negativeInclClosure = new HashSet<OWLAxiom>(tbox.negativeInclClosure);
			this.iri2PosIncAxInTbox = new HashMap<String, Set<OWLSubClassOfAxiom>>();
			this.negIncAxInNegClos = tbox.negIncAxInNegClos!=null?
						new HashSet<OWLSubClassOfAxiom>(tbox.negIncAxInNegClos) : null;
			this.negObjectSubPropAxInNegClos = tbox.negObjectSubPropAxInNegClos!=null?
						new HashSet<OWLDisjointObjectPropertiesAxiom>(tbox.negObjectSubPropAxInNegClos) : null;
			this.negDataSubPropAxInNegClos = tbox.negDataSubPropAxInNegClos!=null ? 
						new HashSet<OWLDisjointDataPropertiesAxiom>(tbox.negDataSubPropAxInNegClos) : null;
			this.unsatisfiableClasses = null;
			this.unsatisfiableProperties = null;
			for (Map.Entry<String, Set<OWLSubClassOfAxiom>> e: tbox.iri2PosIncAxInTbox.entrySet() ) {
				this.iri2PosIncAxInTbox.put(e.getKey(), new HashSet<OWLSubClassOfAxiom>(e.getValue()));
			}
			this.iri2PosSubPropAxInTbox = new HashMap<String, Set<OWLSubPropertyAxiom>>();
			for (Map.Entry<String, Set<OWLSubPropertyAxiom>> e:tbox.iri2PosSubPropAxInTbox.entrySet()) {
				this.iri2PosSubPropAxInTbox.put(e.getKey(), new HashSet<OWLSubPropertyAxiom>(e.getValue()));
			}
		} catch (OWLOntologyCreationException e) {
			throw new RuntimeException(e);
		}
	}

	public Set<OWLAxiom> getNegativeInclClosure() {
		return negativeInclClosure;
	}

	/**
	 * negative object property inclusion axioms in the tbox
	 */
	public Set<OWLDisjointObjectPropertiesAxiom> getNegativeObjectSubPropertyAxioms() {
		return negObjectSubPropAxInTbox;
	}
	/**
	 * negative data property inclusion axioms in the tbox
	 */
	public Set<OWLDisjointDataPropertiesAxiom>  getNegativeDataSubPropertyAxioms() {
		return negDataSubPropAxInTbox;
	}
	/**
	 * negative concept inclusion in the negative closure
	 */
	public Set<OWLSubClassOfAxiom> getNegativeInclInNegClosure() {
		if (negIncAxInNegClos ==null) {
			negIncAxInNegClos = new HashSet<OWLSubClassOfAxiom>();
			negObjectSubPropAxInNegClos = new HashSet<OWLDisjointObjectPropertiesAxiom>();
			negDataSubPropAxInNegClos = new HashSet<OWLDisjointDataPropertiesAxiom>();
			organizeNegativeClosure(negativeInclClosure, negIncAxInNegClos, negObjectSubPropAxInNegClos, negDataSubPropAxInNegClos);
		}
		return negIncAxInNegClos;
	}
	/**
	 * negative object property inclusion axioms in the negative closure
	 */
	protected Set<OWLDisjointObjectPropertiesAxiom> getNegativeObjectSubPropInNegClosure() {
		if (negObjectSubPropAxInNegClos ==null) {
			negIncAxInNegClos = new HashSet<OWLSubClassOfAxiom>();
			negObjectSubPropAxInNegClos = new HashSet<OWLDisjointObjectPropertiesAxiom>();
			negDataSubPropAxInNegClos = new HashSet<OWLDisjointDataPropertiesAxiom>();
			organizeNegativeClosure(negativeInclClosure, negIncAxInNegClos, negObjectSubPropAxInNegClos, negDataSubPropAxInNegClos);
		}
		return negObjectSubPropAxInNegClos;
	}
	/**
	 * negative data property inclusion axioms in the negative closure
	 */
	protected Set<OWLDisjointDataPropertiesAxiom> getNegativeDataSubPropAxInClosure() {
		if (negDataSubPropAxInNegClos == null) {
			negIncAxInNegClos = new HashSet<OWLSubClassOfAxiom>();
			negObjectSubPropAxInNegClos = new HashSet<OWLDisjointObjectPropertiesAxiom>();
			negDataSubPropAxInNegClos = new HashSet<OWLDisjointDataPropertiesAxiom>();
			organizeNegativeClosure(negativeInclClosure, negIncAxInNegClos, negObjectSubPropAxInNegClos, negDataSubPropAxInNegClos);
		}
		return negDataSubPropAxInNegClos ;
	}
	public OWLDataFactory getFactory() {
		return fac;
	}

	public OWLQLNormalizer getNormalizer() {
		return normalizer;
	}

	protected  Set<OWLAxiom> computeNegativeInclusionClosure(Set<OWLSubClassOfAxiom> deltaNegIncAx,
			Set<OWLDisjointObjectPropertiesAxiom> deltaNegObjSubPropAx,Set<OWLDisjointDataPropertiesAxiom> deltaNegDataSubPropAx,
			Set<OWLSubClassOfAxiom> deltaPosIncAx, Set<OWLSubPropertyAxiom> deltaPosSubPropAx) {
		
		return computeNegativeInclusionClosure(deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx, deltaPosIncAx, deltaPosSubPropAx, null);
	}

	
	protected  Set<OWLAxiom> computeNegativeInclusionClosure(Collection<OWLSubClassOfAxiom> deltaNegIncAx,
			Collection<OWLDisjointObjectPropertiesAxiom> deltaNegObjSubPropAx, Collection<OWLDisjointDataPropertiesAxiom> deltaNegDataSubPropAx,
			Collection<OWLSubClassOfAxiom> deltaPosIncAx, Collection<OWLSubPropertyAxiom> deltaPosSubPropAx, OWLAxiom inconsistencyWitness) {
		Set<OWLAxiom> ret = new HashSet<OWLAxiom>();
		/*if (negativeInclClosure!=null) {
			deltaNegIncAx.removeAll(negativeInclClosure);
			deltaNegDataSubPropAx.removeAll(negativeInclClosure);
			deltaNegObjSubPropAx.removeAll(negativeInclClosure);
			ret.addAll(negativeInclClosure);
			deltaPosIncAx.removeAll(posIncAxInTbox);
			deltaPosSubPropAx.removeAll(posSubPropAxInTbox);
		}*/
		
		// 1. add all negative inclusions
		ret.addAll(deltaNegIncAx);
		ret.addAll(deltaNegObjSubPropAx);
		ret.addAll(deltaNegDataSubPropAx);
		//
		
		List<OWLSubClassOfAxiom> negIncAx;
		List<OWLDisjointObjectPropertiesAxiom> negObjSubPropAx;
		List<OWLDisjointDataPropertiesAxiom> negDataSubPropAx;
		List<OWLSubClassOfAxiom>  posIncAx; 
		List<OWLSubPropertyAxiom> posSubPropAx;
		
		for (int i = 0; i <2; i++) {
			if (i==0) {
				// positive closure set to all pos axioms
				Set<OWLSubClassOfAxiom> temp = new HashSet<OWLSubClassOfAxiom>(posIncAxInTbox);
				temp.addAll(deltaPosIncAx);
				posIncAx = new LinkedList<OWLSubClassOfAxiom>(temp);
				Set<OWLSubPropertyAxiom> temp2 = new HashSet<OWLSubPropertyAxiom>(posSubPropAxInTbox);	
				temp2.addAll(deltaPosSubPropAx);
				posSubPropAx = new LinkedList<OWLSubPropertyAxiom>(temp2);
				//
				
				// negative closure set to delta
				negIncAx = new LinkedList<OWLSubClassOfAxiom>( deltaNegIncAx);
				negObjSubPropAx = new LinkedList<OWLDisjointObjectPropertiesAxiom>(deltaNegObjSubPropAx);
				negDataSubPropAx = new LinkedList<OWLDisjointDataPropertiesAxiom>(deltaNegDataSubPropAx);
				//
			} else {
				if (negativeInclClosure == null) {
					continue;
				}
				// negative closure fixed
				negIncAx = /*Collections.unmodifiableSet*/new LinkedList<OWLSubClassOfAxiom>(getNegativeInclInNegClosure());// new HashSet<OWLSubClassOfAxiom>(getNegativeInclInNegClosure());
				//negIncAx.addAll(deltaNegIncAx);
				negObjSubPropAx =/*Collections.unmodifiableSet*/new LinkedList<OWLDisjointObjectPropertiesAxiom>(getNegativeObjectSubPropInNegClosure());// new HashSet<OWLDisjointObjectPropertiesAxiom>(getNegativeObjectSubPropInNegClosure());
				//negObjSubPropAx.addAll(deltaNegObjSubPropAx);
				negDataSubPropAx = /*Collections.unmodifiableSet*/new LinkedList<OWLDisjointDataPropertiesAxiom>(getNegativeDataSubPropAxInClosure());// new HashSet<OWLDisjointDataPropertiesAxiom>(getNegativeDataSubPropAxInClosure());
				//negDataSubPropAx.addAll(deltaNegDataSubPropAx);				
				//			
				
				//
				posIncAx = new LinkedList<OWLSubClassOfAxiom>(deltaPosIncAx);
				posSubPropAx = new LinkedList<OWLSubPropertyAxiom>(deltaPosSubPropAx);
				//
			}
			boolean firstTimeInWhileLoop = true;
			int numOfIterations = 0;
			Set<OWLSubClassOfAxiom> newNegIncAx = new HashSet<OWLSubClassOfAxiom>();
			Set<OWLDisjointObjectPropertiesAxiom> newNegObjSubPropAx = new HashSet<OWLDisjointObjectPropertiesAxiom>();
			Set<OWLDisjointDataPropertiesAxiom> newNegDataSubPropAx = new HashSet<OWLDisjointDataPropertiesAxiom>();

			while (!negIncAx.isEmpty() || !negObjSubPropAx.isEmpty()
					|| !negDataSubPropAx.isEmpty()) {
				if (logger.isDebugEnabled()) {
					logger.debug("Number of iterations "+numOfIterations);
					//if (numOfIterations>50)
					{
						logger.debug("posIncAc: {}\n{}", posIncAx.size(), posIncAx);
						logger.debug("negIncAx: {}\n{}", negIncAx.size(), negIncAx) ;
						logger.debug("negObjSubPropAx: {}\n{}", negObjSubPropAx.size(), negObjSubPropAx);
						logger.debug("negDataSubPropAx: {}\n{}", negDataSubPropAx.size(), negDataSubPropAx);
					}
				}
				numOfIterations++;
				newNegIncAx.clear();
				newNegObjSubPropAx.clear();
				newNegDataSubPropAx.clear();

				// if sub(B1, B2) in Tbox and sub(B2, not(B3)) or sub(B3, not(B2)) in negClos, then
				// add sub(B1, not(B3) to negClos;
				if (!negIncAx.isEmpty()) {
					for (OWLSubClassOfAxiom posSubAx : posIncAx) {
						OWLClassExpression b1 = posSubAx.getSubClass();
						OWLClassExpression b2 = posSubAx.getSuperClass();
						assert !isNegatedOWLQLRHSConcept(b2) : b2 +"\n"+b1;
						for (OWLSubClassOfAxiom negSubAx : negIncAx) {
							OWLClassExpression subclass = negSubAx.getSubClass();
							OWLClassExpression supclass = negSubAx.getSuperClass();
							if (isNegatedOWLQLRHSConcept(supclass)) {
								if (b2.equals(subclass)) {
									newNegIncAx.add(fac.getOWLSubClassOfAxiom(b1,
											supclass));
								} else {
									OWLClassExpression notSupclass = fac
											.getOWLObjectComplementOf(supclass)
											.getNNF();
									if (b2.equals(notSupclass)) {
										OWLClassExpression notSubclass = fac
												.getOWLObjectComplementOf(subclass)
												.getNNF();
										newNegIncAx.add(fac.getOWLSubClassOfAxiom(
												b1, notSubclass));
									}
								}
							}
						}
								
					}
				}

				//
				for (OWLSubPropertyAxiom posSubAx : posSubPropAx) {
					OWLPropertyExpression r1 = posSubAx.getSubProperty();
					OWLPropertyExpression r2 = posSubAx.getSuperProperty();
					OWLQuantifiedRestriction b1;
					OWLQuantifiedRestriction b1Inv;
					if (r1.isObjectPropertyExpression()) {
						b1 = fac.getOWLObjectSomeValuesFrom(
								(OWLObjectPropertyExpression) r1, fac
										.getOWLThing());
						OWLObjectPropertyExpression r1Inv = fac
								.getOWLObjectInverseOf((OWLObjectPropertyExpression) r1).getSimplified();
						b1Inv = fac.getOWLObjectSomeValuesFrom(r1Inv, fac
								.getOWLThing());
					} else {
						assert r1.isDataPropertyExpression() : r1;
						b1 = fac.getOWLDataSomeValuesFrom(
								(OWLDataPropertyExpression) r1, fac
										.getTopDatatype());
						b1Inv = null;
					}
					//if sub(R1, R2) in Tbox and sub(some(R2, top), not(B)) or sub(B, not(some(R2, top))) is in negClos, then
					// add sub(some(R1, top), not(B))  in negClos;

					// if sub(R1, R2) in Tbox and sub(some(inv(R2), top), not(B)) or sub(B, not(some(inv(R2), top))) is in negClos, then
					// add sub(some(inv(R1), top), not(B))  in negClos;
					for (OWLSubClassOfAxiom negSubAx : negIncAx) {
						OWLClassExpression subclass = negSubAx.getSubClass();
						OWLClassExpression supclass = negSubAx.getSuperClass();
						if (isNegatedOWLQLRHSConcept(supclass)) {
							if (subclass
									.getClassExpressionType()
									.equals(
											ClassExpressionType.OBJECT_SOME_VALUES_FROM)
									|| subclass
											.getClassExpressionType()
											.equals(
													ClassExpressionType.DATA_SOME_VALUES_FROM)) {
								OWLQuantifiedRestriction some = (OWLQuantifiedRestriction) subclass;
								OWLPropertyExpression prop = some.getProperty();
								if (r2.equals(prop)) {
									newNegIncAx.add(fac.getOWLSubClassOfAxiom(
											b1, supclass));
								} else if (prop.isObjectPropertyExpression()) {
									OWLObjectPropertyExpression oprop = (OWLObjectPropertyExpression) prop;
									OWLObjectPropertyExpression propInv = fac
											.getOWLObjectInverseOf(oprop).getSimplified();
									if (r2.equals(propInv)) {
										assert b1Inv != null : r2 + "\n" + prop
												+ "\n" + propInv;
										newNegIncAx.add(fac
												.getOWLSubClassOfAxiom(b1Inv,
														supclass));
									}
								}
							}
							OWLClassExpression notSupclass = fac
									.getOWLObjectComplementOf(supclass)
									.getNNF();
							if (notSupclass
									.getClassExpressionType()
									.equals(
											ClassExpressionType.OBJECT_SOME_VALUES_FROM)
									|| notSupclass
											.getClassExpressionType()
											.equals(
													ClassExpressionType.DATA_SOME_VALUES_FROM)) {
								OWLQuantifiedRestriction some = (OWLQuantifiedRestriction) notSupclass;
								OWLPropertyExpression prop = some.getProperty();
								if (r2.equals(prop)) {
									OWLClassExpression notSubclass = fac
											.getOWLObjectComplementOf(subclass)
											.getNNF();
									newNegIncAx.add(fac.getOWLSubClassOfAxiom(
											b1, notSubclass));
								} else if (prop.isObjectPropertyExpression()) {
									OWLObjectPropertyExpression oprop = (OWLObjectPropertyExpression) prop;
									OWLObjectPropertyExpression propInv = fac
											.getOWLObjectInverseOf(oprop).getSimplified();
									if (r2.equals(propInv)) {
										assert b1Inv != null : r2 + "\n" + prop
												+ "\n" + propInv;
										OWLClassExpression notSubclass = fac
												.getOWLObjectComplementOf(
														subclass).getNNF();
										newNegIncAx.add(fac
												.getOWLSubClassOfAxiom(b1Inv,
														notSubclass));
									}
								}
							}
						}

					}
					//

					//if sub(R1,R2) in TBox and sub(R2, not(R3)) or sub(R3, not(R2) in negClos, then
					//add sub(R1, not(R3)) to negClos
					Set<Set<? extends OWLPropertyExpression>> disjointProperties = new HashSet<Set<? extends OWLPropertyExpression>>();
					for (OWLDisjointObjectPropertiesAxiom dpax : negObjSubPropAx) {
						disjointProperties.add(dpax.getProperties());
					}
					for (OWLDisjointDataPropertiesAxiom dpax : negDataSubPropAx) {
						disjointProperties.add(dpax.getProperties());
					}
					for (Set<? extends OWLPropertyExpression> props : disjointProperties) {
						assert props.size() == 2 || props.size() == 1 : props
								+ "\n normalization was not performed properly";
						if (props.contains(r2)) {
							if (r2.isObjectPropertyExpression()) {
								Set<OWLObjectPropertyExpression> disjProps = new HashSet<OWLObjectPropertyExpression>();
								disjProps.add((OWLObjectPropertyExpression) r1);
								disjProps
										.addAll((Set<OWLObjectPropertyExpression>) props);
								if (props.size() > 1) {
									disjProps.remove(r2);
								}
								newNegObjSubPropAx
										.add(fac
												.getOWLDisjointObjectPropertiesAxiom(disjProps));
							} else {
								assert r2.isDataPropertyExpression() : r2;
								Set<OWLDataPropertyExpression> disjProps = new HashSet<OWLDataPropertyExpression>();
								disjProps.add((OWLDataPropertyExpression) r1);
								disjProps
										.addAll((Set<OWLDataPropertyExpression>) props);
								if (props.size() > 1) {
									disjProps.remove(r2);
								}
								newNegDataSubPropAx
										.add(fac
												.getOWLDisjointDataPropertiesAxiom(disjProps));
							}
						}
						//Case not covered in the original DL-Lite techreport:
						// if sub(R1,inv(R2)) in TBox and sub(R2, not(R3)) or sub(R3, not(R2) in negClos, then
						//add sub(inv(R1), not(R3)) to negClos
						if (r2.isObjectPropertyExpression()) {
							OWLObjectPropertyExpression or2 = (OWLObjectPropertyExpression) r2;
							OWLObjectPropertyExpression invR2 =fac.getOWLObjectInverseOf(or2).getSimplified();
							if (props.contains(invR2)) {
								Set<OWLObjectPropertyExpression> disjProps = new HashSet<OWLObjectPropertyExpression>();
								disjProps.add((OWLObjectPropertyExpression) fac.getOWLObjectInverseOf((OWLObjectPropertyExpression)r1).getSimplified());
								disjProps
										.addAll((Set<OWLObjectPropertyExpression>) props);
								if (props.size() > 1) {
									disjProps.remove(invR2);
								}
								newNegObjSubPropAx
										.add(fac
												.getOWLDisjointObjectPropertiesAxiom(disjProps));
							}
								
						}
						//
					}
					//
				}
				//

				// if sub(some(R), not(some(R)) or sub(some(inv(R)), not(some(inv(R))) or sub(R, not(R)) in negClos, then
				// add all three axioms in negClos
				for (OWLSubClassOfAxiom ax : negIncAx) {
					OWLClassExpression subclass = ax.getSubClass();
					OWLClassExpression supclass = ax.getSuperClass();
					OWLClassExpression negSupclass = fac
							.getOWLObjectComplementOf(supclass).getNNF();
					if (negSupclass.equals(subclass)) {
						if (subclass.getClassExpressionType().equals(
								ClassExpressionType.OBJECT_SOME_VALUES_FROM)) {
							OWLObjectSomeValuesFrom some = (OWLObjectSomeValuesFrom) subclass;
							OWLObjectPropertyExpression r = some.getProperty();
							OWLObjectPropertyExpression rInv = fac
									.getOWLObjectInverseOf(r).getSimplified();
							OWLObjectSomeValuesFrom someInv = fac
									.getOWLObjectSomeValuesFrom(rInv, fac
											.getOWLThing());
							OWLClassExpression negSomeInv = fac
									.getOWLObjectComplementOf(someInv).getNNF();
							newNegIncAx.add(fac.getOWLSubClassOfAxiom(someInv,
									negSomeInv));
							Set<OWLObjectPropertyExpression> disjProps = new HashSet<OWLObjectPropertyExpression>();
							disjProps.add(r);
							newNegObjSubPropAx
									.add(fac
											.getOWLDisjointObjectPropertiesAxiom(disjProps));
						} else if (subclass.getClassExpressionType().equals(
								ClassExpressionType.DATA_SOME_VALUES_FROM)) {
							OWLDataSomeValuesFrom some = (OWLDataSomeValuesFrom) subclass;
							OWLDataPropertyExpression r = some.getProperty();
							Set<OWLDataPropertyExpression> disjProps = new HashSet<OWLDataPropertyExpression>();
							disjProps.add(r);
							newNegDataSubPropAx
									.add(fac
											.getOWLDisjointDataPropertiesAxiom(disjProps));
						}
					}

				}
				for (OWLDisjointObjectPropertiesAxiom ax : negObjSubPropAx) {
					Set<OWLObjectPropertyExpression> props = ax.getProperties();
					assert props.size() == 2 || props.size() == 1 : props
							+ "\n normalization was not performed properly";
					if (props.size() == 1) {
						OWLObjectPropertyExpression r = props.iterator().next();
						OWLObjectSomeValuesFrom some = fac
								.getOWLObjectSomeValuesFrom(r, fac
										.getOWLThing());
						OWLClassExpression negSome = fac
								.getOWLObjectComplementOf(some).getNNF();
						newNegIncAx.add(fac
								.getOWLSubClassOfAxiom(some, negSome));

						OWLObjectPropertyExpression rInv = fac
								.getOWLObjectInverseOf(r).getSimplified();
						OWLObjectSomeValuesFrom someInv = fac
								.getOWLObjectSomeValuesFrom(rInv, fac
										.getOWLThing());
						OWLClassExpression negSomeInv = fac
								.getOWLObjectComplementOf(someInv).getNNF();
						newNegIncAx.add(fac.getOWLSubClassOfAxiom(someInv,
								negSomeInv));
					}
				}
				for (OWLDisjointDataPropertiesAxiom ax : negDataSubPropAx) {
					Set<OWLDataPropertyExpression> props = ax.getProperties();
					assert props.size() == 2 || props.size() == 1 : props
							+ "\n normalization was not performed properly";
					if (props.size() == 1) {
						OWLDataPropertyExpression r = props.iterator().next();
						OWLDataSomeValuesFrom some = fac
								.getOWLDataSomeValuesFrom(r, fac
										.getTopDatatype());
						OWLClassExpression negSome = fac
								.getOWLObjectComplementOf(some).getNNF();
						newNegIncAx.add(fac
								.getOWLSubClassOfAxiom(some, negSome));
					}
				}
				//
				newNegDataSubPropAx.removeAll(ret);
				newNegObjSubPropAx.removeAll(ret);
				newNegIncAx.removeAll(ret);
				ret.addAll(newNegIncAx);
				ret.addAll(newNegObjSubPropAx);
				ret.addAll(newNegDataSubPropAx);
				// check for inconsistencies
				if (inconsistencyWitness!=null) {
					if (newNegDataSubPropAx.contains(inconsistencyWitness)
						|| newNegObjSubPropAx.contains(inconsistencyWitness)
						|| newNegIncAx.contains(inconsistencyWitness)) {
						return null;
					}
						
				}
				//
				negIncAx = new LinkedList<OWLSubClassOfAxiom>(newNegIncAx);
				negObjSubPropAx = new LinkedList<OWLDisjointObjectPropertiesAxiom>(newNegObjSubPropAx);
				negDataSubPropAx = new LinkedList<OWLDisjointDataPropertiesAxiom>(newNegDataSubPropAx);
				if (firstTimeInWhileLoop && i==1) {
					//
					Set<OWLSubClassOfAxiom> temp = new HashSet<OWLSubClassOfAxiom>(posIncAx);
					temp.addAll(posIncAxInTbox);
					posIncAx = new LinkedList<OWLSubClassOfAxiom>(temp);
					Set<OWLSubPropertyAxiom> temp2 = new HashSet<OWLSubPropertyAxiom>(posSubPropAx);
					temp2.addAll(posSubPropAxInTbox);
					posSubPropAx = new LinkedList<OWLSubPropertyAxiom>(temp2);
					//
				}
				firstTimeInWhileLoop = false;
			}
		}
		return ret;
			
	}
	
	

	/**
	 *  returns an unmodifiable set of OWL QL normalized axioms
	 * @return
	 */
	public Set<OWLAxiom> getNormalizedAxioms() {
		return Collections.unmodifiableSet(normalizedOntology.getAxioms());
	}
	
	public static Set<OWLAxiom> getTboxRboxAxioms( Set<OWLAxiom> axs) {
		 Set<OWLAxiom> ret = new HashSet<OWLAxiom>();
		 for (OWLAxiom ax: axs) {
			 if (!isAboxAxiom(ax)) {
				 ret.add(ax);
			 }
		 }
		 return ret;
	}

	public OWLOntology getNormalizedOntology() {
		return normalizedOntology;
	}
	/**
	 *  returns an unmodifiable set of non OWL QL axioms
	 * @return
	 */
	public Set<OWLAxiom> getNonQLAxioms() {
		return Collections.unmodifiableSet(nonOWLQLAxioms);
	}

	/**
	 * initialization which includes normalization of the Tbox axioms
	 */
	protected void init(Set<OWLAxiom> axioms) {
		init(axioms, true, true);
	}
	/**
	 * initialization which includes normalization of the Tbox axioms
	 */
	protected void init(Set<OWLAxiom> axioms, boolean normalize, boolean computeNegClos) {
		try {
			if (nonOWLQLAxioms == null) {
				nonOWLQLAxioms = new HashSet<OWLAxiom>();
			}
			if (normalizer == null) {
				normalizer = new OWLQLNormalizer(fac);
			}
			OWLOntology originalOnt = OWLManager.createOWLOntologyManager().createOntology();
			originalOnt.getOWLOntologyManager().addAxioms(originalOnt, axioms);
			
			Set<OWLAxiom> normalizedAxioms = normalize? normalizer.toQLNormalForm(axioms, nonOWLQLAxioms): axioms;
			normalizedOntology = normalizedOntology!=null? normalizedOntology: OWLManager.createOWLOntologyManager().createOntology();
			normalizedOntology.getOWLOntologyManager().addAxioms(normalizedOntology, normalizedAxioms);
			// add properties and types
			for (OWLObjectProperty p: originalOnt.getObjectPropertiesInSignature()) {
				if (!normalizedOntology.getObjectPropertiesInSignature().contains(p)) {
					normalizedOntology.getOWLOntologyManager().addAxiom(normalizedOntology,
							normalizedOntology.getOWLOntologyManager().getOWLDataFactory().getOWLDeclarationAxiom(p));
				}
			}
			for (OWLDataProperty p: originalOnt.getDataPropertiesInSignature()) {
				if (!normalizedOntology.getDataPropertiesInSignature().contains(p)) {
					normalizedOntology.getOWLOntologyManager().addAxiom(normalizedOntology,
							normalizedOntology.getOWLOntologyManager().getOWLDataFactory().getOWLDeclarationAxiom(p));
				}
			}
			for (OWLClass c: originalOnt.getClassesInSignature()) {
				if (!normalizedOntology.getClassesInSignature().contains(c)) {
					normalizedOntology.getOWLOntologyManager().addAxiom(normalizedOntology,
							normalizedOntology.getOWLOntologyManager().getOWLDataFactory().getOWLDeclarationAxiom(c));
				}
			}
			//
			if (logger.isDebugEnabled()) {
				logger.debug("Normalized axioms: {}", normalizedAxioms.size());
				for (OWLAxiom ax : normalizedAxioms) {
					logger.debug("\t{}",ax);
				}
				logger.debug("Non OWL QL axioms: {}", nonOWLQLAxioms.size());
				for (OWLAxiom ax : nonOWLQLAxioms) {
					logger.debug("\t{}",ax);
				}
			}
			if (posIncAxInTbox==null) {
				posIncAxInTbox = new HashSet<OWLSubClassOfAxiom>();
			} 
			if (negIncAxInTbox == null) {
				negIncAxInTbox = new HashSet<OWLSubClassOfAxiom>();
			}
			if (posSubPropAxInTbox == null) {
				posSubPropAxInTbox = new HashSet<OWLSubPropertyAxiom>();
			}
			if (negObjectSubPropAxInTbox == null) {
				negObjectSubPropAxInTbox = new HashSet<OWLDisjointObjectPropertiesAxiom>();
			}
			if (negDataSubPropAxInTbox == null) {
				negDataSubPropAxInTbox = new HashSet<OWLDisjointDataPropertiesAxiom>();
			}
			if (iri2PosIncAxInTbox == null) {
				iri2PosIncAxInTbox = new HashMap<String, Set<OWLSubClassOfAxiom>>();
			} 
			if (iri2PosSubPropAxInTbox == null) {
				iri2PosSubPropAxInTbox = new HashMap<String, Set<OWLSubPropertyAxiom>>();
			}
			if (reflexiveProps == null) {
				reflexiveProps = new HashSet<OWLObjectProperty>();
			}
			if (irreflexiveProps == null) {
				irreflexiveProps = new HashSet<OWLObjectProperty>();
			}
			// organize Tbox axioms per type
			organizeTboxAxioms(normalizedAxioms, negIncAxInTbox, negObjectSubPropAxInTbox, negDataSubPropAxInTbox, posIncAxInTbox, posSubPropAxInTbox, 
					reflexiveProps, irreflexiveProps, iri2PosIncAxInTbox, iri2PosSubPropAxInTbox);
			//
			if (computeNegClos) {
				negativeInclClosure = computeNegativeInclusionClosure(negIncAxInTbox, negObjectSubPropAxInTbox,
					negDataSubPropAxInTbox, posIncAxInTbox, posSubPropAxInTbox);
			}
		} catch (OWLOntologyCreationException e) {
			throw new RuntimeException(e);
		}
	}

	public static boolean isNegatedOWLQLRHSConcept(OWLClassExpression cl) {
		ClassExpressionType type = cl.getNNF().getClassExpressionType();
		return  type.equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)
			|| type.equals(ClassExpressionType.OBJECT_ALL_VALUES_FROM)
			|| type.equals(ClassExpressionType.DATA_ALL_VALUES_FROM);
			
	}

	/**
	 * returns the key associated with a class expression
	 * @param lhsConcept
	 * @return
	 */
	public static String getKey(OWLClassExpression lhsConcept) {
		if (!lhsConcept.isAnonymous()) {
			return lhsConcept.asOWLClass().getIRI().toString();
		} else if (lhsConcept.getClassExpressionType().equals(ClassExpressionType.OBJECT_SOME_VALUES_FROM)
				|| lhsConcept.getClassExpressionType().equals(ClassExpressionType.DATA_SOME_VALUES_FROM)) {
			OWLQuantifiedRestriction rest =  (OWLQuantifiedRestriction) lhsConcept;
			OWLPropertyExpression pe = rest.getProperty();
			return getKey(pe);
		} else {
			assert false : "Invalid left hand side concept: "+lhsConcept;
			return null;
		}
	}

	/**
	 * returns the key associated with a property expression
	 * @param pe
	 * @return
	 */
	public static String getKey(OWLPropertyExpression pe) {
		if (pe.isObjectPropertyExpression()) {
			pe = ((OWLObjectPropertyExpression) pe).getSimplified();
		}
		if (!pe.isAnonymous()) {
			return ((OWLNamedObject) pe).getIRI().toString();
		} else {
			OWLObjectInverseOf ope = (OWLObjectInverseOf) pe;
			OWLObjectProperty op = (OWLObjectProperty) ope.getInverse();
			return op.getIRI().toString();
			
		}
	}

	public static boolean isAboxAxiom(OWLAxiom ax) {
		return OWLQLNormalizer.isAboxAxiom(ax);
	}
	private static void organizeNegativeClosure(Collection<OWLAxiom> normalizedNegClosureAxioms, Collection<OWLSubClassOfAxiom> negIncAxInNegClos,
			Collection<OWLDisjointObjectPropertiesAxiom> negObjectSubPropAxInNegClos, Collection<OWLDisjointDataPropertiesAxiom> negDataSubPropAxInNegClos) {
			organizeTboxAxioms(normalizedNegClosureAxioms, negIncAxInNegClos, negObjectSubPropAxInNegClos, negDataSubPropAxInNegClos,
					new HashSet<OWLSubClassOfAxiom>(), new HashSet<OWLSubPropertyAxiom>(), 
					new HashSet<OWLObjectProperty>(), new HashSet<OWLObjectProperty>(),
					new HashMap<String, Set<OWLSubClassOfAxiom>>(), new HashMap<String, Set<OWLSubPropertyAxiom>>());
	}
	private static void organizeTboxAxioms(Collection<OWLAxiom> normalizedAxioms, Collection<OWLSubClassOfAxiom> negIncAxInTbox,
			Collection<OWLDisjointObjectPropertiesAxiom> negObjectSubPropAxInTbox,Collection<OWLDisjointDataPropertiesAxiom> negDataSubPropAxInTbox,
			Collection<OWLSubClassOfAxiom> posIncAxInTbox, Collection<OWLSubPropertyAxiom> posSubPropAxInTbox,
			Collection<OWLObjectProperty> reflexivePropsInTbox, Collection<OWLObjectProperty> irreflexivePropsInTbox,
			 Map<String, Set<OWLSubClassOfAxiom>> iri2PosIncAxInTbox, Map<String, Set<OWLSubPropertyAxiom>> iri2PosSubPropAxInTbox) {
		// organize Tbox axioms per type
		for (OWLAxiom ax: normalizedAxioms ) {
			if (ax.getAxiomType().equals(AxiomType.SUBCLASS_OF)) {
				OWLSubClassOfAxiom subax = (OWLSubClassOfAxiom) ax;
				OWLClassExpression sub =  subax.getSubClass();
				OWLClassExpression superclass = subax.getSuperClass();
				if (!superclass.isAnonymous()
				|| superclass.getClassExpressionType().equals(ClassExpressionType.OBJECT_SOME_VALUES_FROM)
				|| superclass.getClassExpressionType().equals(ClassExpressionType.DATA_SOME_VALUES_FROM)) {
					posIncAxInTbox.add(subax);
					String key = getKey(superclass);
					Set<OWLSubClassOfAxiom> axs = iri2PosIncAxInTbox.get(key);
					if (axs==null) {
						axs = new HashSet<OWLSubClassOfAxiom>();
						iri2PosIncAxInTbox.put(key, axs);
					}
					axs.add(subax);
				} else {
					assert superclass.getClassExpressionType().equals(ClassExpressionType.OBJECT_ALL_VALUES_FROM)
					|| superclass.getClassExpressionType().equals(ClassExpressionType.DATA_ALL_VALUES_FROM)
					|| superclass.getClassExpressionType().equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)
					: ax;
					negIncAxInTbox.add(subax);
				}
			} else if (ax.getAxiomType().equals(AxiomType.SUB_DATA_PROPERTY) 
					|| ax.getAxiomType().equals(AxiomType.SUB_OBJECT_PROPERTY)) {
				OWLSubPropertyAxiom subax = (OWLSubPropertyAxiom) ax;
				OWLPropertyExpression superprop = subax.getSuperProperty();
				posSubPropAxInTbox.add(subax);
				String key = getKey(superprop);
				Set<OWLSubPropertyAxiom> axs = iri2PosSubPropAxInTbox.get(key);
				if (axs==null) {
					axs = new HashSet<OWLSubPropertyAxiom>();
					iri2PosSubPropAxInTbox.put(key, axs);
				}
				axs.add(subax);				
			} else if (ax.getAxiomType().equals(AxiomType.DISJOINT_OBJECT_PROPERTIES)) {
				OWLDisjointObjectPropertiesAxiom dax = (OWLDisjointObjectPropertiesAxiom) ax;
				negObjectSubPropAxInTbox.add(dax);
			} else if (ax.getAxiomType().equals(AxiomType.DISJOINT_DATA_PROPERTIES)) {
				OWLDisjointDataPropertiesAxiom dax = (OWLDisjointDataPropertiesAxiom) ax;
				negDataSubPropAxInTbox.add(dax);
			} else if (ax.getAxiomType().equals(AxiomType.REFLEXIVE_OBJECT_PROPERTY)) {
				OWLReflexiveObjectPropertyAxiom rax = (OWLReflexiveObjectPropertyAxiom) ax;
				reflexivePropsInTbox.add(rax.getProperty().getNamedProperty());
			} else if (ax.getAxiomType().equals(AxiomType.IRREFLEXIVE_OBJECT_PROPERTY)) {
				OWLIrreflexiveObjectPropertyAxiom irrax = (OWLIrreflexiveObjectPropertyAxiom) ax;
				irreflexivePropsInTbox.add(irrax.getProperty().getNamedProperty());
			}
			else if (isAboxAxiom(ax)) {
				// abox assertions
				
			}  	else {
				assert false : "Unknown Normalized Axiom Type: " + ax;
			}
		}
	}
	
	public Set<OWLSubClassOfAxiom> getPositiveInclusions(String key) {
		return iri2PosIncAxInTbox.get(key);
	}
	public Set<OWLSubPropertyAxiom> getPositivePropertyInclusions(String key) {
		return iri2PosSubPropAxInTbox.get(key);
	}
	
	public Set<OWLObjectProperty> getReflexiveProperties() {
		if (reflexiveProps == null) {
			reflexiveProps = new HashSet<OWLObjectProperty>();
			for (OWLReflexiveObjectPropertyAxiom ax: normalizedOntology.getAxioms(AxiomType.REFLEXIVE_OBJECT_PROPERTY)) {
				reflexiveProps.add(ax.getProperty().getNamedProperty());
			}
		}
		return reflexiveProps;
	}
	
	public Set<OWLObjectProperty> getIrreflexiveProperties() {
		if (irreflexiveProps == null) {
			irreflexiveProps = new HashSet<OWLObjectProperty>();
			for (OWLIrreflexiveObjectPropertyAxiom ax: normalizedOntology.getAxioms(AxiomType.IRREFLEXIVE_OBJECT_PROPERTY)) {
				irreflexiveProps.add(ax.getProperty().getNamedProperty());
			}
		}
		return irreflexiveProps;
	}
	
	/*public NormalizedOWLQLTbox add(Set<OWLAxiom> axioms) {
		NormalizedOWLQLTbox ret = copy();
		Set<OWLAxiom> normalizedAxioms = ret.getNormalizer().toQLNormalForm(axioms, ret.nonOWLQLAxioms);
		ret.init(normalizedAxioms, false, false);
		Set<OWLSubClassOfAxiom> deltaNegIncAx = new HashSet<OWLSubClassOfAxiom>();
		Set<OWLDisjointObjectPropertiesAxiom> deltaNegObjSubPropAx = new HashSet<OWLDisjointObjectPropertiesAxiom>();
		Set<OWLDisjointDataPropertiesAxiom> deltaNegDataSubPropAx = new HashSet<OWLDisjointDataPropertiesAxiom>();
		Set<OWLSubClassOfAxiom> deltaPosIncAx = new HashSet<OWLSubClassOfAxiom>();
		Set<OWLSubPropertyAxiom> deltaPosSubPropAx = new HashSet<OWLSubPropertyAxiom>();
		organizeTboxAxioms(normalizedAxioms, deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx,deltaPosIncAx, deltaPosSubPropAx,
				new HashMap<String, Set<OWLSubClassOfAxiom>>(), new HashMap<String, Set<OWLSubPropertyAxiom>>());
		Set<OWLAxiom> newNegClos = ret.computeNegativeInclusionClosure(deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx, 
				deltaPosIncAx, deltaPosSubPropAx);
		// clear 
		ret.negIncAxInNegClos = null;
		ret.negObjectSubPropAxInNegClos = null;
		ret.negDataSubPropAxInNegClos = null;
		//
		ret.negativeInclClosure = newNegClos;
		return ret;
	
	}*/
	
	public Set<OWLClass> getUnsatisfiableNamedClasses() {
		if (unsatisfiableClasses == null) {
			unsatisfiableClasses = new HashSet<OWLClass>();
			// unsatisfiabe named classes C are in the negative closure in the form
			// subclass( C, not(C)) 
			for (OWLSubClassOfAxiom ax: getNegativeInclInNegClosure()) {
				OWLClassExpression sub = ax.getSubClass();
				OWLClassExpression sup = ax.getSuperClass();
				if (!sub.isAnonymous()
				&& sup.getClassExpressionType().equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)
				&& ((OWLObjectComplementOf) sup).getOperand().equals(sub)) {
					unsatisfiableClasses.add(sub.asOWLClass());
				}
			}
		}
		return Collections.unmodifiableSet(unsatisfiableClasses);
		//return new HashSet<OWLClass>();
	}
	public Set<OWLProperty> getUnsatisfiableProperties() {
		if (unsatisfiableProperties == null) {
			unsatisfiableProperties = new HashSet<OWLProperty>();
			for (OWLDisjointDataPropertiesAxiom ax: getNegativeDataSubPropAxInClosure()) {
				if (ax.getProperties().size() == 1) {
					unsatisfiableProperties.add(ax.getProperties().iterator().next().asOWLDataProperty());
				}
			}
			for (OWLDisjointObjectPropertiesAxiom ax: getNegativeObjectSubPropInNegClosure()) {
				if (ax.getProperties().size() == 1) {
					unsatisfiableProperties.add(ax.getProperties().iterator().next().getNamedProperty().asOWLObjectProperty());
				}
			}
		}
		return Collections.unmodifiableSet(unsatisfiableProperties);
	}
	
	/*public boolean isUnsatisfiable(OWLClassExpression normalizedConcept) {
		if (!normalizedConcept.isAnonymous()) {
			if (normalizedConcept.isOWLNothing()) {
				return  true;
			} 
			if (getUnsatisfiableNamedClasses().contains(normalizedConcept)) {
				return true;
			}
		} else if (normalizedConcept.getClassExpressionType().equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)) {
			OWLObjectComplementOf c = (OWLObjectComplementOf) normalizedConcept;
			OWLClassExpression 
		}
		
	}*/
	
	public boolean isSubClass(OWLClassExpression sub, OWLClassExpression sup) {
		// if sup is equivalent to Top return true
		if (sup.isOWLThing()) {
			return true;
		}
		if (sup.getClassExpressionType().equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)
		&& 
		(
			((OWLObjectComplementOf) sup).getOperand().isOWLNothing()
			|| getUnsatisfiableNamedClasses().contains(((OWLObjectComplementOf) sup).getOperand()) 
		)  ) {
			return true;
		}
		//
		
		// if sub is equivalent to Bottom return true
		if (sub.isOWLNothing() || getUnsatisfiableNamedClasses().contains(sub)) {
			return true;
		}
		if (sub.getClassExpressionType().equals(ClassExpressionType.OBJECT_COMPLEMENT_OF)
		&& ((OWLObjectComplementOf) sub).getOperand().isOWLThing()) {
			return true;
		}
		if ( (sub.getClassExpressionType().equals(ClassExpressionType.OBJECT_SOME_VALUES_FROM)
		|| sub.getClassExpressionType().equals(ClassExpressionType.DATA_SOME_VALUES_FROM))
			&&
		(((OWLQuantifiedRestriction) sub).getProperty().isOWLBottomObjectProperty()
			||((OWLQuantifiedRestriction) sub).getProperty().isOWLBottomDataProperty()
			|| getUnsatisfiableProperties().contains(((OWLQuantifiedRestriction) sub).getProperty()))) {
			return true;
		}
		//
	
		// check that tbox union {subclass(NC, sub), sublcass(NC, not(sup)), NC(Nind)}
		// is inconsistent. NC: new concept, Nind: new individual
		
		List<OWLAxiom> newAxioms = new LinkedList<OWLAxiom>();
		NormalizedOWLQLTbox ret = this;//copy();
		OWLClass newConcept = ret.getNormalizer().createNewNamedClass();
		newAxioms.add(fac.getOWLSubClassOfAxiom(newConcept, sub.getNNF()));
		newAxioms.add(fac.getOWLSubClassOfAxiom(newConcept, sup.getComplementNNF()));
		List<OWLAxiom> newNonOWLQLAxioms = new LinkedList<OWLAxiom>();
		
		Set<OWLAxiom> normalizedAxioms = ret.getNormalizer().toQLNormalForm(newAxioms, newNonOWLQLAxioms);
		if (!newNonOWLQLAxioms.isEmpty()) {
			throw new RuntimeException("Both concepts  must be OWL QL valid concepts");
		}
		List<OWLSubClassOfAxiom> deltaNegIncAx = new LinkedList<OWLSubClassOfAxiom>();
		List<OWLDisjointObjectPropertiesAxiom> deltaNegObjSubPropAx = new LinkedList<OWLDisjointObjectPropertiesAxiom>();
		List<OWLDisjointDataPropertiesAxiom> deltaNegDataSubPropAx = new LinkedList<OWLDisjointDataPropertiesAxiom>();
		List<OWLSubClassOfAxiom> deltaPosIncAx = new LinkedList<OWLSubClassOfAxiom>();
		List<OWLSubPropertyAxiom> deltaPosSubPropAx = new LinkedList<OWLSubPropertyAxiom>();
		organizeTboxAxioms(normalizedAxioms, deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx,deltaPosIncAx, deltaPosSubPropAx,
				new HashSet<OWLObjectProperty>(), new HashSet<OWLObjectProperty>(),
				new HashMap<String, Set<OWLSubClassOfAxiom>>(), new HashMap<String, Set<OWLSubPropertyAxiom>>());
		// inconsistent iff subclass(NC, not(NC)) is in the negative closure 
		OWLAxiom inconsistencyWitness = fac.getOWLSubClassOfAxiom(newConcept, newConcept.getComplementNNF());
		Set<OWLAxiom> newNegClos = ret.computeNegativeInclusionClosure(deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx, 
				deltaPosIncAx, deltaPosSubPropAx, inconsistencyWitness);
		//assert newNegClos == null || !newNegClos.contains(inconsistencyWitness) : newNegClos;
		// 
		
		return newNegClos == null;
		
	}
	public boolean isSubProperty(OWLPropertyExpression sub, OWLPropertyExpression sup) {
		// false if one is data property and the other is object property
		if (sub.isDataPropertyExpression() ^ sup.isDataPropertyExpression()) {
			return false;
		}
		//
		
		// true if sub is the bottom property
		if (sub.isOWLBottomDataProperty() || sub.isOWLBottomObjectProperty()
			|| getUnsatisfiableProperties().contains(sub)) {
			return true;
		}
		// true if sup is the top property
		if (sup.isOWLTopDataProperty() || sup.isOWLTopObjectProperty()) {
			return true;
		}
		// 
		if (sub.isObjectPropertyExpression()) {
			OWLObjectPropertyExpression objSub = (OWLObjectPropertyExpression) sub;
			OWLObjectPropertyExpression objSup = (OWLObjectPropertyExpression) sup;
			// true if sub is equal or equivalent to the inverse of the bottom property
			// true if sup is the inverse of the top property
			if (objSub.getNamedProperty().isOWLBottomObjectProperty()
			|| getUnsatisfiableProperties().contains(objSub.getNamedProperty())		
			|| objSup.getNamedProperty().isOWLTopObjectProperty()
			) {
				return true;
			}
		}
		
		
		
		// check that tbox union {subprop(NP, sub), subprop(NP, not(sup), NP(Nind1, Nind2)}
		// is inconsistent. NP: new property, Nind1, Nind2: new individuals
		
		NormalizedOWLQLTbox ret = this;//copy();
		
		Set<OWLAxiom> newAxioms = new HashSet<OWLAxiom>();
		OWLAxiom inconsistencyWitness;
		if (sub.isDataPropertyExpression()) {
			OWLDataProperty newProp = ret.getNormalizer().createNewDataProperty();
			newAxioms.add(fac.getOWLSubDataPropertyOfAxiom(newProp,(OWLDataPropertyExpression) sub));
			newAxioms.add(fac.getOWLDisjointDataPropertiesAxiom(newProp, (OWLDataPropertyExpression) sup));
			// inconsistent iff subprop(NP, not(NP)) is in the negative closure 
			inconsistencyWitness = fac.getOWLDisjointDataPropertiesAxiom(newProp, newProp);
		} else {
			OWLObjectProperty newProp = ret.getNormalizer().createNewObjectProperty();
			newAxioms.add(fac.getOWLSubObjectPropertyOfAxiom(newProp,(OWLObjectPropertyExpression) sub));
			newAxioms.add(fac.getOWLDisjointObjectPropertiesAxiom(newProp, (OWLObjectPropertyExpression) sup));
			// inconsistent iff subprop(NP, not(NP)) is in the negative closure 
			inconsistencyWitness = fac.getOWLDisjointObjectPropertiesAxiom(newProp, newProp);
		}
		Set<OWLAxiom> newNonOWLQLAxioms = new HashSet<OWLAxiom>();
		Set<OWLAxiom> normalizedAxioms = ret.getNormalizer().toQLNormalForm(newAxioms, newNonOWLQLAxioms);
		if (!newNonOWLQLAxioms.isEmpty()) {
			throw new RuntimeException("Both concepts  must be OWL QL valid concepts");
		}
		Set<OWLSubClassOfAxiom> deltaNegIncAx = new HashSet<OWLSubClassOfAxiom>();
		Set<OWLDisjointObjectPropertiesAxiom> deltaNegObjSubPropAx = new HashSet<OWLDisjointObjectPropertiesAxiom>();
		Set<OWLDisjointDataPropertiesAxiom> deltaNegDataSubPropAx = new HashSet<OWLDisjointDataPropertiesAxiom>();
		Set<OWLSubClassOfAxiom> deltaPosIncAx = new HashSet<OWLSubClassOfAxiom>();
		Set<OWLSubPropertyAxiom> deltaPosSubPropAx = new HashSet<OWLSubPropertyAxiom>();
		organizeTboxAxioms(normalizedAxioms, deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx,deltaPosIncAx, deltaPosSubPropAx,
				new HashSet<OWLObjectProperty>(), new HashSet<OWLObjectProperty>(),
				new HashMap<String, Set<OWLSubClassOfAxiom>>(), new HashMap<String, Set<OWLSubPropertyAxiom>>());
		
		Set<OWLAxiom> newNegClos = ret.computeNegativeInclusionClosure(deltaNegIncAx, deltaNegObjSubPropAx, deltaNegDataSubPropAx, 
				deltaPosIncAx, deltaPosSubPropAx, inconsistencyWitness);
		
		return newNegClos == null;
		
	}
	
	protected boolean isGeneratedRole(String uri) {
		return getNormalizer().isGeneratedRole(uri);
	}

	protected boolean isGeneratedClass(String uri) {
		return getNormalizer().isGeneratedClass(uri);
	}

	protected boolean isTripleAbsentFromAbox(Triple qt) {
		Node pred = qt.getPredicate();
		if (pred.isURI() && isGeneratedRole(pred.getURI())) {
			return true;
		} else if (pred.isURI() && pred.getURI().equals(RDFConstants.RDF_TYPE)) {
			Node obj = qt.getObject();
			if (obj.isURI() && isGeneratedClass(obj.getURI())) {
				return true;
			}
		}
		return false;
	}
	
	protected void addOnlyAboxTriples(ElementUnion patterns, List<Triple> triples){
		boolean isAbsentFromAbox = false;
		ElementTriplesBlock sp = new ElementTriplesBlock();
		for (Triple t : triples) {
			if (isTripleAbsentFromAbox(t)) {
				isAbsentFromAbox = true;
				break;
			}
			sp.addTriple(t);
		}
		if (!isAbsentFromAbox)
			patterns.addElement(sp);
	}
	
	/**
	 * returns <code> null</code> if the Tbox entails an inconsistent KB;otherwise, returns a boolean query which, when evaluated against the Abox, returns <code>true</code> to indicate 
	 * that the KB is inconsistent.
	 * @param taxo
	 * @return
	 */
	public ConsistencyCheckResult computeConsistencyCheck(Taxonomy taxo) {
		ElementUnion patterns  = new ElementUnion();
		NewVariableGenerator varGen = new NewVariableGenerator("var_");
		for (OWLAxiom ax: getNegativeInclClosure()) {
			List<Triple> triples = new  ArrayList<Triple>();
			if (ax.getAxiomType().equals(AxiomType.SUBCLASS_OF)) {
				OWLSubClassOfAxiom subax = (OWLSubClassOfAxiom) ax;
				OWLClassExpression subclass = subax.getSubClass();
				OWLClassExpression superclass = subax.getSuperClass();
				Node var = NodeFactory.createVariable(varGen.createNewVariable());
				assert NormalizedOWLQLTbox.isNegatedOWLQLRHSConcept(superclass) : "Invalid negative subclass axiom: "+ ax;
				Triple t1 = toTriple(subclass, var, varGen, getFactory());
				if (!getReflexiveProperties().contains(getFactory().getOWLObjectProperty(IRI.create(t1.getPredicate().getURI())))) {
					triples.add(t1);
				}				
				OWLClassExpression negSuperclass = getFactory().getOWLObjectComplementOf(superclass).getNNF();
				Triple t2 = toTriple(negSuperclass, var, varGen, getFactory());
				if (!getReflexiveProperties().contains(getFactory().getOWLObjectProperty(IRI.create(t2.getPredicate().getURI())))) {
					triples.add(t2);
				}	
				if (triples.isEmpty()) {
					// t1 and t2 are satisfied based on the reflexivity of their predicate
					return new ConsistencyCheckResult(false);	
				}
			} else if (ax.getAxiomType().equals(AxiomType.DISJOINT_OBJECT_PROPERTIES))  {
				OWLDisjointObjectPropertiesAxiom disjAx = (OWLDisjointObjectPropertiesAxiom) ax;
				Set<OWLObjectPropertyExpression> props = disjAx.getProperties();
				assert props.size() == 1 || props.size() ==2: ax;
				Node x = NodeFactory.createVariable(varGen.createNewVariable());
				Node y = NodeFactory.createVariable(varGen.createNewVariable());
				int numberOfReflexiveProps =  0;
				for (OWLObjectPropertyExpression prop: props) {
					triples.add(toTriple(prop, x, y, varGen));
					if (getReflexiveProperties().contains(prop.getNamedProperty())) {
						numberOfReflexiveProps++;
					}
				}				
				if (numberOfReflexiveProps==props.size()) {
					// two disjoint properties (or bottom property) that are reflexive
					return new ConsistencyCheckResult(false);	
				} else if (numberOfReflexiveProps>0) {
					assert numberOfReflexiveProps == 1 : numberOfReflexiveProps+"\n"+props;
					// we add a new disjoint of the form: R(x, x), where R is the property which is not reflexive
					{
						List<Triple> newtriples = new  ArrayList<Triple>();
						for (OWLObjectPropertyExpression prop: props) {
							if (!getReflexiveProperties().contains(prop.getNamedProperty())) {
								newtriples.add(toTriple(prop, x, x, varGen));
							}
						}	
						
						addOnlyAboxTriples(patterns, newtriples);
					}
				}
				
			} else if (ax.getAxiomType().equals(AxiomType.DISJOINT_DATA_PROPERTIES))  {
				OWLDisjointDataPropertiesAxiom disjAx = (OWLDisjointDataPropertiesAxiom) ax;
				Set<OWLDataPropertyExpression> props = disjAx.getProperties();
				assert props.size() == 1 || props.size() ==2: ax;
				Node x = NodeFactory.createVariable(varGen.createNewVariable());
				Node y = NodeFactory.createVariable(varGen.createNewVariable());
				for (OWLDataPropertyExpression prop: props) {
					triples.add(toTriple(prop, x, y, varGen));
				}			
			} else {
				assert false : "Invalid axiom type in negative closure: "+ ax;
			}
			
			addOnlyAboxTriples(patterns, triples);
		}
		
		// handle irreflexive properties
		// check p(x, x) does not exist for an irreflexive property p
		if (!getIrreflexiveProperties().isEmpty()) {
			try {
				if (taxo == null) {
					taxo = new TaxonomyImpl(this);
				}
			} catch (OWLOntologyCreationException e) {
				throw new RuntimeException(e);
			}
			for (OWLObjectProperty p: getIrreflexiveProperties()) {
				for (OWLPropertyExpression subp: taxo.getAllSubproperties(p)) {
					OWLObjectPropertyExpression osubp = (OWLObjectPropertyExpression) subp;
					
					if (getReflexiveProperties().contains(osubp.getNamedProperty())) {
						// an irreflexive property has a reflexive subproperty
						return new ConsistencyCheckResult(false);	
					} else {
						ElementTriplesBlock sp = new ElementTriplesBlock();
						Node x = NodeFactory.createVariable(varGen.createNewVariable());
						Triple triple = toTriple(subp, x, x, varGen);
						if (!isTripleAbsentFromAbox(triple)) {
							sp.addTriple(toTriple(subp, x, x, varGen));
							patterns.addElement(sp);
						}
					}
				}
			}		
		}
		//
		
		Query ret = new Query();
		ret.setQueryAskType();
		if (patterns.getElements().size()>1) {
			ret.setQueryPattern(patterns);
		} else if (patterns.getElements().size() ==1) {
			ret.setQueryPattern(patterns.getElements().get(0));
		} else {
			return new ConsistencyCheckResult(true);
		}
		return new ConsistencyCheckResult((Boolean)null, ret);
	}
	/**
	 *  returns a query triple corresponding to a left hand side concept.
	 *  <ul>
	 *   <li> B --> x rdf:type B </li>
	 *   <li> some(R, top) --> x R ?y if  </li>
	 *   <li> some(inv(R), top) --> ?y R x </li>
	 *   </ul>
	 * @param lhsConcept
	 * @param var
	 * @return
	 */
	protected static Triple toTriple(OWLClassExpression lhsConcept, Node var, NewVariableGenerator varGen, OWLDataFactory fac) {
		if (!lhsConcept.isAnonymous()) {
			return new Triple(
						var,
						NodeFactory.createURI(RDFConstants.RDF_TYPE),
						NodeFactory.createURI(((OWLClass)lhsConcept).getIRI().toString()));
		 } else if (lhsConcept.getClassExpressionType().equals(ClassExpressionType.OBJECT_SOME_VALUES_FROM)
				 || lhsConcept.getClassExpressionType().equals(ClassExpressionType.DATA_SOME_VALUES_FROM)) {
			 OWLQuantifiedRestriction rest = (OWLQuantifiedRestriction) lhsConcept;
			 OWLPropertyExpression prop = rest.getProperty();
			 OWLPropertyRange propRange = rest.getFiller();
			 if (prop.isObjectPropertyExpression()) {
				 prop = ((OWLObjectPropertyExpression)prop).getSimplified();
			 }
			 assert propRange.equals(fac.getOWLThing()) || propRange.equals(fac.getTopDatatype()) : propRange;
			 if (!prop.isAnonymous()) {
				 OWLProperty p = (OWLProperty) prop;
				 return new Triple(
							var,
							NodeFactory.createURI(p.getIRI().toString()),
							NodeFactory.createVariable(varGen.createNewVariable()));
			 } else {
				assert prop instanceof OWLObjectInverseOf : prop;
			 	OWLObjectInverseOf inv = (OWLObjectInverseOf) prop;
			 	assert !inv.getInverse().isAnonymous() : inv+"\n"+ "Property expression simplification failed!";
			 	OWLObjectProperty op = (OWLObjectProperty) inv.getInverse();
			 	return new Triple(
			 			NodeFactory.createVariable(varGen.createNewVariable()),
						NodeFactory.createURI(op.getIRI().toString()),
						var);
			 	
			 }
		 } else {
			 throw new RuntimeException(lhsConcept+" is not a valid concept expression in the left hand side of a subclass axiom in OWL QL");
		 }
		
	}
	/**
	 * returns a query triple corresponding to a property expression
	 * 	<ul>
	 * 	<li> R  --> x R y </li>
	 *  <li> inv(R) --> y R x </li>
	 * </ul>
	 * 
	 */
	protected static Triple toTriple(OWLPropertyExpression pe, Node x, Node y, NewVariableGenerator varGen) {
		pe = getSimplified(pe);
		if (!pe.isAnonymous()) {
			OWLProperty p = (OWLProperty) pe;
			 return new Triple(
						x,
						NodeFactory.createURI(p.getIRI().toString()),
						y); 
		} else {
			assert pe instanceof OWLObjectInverseOf: pe;
			OWLObjectInverseOf inv = (OWLObjectInverseOf) pe;
			OWLObjectProperty p = (OWLObjectProperty) inv.getInverse();
			 return new Triple(
						y,
						NodeFactory.createURI(p.getIRI().toString()),
						x); 
		}
	}
	protected static OWLPropertyExpression getSimplified(OWLPropertyExpression pe) {
		if (pe.isObjectPropertyExpression()) {
			pe = ((OWLObjectPropertyExpression) pe).getSimplified();
		}
		return pe;
	}
	
	public NormalizedOWLQLTbox copy() {
		return new NormalizedOWLQLTbox(this);
	}
	public Object clone() {
		return copy();
	}
	
}