/*
 * #%L
 * elk-reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.stages;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.liveontologies.puli.statistics.NestedStats;
import org.semanticweb.elk.exceptions.ElkException;
import org.semanticweb.elk.exceptions.ElkRuntimeException;
import org.semanticweb.elk.loading.AxiomLoader;
import org.semanticweb.elk.loading.ClassQueryLoader;
import org.semanticweb.elk.loading.ComposedAxiomLoader;
import org.semanticweb.elk.loading.EntailmentQueryLoader;
import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkClassExpression;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.owl.interfaces.ElkSubObjectPropertyExpression;
import org.semanticweb.elk.owl.visitors.ElkSubObjectPropertyExpressionVisitor;
import org.semanticweb.elk.reasoner.ElkInconsistentOntologyException;
import org.semanticweb.elk.reasoner.ProgressMonitor;
import org.semanticweb.elk.reasoner.ReasonerInterrupter;
import org.semanticweb.elk.reasoner.completeness.IncompletenessManager;
import org.semanticweb.elk.reasoner.config.ReasonerConfiguration;
import org.semanticweb.elk.reasoner.consistency.ConsistencyCheckingState;
import org.semanticweb.elk.reasoner.indexing.classes.DifferentialIndex;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkAxiomConverterImpl;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverter;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverterImpl;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.IndexedPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableOntologyIndex;
import org.semanticweb.elk.reasoner.indexing.model.OntologyIndex;
import org.semanticweb.elk.reasoner.query.EntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.QueryNode;
import org.semanticweb.elk.reasoner.saturation.SaturationState;
import org.semanticweb.elk.reasoner.saturation.SaturationStateFactory;
import org.semanticweb.elk.reasoner.saturation.SaturationStatistics;
import org.semanticweb.elk.reasoner.saturation.conclusions.classes.SaturationConclusionBaseFactory;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SaturationConclusion;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.taxonomy.ElkClassKeyProvider;
import org.semanticweb.elk.reasoner.taxonomy.ElkIndividualKeyProvider;
import org.semanticweb.elk.reasoner.taxonomy.ElkObjectPropertyKeyProvider;
import org.semanticweb.elk.reasoner.taxonomy.OrphanInstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.OrphanTaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.OrphanTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.SingletoneInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.SingletoneTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.Node;
import org.semanticweb.elk.reasoner.taxonomy.model.Taxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.TaxonomyNodeFactory;
import org.semanticweb.elk.reasoner.tracing.Conclusion;
import org.semanticweb.elk.reasoner.tracing.TraceState;
import org.semanticweb.elk.reasoner.tracing.TracingInference;
import org.semanticweb.elk.reasoner.tracing.TracingProof;
import org.semanticweb.elk.util.collections.ArrayHashSet;
import org.semanticweb.elk.util.concurrent.computation.ConcurrentExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The execution state of the reasoner containing information about which
 * reasoning stages have been completed and holding the results of these
 * reasoning stages, such as the consistency status of the ontology, class, or
 * instance taxonomy.
 *
 * @author "Yevgeny Kazakov"
 * @author Peter Skocovsky
 */
public abstract class AbstractReasonerState implements TracingProof {

	// logger for this class
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(AbstractReasonerState.class);

	/**
	 * The factory for creating auxiliary ElkObjects
	 */
	private final ElkObject.Factory elkFactory_;

	/**
	 * Accumulated statistics regarding produced conclusions and rule
	 * applications. Stored here because more than one stage can apply inference
	 * rules (e.g. during incremental reasoning).
	 *
	 * TODO Better to hide this behind a common interface? Then we can uniformly
	 * maintain aggregated stats of different kinds produced by different
	 * computations across multiple stages
	 */
	final SaturationStatistics ruleAndConclusionStats;
	/**
	 * the (differential) index for loading of axioms and changes
	 */
	final DifferentialIndex ontologyIndex;
	/**
	 * Stores information about occurrences in stated axioms.
	 */
	final OccurrencesInStatedAxiomsStore occurrencesInStatedAxiomsStore = new OccurrencesInStatedAxiomsStore();
	/**
	 * The source where axioms and changes in ontology can be loaded
	 */
	private AxiomLoader axiomLoader_ = null;
	/**
	 * Manages information about property hierarchy computation.
	 */
	final PropertyHierarchyCompositionState propertyHierarchyCompositionState_;
	/**
	 * Maintains the state of saturation.
	 */
	final SaturationState<? extends Context> saturationState;
	/**
	 * Stores (partial) information about consistency checking computation
	 */
	final ConsistencyCheckingState consistencyCheckingState;
	/**
	 * Taxonomy state that stores (partial) classification
	 */
	final ClassTaxonomyState classTaxonomyState;
	/**
	 * Taxonomy state that stores property hierarchy
	 */
	final ObjectPropertyTaxonomyState objectPropertyTaxonomyState;
	/**
	 * Stores information about queried class expressions.
	 */
	final ClassExpressionQueryState classExpressionQueryState_;
	/**
	 * Stores information about entailment queries.
	 */
	final EntailmentQueryState entailmentQueryState_;
	/**
	 * Taxonomy that stores (partial) classification and (partial) realization
	 * of individuals
	 */
	final InstanceTaxonomyState instanceTaxonomyState;
	/**
	 * Keeps relevant information about tracing
	 */
	private final TraceState traceState_;
	/**
	 * Defines reasoning stages and dependencies between them
	 */
	final ReasonerStageManager stageManager;
	/**
	 * Responsible for monitoring completeness.
	 */
	private final IncompletenessManager incompleteness_ = new IncompletenessManager(
			occurrencesInStatedAxiomsStore);

	/**
	 * if {@code true}, reasoning will be done incrementally whenever possible
	 */
	private boolean allowIncrementalMode_ = true;

	/**
	 * creates conclusions for tracing
	 */
	private final SaturationConclusion.Factory factory_ = new SaturationConclusionBaseFactory();

	private final ElkPolarityExpressionConverter expressionConverter_;

	private final ElkSubObjectPropertyExpressionVisitor<ModifiableIndexedPropertyChain> subPropertyConverter_;

	protected AbstractReasonerState(ElkObject.Factory elkFactory,
			final ReasonerConfiguration config) {
		this.elkFactory_ = elkFactory;
		this.ontologyIndex = new DifferentialIndex(elkFactory);
		this.propertyHierarchyCompositionState_ = new PropertyHierarchyCompositionState();
		this.saturationState = SaturationStateFactory
				.createSaturationState(ontologyIndex);
		this.consistencyCheckingState = ConsistencyCheckingState
				.create(saturationState, propertyHierarchyCompositionState_);
		this.instanceTaxonomyState = new InstanceTaxonomyState(saturationState,
				ontologyIndex, elkFactory);
		this.classTaxonomyState = new ClassTaxonomyState(saturationState,
				ontologyIndex, elkFactory, Arrays.asList(
						instanceTaxonomyState.getClassTaxonomyStateListener()));
		this.objectPropertyTaxonomyState = new ObjectPropertyTaxonomyState(
				elkFactory);
		this.ruleAndConclusionStats = new SaturationStatistics();
		this.stageManager = new ReasonerStageManager(this);
		this.expressionConverter_ = new ElkPolarityExpressionConverterImpl(
				elkFactory, ontologyIndex);
		this.subPropertyConverter_ = new ElkAxiomConverterImpl(elkFactory,
				ontologyIndex);
		this.traceState_ = new TraceState(config, saturationState,
				propertyHierarchyCompositionState_, elkFactory, ontologyIndex);
		this.classExpressionQueryState_ = new ClassExpressionQueryState(config,
				saturationState, elkFactory, ontologyIndex, factory_);
		this.entailmentQueryState_ = new EntailmentQueryState(config,
				saturationState, consistencyCheckingState, factory_,
				incompleteness_);
	}

	public ElkObject.Factory getElkFactory() {
		return elkFactory_;
	}

	protected void complete(ReasonerStage stage) throws ElkException {
		getStageExecutor().complete(stage);
	}

	/**
	 * Completes the provided stage despite interruptions, if it was not
	 * completed yet.
	 *
	 * @param stage
	 *            The stage that should be completed.
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected void completeUninterruptibly(final ReasonerStage stage)
			throws ElkException {
		while (true) {
			try {
				complete(stage);
				break;
			} catch (final ElkInterruptedException e) {
				continue;
			}
		}
	}

	public synchronized void setAllowIncrementalMode(boolean allow) {
		if (allowIncrementalMode_ == allow)
			return;
		if (LOGGER_.isInfoEnabled()) {
			LOGGER_.debug("Incremental mode is "
					+ (allow ? "allowed" : "disallowed"));
		}
		allowIncrementalMode_ = allow;

		if (!allow) {
			setNonIncrementalMode();
		}

	}

	public synchronized boolean isAllowIncrementalMode() {
		return allowIncrementalMode_;
	}

	public synchronized boolean isIncrementalMode() {
		return ontologyIndex.isIncrementalMode();
	}

	void setNonIncrementalMode() {
		if (!isIncrementalMode()) {
			return;
		}
		ontologyIndex.setIncrementalMode(false);
	}

	boolean trySetIncrementalMode() {
		if (!allowIncrementalMode_) {
			// switching to incremental mode not allowed
			return false;
		}
		// else
		ontologyIndex.setIncrementalMode(true);
		return true;

	}

	public synchronized void registerAxiomLoader(
			final AxiomLoader.Factory axiomLoaderFactory) {
		LOGGER_.trace("Registering new axiom loader");

		final AxiomLoader newAxiomLoader = axiomLoaderFactory
				.getAxiomLoader(getInterrupter());

		if (axiomLoader_ == null || axiomLoader_.isLoadingFinished()) {
			axiomLoader_ = newAxiomLoader;
		} else {
			axiomLoader_ = new ComposedAxiomLoader(axiomLoader_,
					newAxiomLoader);
		}
	}

	/**
	 * @return the {@link AxiomLoader} currently registered for loading of
	 *         axioms or {@code null} if no loader is registered
	 */
	AxiomLoader getAxiomLoader() {
		return this.axiomLoader_;
	}

	/**
	 * @return the {@link ClassQueryLoader} currently registered for loading of
	 *         class queries or {@code null} if no loader is registered
	 */
	ClassQueryLoader getClassQueryLoader() {
		return classExpressionQueryState_.getQueryLoader(getInterrupter());
	}

	/**
	 * @return the {@link EntailmentQueryLoader} currently registered for
	 *         loading of entailment queries or {@code null} if no loader is
	 *         registered
	 */
	EntailmentQueryLoader getEntailmentQueryLoader() {
		return entailmentQueryState_.getQueryLoader(getInterrupter());
	}

	/**
	 * @return {@code true} if there is no input pending loading, {@code false}
	 *         otherwise.
	 */
	protected synchronized boolean isLoadingFinished() {
		final ClassQueryLoader classQueryLoader = getClassQueryLoader();
		final EntailmentQueryLoader entailmentQueryLoader = getEntailmentQueryLoader();
		return (axiomLoader_ == null || axiomLoader_.isLoadingFinished())
				&& (classQueryLoader == null
						|| classQueryLoader.isLoadingFinished())
				&& (entailmentQueryLoader == null
						|| entailmentQueryLoader.isLoadingFinished());
	}

