content: package at.ac.tuwien.kr.alpha.core.grounder;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  import at.ac.tuwien.kr.alpha.api.programs.atoms.Atom;
  import at.ac.tuwien.kr.alpha.api.programs.terms.ConstantTerm;
  import at.ac.tuwien.kr.alpha.api.programs.terms.FunctionTerm;
  import at.ac.tuwien.kr.alpha.api.programs.terms.IntervalTerm;
  import at.ac.tuwien.kr.alpha.api.programs.terms.Term;
  import at.ac.tuwien.kr.alpha.commons.programs.terms.Terms;
  import at.ac.tuwien.kr.alpha.commons.substitutions.Instance;
  
  /**
   * Helper functions to evaluate facts potentially containing intervals.
   * Copyright (c) 2017, the Alpha Team.
   */
  public class FactIntervalEvaluator {
  
  	/**
  	 * Helper to construct Instances from a fact that may contain intervals.
  	 *
  	 * @param fact the fact potentially containing intervals.
  	 * @return all instances stemming from unfolding the intervals.
  	 */
  	public static List<Instance> constructFactInstances(Atom fact) {
  		// Construct instance(s) from the fact.
  		int arity = fact.getPredicate().getArity();
  		Term[] currentTerms = new Term[arity];
  		boolean containsIntervals = false;
  		// Check if instance contains intervals at all.
  		for (int i = 0; i < arity; i++) {
  			Term term = fact.getTerms().get(i);
  			currentTerms[i] = term;
  			if (term instanceof IntervalTerm) {
  				containsIntervals = true;
  			} else if (term instanceof FunctionTerm && Terms.functionTermContainsIntervals((FunctionTerm) term)) {
  				containsIntervals = true;
  				throw new UnsupportedOperationException("Intervals inside function terms in facts are not supported yet. Try turning the fact into a rule.");
  			}
  		}
  		// If fact contains no intervals, simply return the single instance.
  		if (!containsIntervals) {
  			return Collections.singletonList(new Instance(currentTerms));
  		}
  		// Fact contains intervals, unroll them all.
  		return unrollInstances(currentTerms, 0);
  	}
  
  	@SuppressWarnings("unchecked")
  	private static List<Instance> unrollInstances(Term[] currentTerms, int currentPosition) {
  		if (currentPosition == currentTerms.length) {
  			return Collections.singletonList(new Instance(currentTerms));
  		}
  		Term currentTerm = currentTerms[currentPosition];
  		if (!(currentTerm instanceof IntervalTerm)) {
  			return unrollInstances(currentTerms, currentPosition + 1);
  		}
  
  		List<Instance> instances = new ArrayList<>();
  
  		Term lowerBoundTerm = ((IntervalTerm) currentTerm).getLowerBound();
  		Term upperBoundTerm = ((IntervalTerm) currentTerm).getUpperBound();
  		if (!(lowerBoundTerm instanceof ConstantTerm)) {
  			throw new IllegalArgumentException("Cannot unroll interval term with non-integer bound: " + lowerBoundTerm);
  		}
  		if (!(upperBoundTerm instanceof ConstantTerm)) {
  			throw new IllegalArgumentException("Cannot unroll interval term with non-integer bound: " + upperBoundTerm);
  		}
  
  		int lower = ((ConstantTerm<Integer>) lowerBoundTerm).getObject();
  		int upper = ((ConstantTerm<Integer>) upperBoundTerm).getObject();
  
  
  		for (int i = lower; i <= upper; i++) {
  			Term[] clonedTerms = currentTerms.clone();
  			clonedTerms[currentPosition] = Terms.newConstant(i);
  			instances.addAll(unrollInstances(clonedTerms, currentPosition + 1));
  		}
  		return instances;
  	}
  }
  