	/**
	 * Flushes index, if needed, and completes loading if there is new input.
	 * Incremental mode should be changed only during completing loading.
	 *
	 * @throws ElkException
	 */
	public synchronized void ensureLoading() throws ElkException {

		if (!isLoadingFinished()) {
			if (isIncrementalMode()) {
				if (!stageManager.incrementalAdditionStage.isCompleted()) {
					complete(stageManager.incrementalAdditionStage);
				}
			} else {
				if (!stageManager.contextInitializationStage.isCompleted()) {
					complete(stageManager.contextInitializationStage);
				}
			}
			LOGGER_.trace("Reset axiom loading");
			stageManager.inputLoadingStage.invalidateRecursive();
			// Invalidate stages at the beginnings of the dependency chains.
			stageManager.contextInitializationStage.invalidateRecursive();
			stageManager.incrementalCompletionStage.invalidateRecursive();
		}

		complete(stageManager.inputLoadingStage);

	}

	/**
	 * Ensures that saturation is restored and taxonomies are cleaned. Also
	 * invalidates stages that depend on the saturation if it changed.
	 *
	 * @throws ElkException
	 */
	private void restoreSaturation() throws ElkException {

		ensureLoading();

		final boolean changed;

		if (isIncrementalMode()) {
			changed = !stageManager.incrementalTaxonomyCleaningStage
					.isCompleted();
			complete(stageManager.incrementalTaxonomyCleaningStage);
		} else {
			changed = !stageManager.contextInitializationStage.isCompleted();
			complete(stageManager.contextInitializationStage);
		}

		if (changed) {
			stageManager.consistencyCheckingStage.invalidateRecursive();
		}

	}

	/**
	 * @return the maximal number of workers that can be used for running
	 *         concurrent reasoning tasks
	 */
	protected abstract int getNumberOfWorkers();

	/**
	 * @return the {@link ReasonerStageExecutor} that is used for executing the
	 *         stages of the reasoner.
	 */
	protected abstract ReasonerStageExecutor getStageExecutor();

	/**
	 * @return The object that is notified and propagates the information about
	 *         interruption.
	 */
	protected abstract ReasonerInterrupter getInterrupter();

	/**
	 * @return the {@link ConcurrentExecutor} that is used for execution of
	 *         reasoning processes
	 */
	protected abstract ConcurrentExecutor getProcessExecutor();

	/**
	 * @return the {@link ProgressMonitor} that is used for reporting progress
	 *         on all potentially long-running operations.
	 */
	protected abstract ProgressMonitor getProgressMonitor();

	protected ModifiableOntologyIndex getModifiableOntologyIndex() {
		return this.ontologyIndex;
	}

	/**
	 * interrupts running reasoning stages
	 */
	public void interrupt() {
		LOGGER_.debug("Interrupt requested");
		getInterrupter().interrupt();
	}

	/**
	 * If interrupted, clears the interruption status and throws
	 * ElkInterruptedException.
	 * <p>
	 * This method should be called only from inside of the reasoner. Also it
	 * must be called from the same thread as the one on which the reasoning
	 * methods run. Otherwise it blocks until the reasoning method exits.
	 *
	 * @throws ElkInterruptedException
	 *             if interrupted
	 */
	public synchronized void checkInterrupt() throws ElkInterruptedException {
		getInterrupter().checkInterrupt();
	}

	/**
	 * Completes consistency checking stage and the stages it depends on, if
	 * this has not been done yet.
	 *
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	private void restoreConsistencyCheck() throws ElkException {
		ruleAndConclusionStats.reset();
		restoreSaturation();
		complete(stageManager.consistencyCheckingStage);
	}

	/**
	 * Check consistency of the current ontology, if this has not been done yet.
	 *
	 * @return {@code true} if the ontology is inconsistent, that is,
	 *         unsatisfiable.
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized boolean isInconsistent() throws ElkException {

		restoreConsistencyCheck();

		if (!consistencyCheckingState.isInconsistent()) {
			incompleteness_.log(incompleteness_
					.getIncompletenessMonitorForClassification());
		}
		return consistencyCheckingState.isInconsistent();
	}

	/**
	 * Complete the taxonomy computation stage and the stages it depends on, if
	 * it has not been done yet.
	 *
	 * @return the class taxonomy implied by the current ontology
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected Taxonomy<ElkClass> restoreTaxonomy()
			throws ElkInconsistentOntologyException, ElkException {

		ruleAndConclusionStats.reset();

		// also restores saturation and cleans the taxonomy if necessary
		restoreConsistencyCheck();
		if (consistencyCheckingState.isInconsistent()) {
			throw new ElkInconsistentOntologyException();
		}

		complete(stageManager.classTaxonomyComputationStage);

		return classTaxonomyState.getTaxonomy();
	}

	/**
	 * Compute the inferred taxonomy of the named classes for the given ontology
	 * if it has not been done yet.
	 *
	 * @return the class taxonomy implied by the current ontology
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized Taxonomy<ElkClass> getTaxonomy()
			throws ElkInconsistentOntologyException, ElkException {

		restoreTaxonomy();

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassification());
		return classTaxonomyState.getTaxonomy();
	}

	/**
	 * Compute the inferred taxonomy of the named classes for the given ontology
	 * if it has not been done yet.
	 *
	 * @return the class taxonomy implied by the current ontology
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized Taxonomy<ElkClass> getTaxonomyQuietly()
			throws ElkException {
		Taxonomy<ElkClass> result;

		try {
			result = getTaxonomy();
		} catch (ElkInconsistentOntologyException e) {
			LOGGER_.debug("Ontology is inconsistent");

			result = new SingletoneTaxonomy<ElkClass, OrphanTaxonomyNode<ElkClass>>(
					ElkClassKeyProvider.INSTANCE, getAllClasses(),
					new TaxonomyNodeFactory<ElkClass, OrphanTaxonomyNode<ElkClass>, Taxonomy<ElkClass>>() {
						@Override
						public OrphanTaxonomyNode<ElkClass> createNode(
								final Iterable<? extends ElkClass> members,
								final int size,
								final Taxonomy<ElkClass> taxonomy) {
							return new OrphanTaxonomyNode<ElkClass>(members,
									size, elkFactory_.getOwlNothing(),
									taxonomy);
						}
					});
		}

		return result;
	}

	/**
	 * Compute the inferred taxonomy of the named classes for the given ontology
	 * despite interruptions if it has not been computed yet.
	 *
	 * @return the class taxonomy implied by the current ontology
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized Taxonomy<ElkClass> getTaxonomyQuietlyUninterruptibly()
			throws ElkException {
		while (true) {
			try {
				return getTaxonomyQuietly();
			} catch (final ElkInterruptedException e) {
				continue;
			}
		}
	}

	/**
	 * Completes instance taxonomy computation stage and the stages that it
	 * depends on, if this has not been done yet.
	 *
	 * @return the instance taxonomy implied by the current ontology
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected InstanceTaxonomy<ElkClass, ElkNamedIndividual> restoreInstanceTaxonomy()
			throws ElkInconsistentOntologyException, ElkException {

		ruleAndConclusionStats.reset();

		// also restores saturation and cleans the taxonomy if necessary
		restoreConsistencyCheck();
		if (consistencyCheckingState.isInconsistent()) {
			throw new ElkInconsistentOntologyException();
		}

		complete(stageManager.instanceTaxonomyComputationStage);

		return instanceTaxonomyState.getTaxonomy();
	}

	/**
	 * Compute the inferred taxonomy of the named classes with instances if this
	 * has not been done yet.
	 *
	 * @return the instance taxonomy implied by the current ontology
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized InstanceTaxonomy<ElkClass, ElkNamedIndividual> getInstanceTaxonomy()
			throws ElkInconsistentOntologyException, ElkException {

		restoreInstanceTaxonomy();

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassification());
		return instanceTaxonomyState.getTaxonomy();
	}

	/**
	 * Compute the inferred taxonomy of the named classes with instances if this
	 * has not been done yet.
	 *
	 * @return the instance taxonomy implied by the current ontology
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized InstanceTaxonomy<ElkClass, ElkNamedIndividual> getInstanceTaxonomyQuietly()
			throws ElkException {

		InstanceTaxonomy<ElkClass, ElkNamedIndividual> result;

		try {
			result = getInstanceTaxonomy();
		} catch (ElkInconsistentOntologyException e) {
			LOGGER_.debug("Ontology is inconsistent");
			result = new SingletoneInstanceTaxonomy<ElkClass, ElkNamedIndividual, OrphanTypeNode<ElkClass, ElkNamedIndividual>>(
					ElkClassKeyProvider.INSTANCE, getAllClasses(),
					new TaxonomyNodeFactory<ElkClass, OrphanTypeNode<ElkClass, ElkNamedIndividual>, Taxonomy<ElkClass>>() {
						@Override
						public OrphanTypeNode<ElkClass, ElkNamedIndividual> createNode(
								final Iterable<? extends ElkClass> members,
								final int size,
								final Taxonomy<ElkClass> taxonomy) {
							final OrphanTypeNode<ElkClass, ElkNamedIndividual> node = new OrphanTypeNode<ElkClass, ElkNamedIndividual>(
									members, size, elkFactory_.getOwlNothing(),
									taxonomy, 1);
							final Set<ElkNamedIndividual> allNamedIndividuals = getAllNamedIndividuals();
							final Iterator<ElkNamedIndividual> namedIndividualIterator = allNamedIndividuals
									.iterator();
							if (namedIndividualIterator.hasNext()) {
								// there is at least one individual
								node.addInstanceNode(
										new OrphanInstanceNode<ElkClass, ElkNamedIndividual>(
												allNamedIndividuals,
												allNamedIndividuals.size(),
												namedIndividualIterator.next(),
												ElkIndividualKeyProvider.INSTANCE,
												node));
							}
							return node;
						}
					}, ElkIndividualKeyProvider.INSTANCE);
		}

		return result;
	}

	/**
	 * Compute the inferred taxonomy of the object properties for the given
	 * ontology if it has not been done yet.
	 *
	 * @return the object property taxonomy implied by the current ontology
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized Taxonomy<ElkObjectProperty> getObjectPropertyTaxonomy()
			throws ElkInconsistentOntologyException, ElkException {

		ruleAndConclusionStats.reset();

		restoreConsistencyCheck();
		if (consistencyCheckingState.isInconsistent()) {
			throw new ElkInconsistentOntologyException();
		}

		LOGGER_.trace("Property hierarchy computation");
		complete(stageManager.objectPropertyTaxonomyComputationStage);

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassification());
		return objectPropertyTaxonomyState.getTaxonomy();
	}

	/**
	 * Compute the inferred taxonomy of the object properties for the given
	 * ontology if it has not been done yet.
	 *
	 * @return the object property taxonomy implied by the current ontology
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	public synchronized Taxonomy<ElkObjectProperty> getObjectPropertyTaxonomyQuietly()
			throws ElkException {
		Taxonomy<ElkObjectProperty> result;

		try {
			result = getObjectPropertyTaxonomy();
		} catch (ElkInconsistentOntologyException e) {
			LOGGER_.debug("Ontology is inconsistent");

			result = new SingletoneTaxonomy<ElkObjectProperty, OrphanTaxonomyNode<ElkObjectProperty>>(
					ElkObjectPropertyKeyProvider.INSTANCE,
					getAllObjectProperties(),
					new TaxonomyNodeFactory<ElkObjectProperty, OrphanTaxonomyNode<ElkObjectProperty>, Taxonomy<ElkObjectProperty>>() {
						@Override
						public OrphanTaxonomyNode<ElkObjectProperty> createNode(
								final Iterable<? extends ElkObjectProperty> members,
								final int size,
								final Taxonomy<ElkObjectProperty> taxonomy) {
							return new OrphanTaxonomyNode<ElkObjectProperty>(
									members, size,
									elkFactory_.getOwlBottomObjectProperty(),
									taxonomy);
						}
					});
		}

		return result;
	}

	/**
	 * If the query results are not cached yet, indexes the supplied class
	 * expression and, if successful, computes the query so that the results for
	 * this expressions are ready in {@link #classExpressionQueryState_}.
	 *
	 * @param classExpression
	 * @param computeInstanceTaxonomy
	 *            if {@code false}, only class taxonomy is computed, if
	 *            {@code true}, also instance taxonomy is computed.
	 * @return <code>true</code> if the query was indexed successfully,
	 *         <code>false</code> otherwise, i.e., when the class expression is
	 *         not supported
	 * @throws ElkInconsistentOntologyException
	 * @throws ElkException
	 */
	private boolean computeQuery(final ElkClassExpression classExpression,
			final boolean computeInstanceTaxonomy)
			throws ElkInconsistentOntologyException, ElkException {

		// Load the query
		classExpressionQueryState_.registerQuery(classExpression);
		ensureLoading();

		// Complete all stages
		if (computeInstanceTaxonomy) {
			restoreInstanceTaxonomy();
		} else {
			restoreTaxonomy();
		}

		if (!classExpressionQueryState_.isIndexed(classExpression)) {
			return false;
		}

		/*
		 * If query result is cashed, but there were some changes to the
		 * ontology, it may not be up to date. Whether it is is checked during
		 * stages that clean contexts. These are run, if necessary, by the call
		 * above.
		 */
		if (classExpressionQueryState_.isComputed(classExpression)) {
			return true;
		}
		stageManager.classExpressionQueryStage.invalidateRecursive();
		try {
			complete(stageManager.classExpressionQueryStage);
		} catch (final ElkInterruptedException e) {
			if (classExpressionQueryState_.isComputed(classExpression)) {
				/*
				 * If the stage was interrupted, but the query is already
				 * computed, completing the stage will not be attempted during
				 * the next call. We need to call postExecute() manually, so
				 * that the stage wouldn't stay initialized with computation
				 * that already processed all its inputs (or at least the
				 * queried class).
				 */
				stageManager.classExpressionQueryStage.postExecute();
			} else {
				throw e;
			}
		}

		return true;
	}

	/**
	 * Decides whether the supplied (possibly complex) class expression is
	 * satisfiable. The query state is updated accordingly.
	 *
	 * @param classExpression
	 *            The queried class expression.
	 * @return whether the supplied class expression is satisfiable.
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected boolean querySatisfiability(
			final ElkClassExpression classExpression)
			throws ElkInconsistentOntologyException, ElkException {

		final boolean result;

		if (computeQuery(classExpression, false)) {
			result = classExpressionQueryState_.isSatisfiable(classExpression);
		} else {
			// classExpression couldn't be indexed; pretend it is a fresh class
			result = true;
		}

		// If classExpression is unsatisfiable, the result is complete.
		if (result) {
			incompleteness_.log(incompleteness_
					.getIncompletenessMonitorForClassExpressionQuery(
							classExpressionQueryState_
									.getOccurrenceStore(classExpression)));
		}
		return result;
	}

	/**
	 * Computes all atomic classes that are equivalent to the supplied (possibly
	 * complex) class expression. The query state is updated accordingly.
	 *
	 * @param classExpression
	 *            The queried class expression.
	 * @return all atomic classes that are equivalent to the supplied class
	 *         expression.
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected Node<ElkClass> queryEquivalentClasses(
			final ElkClassExpression classExpression)
			throws ElkInconsistentOntologyException, ElkException {

		final Node<ElkClass> result;

		if (computeQuery(classExpression, false)) {

			final Node<ElkClass> r = classExpressionQueryState_
					.getEquivalentClasses(classExpression);
			if (r == null) {
				result = classTaxonomyState.getTaxonomy().getBottomNode();
			} else {
				result = r;
			}

		} else {
			// classExpression couldn't be indexed; pretend it is a fresh class

			result = new QueryNode<ElkClass>(ElkClassKeyProvider.INSTANCE);
		}

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassExpressionQuery(
						classExpressionQueryState_
								.getOccurrenceStore(classExpression)));
		return result;
	}

	/**
	 * Computes all atomic direct super-classes of the supplied (possibly
	 * complex) class expression. The query state is updated accordingly.
	 *
	 * @param classExpression
	 *            The queried class expression.
	 * @return all atomic direct super-classes of the supplied class expression.
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected Set<? extends Node<ElkClass>> queryDirectSuperClasses(
			final ElkClassExpression classExpression)
			throws ElkInconsistentOntologyException, ElkException {

		final Set<? extends Node<ElkClass>> result;

		if (computeQuery(classExpression, false)) {

			final Set<? extends Node<ElkClass>> r = classExpressionQueryState_
					.getDirectSuperClasses(classExpression);
			if (r == null) {
				result = classTaxonomyState.getTaxonomy().getBottomNode()
						.getDirectSuperNodes();
			} else {
				result = r;
			}

		} else {
			// classExpression couldn't be indexed; pretend it is a fresh class

			result = Collections
					.singleton(classTaxonomyState.getTaxonomy().getTopNode());
		}

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassExpressionQuery(
						classExpressionQueryState_
								.getOccurrenceStore(classExpression)));
		return result;
	}

	/**
	 * Computes all atomic direct sub-classes of the supplied (possibly complex)
	 * class expression. The query state is updated accordingly.
	 *
	 * @param classExpression
	 *            The queried class expression.
	 * @return all atomic direct sub-classes of the supplied class expression.
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected Set<? extends Node<ElkClass>> queryDirectSubClasses(
			final ElkClassExpression classExpression)
			throws ElkInconsistentOntologyException, ElkException {

		final Set<? extends Node<ElkClass>> result;

		if (computeQuery(classExpression, false)) {

			final Taxonomy<ElkClass> taxonomy = classTaxonomyState
					.getTaxonomy();

			final Set<? extends Node<ElkClass>> r = classExpressionQueryState_
					.getDirectSubClasses(classExpression, taxonomy);

			if (r == null) {
				result = taxonomy.getBottomNode().getDirectSubNodes();
			} else {
				result = r;
			}

		} else {
			// classExpression couldn't be indexed; pretend it is a fresh class

			result = Collections.singleton(
					classTaxonomyState.getTaxonomy().getBottomNode());
		}

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassExpressionQuery(
						classExpressionQueryState_
								.getOccurrenceStore(classExpression)));
		return result;
	}

	/**
	 * Computes all direct instances of the supplied (possibly complex) class
	 * expression. The query state is updated accordingly.
	 *
	 * @param classExpression
	 *            The queried class expression.
	 * @return all direct instances of the supplied class expression.
	 * @throws ElkInconsistentOntologyException
	 *             if the ontology is inconsistent
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected Set<? extends Node<ElkNamedIndividual>> queryDirectInstances(
			final ElkClassExpression classExpression)
			throws ElkInconsistentOntologyException, ElkException {

		final Set<? extends Node<ElkNamedIndividual>> result;

		if (computeQuery(classExpression, true)) {

			final InstanceTaxonomy<ElkClass, ElkNamedIndividual> taxonomy = instanceTaxonomyState
					.getTaxonomy();

			final Set<? extends Node<ElkNamedIndividual>> r = classExpressionQueryState_
					.getDirectInstances(classExpression, taxonomy);

			if (r == null) {
				result = taxonomy.getBottomNode().getDirectInstanceNodes();
			} else {
				result = r;
			}

		} else {
			// classExpression couldn't be indexed; pretend it is a fresh class

			result = Collections.emptySet();
		}

		incompleteness_.log(
				incompleteness_.getIncompletenessMonitorForClassExpressionQuery(
						classExpressionQueryState_
								.getOccurrenceStore(classExpression)));
		return result;
	}

	/**
	 * Decides whether the supplied {@code axioms} are entailed by the currently
	 * loaded ontology.
	 *
	 * @param axioms
	 *            Entailment of what axioms is queried.
	 * @return A map from each queried axiom to the
	 *         {@link EntailmentQueryResult} for that axiom.
	 * @throws ElkException
	 */
	public synchronized Map<ElkAxiom, EntailmentQueryResult> isEntailed(
			final Iterable<? extends ElkAxiom> axioms) throws ElkException {

		entailmentQueryState_.registerQueries(axioms);

		restoreSaturation();

		stageManager.entailmentQueryStage.invalidateRecursive();
		complete(stageManager.entailmentQueryStage);

		return entailmentQueryState_.isEntailed(axioms);
	}

	/**
	 * Decides whether the supplied {@code axiom} is entailed by the currently
	 * loaded ontology.
	 *
	 * @param axiom
	 *            The queries axiom.
	 * @return the {@link EntailmentQueryResult} for the queried axiom.
	 * @throws ElkException
	 */
	public synchronized EntailmentQueryResult isEntailed(final ElkAxiom axiom)
			throws ElkException {
		return isEntailed(Collections.singleton(axiom)).get(axiom);
	}

	/**
	 * @return all {@link ElkClass}es occurring in the ontology
	 */
	public synchronized Set<ElkClass> getAllClasses() {
		Set<ElkClass> result = new ArrayHashSet<ElkClass>(
				ontologyIndex.getClasses().size());
		for (IndexedClass ic : ontologyIndex.getClasses())
			result.add(ic.getElkEntity());
		return result;
	}

	/**
	 * @return all {@link ElkNamedIndividual}s occurring in the ontology
	 */
	public synchronized Set<ElkNamedIndividual> getAllNamedIndividuals() {
		Set<ElkNamedIndividual> allNamedIndividuals = new ArrayHashSet<ElkNamedIndividual>(
				ontologyIndex.getClasses().size());
		for (IndexedIndividual ii : ontologyIndex.getIndividuals())
			allNamedIndividuals.add(ii.getElkEntity());
		return allNamedIndividuals;
	}

	/**
	 * @return all {@link ElkObjectProperty}es occurring in the ontology
	 */
	public synchronized Set<ElkObjectProperty> getAllObjectProperties() {
		final Set<ElkObjectProperty> result = new ArrayHashSet<ElkObjectProperty>(
				ontologyIndex.getObjectProperties().size());
		for (final IndexedObjectProperty prop : ontologyIndex
				.getObjectProperties()) {
			result.add(prop.getElkEntity());
		}
		return result;
	}

	/**
	 * @return {@code true} if the ontology has been checked for consistency.
	 */
	public synchronized boolean doneConsistencyCheck() {
		return stageManager.consistencyCheckingStage.isCompleted();
	}

	/**
	 * @return {@code true} if the class taxonomy has been computed
	 */
	public synchronized boolean doneTaxonomy() {
		return stageManager.classTaxonomyComputationStage.isCompleted();
	}

	/**
	 * @return {@code true} if the instance taxonomy has been computed
	 */
	public synchronized boolean doneInstanceTaxonomy() {
		return stageManager.instanceTaxonomyComputationStage.isCompleted();
	}

	/**
	 * @return {@code true} if the object property taxonomy has been computed
	 */
	public synchronized boolean doneObjectPropertyTaxonomy() {
		return stageManager.objectPropertyTaxonomyComputationStage
				.isCompleted();
	}

	/**
	 * Compute the index representation of the given ontology if it has not been
	 * done yet.
	 *
	 * @return the index representation of the given ontology
	 * @throws ElkException
	 *             if the reasoning process cannot be completed successfully
	 */
	protected OntologyIndex getOntologyIndex() throws ElkException {
		ensureLoading();
		return ontologyIndex;
	}

	@Deprecated
	public ElkPolarityExpressionConverter getExpressionConverter() {
		return this.expressionConverter_;
	}

	@Deprecated
	public ElkSubObjectPropertyExpressionVisitor<? extends IndexedPropertyChain> getSubPropertyConverter() {
		return this.subPropertyConverter_;
	}

	/*---------------------------------------------------
	 * TRACING METHODS
	 *---------------------------------------------------*/

	public TracingProof getProof() {
		return this;
	}

	@Override
	public Collection<? extends TracingInference> getInferences(
			final Object conclusion) {
		if (!(conclusion instanceof Conclusion)) {
			return Collections.emptySet();
		}
		// else
		try {
			// Ensure that classes are saturated.
			getTaxonomyQuietlyUninterruptibly();
			if (!traceState_.requestInferences((Conclusion) conclusion)) {
				stageManager.inferenceTracingStage.invalidateRecursive();
				completeUninterruptibly(stageManager.inferenceTracingStage);
			}
		} catch (final ElkException e) {
			throw new ElkRuntimeException(e);
		}
		return traceState_.getInferences(conclusion);
	}

	@NestedStats(name = "traceState")
	public Object getStatsNestedInTraceSate() {
		return traceState_.getStats();
	}

	@Deprecated
	IndexedClassExpression transform(ElkClassExpression ce) {
		return ce.accept(expressionConverter_);
	}

	@Deprecated
	IndexedObjectProperty transform(ElkObjectProperty ce) {
		return ce.accept(expressionConverter_);
	}

	@Deprecated
	IndexedPropertyChain transform(ElkSubObjectPropertyExpression ce) {
		return ce.accept(subPropertyConverter_);
	}

	TraceState getTraceState() {
		return traceState_;
	}

	// ////////////////////////////////////////////////////////////////
	/*
	 * SOME DEBUG METHODS, FIXME: REMOVE
	 */
	// ////////////////////////////////////////////////////////////////
	@Deprecated
	public synchronized Collection<? extends IndexedClassExpression> getIndexedClassExpressions() {
		return ontologyIndex.getClassExpressions();
	}
}


/*
 * #%L
 * ELK Reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 - 2012 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.saturation;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpressionList;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectSomeValuesFrom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedPropertyChain;
import org.semanticweb.elk.reasoner.saturation.conclusions.classes.BackwardLinkImpl;
import org.semanticweb.elk.reasoner.saturation.conclusions.classes.ContextInitializationImpl;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.BackwardLink;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ClassConclusion;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ContextInitialization;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ClassInconsistency;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.DisjointSubsumer;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ForwardLink;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.Propagation;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubClassInclusion;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubClassInclusionComposed;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubClassInclusionDecomposed;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubContextInitialization;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.saturation.context.SubContext;
import org.semanticweb.elk.reasoner.saturation.context.SubContextPremises;
import org.semanticweb.elk.reasoner.saturation.inferences.ClassInference;
import org.semanticweb.elk.reasoner.saturation.rules.backwardlinks.BackwardLinkChainFromBackwardLinkRule;
import org.semanticweb.elk.reasoner.saturation.rules.backwardlinks.ContradictionOverBackwardLinkRule;
import org.semanticweb.elk.reasoner.saturation.rules.backwardlinks.LinkableBackwardLinkRule;
import org.semanticweb.elk.util.collections.ArrayHashMap;
import org.semanticweb.elk.util.collections.ArrayHashSet;
import org.semanticweb.elk.util.collections.chains.AbstractChain;
import org.semanticweb.elk.util.collections.chains.Chain;
import org.semanticweb.elk.util.concurrent.collections.ActivationStack;
import org.semanticweb.elk.util.concurrent.collections.SynchronizedArrayListActivationStack;

/**
 * Context implementation that is used for EL reasoning. It provides data
 * structures for storing and retrieving various types of derived expressions,
 * including computed subsumptions between class expressions.
 *
 * @author Markus Kroetzsch
 * @author Frantisek Simancik
 * @author "Yevgeny Kazakov"
 *
 */
public class ContextImpl implements ExtendedContext {

	/**
	 * the rules that should be applied to each derived {@link BackwardLinkImpl}
	 * in this {@link Context}; can be {@code null}
	 */
	private LinkableBackwardLinkRule backwardLinkRules_ = null;

	/**
	 * the indexed representation of all derived reflexive
	 * {@link BackwardLinkImpl} s, i.e., the {@link BackwardLinkImpl}s whose
	 * source is this {@link Context}; can be {@code null}
	 *
	 * @see BackwardLinkImpl#getTraceRoot()
	 */
	private Set<IndexedObjectProperty> reflexiveBackwardLinks_ = null;

	/**
	 * the {@link SubContext}s of this {@link Context} indexed by their root
	 * {@link IndexedObjectProperty}s
	 */
	private Map<IndexedObjectProperty, SubContext> subContextsByObjectProperty_ = null;

	/**
	 * the map from {@link IndexedClassExpressionList}s to the positions
	 * corresponding to the derived {@link IndexedClassExpression} subsumers
	 */
	private Map<IndexedClassExpressionList, Set<Integer>> disjointnessAxioms_;

	/**
	 * {@code true} if {@code owl:Nothing} is stored in
	 * {@link #composedSubsumers_}
	 */
	private volatile boolean isInconsistent_ = false;

	/**
	 * {@code true} if it is not initialized or otherwise all derived
	 * {@link SubClassInclusion}s for {@link #root_} have been computed.
	 * @see SubClassInclusion#getDestination()
	 */
	private volatile boolean isSaturated_ = true;

	/**
	 * the {@link IndexedContextRoot} for which the {@link #composedSubsumers_}
	 * are computed
	 */
	private final IndexedContextRoot root_;

	/**
	 * the derived {@link IndexedClassExpression}s that subsume {@link #root_}
	 * obtained by composition rules
	 */
	private final Set<IndexedClassExpression> composedSubsumers_;

	/**
	 * the derived {@link IndexedClassExpression}s that subsume {@link #root_}
	 * obtained by decomposition rules
	 */
	private final Set<IndexedClassExpression> decomposedSubsumers_;

	/**
	 * the queue of unprocessed {@code ClassInference}s of this {@link Context}
	 */
	private final ActivationStack<ClassInference> toDo_;

	/**
	 * {@code true} if this {@link Context} is initialized, i.e., contains
	 * {@link ContextInitializationImpl}
	 */
	private volatile boolean isInitialized_ = false;

	/**
	 * the number of different {@link ClassConclusion}s contained in this {@code ConclusionSet}
	 */
	private int size = 0;

	/**
	 * Construct a new {@link Context} for the given {@link IndexedContextRoot}.
	 * Initially, the context is not active.
	 *
	 * @param root
	 */
	public ContextImpl(IndexedContextRoot root) {
		this.root_ = root;
		this.toDo_ = new SynchronizedArrayListActivationStack<ClassInference>();
		this.composedSubsumers_ = new ArrayHashSet<IndexedClassExpression>(16);
		this.decomposedSubsumers_ = new ArrayHashSet<IndexedClassExpression>(8);
	}

	@Override
	public boolean addConclusion(ClassConclusion conclusion) {
		boolean success = conclusion.accept(new ConclusionInserter());
		if (success)
			size++;
		return success;
	}

	@Override
	public boolean removeConclusion(ClassConclusion conclusion) {
		boolean success = conclusion.accept(new ConclusionDeleter());
		if (success)
			size--;
		return success;
	}

	@Override
	public boolean containsConclusion(ClassConclusion conclusion) {
		return conclusion.accept(new ConclusionOccurrenceChecker());
	}

	@Override
	public boolean isEmpty() {
		return size == 0;

	}

	@Override
	public boolean isEmpty(IndexedObjectProperty subRoot) {
		if (subContextsByObjectProperty_ == null)
			return true;
		// else
		SubContext subContext = subContextsByObjectProperty_.get(subRoot);
		return subContext == null || subContext.isEmpty();
	}

	@Override
	public boolean addToDo(ClassInference inference) {
		return toDo_.push(inference);
	}

	@Override
	public Map<IndexedObjectProperty, ? extends SubContextPremises> getSubContextPremisesByObjectProperty() {
		if (subContextsByObjectProperty_ == null)
			return Collections.emptyMap();
		// else
		return subContextsByObjectProperty_;
	}

	SubContext getCreateSubContext(IndexedObjectProperty subRoot) {
		if (subContextsByObjectProperty_ == null)
			subContextsByObjectProperty_ = new ArrayHashMap<IndexedObjectProperty, SubContext>(
					3);
		SubContext result = subContextsByObjectProperty_.get(subRoot);
		if (result == null) {
			result = new SubContextImpl();
			subContextsByObjectProperty_.put(subRoot, result);
		}
		return result;
	}

	public boolean removeSubContext(IndexedPropertyChain subRoot) {
		if (subContextsByObjectProperty_ == null)
			return false;
		boolean changed = subContextsByObjectProperty_.remove(subRoot) != null;
		if (changed && subContextsByObjectProperty_.isEmpty())
			subContextsByObjectProperty_ = null;
		return changed;
	}

	@Override
	public Chain<LinkableBackwardLinkRule> getBackwardLinkRuleChain() {
		return new AbstractChain<LinkableBackwardLinkRule>() {

			@Override
			public LinkableBackwardLinkRule next() {
				return backwardLinkRules_;
			}

			@Override
			public void setNext(LinkableBackwardLinkRule tail) {
				backwardLinkRules_ = tail;
			}
		};
	}

	@Override
	public LinkableBackwardLinkRule getBackwardLinkRuleHead() {
		return backwardLinkRules_;
	}

	@Override
	public Set<IndexedObjectProperty> getLocalReflexiveObjectProperties() {
		return reflexiveBackwardLinks_ == null ? Collections
				.<IndexedObjectProperty> emptySet() : reflexiveBackwardLinks_;
	}

	@Override
	public IndexedContextRoot getRoot() {
		return root_;
	}

	@Override
	public Set<IndexedClassExpression> getComposedSubsumers() {
		return composedSubsumers_;
	}

	@Override
	public Set<IndexedClassExpression> getDecomposedSubsumers() {
		return decomposedSubsumers_;
	}

	/*
	 * @Override public boolean isInconsistForDisjointnessAxiom(
	 * IndexedDisjointnessAxiom axiom) { if (disjointnessAxioms_ == null) return
	 * false; IndexedClassExpression[] positions = disjointnessAxioms_.get(axiom);
	 * if (positions == null) return false; // check if both positions are not null;
	 * this is always when the second // position is not null return (positions[1]
	 * != null); }
	 */

	@Override
	public Set<? extends Integer> getSubsumerPositions(
			IndexedClassExpressionList disjoint) {
		if (disjointnessAxioms_ == null) {
			return null;
		}

		return disjointnessAxioms_.get(disjoint);
	}

	@Override
	public boolean isSaturated() {
		return isSaturated_;
	}

	@Override
	public boolean isInitialized() {
		return isInitialized_;
	}

	@Override
	public ClassInference takeToDo() {
		return toDo_.pop();
	}

	@Override
	public String toString() {
		return root_.toString() + (this != root_.getContext() ? "[local]" : "");
	}

	@Override
	public synchronized boolean setSaturated(boolean saturated) {
		// synchronized to ensure consistency when updated from two workers
		boolean previous = isSaturated_;
		isSaturated_ = saturated;
		return previous;
	}

	@Override
	public Iterable<? extends IndexedObjectSomeValuesFrom> getPropagatedSubsumers(
			IndexedPropertyChain subRoot) {
		if (subContextsByObjectProperty_ == null) {
			return Collections.emptyList();
		}

		SubContext subContext = subContextsByObjectProperty_.get(subRoot);

		if (subContext == null) {
			return Collections.emptyList();
		}

		return subContext.getPropagatedSubsumers();
	}

	private class ConclusionInserter
			implements
				ClassConclusion.Visitor<Boolean> {

		@Override
		public Boolean visit(BackwardLink subConclusion) {
			IndexedObjectProperty relation = subConclusion
					.getRelation();
			// make sure that relevant context always exists
			SubContext subContext = getCreateSubContext(relation);
			if (subConclusion.getTraceRoot() == root_) {
				// reflexive
				if (reflexiveBackwardLinks_ == null) {
					reflexiveBackwardLinks_ = new ArrayHashSet<IndexedObjectProperty>(
							3);
				}
				return reflexiveBackwardLinks_.add(relation);
			}
			// else non-reflexive
			return subContext.addSubConclusion(subConclusion);
		}

		@Override
		public Boolean visit(SubClassInclusionComposed conclusion) {
			return composedSubsumers_.add(conclusion.getSubsumer());
		}

		@Override
		public Boolean visit(SubClassInclusionDecomposed conclusion) {
			return decomposedSubsumers_.add(conclusion.getSubsumer());
		}

		@Override
		public Boolean visit(ContextInitialization conclusion) {
			if (isInitialized_)
				// nothing changes
				return false;
			// else
			isInitialized_ = true;
			return true;
		}

		@Override
		public Boolean visit(ClassInconsistency conclusion) {
			boolean before = isInconsistent_;
			isInconsistent_ = true;
			ContradictionOverBackwardLinkRule.addTo(ContextImpl.this);
			return before != isInconsistent_;
		}

		@Override
		public Boolean visit(DisjointSubsumer conclusion) {
			if (disjointnessAxioms_ == null) {
				disjointnessAxioms_ = new ArrayHashMap<IndexedClassExpressionList, Set<Integer>>();
			}
			IndexedClassExpressionList disjoint = conclusion.getDisjointExpressions();
			int position = conclusion.getPosition();
			Set<Integer> positions = disjointnessAxioms_
					.get(disjoint);
			if (positions == null) {
				positions = new ArrayHashSet<Integer>(2);
				disjointnessAxioms_.put(disjoint, positions);
			}
			if (positions.contains(position)) {
				return false;
			}
			// else
			positions.add(position);
			return true;
		}

		@Override
		public Boolean visit(ForwardLink conclusion) {
			return BackwardLinkChainFromBackwardLinkRule.addRuleFor(conclusion,
					ContextImpl.this);
		}

		@Override
		public Boolean visit(Propagation subConclusion) {
			return getCreateSubContext(subConclusion.getSubDestination())
					.addSubConclusion(subConclusion);
		}

		@Override
		public Boolean visit(SubContextInitialization subConclusion) {
			return getCreateSubContext(
					subConclusion.getSubDestination()).addSubConclusion(
					subConclusion);
		}

	}

	private class ConclusionDeleter
			implements
				ClassConclusion.Visitor<Boolean> {

		@Override
		public Boolean visit(BackwardLink subConclusion) {
			boolean changed = false;
			IndexedObjectProperty relation = subConclusion
					.getRelation();
			SubContext subContext = getCreateSubContext(relation);
			if (subConclusion.getTraceRoot() == root_) {
				// link is reflexive
				if (reflexiveBackwardLinks_ != null) {
					changed = reflexiveBackwardLinks_.remove(relation);
					if (reflexiveBackwardLinks_.isEmpty()) {
						reflexiveBackwardLinks_ = null;
					}
				}
			} else {
				// link is not reflexive
				if (subContext == null)
					return false;
				// else
				changed = subContext.removeSubConclusion(subConclusion);
			}
			return changed;
		}

		@Override
		public Boolean visit(SubClassInclusionComposed conclusion) {
			return composedSubsumers_.remove(conclusion.getSubsumer());
		}

		@Override
		public Boolean visit(SubClassInclusionDecomposed conclusion) {
			return decomposedSubsumers_
					.remove(conclusion.getSubsumer());
		}

		@Override
		public Boolean visit(ContextInitialization conclusion) {
			if (!isInitialized_)
				// nothing changes
				return false;
			// else
			isInitialized_ = false;
			return true;
		}

		@Override
		public Boolean visit(ClassInconsistency conclusion) {
			boolean before = isInconsistent_;
			isInconsistent_ = false;
			ContradictionOverBackwardLinkRule.removeFrom(ContextImpl.this);
			return before != isInconsistent_;
		}

		@Override
		public Boolean visit(DisjointSubsumer conclusion) {
			if (disjointnessAxioms_ == null) {
				return false;
			}
			IndexedClassExpressionList disjoint = conclusion.getDisjointExpressions();
			int position = conclusion.getPosition();
			Set<Integer> positions = disjointnessAxioms_.get(disjoint);
			if (positions == null) {
				return false;
			}
			// else
			return (positions.remove(position));
		}

		@Override
		public Boolean visit(ForwardLink conclusion) {
			return BackwardLinkChainFromBackwardLinkRule.removeRuleFor(
					conclusion, ContextImpl.this);
		}

		@Override
		public Boolean visit(Propagation subConclusion) {
			SubContext subContext = getCreateSubContext(subConclusion
					.getSubDestination());
			if (subContext == null)
				return false;
			// else
			return subContext.removeSubConclusion(subConclusion);
		}

		@Override
		public Boolean visit(SubContextInitialization subConclusion) {
			SubContext subContext = getCreateSubContext(subConclusion
					.getSubDestination());
			if (subContext == null)
				return false;
			// else
			return subContext.removeSubConclusion(subConclusion);
		}

	}

	private class ConclusionOccurrenceChecker implements
			ClassConclusion.Visitor<Boolean> {

		@Override
		public Boolean visit(BackwardLink subConclusion) {
			if (subConclusion.getTraceRoot() == root_) {
				// reflexive
				return reflexiveBackwardLinks_ != null
						&& reflexiveBackwardLinks_.contains(subConclusion
								.getRelation());
			}
			// else non-reflexive
			SubContext subContext = getCreateSubContext(subConclusion
					.getRelation());
			return subContext != null
					&& subContext.containsSubConclusion(subConclusion);
		}

		@Override
		public Boolean visit(SubClassInclusionComposed conclusion) {
			return composedSubsumers_
					.contains(conclusion.getSubsumer());
		}

		@Override
		public Boolean visit(SubClassInclusionDecomposed conclusion) {
			return decomposedSubsumers_.contains(conclusion
					.getSubsumer());
		}

		@Override
		public Boolean visit(ContextInitialization conclusion) {
			return isInitialized_;
		}

		@Override
		public Boolean visit(ClassInconsistency conclusion) {
			return isInconsistent_;
		}

		@Override
		public Boolean visit(DisjointSubsumer conclusion) {
			if (disjointnessAxioms_ == null) {
				return false;
			}
			IndexedClassExpressionList disjoint = conclusion.getDisjointExpressions();
			int position = conclusion.getPosition();
			Set<Integer> positions = disjointnessAxioms_.get(disjoint);
			if (positions == null) {
				return false;
			}
			// else
			return positions.contains(position);
		}

		@Override
		public Boolean visit(ForwardLink conclusion) {
			return BackwardLinkChainFromBackwardLinkRule.containsRuleFor(
					conclusion, ContextImpl.this);
		}

		@Override
		public Boolean visit(Propagation subConclusion) {
			SubContext subContext = getCreateSubContext(subConclusion
					.getSubDestination());
			if (subContext == null)
				return false;
			// else
			return subContext.containsSubConclusion(subConclusion);
		}

		@Override
		public Boolean visit(SubContextInitialization subConclusion) {
			SubContext subContext = getCreateSubContext(subConclusion
					.getSubDestination());
			if (subContext == null)
				return false;
			// else
			return subContext.containsSubConclusion(subConclusion);
		}

	}
}


/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2016 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.consistency;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.liveontologies.puli.Proof;
import org.semanticweb.elk.reasoner.entailments.impl.IndividualInconsistencyEntailsOntologyInconsistencyImpl;
import org.semanticweb.elk.reasoner.entailments.impl.OntologyInconsistencyImpl;
import org.semanticweb.elk.reasoner.entailments.impl.OwlThingInconsistencyEntailsOntologyInconsistencyImpl;
import org.semanticweb.elk.reasoner.entailments.impl.TopObjectPropertyInBottomEntailsOntologyInconsistencyImpl;
import org.semanticweb.elk.reasoner.entailments.model.EntailmentInference;
import org.semanticweb.elk.reasoner.entailments.model.OntologyInconsistencyEntailmentInference;
import org.semanticweb.elk.reasoner.indexing.classes.OntologyIndexDummyChangeListener;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassEntity;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.IndexedPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.OntologyIndex;
import org.semanticweb.elk.reasoner.saturation.SaturationState;
import org.semanticweb.elk.reasoner.saturation.SaturationStateDummyChangeListener;
import org.semanticweb.elk.reasoner.saturation.conclusions.classes.SaturationConclusionBaseFactory;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ClassInconsistency;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SaturationConclusion;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.stages.PropertyHierarchyCompositionState;
import org.semanticweb.elk.util.collections.Operations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Stores information about the state of consistency checking computation
 *
 * @author Yevgeny Kazakov
 * @author Peter Skocovsky
 */
public class ConsistencyCheckingState {

	// logger for this class
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(ConsistencyCheckingState.class);

	/**
	 * the saturation state of the reasoner
	 */
	private final SaturationState<?> saturationState_;

	private final SaturationConclusion.Factory conclusionFactory_ = new SaturationConclusionBaseFactory();

	private final IndexedClass owlThing_;

	private final IndexedObjectProperty bottomProperty_;

	private final IndexedObjectProperty topProperty_;

	/**
	 * {@code true} if the ontology is consistent due to some syntactic
	 * sufficient conditions
	 */
	private boolean isTriviallyConsistent_ = false;

	/**
	 * {@code true} if inconsistency is derived for {@code owl:Thing}
	 */
	private volatile boolean isOwlThingInconsistent_ = false;
	/**
	 * {@code true} if inconsistency is derived for
	 * {@code owl:bottomObjectProperty}
	 */
	private volatile boolean isTopObjectPropertyInBottom_ = false;
	/**
	 * the set of individuals for which inconsistency is derived
	 */
	private final Set<IndexedIndividual> inconsistentIndividuals_ = Collections
			.synchronizedSet(new HashSet<IndexedIndividual>());
	/**
	 * the entities for which consistency needs to be checked
	 */
	private final Queue<IndexedClassEntity> toDoEntities_;

	private <C extends Context> ConsistencyCheckingState(
			SaturationState<C> saturationState,
			final PropertyHierarchyCompositionState propertHierarchyState) {
		this.saturationState_ = saturationState;
		final OntologyIndex index = saturationState.getOntologyIndex();
		this.owlThing_ = index.getOwlThing();
		this.bottomProperty_ = index.getOwlBottomObjectProperty();
		this.topProperty_ = index.getOwlTopObjectProperty();
		toDoEntities_ = new ConcurrentLinkedQueue<IndexedClassEntity>(
				index.getIndividuals());
		toDoEntities_.add(index.getOwlThing());
		isTriviallyConsistent_ = !index.hasPositiveOwlNothing();
		// listening to changes in the ontology
		index.addListener(new OntologyIndexDummyChangeListener() {

			@Override
			public void individualAddition(IndexedIndividual ind) {
				toDoEntities_.add(ind);
			}

			@Override
			public void individualRemoval(IndexedIndividual ind) {
				inconsistentIndividuals_.remove(ind);
			}

			@Override
			public void positiveOwlNothingAppeared() {
				isTriviallyConsistent_ = false;
			}

			@Override
			public void positiveOwlNothingDisappeared() {
				isTriviallyConsistent_ = true;
			}

		});
		// listening to changes in the saturation state
		saturationState
				.addListener(new SaturationStateDummyChangeListener<C>() {

					@Override
					public void contextsClear() {
						toDoEntities_.addAll(index.getIndividuals());
						toDoEntities_.add(owlThing_);
						inconsistentIndividuals_.clear();
						isOwlThingInconsistent_ = false;
					}

					@Override
					public void contextMarkNonSaturated(C context) {
						IndexedContextRoot root = context.getRoot();
						if (root instanceof IndexedIndividual) {
							IndexedIndividual ind = (IndexedIndividual) root;
							inconsistentIndividuals_.remove(ind);
							toDoEntities_.add(ind);
						} else if (root == owlThing_) {
							isOwlThingInconsistent_ = false;
							toDoEntities_.add(owlThing_);
						}
					}

					@Override
					public void contextMarkSaturated(C context) {

						IndexedContextRoot root = context.getRoot();
						if (!context.containsConclusion(
								conclusionFactory_.getContradiction(root))) {
							return;
						}
						// else
						if (root instanceof IndexedIndividual) {
							tellInconsistentIndividual(
									(IndexedIndividual) root);
						} else if (root == owlThing_) {
							tellInconsistentOwlThing();
						}

					}

				});
		// listening to changes in the property hierarchy state
		propertHierarchyState
				.addListener(new PropertyHierarchyCompositionState.Listener() {

					@Override
					public void propertyBecameSaturated(
							final IndexedPropertyChain chain) {
						if (bottomProperty_ == chain && chain.getSaturated()
								.getSubProperties().contains(topProperty_)) {
							tellTopObjectPropertyInBottom();
						}
					}

					@Override
					public void propertyBecameNotSaturated(
							final IndexedPropertyChain chain) {
						if (bottomProperty_ == chain) {
							isTopObjectPropertyInBottom_ = false;
						}
					}

				});
	}

	/**
	 * @param saturationState
	 * @return a new {@link ConsistencyCheckingState} associated with the given
	 *         {@link SaturationState}
	 */
	public static ConsistencyCheckingState create(
			SaturationState<?> saturationState,
			final PropertyHierarchyCompositionState propertHierarchyState) {
		return new ConsistencyCheckingState(saturationState,
				propertHierarchyState);
	}

	/**
	 * Removes from {@link #toDoEntities_} the entities which no longer occur in
	 * the ontology or for the context is already saturated (thus consistency is
	 * already checked)
	 *
	 * @return the number of the remaining entries in {@link #toDoEntities_}
	 */
	private int pruneToDo() {
		int size = 0;
		Iterator<IndexedClassEntity> itr = toDoEntities_.iterator();
		while (itr.hasNext()) {
			IndexedClassEntity next = itr.next();
			if (!next.occurs()) {
				itr.remove();
				continue;
			}
			// else
			Context context = saturationState_.getContext(next);
			if (context != null && context.isSaturated()) {
				itr.remove();
			} else {
				size++;
			}
		}
		return size;
	}

	public Collection<? extends IndexedClassEntity> getTestEntitites() {
		if (isTriviallyConsistent_) {
			return Collections.emptyList();
		}
		int size = pruneToDo();
		// since getting the size of the queue is a linear operation,
		// use the computed size
		return Operations.getCollection(toDoEntities_, size);
	}

	/**
	 * tells that {@code owl:Thing} is inconsistent in the
	 * {@link SaturationState}, i.e., its context is saturated and contains
	 * {@link ClassInconsistency}
	 */
	private void tellInconsistentOwlThing() {
		isOwlThingInconsistent_ = true;
		LOGGER_.trace("owl:Thing inconsistent");
	}

	/**
	 * tells that derived sub-properties of {@code owl:bottomObjectProperty}
	 * contain {@code owl:topObjectProperty}
	 */
	private void tellTopObjectPropertyInBottom() {
		isTopObjectPropertyInBottom_ = true;
		LOGGER_.trace(
				"owl:topObjectProperty is a sub-property of owl:bottomObjectProperty");
	}

	/**
	 * tells that the given {@link IndexedIndividual} is inconsistent in the
	 * {@link SaturationState},
	 *
	 * @param ind
	 */
	private void tellInconsistentIndividual(IndexedIndividual ind) {
		inconsistentIndividuals_.add(ind);
		LOGGER_.trace("{} inconsistent", ind);
	}

	/**
	 * @return {@code true} if the ontology is currently in the inconsistent
	 *         state
	 */
	public boolean isInconsistent() {
		return isOwlThingInconsistent_ || isTopObjectPropertyInBottom_
				|| !inconsistentIndividuals_.isEmpty();
	}

	/**
	 * @return {@code true} if inconsistency for {@code owl:Thing} is derived
	 */
	public boolean isOwlThingInconsistent() {
		return isOwlThingInconsistent_;
	}

	/**
	 * @return {@code true} if inconsistency is derived from object property
	 *         hierarchy
	 */
	public boolean isTopObjectPropertyInBottom() {
		return isTopObjectPropertyInBottom_;
	}

	/**
	 * @return all individuals for which inconsistency is derived
	 */
	public Collection<? extends IndexedIndividual> getInconsistentIndividuals() {
		return inconsistentIndividuals_;
	}

	/**
	 * Explains why an ontology inconsistency is entailed. If it is not
	 * entailed, the returned proof is empty.
	 *
	 * @param atMostOne
	 *            Whether at most one explanation should be returned.
	 * @return An evidence of entailment of ontology inconsistency.
	 */
	public Proof<? extends EntailmentInference> getEvidence(
			final boolean atMostOne) {

		return new Proof<EntailmentInference>() {

			@SuppressWarnings("unchecked")
			@Override
			public Collection<OntologyInconsistencyEntailmentInference> getInferences(
					final Object conclusion) {

				if (!OntologyInconsistencyImpl.INSTANCE.equals(conclusion)) {
					return Collections.emptyList();
				}
				// else

				final Collection<? extends IndexedIndividual> inconsistentIndividuals = getInconsistentIndividuals();
				Iterable<OntologyInconsistencyEntailmentInference> result = Operations
						.map(inconsistentIndividuals,
								INDIVIDUAL_TO_ENTAILMENT_INFERENCE);
				int size = inconsistentIndividuals.size();

				if (isTopObjectPropertyInBottom_) {
					result = Operations.concat(Operations
							.<OntologyInconsistencyEntailmentInference> singleton(
									new TopObjectPropertyInBottomEntailsOntologyInconsistencyImpl(
											conclusionFactory_
													.getSubPropertyChain(
															topProperty_,
															bottomProperty_))),
							result);
					size++;
				}

				if (isOwlThingInconsistent_) {
					result = Operations.concat(Operations
							.<OntologyInconsistencyEntailmentInference> singleton(
									new OwlThingInconsistencyEntailsOntologyInconsistencyImpl(
											conclusionFactory_.getContradiction(
													owlThing_))),
							result);
					size++;
				}

				if (atMostOne) {
					final Iterator<OntologyInconsistencyEntailmentInference> iter = result
							.iterator();
					if (!iter.hasNext()) {
						return Collections.emptyList();
					}
					// else
					return Collections.singleton(iter.next());
				}
				// else

				return Operations.getCollection(result, size);
			}

		};

	}

	private final Operations.Transformation<IndexedIndividual, OntologyInconsistencyEntailmentInference> INDIVIDUAL_TO_ENTAILMENT_INFERENCE = new Operations.Transformation<IndexedIndividual, OntologyInconsistencyEntailmentInference>() {

		@Override
		public OntologyInconsistencyEntailmentInference transform(
				final IndexedIndividual ind) {
			return new IndividualInconsistencyEntailsOntologyInconsistencyImpl(
					conclusionFactory_.getContradiction(ind),
					ind.getElkEntity());
		}

	};

}


/*-
 * #%L
 * ELK Reasoner Core
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2016 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.stages;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.liveontologies.puli.Proof;
import org.liveontologies.puli.Proofs;
import org.semanticweb.elk.loading.AbstractEntailmentQueryLoader;
import org.semanticweb.elk.loading.ElkLoadingException;
import org.semanticweb.elk.loading.EntailmentQueryLoader;
import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.visitors.ElkAxiomVisitor;
import org.semanticweb.elk.reasoner.completeness.IncompletenessManager;
import org.semanticweb.elk.reasoner.config.ReasonerConfiguration;
import org.semanticweb.elk.reasoner.consistency.ConsistencyCheckingState;
import org.semanticweb.elk.reasoner.entailments.InconsistencyProofWrapper;
import org.semanticweb.elk.reasoner.entailments.model.Entailment;
import org.semanticweb.elk.reasoner.entailments.model.EntailmentInference;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexingListener;
import org.semanticweb.elk.reasoner.indexing.model.Occurrence;
import org.semanticweb.elk.reasoner.indexing.model.OccurrenceStore;
import org.semanticweb.elk.reasoner.query.AbstractProperEntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.ElkQueryException;
import org.semanticweb.elk.reasoner.query.EntailmentQueryConverter;
import org.semanticweb.elk.reasoner.query.EntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.IndexedEntailmentQuery;
import org.semanticweb.elk.reasoner.query.UnsupportedIndexingEntailmentQueryResultImpl;
import org.semanticweb.elk.reasoner.query.UnsupportedQueryTypeEntailmentQueryResultImpl;
import org.semanticweb.elk.reasoner.saturation.SaturationState;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SaturationConclusion;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.util.collections.ArrayHashMap;
import org.semanticweb.elk.util.collections.ArrayHashSet;
import org.semanticweb.elk.util.collections.Condition;
import org.semanticweb.elk.util.collections.Evictor;
import org.semanticweb.elk.util.collections.Operations;
import org.semanticweb.elk.util.concurrent.computation.InterruptMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;

/**
 * Keeps track of axioms that were queried for entailment.
 *
 * @author Peter Skocovsky
 */
public class EntailmentQueryState implements EntailmentQueryLoader.Factory {

	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(EntailmentQueryState.class);

	/**
	 * Maps axioms that were queried to the states of their query.
	 */
	private final Map<ElkAxiom, QueryState> queried_ = new ConcurrentHashMap<ElkAxiom, QueryState>();

	/**
	 * Contains axioms that were queried but not loaded.
	 */
	private final Queue<ElkAxiom> toLoad_ = new ConcurrentLinkedQueue<ElkAxiom>();

	/**
	 * Manages eviction from {@link #queried_}.
	 */
	private final Evictor<QueryState> queriedEvictor_;

	/**
	 * The axioms that were registered by the last call.
	 */
	private final Set<ElkAxiom> lastQueries_ = new ArrayHashSet<ElkAxiom>();

	/**
	 * State of the query of a particular axiom. There are two forbidden states:
	 *

	 *
when an axiom does not have a state, it must not be loaded,
	 *
when {@link #isLoaded} is {@code false} and {@link #indexed} is not
	 * {@code null},
	 *

	 *
	 * @author Peter Skocovsky
	 */
	private class QueryState extends AbstractProperEntailmentQueryResult {
		/**
		 * Whether the queried axiom was loaded (whether it was attempted to
		 * index it). If this is {@code false}, then {@link #indexed} must be
		 * {@code null}.
		 */
		boolean isLoaded = false;
		/**
		 * Results of indexing of the entailment query. If this is {@code null},
		 * then {@link #isComputed} must be {@code false}.
		 */
		IndexedEntailmentQuery<? extends Entailment> indexed = null;
		/**
		 * Whether the query is locked. While this is true and {@link #indexed}
		 * is not {@code null}, the query results must be available.
		 */
		private int lockedCount_ = 0;
		/**
		 * How many times an {@link Occurrence} occurs in the query.
		 * {@code null} means no occurrences.
		 */
		Map<Occurrence, Integer> occurrences = null;

		public QueryState(final ElkAxiom query) {
			super(query);
		}

		@Override
		public Entailment getEntailment() throws ElkQueryException {
			if (indexed == null) {
				throw new ElkQueryException(
						"Query was not indexed: " + getQuery());
			}
			// else
			return indexed.getQuery();
		}

		@Override
		public boolean isEntailed() throws ElkQueryException {
			if (indexed == null) {
				throw new ElkQueryException(
						"Query was not indexed: " + getQuery());
			}
			// else
			final boolean result = Proofs.isDerivable(getEvidence(true),
					indexed.getQuery());
			if (!result) {
				incompleteness_.log(incompleteness_
						.getIncompletenessMonitorForEntailmentQuery(
								getOccurrenceStore(this)));
			}
			// If the query is entailed, the result is complete.
			return result;
		}

		@SuppressWarnings("unchecked")
		@Override
		public Proof<EntailmentInference> getEvidence(final boolean onlyOne)
				throws ElkQueryException {
			if (indexed == null) {
				throw new ElkQueryException(
						"Query was not indexed: " + getQuery());
			}
			// else

			incompleteness_.log(
					incompleteness_.getIncompletenessMonitorForEntailmentQuery(
							getOccurrenceStore(this)));

			final Proof<EntailmentInference> inconsistencyEvidence = new InconsistencyProofWrapper(
					consistencyCheckingState_.getEvidence(onlyOne));

			if (consistencyCheckingState_.isInconsistent() && onlyOne) {
				return inconsistencyEvidence;
			}
			// else

			final Proof<EntailmentInference> entailmentEvidence = indexed
					.getEvidence(onlyOne, saturationState_, conclusionFactory_);

			return Proofs.union(inconsistencyEvidence, entailmentEvidence);
		}

		public synchronized boolean lock() {
			final boolean wasLocked = isLocked();
			lockedCount_++;
			return wasLocked != isLocked();
		}

		@Override
		public synchronized boolean isLocked() {
			return lockedCount_ > 0;
		}

		@Override
		public synchronized boolean unlock() {
			if (!isLocked()) {
				return false;
			}
			// else
			lockedCount_--;
			return !isLocked();
		}

	}

	private final SaturationState<? extends Context> saturationState_;

	private final ConsistencyCheckingState consistencyCheckingState_;

	private final SaturationConclusion.Factory conclusionFactory_;

	private final IncompletenessManager incompleteness_;

	public <C extends Context> EntailmentQueryState(
			final ReasonerConfiguration config,
			final SaturationState<C> saturationState,
			final ConsistencyCheckingState consistencyCheckingState,
			final SaturationConclusion.Factory factory,
			final IncompletenessManager incompletenessManager) {
		this.saturationState_ = saturationState;
		this.consistencyCheckingState_ = consistencyCheckingState;
		this.conclusionFactory_ = factory;
		this.incompleteness_ = incompletenessManager;
		final Object builder = config
				.getParameter(ReasonerConfiguration.ENTAILMENT_QUERY_EVICTOR);
		LOGGER_.info("{} = {}", ReasonerConfiguration.ENTAILMENT_QUERY_EVICTOR,
				builder);
		this.queriedEvictor_ = ((Evictor.Builder) builder).build();
	}

	/**
	 * Registers the supplied axioms for querying. If all necessary stages are
	 * run after calling this method for some axioms, neither
	 * {@link #isEntailed(Iterable)} nor a
	 * {@link org.semanticweb.elk.reasoner.query.ProperEntailmentQueryResult
	 * ProperEntailmentQueryResult} for any of these axioms will throw
	 * {@link ElkQueryException}.
	 *
	 * @param axioms
	 * @return {@code true} if this is a new query, {@code false} if this class
	 *         expression has already been registered.
	 */
	void registerQueries(final Iterable<? extends ElkAxiom> axioms) {

		lastQueries_.clear();
		for (final ElkAxiom axiom : axioms) {

			LOGGER_.trace("entailment query registered {}", axiom);

			lastQueries_.add(axiom);

			QueryState state = queried_.get(axiom);
			if (state != null) {
				queriedEvictor_.add(state);
				continue;
			}
			// Create query state.
			state = new QueryState(axiom);
			queried_.put(axiom, state);
			queriedEvictor_.add(state);
			toLoad_.offer(axiom);

		}

	}

	public IndexingListener getIndexingListener() {
		return indexingListener_;
	}

	@Override
	public EntailmentQueryLoader getQueryLoader(
			final InterruptMonitor interrupter) {
		return indexingListener_.new Loader(interrupter);
	}

	private final QueryIndexingListener indexingListener_ = new QueryIndexingListener();

	private class QueryIndexingListener implements IndexingListener {

		// Indexing state.
		private boolean isInsertion_;
		private QueryState beingIndexed_ = null;

		private void checkStateOnIndexing() throws IllegalStateException {
			if (beingIndexed_ == null) {
				throw new IllegalStateException(
						"Indexing listener notified while no query is being loaded!");
			}
		}

		private class Loader extends AbstractEntailmentQueryLoader {

			public Loader(final InterruptMonitor interrupter) {
				super(interrupter);
			}

			@Override
			public void load(
					final ElkAxiomVisitor<IndexedEntailmentQuery<? extends Entailment>> inserter,
					final ElkAxiomVisitor<IndexedEntailmentQuery<? extends Entailment>> deleter)
					throws ElkLoadingException {

				// First evict and unload.
				isInsertion_ = false;
				final Iterator<QueryState> evicted = queriedEvictor_
						.evict(doNotEvict_);
				while (evicted.hasNext()) {
					final QueryState state = evicted.next();
					queried_.remove(state.getQuery());
					if (state.isLoaded) {
						beingIndexed_ = state;
						state.getQuery().accept(deleter);
						state.indexed = null;
						state.isLoaded = false;
					}
				}

				/*
				 * Load all registered queries that are not loaded and assign
				 * state.indexed if successful.
				 */
				isInsertion_ = true;
				ElkAxiom axiom;
				while ((axiom = toLoad_.poll()) != null) {
					final QueryState state = queried_.get(axiom);
					if (state == null) {
						continue;
					}

					state.isLoaded = true;
					beingIndexed_ = state;
					state.indexed = axiom.accept(inserter);

					if (isInterrupted()) {
						return;
					}
				}

			}

			@Override
			public boolean isLoadingFinished() {
				return toLoad_.isEmpty();
			}

		}

		@Override
		public void onIndexing(final Occurrence occurrence) {
			checkStateOnIndexing();
			if (isInsertion_) {
				if (beingIndexed_.occurrences == null) {
					beingIndexed_.occurrences = new ArrayHashMap<Occurrence, Integer>(
							4);
				}
				Integer noOccurrences = beingIndexed_.occurrences
						.get(occurrence);
				if (noOccurrences == null) {
					noOccurrences = 0;
				}
				noOccurrences++;
				beingIndexed_.occurrences.put(occurrence, noOccurrences);
			} else {
				if (beingIndexed_.occurrences == null) {
					return;
				}
				Integer noOccurrences = beingIndexed_.occurrences
						.get(occurrence);
				if (noOccurrences == null) {
					return;
				}
				noOccurrences--;
				if (noOccurrences <= 0) {
					beingIndexed_.occurrences.remove(occurrence);
					if (beingIndexed_.occurrences.isEmpty()) {
						beingIndexed_.occurrences = null;
					}
				} else {
					beingIndexed_.occurrences.put(occurrence, noOccurrences);
				}
			}
		}

	}

	private final Predicate<QueryState> doNotEvict_ = new Predicate<QueryState>() {
		@Override
		public boolean apply(final QueryState state) {
			return state.isLocked() || lastQueries_.contains(state.getQuery());
		}
	};

	private final Condition<IndexedContextRoot> IS_NOT_SATURATED = new Condition<IndexedContextRoot>() {
		@Override
		public boolean holds(final IndexedContextRoot root) {
			final Context context = saturationState_.getContext(root);
			return context == null || !context.isInitialized()
					|| !context.isSaturated();
		}
	};

	private final Operations.Transformation<QueryState, Iterable<? extends IndexedContextRoot>> POSITIVELY_INDEXED = new Operations.Transformation<QueryState, Iterable<? extends IndexedContextRoot>>() {
		@Override
		public Iterable<? extends IndexedContextRoot> transform(
				final QueryState state) {
			if (state.indexed == null) {
				return null;
			}
			// else
			final Collection<? extends IndexedContextRoot> roots = state.indexed
					.getPositivelyIndexed();
			return Operations.filter(roots, IS_NOT_SATURATED);
		}
	};

	/**
	 * @return {@link IndexedContextRoot} that are needed to answer the
	 *         registered entailment queries and not saturated.
	 */
	Collection<IndexedContextRoot> getNotSaturatedPositivelyIndexedRoots() {
		int sizeUpper = 0;
		for (final QueryState state : queried_.values()) {
			if (state.indexed != null) {
				sizeUpper += state.indexed.getPositivelyIndexed().size();
			}
		}
		final Iterable<IndexedContextRoot> result = Operations
				.concat(Operations.map(queried_.values(), POSITIVELY_INDEXED));
		return Operations.getCollection(result, sizeUpper);
	}

	private OccurrenceStore getOccurrenceStore(final QueryState state) {
		return new OccurrenceStore() {

			@Override
			public Collection<? extends ElkObject> occursIn(
					final Occurrence occurrence) {
				if (state == null || state.occurrences == null
						|| !state.occurrences.containsKey(occurrence)) {
					return Collections.emptySet();
				}
				// else
				return Collections.singleton(state.getQuery());
			}

		};
	}

	/**
	 * Decides whether the supplied {@code axioms} are entailed. If some of the
	 * supplied axioms was not registered by {@link #registerQueries(Iterable)}.
	 *
	 * @param axioms
	 *            Entailment of what axioms is queried.
	 * @return A map from each queried axiom to the result of entailment query
	 *         for that axiom.
	 * @param axioms
	 * @return
	 * @throws ElkQueryException
	 *             When some of the axioms was not registered by
	 *             {@link #registerQueries(Iterable)}.
	 */
	Map<ElkAxiom, EntailmentQueryResult> isEntailed(
			final Iterable<? extends ElkAxiom> axioms)
			throws ElkQueryException {

		final Map<ElkAxiom, EntailmentQueryResult> results = new ArrayHashMap<ElkAxiom, EntailmentQueryResult>();

		for (final ElkAxiom axiom : axioms) {
			if (!EntailmentQueryConverter
					.isEntailmentCheckingSupported(axiom.getClass())) {
				results.put(axiom,
						new UnsupportedQueryTypeEntailmentQueryResultImpl(
								axiom));
				continue;
			}
			// else
			final QueryState state = queried_.get(axiom);
			if (state == null) {
				throw new ElkQueryException(
						"Query was not registered: " + axiom);
			}
			// else
			if (state.indexed == null) {
				results.put(axiom,
						new UnsupportedIndexingEntailmentQueryResultImpl(
								axiom));
				continue;
			}
			// else
			state.lock();
			results.put(axiom, state);
		}

		return results;
	}

}


/*
 * #%L
 * ELK Reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 - 2012 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.stages;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.semanticweb.elk.exceptions.ElkException;
import org.semanticweb.elk.reasoner.saturation.SaturationStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A common implementation of {@link ReasonerStage}s for a given reasoner.
 *
 * @author "Yevgeny Kazakov"
 * @author Peter Skocovsky
 */
abstract class AbstractReasonerStage implements ReasonerStage {

	// logger for this class
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(AbstractReasonerStage.class);

	final AbstractReasonerState reasoner;

	/**
	 * {@code true} if all information required for execution of this
	 * {@link ReasonerStage} has been initialized
	 */
	private boolean isInitialized_ = false;

	/**
	 * {@code true} if the stage does not require execution, i.e., if it is
	 * already executed
	 */
	private boolean isCompleted_ = false;

	/**
	 * the maximal number of concurrent workers used in this computation stage
	 */
	int workerNo;

	/**
	 * Stages that need to be executed before this stage
	 */
	private final Iterable<AbstractReasonerStage> preStages_;

	/**
	 * Stages that need to be executed after this stage
	 */
	private final List<AbstractReasonerStage> postStages_ = new LinkedList<AbstractReasonerStage>();

	/**
	 * Creates a new reasoner stage for a given reasoner.
	 *
	 * @param reasoner
	 *            the reasoner for which the reasoner stage is created
	 * @param preStages
	 *            the reasoner stages that should be executed directly before
	 *            this stage
	 */
	public AbstractReasonerStage(AbstractReasonerState reasoner,
			AbstractReasonerStage... preStages) {
		this.reasoner = reasoner;
		this.preStages_ = Arrays.asList(preStages);
		for (AbstractReasonerStage preStage : preStages)
			preStage.postStages_.add(this);
	}

	@Override
	public boolean isCompleted() {
		return isCompleted_;
	}

	@Override
	public Iterable<? extends ReasonerStage> getPreStages() {
		return preStages_;
	}

	/**
	 * Initialize the parameters of the computation for this stage; this is the
	 * first thing to be done before stage is executed
	 *
	 * @return {@code true} if the operation is successful
	 */
	@Override
	public boolean preExecute() {
		if (isInitialized_)
			return false;
		LOGGER_.trace("{}: initialized", this);
		this.workerNo = reasoner.getNumberOfWorkers();
		return isInitialized_ = true;
	}

	/**
	 * Clear the parameters of the computation for this stage; this is the last
	 * thing to be done when the stage is executed *
	 *
	 * @return {@code true} if the operation is successful
	 */
	@Override
	public boolean postExecute() {
		if (!isInitialized_)
			return false;
		LOGGER_.trace("{}: done", this);
		this.isCompleted_ = true;
		this.workerNo = 0;
		this.isInitialized_ = false;
		return true;
	}

	/**
	 * Execute the stage with initialized parameters
	 *
	 * @throws ElkException
	 */
	abstract void executeStage() throws ElkException;

	@Override
	public void execute() throws ElkException {
		LOGGER_.info("{} using {} workers", this, workerNo);
		reasoner.getProgressMonitor().start(getName());

		try {
			executeStage();
			checkInterrupt();
		} finally {
			reasoner.getProgressMonitor().finish();
		}
	}

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

	/**
	 * Marks this {@link AbstractReasonerStage} as not completed; this will
	 * require its execution next time unless {@link #setCompleted()} is called
	 *
	 * @return {@code true} if this stage was not invalidated and {@code false}
	 *         if this stage was already invalidated before the call
	 */
	boolean invalidate() {
		if (!isCompleted_ && !isInitialized_) {
			return false;
		}
		LOGGER_.trace("{}: invalidated", this);
		isCompleted_ = false;
		isInitialized_ = false;
		return true;
	}

	/**
	 * Invalidates this stage and all subsequent stages if not already done so
	 */
	public void invalidateRecursive() {
		Queue<AbstractReasonerStage> toInvalidate_ = new LinkedList<AbstractReasonerStage>();
		toInvalidate_.add(this);
		AbstractReasonerStage next;
		while ((next = toInvalidate_.poll()) != null) {
			if (next.invalidate()) {
				for (AbstractReasonerStage postStage : next.postStages_) {
					toInvalidate_.add(postStage);
				}
			}
		}
	}

	/**
	 * Marks this {@link AbstractReasonerStage} as completed; next time the
	 * stage will not be executed unless {@link #invalidate()} is called
	 *
	 * @return {@code true} if this stage was invalidated and {@code false} if
	 *         this stage was already not invalidated before the call
	 */
	boolean setCompleted() {
		if (isCompleted_)
			return false;
		LOGGER_.trace("{}: marked completed", this);
		isCompleted_ = true;
		return true;
	}

	protected void checkInterrupt() throws ElkInterruptedException {
		reasoner.getInterrupter().checkInterrupt();
	}

	protected void markAllContextsAsSaturated() {
		reasoner.saturationState.setContextsSaturated(Integer.MAX_VALUE);
	}

	protected SaturationStatistics getRuleAndConclusionStatistics() {
		return reasoner.ruleAndConclusionStats;
	}

	@Override
	public boolean isInterrupted() {
		return reasoner.getInterrupter().isInterrupted();
	}

}


/*
 * #%L
 * ELK Reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 - 2012 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package org.semanticweb.elk.reasoner.taxonomy.hashing;

import java.util.LinkedHashMap;
import java.util.Map;

import org.semanticweb.elk.owl.interfaces.ElkEntity;
import org.semanticweb.elk.reasoner.taxonomy.model.Node;
import org.semanticweb.elk.util.hashing.HashGenerator;
import org.semanticweb.elk.util.hashing.Hasher;

/**
 * Helper class for hashing Nodes based on getMembers(). Implements some caching
 * to ensure good performance even when some nodes have unusually many members.
 *
 * @author Markus Kroetzsch
 * @author Frantisek Simancik
 *
 */
public class NodeHasher implements Hasher<Node<? extends ElkEntity>> {

	/**
	 * We use one static instance for hashing (and caching!) all nodes.
	 */
	public static NodeHasher INSTANCE = new NodeHasher();

	private NodeHasher() {
	}

	/**
	 * IRI-based hasher used to cache members of a node.
	 */
	final Hasher<ElkEntity> elkEntityHasher = new Hasher<ElkEntity>() {

		@Override
		public int hash(ElkEntity elkEntity) {
			return elkEntity.getIri().hashCode();
		}
	};

	/**
	 * For nodes with at least this number of member classes, hash codes are
	 * cached in the hashCache.
	 */
	final int cacheNodeMemberNo = 50;

	/**
	 * Maximum number of large class nodes for which the hash value is cached.
	 */
	final int cacheMaxSize = 100;

	/**
	 * A simple Least Recently Used cache for the hashcodes of nodes that have a
	 * particularly large number of members. There should not be many such nodes
	 * in typical ontologies. Normally, this occurs if many classes are
	 * equivalent to owl:Nothing due to some modelling error.
	 */
	final LinkedHashMap<Node<? extends ElkEntity>, Integer> hashCache = new LinkedHashMap<Node<? extends ElkEntity>, Integer>() {
		private static final long serialVersionUID = 1;

		@Override
		protected boolean removeEldestEntry(
				Map.Entry<Node<? extends ElkEntity>, Integer> eldest) {
			return size() > cacheMaxSize;
		}
	};

	/**
	 * Compute the hash for an entity node. This method implements a simple
	 * cache for nodes with unusually large numbers of members. This mainly
	 * covers the case where a huge number of classes is equal to owl:Nothing
	 * due to modelling errors.
	 *
	 * @param node
	 * @return the has for the given entity node
	 */
	@Override
	public int hash(Node<? extends ElkEntity> node) {
		if (node.size() >= cacheNodeMemberNo) {
			if (hashCache.containsKey(node)) {
				return hashCache.get(node);
			}
			// else
			int hash = HashGenerator.combineMultisetHash(true, node,
					elkEntityHasher);
			hashCache.put(node, hash);
			return hash;
		}
		// else
		return HashGenerator.combineMultisetHash(true, node, elkEntityHasher);
	}
}