public interface Condition {

	/**
	 * Returns an object instance of a class implementing {@code Condition}, built from the specified arguments.
	 * 
	 * @param operator
	 *            a relational operator {@code TypeConditionOperatorRel}, a set operator
	 *            {@code TypeConditionOperatorSet} or a more general object {@code TypeConditionOperator}
	 * @param limit
	 *            an integer (object {@code Number}), a variable (object {@code IVar}), a range (object {@code Range})
	 *            or a 1-dimensional array of {@code int}
	 * @return an object instance of a class implementing {@code Condition}, built from the specified arguments
	 */
	public static Condition buildFrom(Object operator, Object limit) {
		if (limit instanceof XParameter)
			return new ConditionPar(operator, (XParameter) limit);
		if (operator instanceof TypeConditionOperatorRel) {
			TypeConditionOperatorRel op = (TypeConditionOperatorRel) operator;
			return limit instanceof Number ? new ConditionVal(op, ((Number) limit).longValue()) : new ConditionVar(op, (IVar) limit);
		} else if (operator instanceof TypeConditionOperatorSet) {
			TypeConditionOperatorSet op = (TypeConditionOperatorSet) operator;
			return limit instanceof Range ? new ConditionIntvl(op, ((Range) limit)) : new ConditionIntset(op, ((int[]) limit));
		} else {
			control(operator instanceof TypeConditionOperator, " Bad Argument");
			TypeConditionOperator op = (TypeConditionOperator) operator;
			if (limit instanceof Long)
				return new ConditionVal(op.toRel(), (Long) limit);
			if (limit instanceof IVar)
				return new ConditionVar(op.toRel(), (IVar) limit);
			if (limit instanceof XNodeLeaf && ((XNodeLeaf<?>) limit).type == TypeExpr.VAR)
				return new ConditionVar(op.toRel(), (IVar) ((XNodeLeaf<?>) limit).value);
			if (limit instanceof IntegerInterval)
				return new ConditionIntvl(op.toSet(), ((IntegerInterval) limit).inf, ((IntegerInterval) limit).sup);
			assert limit instanceof long[];
			return new ConditionIntset(op.toSet(), LongStream.of((long[]) limit).mapToInt(l -> Utilities.safeInt(l)).toArray());
		}
	}

	/**
	 * Returns the variable involved in the condition, if one is present, {@code null} otherwise.
	 * 
	 * @return the variable involved in the condition, if one is present, {@code null} otherwise
	 */
	default IVar involvedVar() {
		return null;
	}

	TypeExpr operatorTypeExpr();

	Object rightTerm();

	default int[] filtering(int[] values) {
		throw new AssertionError("should not be called for this kind of objects");
	}

	public class ConditionPar implements Condition {
		public Object operator;
		public XParameter par;

		public ConditionPar(Object operator, XParameter par) {
			this.operator = operator;
			this.par = par;
		}

		public Condition concretizeWith(Object limit) {
			return Condition.buildFrom(operator, limit);
		}

		@Override
		public TypeExpr operatorTypeExpr() {
			return operator instanceof TypeConditionOperatorRel ? ((TypeConditionOperatorRel) operator).toExpr()
					: ((TypeConditionOperatorSet) operator).toExpr();
		}

		@Override
		public Object rightTerm() {
			return par;
		}

	}

	/**
	 * Represents a condition based on a relational operator.
	 */
	public abstract class ConditionRel implements Condition {
		/**
		 * The relational operator on which the condition is based.
		 */
		public TypeConditionOperatorRel operator;

		/**
		 * Constructs a condition based on a relational operator.
		 * 
		 * @param operator
		 *            a relational operator
		 */
		public ConditionRel(TypeConditionOperatorRel operator) {
			this.operator = operator;
		}

		@Override
		public TypeExpr operatorTypeExpr() {
			return operator.toExpr();
		}
	}

	/**
	 * Represents a condition composed of a relational operator and a variable as operand.
	 */
	public static class ConditionVar extends ConditionRel {
		/**
		 * The variable that represents the operand of the condition.
		 */
		public IVar x;

		/**
		 * Constructs a condition composed of the specified relational operator and the specified variable as (right)
		 * operand
		 * 
		 * @param operator
		 *            a relational operator
		 * @param x
		 *            a variable
		 */
		public ConditionVar(TypeConditionOperatorRel operator, IVar x) {
			super(operator);
			this.x = x;
		}

		@Override
		public IVar involvedVar() {
			return x;
		}

		@Override
		public Object rightTerm() {
			return x;
		}

		@Override
		public String toString() {
			return "(" + operator.name().toLowerCase() + "," + x + ")";
		}
	}

	/**
	 * Represents a condition composed of a relational operator and a value (long integer) as (right) operand.
	 */
	public static class ConditionVal extends ConditionRel {
		/**
		 * The value that represents the operand of the condition.
		 */
		public long k;

		/**
		 * Constructs a condition composed of the specified relational operator and the specified value as (right)
		 * operand
		 * 
		 * @param operator
		 *            a relational operator
		 * @param k
		 *            an integer
		 */
		public ConditionVal(TypeConditionOperatorRel operator, long k) {
			super(operator);
			this.k = k;
		}

		@Override
		public Object rightTerm() {
			return k;
		}

		@Override
		public int[] filtering(int[] values) {
			return IntStream.of(values).filter(v -> {
				switch (operator) {
				case LT:
					return v < k;
				case LE:
					return v <= k;
				case GE:
					return v >= k;
				case GT:
					return v > k;
				case EQ:
					return v == k;
				case NE:
					return v != k;
				}
				throw new AssertionError();
			}).toArray();
		}

		@Override
		public String toString() {
			return "(" + operator.name().toLowerCase() + "," + k + ")";
		}
	}

	/**
	 * Represents a condition based on a set operator.
	 */
	public abstract class ConditionSet implements Condition {
		/**
		 * The set operator on which the condition is based.
		 */
		public TypeConditionOperatorSet operator;

		/**
		 * Constructs a condition based on a set operator.
		 * 
		 * @param operator
		 *            a set operator
		 */
		public ConditionSet(TypeConditionOperatorSet operator) {
			this.operator = operator;
		}

		@Override
		public TypeExpr operatorTypeExpr() {
			return operator.toExpr();
		}
	}

	/** The class denoting a condition where the operand is an interval. */

	/**
	 * Represents a condition composed of a set operator and an interval (defined by its two inclusive bounds) as
	 * (right) operand.
	 */
	public static class ConditionIntvl extends ConditionSet {
		/**
		 * The lower bound (inclusive) of the interval.
		 */
		public long min;

		/**
		 * The upper bound (inclusive) of the interval.
		 */
		public long max;

		/**
		 * Constructs a condition composed of a set operator and an interval (defined by its two inclusive bounds) as
		 * (right) operand
		 * 
		 * @param operator
		 *            a set operator
		 * @param min
		 *            the lower bound (inclusive) of the interval
		 * @param max
		 *            the upper bound (inclusive) of the interval
		 */
		public ConditionIntvl(TypeConditionOperatorSet operator, long min, long max) {
			super(operator);
			control(min <= max, "The specified bouds are not valid.");
			this.min = min;
			this.max = max;
		}

		/**
		 * Constructs a condition composed of a set operator and an interval defined by a range.
		 * 
		 * @param operator
		 *            a set operator
		 * @param range
		 *            a range denoting an interval
		 */
		public ConditionIntvl(TypeConditionOperatorSet operator, Range range) {
			this(operator, range.start, range.stop - 1);
			control(range.step == 1, "Specified ranges must have a step equal to 1");
		}

		public Range range() {
			return new Range(safeInt(min), safeInt(max) + 1);
		}

		@Override
		public Object rightTerm() {
			return range();
		}

		@Override
		public int[] filtering(int[] values) {
			return IntStream.of(values).filter(v -> {
				switch (operator) {
				case IN:
					return min <= v && v <= max;
				case NOTIN:
					return v < min || v > max;
				}
				throw new AssertionError();
			}).toArray();
		}

		@Override
		public String toString() {
			return "(" + operator.name().toLowerCase() + "," + min + ".." + max + ")";
		}

	}

	/**
	 * Represents a condition composed of a set operator and an array of values (int) as (right) operand.
	 */
	public static class ConditionIntset extends ConditionSet {
		/**
		 * The array of values, used as (right) operand.
		 */
		public int[] t;

		/**
		 * Constructs a condition composed of a set operator and an array of values (int) as (right) operand.
		 * 
		 * @param operator
		 *            a set operator
		 * @param t
		 *            the array of values, used as (right) operand
		 */
		public ConditionIntset(TypeConditionOperatorSet operator, int[] t) {
			super(operator);
			t = IntStream.of(t).sorted().distinct().toArray();
			control(t.length > 0, "The specified array is empty (and so, not valid).");
			this.t = t;
		}

		@Override
		public Object rightTerm() {
			return t;
		}

		@Override
		public int[] filtering(int[] values) {
			return IntStream.of(values).filter(v -> {
				switch (operator) {
				case IN:
					return Arrays.binarySearch(t, v) >= 0;
				case NOTIN:
					return Arrays.binarySearch(t, v) < 0;
				}
				throw new AssertionError();
			}).toArray();
		}

		@Override
		public String toString() {
			return "(" + operator.name().toLowerCase() + ",{" + join(t, ",") + "})";
		}
	}
} public class Range implements Iterable<Integer> {

	/**
	 * The lower bound (inclusive) of this range.
	 */
	public final int start;

	/**
	 * The upper bound (exclusive) of this range.
	 */
	public final int stop;

	/**
	 * The step of this range (difference between each two successive numbers in this range).
	 */
	public final int step;

	/**
	 * Returns an iterator over all integers in this range.
	 */
	@Override
	public Iterator<Integer> iterator() {
		return new Iterator<Integer>() {
			int cursor = start;

			@Override
			public boolean hasNext() {
				return cursor < stop;
			}

			@Override
			public Integer next() {
				assert hasNext();
				int v = cursor;
				cursor += step;
				return v;
			}
		};
	}

	@Override
	public String toString() {
		return "[" + start + ".." + stop + "[" + (step == 1 ? "" : "(" + step + ")");
	}

	/**
	 * Constructs an object {@code Range} from the specified bounds and step.
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of this range
	 * @param endExclusive
	 *            the upper bound (exclusive) of this range
	 * @param step
	 *            the step of this range
	 */
	public Range(int startInclusive, int endExclusive, int step) {
		this.start = startInclusive;
		this.stop = endExclusive;
		this.step = step;
		Utilities.control(step > 0, "Bad values of step : " + step);
	}

	/**
	 * Constructs an object {@code Range} from the specified bounds (using implicitly a step equal to 1).
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of this rangerange.startInclusive
	 * @param endExclusive
	 *            the upper bound (exclusive) of this range
	 */
	public Range(int startInclusive, int endExclusive) {
		this(startInclusive, endExclusive, 1);
	}

	/**
	 * Constructs an object {@code Range} from the specified length (using implicitly a lower bound equal to 0 and a step equal to 1).
	 * 
	 * @param length
	 *            the length of this range
	 */
	public Range(int length) {
		this(0, length, 1);
	}

	public Range(IntegerInterval ii) {
		this(Utilities.safeInt(ii.inf), Utilities.safeInt(ii.sup + 1));
	}

	/**
	 * Returns a double range obtained by combining this range with a range built from the specified bounds and step.
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of the second range to be built
	 * @param endExclusive
	 *            the upper bound (exclusive) of the second range to be built
	 * @param step
	 *            the step of the second range to be built
	 * @return a double range obtained by combining this range with a range built from the specified bounds and step
	 */
	public Rangesx2 range(int startInclusive, int endExclusive, int step) {
		return new Rangesx2(this, new Range(startInclusive, endExclusive, step));
	}

	/**
	 * Returns a double range obtained by combining this range with a range built from the specified bounds and step.
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of the second range to be built
	 * @param endInclusive
	 *            the upper bound (inclusive) of the second range to be built
	 * @param step
	 *            the step of the second range to be built
	 * @return a double range obtained by combining this range with a range built from the specified bounds and step
	 */
	public Rangesx2 rangeClosed(int startInclusive, int endInclusive, int step) {
		return range(startInclusive, endInclusive + 1, step);
	}

	/**
	 * Returns a double range obtained by combining this range with a range built from the specified bounds (using implicitly a step equal to 1).
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of the second range to be built
	 * @param endExclusive
	 *            the upper bound (exclusive) of the second range to be built
	 * @return a double range obtained by combining this range with a range built from the specified bounds
	 */
	public Rangesx2 range(int startInclusive, int endExclusive) {
		return new Rangesx2(this, new Range(startInclusive, endExclusive));
	}

	/**
	 * Returns a double range obtained by combining this range with a range built from the specified bounds (using implicitly a step equal to 1).
	 * 
	 * @param startInclusive
	 *            the lower bound (inclusive) of the second range to be built
	 * @param endInclusive
	 *            the upper bound (inclusive) of the second range to be built
	 * @return a double range obtained by combining this range with a range built from the specified bounds
	 */
	public Rangesx2 rangeClosed(int startInclusive, int endInclusive) {
		return range(startInclusive, endInclusive + 1);
	}

	/**
	 * Returns a double range obtained by combining this range with a range built from the specified length (using implicitly a lower bound equal to 0
	 * and a step equal to 1).
	 * 
	 * @param length
	 *            the length of the second range
	 * @return a double range by combining this range with a range built from the specified length
	 */
	public Rangesx2 range(int length) {
		return new Rangesx2(this, new Range(length));
	}

	/**
	 * Returns {@code true} iff this range is basic, i.e., starts at 0 and admits a step equal to 1
	 * 
	 * @return {@code true} iff this range is basic,
	 */
	public boolean isBasic() {
		return start == 0 && step == 1;
	}

	/**
	 * Returns {@code true} iff this range contains the specified value
	 * 
	 * @param i
	 *            an integer
	 * @return {@code true} iff this range contains the specified value
	 */
	public boolean contains(int i) {
		return start <= i && i < stop && ((i - start) % step == 0);
	}

	/**
	 * Returns the length (number of integers) in this range.
	 * 
	 * @return the length (number of integers) in this range
	 */
	public int length() {
		return (stop - start) / step;
	}

	public Range neg() {
		Utilities.control(this.step == 1, "currently, the step must be equal to 1");
		return new Range(-this.stop + 1, -this.start + 1);
	}

	public Range abs() {
		Utilities.control(this.step == 1, "currently, the step must be equal to 1");
		return new Range(this.start <= 0 && 0 < this.stop ? 0 : Math.min(Math.abs(this.start), Math.abs(this.stop - 1)),
				Math.max(Math.abs(this.start), Math.abs(this.stop - 1)) + 1);
	}

	public Range add(Range r) {
		Utilities.control(this.step == 1 && r.step == 1, "currently, steps must be equal to 1");
		return new Range(this.start + r.start, this.stop + r.stop - 1);
	}

	// /** Start Experimental **/
	//
	// private ProblemIMP imp;
	//
	// public Range setImp(ProblemIMP imp) {
	// this.imp = imp;
	// return this;
	// }
	//
	// public CtrArray forall(IntConsumer c) {
	// return imp.manageLoop(() -> this.execute(c));
	// }
	//
	// /** End Experimental **/

	/**
	 * Builds and returns a 1-dimensional array of integers, obtained by selecting from this range any integer that satisfies the specified predicate.
	 * 
	 * @param p
	 *            a predicate allowing us to test if a value in this range must be selected
	 * @return a 1-dimensional array of integers (possibly, of length 0)
	 */
	public int[] select(Intx1Predicate p) {
		List<Integer> list = new ArrayList<>();
		for (int i : this)
			if (p.test(i))
				list.add(i);
		return list.stream().mapToInt(i -> i).toArray();
	}

	private <T> List<T> mapToObj(IntFunction<T> op, List<T> list) {
		for (int i : this) {
			T x = op.apply(i);
			if (x != null)
				list.add(x);
		}
		return list;
	}

	/**
	 * Returns a 1-dimensional array of objects (from class T), obtained after collecting the objects returned by the specified function when executed
	 * on all values in this range. Note that {@code null} values are simply discarded, if ever generated. Be careful: in case, no variable is
	 * obtained, {@code null} is returned.
	 * 
	 * @param f
	 *            a function to convert integer values into objects of class T
	 * @return a non-empty 1-dimensional array of objects or {@code null}
	 */
	public <T> T[] mapToObj(IntFunction<T> f) {
		return Utilities.convert(mapToObj(f, new ArrayList<>()));
	}

	@Deprecated
	/**
	 * Use Methods {@code variablesFrom} in {@code ProblemAPi} instead.
	 */
	public <T extends IVar> T[] provideVars(IntFunction<T> f) {
		return Utilities.convert(mapToObj(f, new ArrayList<>()));
	}

	@Deprecated
	/**
	 * Use Methods {@code varluesFrom} in {@code ProblemAPi} instead.
	 */
	public int[] provideVals(IntFunction<Integer> f) {
		List<Integer> list = new ArrayList<>();
		for (int i : this) {
			Integer v = f.apply(i);
			if (v != null)
				list.add(v);
		}
		return list.stream().mapToInt(i -> i).toArray();
	}

	@Deprecated
	/**
	 * Use Methods {@code addFrom} in {@code Table} instead.
	 */
	public int[][] provideTuples(IntFunction<int[]> f) {
		List<int[]> list = new ArrayList<>();
		for (int i : this) {
			int[] v = f.apply(i);
			if (v != null)
				list.add(v);
		}
		return list.stream().toArray(int[][]::new);
	}

	/**
	 * Returns a 1-dimensional array of integers, obtained after mapping every integer in this range in a value given by the specified unary operator.
	 * 
	 * @param op
	 *            a unary operator that converts an {@code int} into another {@code int}
	 * @return a 1-dimensional array of integers
	 */
	public int[] map(IntUnaryOperator op) {
		// return IntStream.iterate(startInclusive, n -> n + step).takeWhile(n -> n <= endInclusive).toArray(); // WAIT FOR JDK9
		List<Integer> list = new ArrayList<>();
		for (int i : this)
			list.add(op.applyAsInt(i));
		return list.stream().mapToInt(i -> i).toArray();
	}

	/**
	 * Executes the specified consumer on each integer value contained in this range.
	 * 
	 * @param c
	 *            an integer value consumer
	 */
	public void execute(IntConsumer c) {
		for (int i : this)
			c.accept(i);
	}

	/**
	 * Returns a 1-dimensional array containing all integers in this range.
	 * 
	 * @return a 1-dimensional array containing all integers in this range
	 */
	public int[] toArray() {
		return map(i -> i);
	}

	/**
	 * Converts this range into a stream.
	 * 
	 * @return the stream corresponding to this range
	 */
	public IntStream stream() {
		return IntStream.of(toArray());
	}

	/**
	 * The abstract root class of multiple ranges.
	 */
	private static abstract class Ranges {
		/**
		 * The sequence (array) of simple ranges, that when combined form this multiple range.
		 */
		public final Range[] items;

		protected Ranges(Range[] items) {
			this.items = items;
		}

		/**
		 * Returns {@code true} iff this multiple range contains the specified tuple
		 * 
		 * @param tupleRange
		 *            a tuple of integer values
		 * @return {@code true} iff this multiple range contains the specified tuple
		 */
		public boolean contains(int... tuple) {
			Utilities.control(tuple.length == items.length, "bad number of indexes");
			return IntStream.range(0, tuple.length).allMatch(i -> items[i].contains(tuple[i]));
		}
	}

	/**
	 * A class denoting a double range.
	 */
	public static class Rangesx2 extends Ranges {

		private Rangesx2(Range range1, Range range2) {
			super(new Range[] { range1, range2 });
		}

		/**
		 * Returns a triple range obtained by combining this double range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the third range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the third range to be built
		 * @param step
		 *            the step of the third range to be built
		 * @return a triple range obtained by combining this double range with a range built from the specified bounds and step
		 */
		public Rangesx3 range(int startInclusive, int endExclusive, int step) {
			return new Rangesx3(items[0], items[1], new Range(startInclusive, endExclusive, step));
		}

		/**
		 * Returns a triple range obtained by combining this double range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the third range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the third range to be built
		 * @param step
		 *            the step of the third range to be built
		 * @return a triple range obtained by combining this double range with a range built from the specified bounds and step
		 */
		public Rangesx3 rangeClosed(int startInclusive, int endInclusive, int step) {
			return range(startInclusive, endInclusive + 1, step);
		}

		/**
		 * Returns a triple range obtained by combining this double range with a range built from the specified bounds (using implicitly a step equal
		 * to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the third range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the third range to be built
		 * @return a triple range obtained by combining this double range with a range built from the specified bounds
		 */
		public Rangesx3 range(int startInclusive, int endExclusive) {
			return new Rangesx3(items[0], items[1], new Range(startInclusive, endExclusive));
		}

		/**
		 * Returns a triple range obtained by combining this double range with a range built from the specified bounds (using implicitly a step equal
		 * to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the third range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the third range to be built
		 * @return a triple range obtained by combining this double range with a range built from the specified bounds
		 */
		public Rangesx3 rangeClosed(int startInclusive, int endInclusive) {
			return range(startInclusive, endInclusive + 1);
		}

		/**
		 * Returns a triple range obtained by combining this double range with a range built from the specified length (using implicitly a lower bound
		 * equal to 0 and a step equal to 1).
		 * 
		 * @param length
		 *            the length of the third range
		 * @return a triple range obtained by combining this double range with a range built from the specified length
		 */
		public Rangesx3 range(int length) {
			return new Rangesx3(items[0], items[1], new Range(length));
		}

		/**
		 * Builds and returns a 2-dimensional array of integers, obtained by selecting from this double range any pair that satisfies the specified
		 * predicate.
		 * 
		 * @param p
		 *            a predicate allowing us to test if a pair of values in this double range must be selected
		 * @return a 2-dimensional array of integers (possibly, of length 0)
		 */
		public int[][] select(Intx2Predicate p) {
			List<int[]> list = new ArrayList<>();
			for (int i : items[0])
				for (int j : items[1])
					if (p.test(i, j))
						list.add(new int[] { i, j });
			return list.stream().toArray(int[][]::new);
		}

		/**
		 * Returns a 2-dimensional array of integers, obtained after mapping every pair of values from this double range in a value given by the
		 * specified binary operator. If {@code v1} is the ith value in the first range, and {@code v2} is the jth value in the second range, the
		 * value {@code op(v1,v2)} is put in the 2-dimensional array at index {@code (i,j)}. Do note that the number of rows of the built array is
		 * given by the length of the first range and the number of columns is given by the length of the second range.
		 * 
		 * @param op
		 *            a binary operator that converts a pair of integers into another integer
		 * @return a 2-dimensional array of integers
		 */
		public int[][] map(IntBinaryOperator op) {
			List<int[]> list = new ArrayList<>();
			for (int i : items[0])
				list.add(items[1].map(j -> op.applyAsInt(i, j)));
			return list.stream().toArray(int[][]::new);
		}

		/**
		 * Executes the specified consumer on each pair of values contained in this double range.
		 * 
		 * @param c2
		 *            an object consuming pairs of integers.
		 */
		public void execute(Intx2Consumer c2) {
			for (int i : items[0])
				items[1].execute(j -> c2.accept(i, j));
		}

		/**
		 * Returns a 2-dimensional array containing all pairs of integers in this double range.
		 * 
		 * @return a 2-dimensional array containing all pairs of integers in this double range
		 */
		public int[][] toArray() {
			List<int[]> list = new ArrayList<>();
			for (int i : items[0])
				for (int j : items[1])
					list.add(new int[] { i, j });
			return list.stream().toArray(int[][]::new);
		}

		/**
		 * Converts this double range into a stream.
		 * 
		 * @return the stream corresponding to this double range
		 */
		public Stream<int[]> stream() {
			return Stream.of(toArray());
		}
	}

	/**
	 * A class denoting a triple range.
	 */
	public static class Rangesx3 extends Ranges {
		private Rangesx3(Range range1, Range range2, Range range3) {
			super(new Range[] { range1, range2, range3 });
		}

		/**
		 * Returns a quadruple range obtained by combining this triple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fourth range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the fourth range to be built
		 * @param step
		 *            the step of the fourth range to be built
		 * @return a quadruple range obtained by combining this triple range with a range built from the specified bounds and step
		 */
		public Rangesx4 range(int startInclusive, int endExclusive, int step) {
			return new Rangesx4(items[0], items[1], items[2], new Range(startInclusive, endExclusive, step));
		}

		/**
		 * Returns a quadruple range obtained by combining this triple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fourth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the fourth range to be built
		 * @param step
		 *            the step of the fourth range to be built
		 * @return a quadruple range obtained by combining this triple range with a range built from the specified bounds and step
		 */
		public Rangesx4 rangeClosed(int startInclusive, int endInclusive, int step) {
			return range(startInclusive, endInclusive + 1, step);
		}

		/**
		 * Returns a quadruple range obtained by combining this triple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fourth range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the fourth range to be built
		 * @return a quadruple range obtained by combining this triple range with a range built from the specified bounds
		 */
		public Rangesx4 range(int startInclusive, int endExclusive) {
			return new Rangesx4(items[0], items[1], items[2], new Range(startInclusive, endExclusive));
		}

		/**
		 * Returns a quadruple range obtained by combining this triple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fourth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the fourth range to be built
		 * @return a quadruple range obtained by combining this triple range with a range built from the specified bounds
		 */
		public Rangesx4 rangeClosed(int startInclusive, int endInclusive) {
			return range(startInclusive, endInclusive + 1);
		}

		/**
		 * Returns a quadruple range obtained by combining this triple range with a range built from the specified length (using implicitly a lower
		 * bound equal to 0 and a step equal to 1).
		 * 
		 * @param length
		 *            the length of the fourth range
		 * @return a quadruple range obtained by combining this triple range with a range built from the specified length
		 */
		public Rangesx4 range(int length) {
			return new Rangesx4(items[0], items[1], items[2], new Range(length));
		}

		// private <T> List<T> provideVars(Intx3Function<T> f, List<T> list) {
		// for (int i : items[0])
		// new Rangesx2(items[1], items[2]).provideObjects((j, k) -> f.apply(i, j, k), list);
		// return list;
		// }

		/**
		 * Executes the specified consumer on each triple contained in this range.
		 * 
		 * @param c3
		 *            an object consuming triples of integers.
		 */
		public void execute(Intx3Consumer c3) {
			for (int i : items[0])
				new Rangesx2(items[1], items[2]).execute((j, k) -> c3.accept(i, j, k));
		}

	}

	/**
	 * A class denoting a quadruple range.
	 */
	public static class Rangesx4 extends Ranges {
		private Rangesx4(Range range1, Range range2, Range range3, Range range4) {
			super(new Range[] { range1, range2, range3, range4 });
		}

		/**
		 * Returns a quintuple range obtained by combining this quadruple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fifth range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the fifth range to be built
		 * @param step
		 *            the step of the fifth range to be built
		 * @return a quintuple range obtained by combining this quadruple range with a range built from the specified bounds and step
		 */
		public Rangesx5 range(int startInclusive, int endExclusive, int step) {
			return new Rangesx5(items[0], items[1], items[2], items[3], new Range(startInclusive, endExclusive, step));
		}

		/**
		 * Returns a quintuple range obtained by combining this quadruple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fifth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the fifth range to be built
		 * @param step
		 *            the step of the fifth range to be built
		 * @return a quintuple range obtained by combining this quadruple range with a range built from the specified bounds and step
		 */
		public Rangesx5 rangeClosed(int startInclusive, int endInclusive, int step) {
			return range(startInclusive, endInclusive + 1, step);
		}

		/**
		 * Returns a quintuple range obtained by combining this quadruple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (exclusive) of the fifth range to be built
		 * @param endExclusive
		 *            the upper bound (inclusive) of the fifth range to be built
		 * @return a quintuple range obtained by combining this quadruple range with a range built from the specified bounds
		 */
		public Rangesx5 range(int startInclusive, int endExclusive) {
			return new Rangesx5(items[0], items[1], items[2], items[3], new Range(startInclusive, endExclusive));
		}

		/**
		 * Returns a quintuple range obtained by combining this quadruple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the fifth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the fifth range to be built
		 * @return a quintuple range obtained by combining this quadruple range with a range built from the specified bounds
		 */
		public Rangesx5 rangeClosed(int startInclusive, int endInclusive) {
			return range(startInclusive, endInclusive + 1);
		}

		/**
		 * Returns a quintuple range obtained by combining this quadruple range with a range built from the specified length (using implicitly a lower
		 * bound equal to 0 and a step equal to 1).
		 * 
		 * @param length
		 *            the length of the fifth range
		 * @return a quintuple range obtained by combining this quadruple range with a range built from the specified length
		 */
		public Rangesx5 range(int length) {
			return new Rangesx5(items[0], items[1], items[2], items[3], new Range(length));
		}

		/**
		 * Executes the specified consumer for each quadruple of integers contained in this range.
		 * 
		 * @param c4
		 *            an object consuming quadruples of integers.
		 */
		public void execute(Intx4Consumer c4) {
			for (int i : items[0])
				new Rangesx3(items[1], items[2], items[3]).execute((j, k, l) -> c4.accept(i, j, k, l));
		}

	}

	/**
	 * A class denoting a quintuple range.
	 */
	public static class Rangesx5 extends Ranges {
		private Rangesx5(Range range1, Range range2, Range range3, Range range4, Range range5) {
			super(new Range[] { range1, range2, range3, range4, range5 });
		}

		/**
		 * Returns a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the sixth range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the sixth range to be built
		 * @param step
		 *            the step of the sixth range to be built
		 * @return a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds and step
		 */
		public Rangesx6 range(int startInclusive, int endExclusive, int step) {
			return new Rangesx6(items[0], items[1], items[2], items[3], items[4], new Range(startInclusive, endExclusive, step));
		}

		/**
		 * Returns a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds and step.
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the sixth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the sixth range to be built
		 * @param step
		 *            the step of the sixth range to be built
		 * @return a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds and step
		 */
		public Rangesx6 rangeClosed(int startInclusive, int endInclusive, int step) {
			return range(startInclusive, endInclusive + 1, step);
		}

		/**
		 * Returns a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the sixth range to be built
		 * @param endExclusive
		 *            the upper bound (exclusive) of the sixth range to be built
		 * @return a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds
		 */
		public Rangesx6 range(int startInclusive, int endExclusive) {
			return new Rangesx6(items[0], items[1], items[2], items[3], items[4], new Range(startInclusive, endExclusive));
		}

		/**
		 * Returns a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds (using implicitly a step
		 * equal to 1).
		 * 
		 * @param startInclusive
		 *            the lower bound (inclusive) of the sixth range to be built
		 * @param endInclusive
		 *            the upper bound (inclusive) of the sixth range to be built
		 * @return a sixtuple range obtained by combining this quintuple range with a range built from the specified bounds
		 */
		public Rangesx6 rangeClosed(int startInclusive, int endInclusive) {
			return range(startInclusive, endInclusive + 1);
		}

		/**
		 * Returns a sixtuple range obtained by combining this quintuple range with a range built from the specified length (using implicitly a lower
		 * bound equal to 0 and a step equal to 1).
		 * 
		 * @param length
		 *            the length of the sixth range
		 * @return a sixtuple range obtained by combining this qintuple range with a range built from the specified length
		 */
		public Rangesx6 range(int length) {
			return new Rangesx6(items[0], items[1], items[2], items[3], items[4], new Range(length));
		}

		/**
		 * Executes the specified consumer on each quintuple of integers contained in this range.
		 * 
		 * @param c5
		 *            an object consuming quintuples of integers.
		 */
		public void execute(Intx5Consumer c5) {
			for (int i : items[0])
				new Rangesx4(items[1], items[2], items[3], items[4]).execute((j, k, l, m) -> c5.accept(i, j, k, l, m));
		}
	}

	/**
	 * A class denoting a sixtuple range.
	 */
	public static class Rangesx6 extends Ranges {
		private Rangesx6(Range range1, Range range2, Range range3, Range range4, Range range5, Range range6) {
			super(new Range[] { range1, range2, range3, range4, range5, range6 });
		}

		/**
		 * Executes the specified consumer on each sixtuple of integers contained in this range.
		 * 
		 * @param c6
		 *            an object consuming sixtuples of integers.
		 */
		public void execute(Intx6Consumer c6) {
			for (int i : items[0])
				new Rangesx5(items[1], items[2], items[3], items[4], items[5]).execute((j, k, l, m, n) -> c6.accept(i, j, k, l, m, n));
		}
	}
} public static enum TypeExpr {
	NEG(1),
	ABS(1),
	SQR(1),
	ADD(2, Integer.MAX_VALUE),
	SUB(2),
	MUL(2, Integer.MAX_VALUE),
	DIV(2),
	MOD(2),
	POW(2),
	DIST(2),
	MIN(2, Integer.MAX_VALUE),
	MAX(2, Integer.MAX_VALUE),
	LT(2),
	LE(2),
	GE(2),
	GT(2),
	NE(2, Integer.MAX_VALUE),
	EQ(2, Integer.MAX_VALUE),
	SET(0, Integer.MAX_VALUE),
	IN(2),
	NOTIN(2),
	NOT(1),
	AND(2, Integer.MAX_VALUE),
	OR(2, Integer.MAX_VALUE),
	XOR(2, Integer.MAX_VALUE),
	IFF(2, Integer.MAX_VALUE),
	IMP(2),
	IF(3),
	CARD(1),
	UNION(2, Integer.MAX_VALUE),
	INTER(2, Integer.MAX_VALUE),
	DIFF(2),
	SDIFF(2, Integer.MAX_VALUE),
	HULL(1),
	DJOINT(2),
	SUBSET(2),
	SUBSEQ(2),
	SUPSEQ(2),
	SUPSET(2),
	CONVEX(1),
	FDIV(2),
	FMOD(2),
	SQRT(1),
	NROOT(2),
	EXP(1),
	LN(1),
	LOG(2),
	SIN(1),
	COS(1),
	TAN(1),
	ASIN(1),
	ACOS(1),
	ATAN(1),
	SINH(1),
	COSH(1),
	TANH(1),
	VAR(0),
	PAR(0),
	LONG(0),
	RATIONAL(0),
	DECIMAL(0),
	SYMBOL(0),
	SPECIAL(0, Integer.MAX_VALUE); // SPECIAL is a virtual type, useful when managing abstract trees

	/**
	 * The name of this type in lower-case.
	 */
	public final String lcname;

	/**
	 * The minimum arity (number of sons) for a node of this type.
	 */
	public final int arityMin;

	/**
	 * The maximum arity (number of sons) for a node of this type.
	 */
	public final int arityMax;

	/** Builds a constant, while specifying its minimum and maximum arity (number of sons). */
	private TypeExpr(int arityMin, int arityMax) {
		this.arityMin = arityMin;
		this.arityMax = arityMax;
		this.lcname = name().toLowerCase();
	}

	/** Builds a constant, while specifying its arity (number of sons). */
	private TypeExpr(int arity) {
		this(arity, arity);
	}

	/**
	 * Returns {@code true} iff this type is one of the specified types.
	 * 
	 * @param types
	 *            a sequence of types
	 * @return {@code true} iff this type is one of the specified types
	 */
	public boolean oneOf(TypeExpr... types) {
		return Stream.of(types).anyMatch(t -> t == this);
	}

	/**
	 * Returns {@code true} iff this type is not one of the specified types.
	 * 
	 * @param types
	 *            a sequence of types
	 * @return {@code true} iff this type is not one of the specified types
	 */
	public boolean notOneOf(TypeExpr... types) {
		return Stream.of(types).noneMatch(t -> t == this);
	}

	/**
	 * Returns {@code true} iff this type denotes an operator that is commutative (and also associative when it is a
	 * non-binary operator).
	 * 
	 * @return {@code true} iff this type denotes an operator that is symmetric
	 */
	public boolean isSymmetricOperator() {
		return oneOf(ADD, MUL, MIN, MAX, DIST, NE, EQ, SET, AND, OR, XOR, IFF, UNION, INTER, DJOINT);
	}

	/**
	 * Returns {@code true} iff this type denotes a binary non-symmetric relational operator (i.e.,
	 * {@code LT, LE, GE and GT}).
	 * 
	 * @return {@code true} iff this type denotes a binary non-symmetric relational
	 */
	public boolean isUnsymmetricRelationalOperator() {
		return oneOf(LT, LE, GE, GT);
	}

	/**
	 * Returns {@code true} iff this type denotes a relational operator (i.e., {@code LT, LE, GE, GT, EQ and NE}).
	 * 
	 * @return {@code true} iff this type denotes a relational operator
	 */
	public boolean isRelationalOperator() {
		return isUnsymmetricRelationalOperator() || oneOf(NE, EQ);
	}

	/**
	 * Returns {@code true} iff this type denotes a unary arithmetic or logic operator (i.e.,
	 * {@code ABS, NEG, SQR, and NOT}).
	 * 
	 * @return {@code true} iff this type denotes a unary arithmetic or logic operator
	 */
	public boolean isUnaryArithmeticOrLogicOperator() {
		return oneOf(ABS, NEG, SQR, NOT);
	}

	/**
	 * Returns {@code true} iff this type denotes a (non-unary) arithmetic operator (i.e.,
	 * {@code ADD, SUB, MUL, DIV, MOD, POW and DIST}).
	 * 
	 * @return {@code true} iff this type denotes a (non-unary) arithmetic operator
	 */
	public boolean isArithmeticOperator() {
		return oneOf(ADD, SUB, MUL, DIV, MOD, POW, DIST);
	}

	/**
	 * Returns {@code true} iff this type denotes a (non-unary) logical operator (i.e.,
	 * {@code AND, OR, XOR, IFF, and IMP}).
	 * 
	 * @return {@code true} iff this type denotes a (non-unary) logical operator
	 */
	public boolean isLogicalOperator() {
		return oneOf(AND, OR, XOR, IFF, IMP);
	}

	/**
	 * Returns {@code true} iff this type denotes an operator that evaluates to 0/1 (false/true).
	 * 
	 * @return {@code true} iff this type denotes an operator that evaluates to 0/1 (false/true)
	 */
	public boolean isPredicateOperator() {
		return isRelationalOperator() || isLogicalOperator() || oneOf(NOT, IN, NOTIN);
	}

	/**
	 * Returns {@code true} iff this type denotes an operator that behaves like identity if only one operand was
	 * given.
	 * 
	 * @return @code true} iff this type denotes an operator that behaves like identity for only one operand
	 */
	public boolean isIdentityWhenOneOperand() {
		return oneOf(ADD, MUL, MIN, MAX, EQ, AND, OR, XOR, IFF);
	}

	/**
	 * Returns the type denoting the arithmetic inversion of this type, if this type denotes a relational operator,
	 * {@code null} otherwise. The arithmetic inversion is not obtained by applying a logical negation but a
	 * multiplication by -1. For example, the arithmetic inversion of {@code LT} is {@code GT} (and not {@code GE}).
	 * Also, the arithmetic inversion of {@code EQ} is {@code EQ}.
	 * 
	 * @return the type denoting the arithmetic inversion of this type, if this type denotes a relational operator,
	 *         {@code null} otherwise
	 */
	public TypeExpr arithmeticInversion() {
		return this == LT ? GT : this == LE ? GE : this == GE ? LE : this == GT ? LT : this == NE ? NE : this == EQ ? EQ : null;
	}

	/**
	 * Returns the type denoting the logical inversion of this type, if this type denotes a a Boolean operator (that
	 * can be inverted when considering the current pool of constants), {@code null} otherwise. The logical
	 * inversion is different from the arithmetic inversion. For example, the logical inversion of {@code LT} is
	 * {@code GE} (and not {@code GT}). Also, the logical inversion of {@code EQ} is {@code NE}.
	 * 
	 * @return the type denoting the logical inversion of this type, if this type denotes a a Boolean operator that
	 *         can be inverted, {@code null} otherwise
	 */
	public TypeExpr logicalInversion() {
		return this == LT ? GE
				: this == LE ? GT
						: this == GE ? LT
								: this == GT ? LE
										: this == NE ? EQ
												: this == EQ ? NE
														: this == IN ? NOTIN
																: this == NOTIN ? IN
																		: this == SUBSET ? SUPSEQ
																				: this == SUBSEQ ? SUPSET
																						: this == SUPSEQ ? SUBSET : this == SUPSET ? SUBSEQ : null;
	}

	/**
	 * Returns {@code true} iff this type denotes an operator that can be logically inverted, {@code null}
	 * otherwise.
	 * 
	 * @return {@code true} iff this type denotes an operator that can be logically inverted, {@code null} otherwise
	 */
	public boolean isLogicallyInvertible() {
		return logicalInversion() != null;
	}

	/**
	 * The type from {@code TypeConditionOperatorRel} with the same name as this type, if it exists, {@code null}
	 * otherwise.
	 * 
	 * @return The type from {@code TypeConditionOperatorRel} with the same name as this type, if it exists,
	 *         {@code null} otherwise
	 */
	public TypeConditionOperatorRel toRelop() {
		return this == LT ? TypeConditionOperatorRel.LT
				: this == LE ? TypeConditionOperatorRel.LE
						: this == GE ? TypeConditionOperatorRel.GE
								: this == GT ? TypeConditionOperatorRel.GT
										: this == EQ ? TypeConditionOperatorRel.EQ : this == NE ? TypeConditionOperatorRel.NE : null;
	}

	/**
	 * The type from {@code TypeConditionOperatorSet} with the same name as this type, if it exists, {@code null}
	 * otherwise.
	 * 
	 * @return The type from {@code TypeConditionOperatorSet} with the same name as this type, if it exists,
	 *         {@code null} otherwise
	 */
	public TypeConditionOperatorSet toSetop() {
		return this == IN ? TypeConditionOperatorSet.IN : this == NOTIN ? TypeConditionOperatorSet.NOTIN : null;
	}

	/**
	 * The type from {@code TypeArithmeticOperator} with the same name as this type, if it exists, {@code null}
	 * otherwise.
	 * 
	 * @return The type from {@code TypeArithmeticOperator} with the same name as this type, if it exists,
	 *         {@code null} otherwise
	 */
	public TypeArithmeticOperator toAriop() {
		return this == ADD ? TypeArithmeticOperator.ADD
				: this == SUB ? TypeArithmeticOperator.SUB
						: this == MUL ? TypeArithmeticOperator.MUL
								: this == DIV ? TypeArithmeticOperator.DIV
										: this == MOD ? TypeArithmeticOperator.MOD
												: this == POW ? TypeArithmeticOperator.POW : this == DIST ? TypeArithmeticOperator.DIST : null;
	}

	/**
	 * The type from {@code TypeUnaryArithmeticOperator} with the same name as this type, if it exists, {@code null}
	 * otherwise.
	 * 
	 * @return The type from {@code TypeUnaryArithmeticOperator} with the same name as this type, if it exists,
	 *         {@code null} otherwise
	 */
	public TypeUnaryArithmeticOperator toUnalop() {
		return this == ABS ? TypeUnaryArithmeticOperator.ABS
				: this == NEG ? TypeUnaryArithmeticOperator.NEG
						: this == SQR ? TypeUnaryArithmeticOperator.SQR : this == NOT ? TypeUnaryArithmeticOperator.NOT : null;
	}

	/**
	 * The type from {@code TypeLogicalOperator} with the same name as this type, if it exists, {@code null}
	 * otherwise.
	 * 
	 * @return The type from {@code TypeLogicalOperator} with the same name as this type, if it exists, {@code null}
	 *         otherwise
	 */
	public TypeLogicalOperator toLogop() {
		return this == AND ? TypeLogicalOperator.AND
				: this == OR ? TypeLogicalOperator.OR
						: this == XOR ? TypeLogicalOperator.XOR : this == IFF ? TypeLogicalOperator.IFF : this == IMP ? TypeLogicalOperator.IMP : null;
	}

} public static enum TypeObjective {
	EXPRESSION, SUM, PRODUCT, MINIMUM, MAXIMUM, NVALUES, LEX;

	public boolean generalizable() {
		return this == SUM || this == MINIMUM || this == MAXIMUM || this == NVALUES;
	}
} public static enum TypeOperatorRel {
	LT, LE, GE, GT;

	/**
	 * Returns the operator that is the reverse operator of this operator (no change for NE and EQ).
	 */
	public TypeOperatorRel arithmeticInversion() {
		return this == LT ? GT : this == LE ? GE : this == GE ? LE : LT;
	}

	/**
	 * Returns true iff this operator evaluates to true when given the two specified operands.
	 */
	public boolean isValidFor(long v1, long v2) {
		return this == LT ? v1 < v2 : this == LE ? v1 <= v2 : this == GE ? v1 >= v2 : v1 > v2;
	}

	/**
	 * The type from {@code TypeExpr} with the same name as this type.
	 * 
	 * @return The type from {@code TypeExpr} with the same name as this type
	 */
	public TypeExpr toExpr() {
		return this == LT ? TypeExpr.LT : this == LE ? TypeExpr.LE : this == GE ? TypeExpr.GE : TypeExpr.GT;
	}

	public TypeConditionOperatorRel toConditionOperator() {
		return this == LT ? TypeConditionOperatorRel.LT
				: this == LE ? TypeConditionOperatorRel.LE : this == GE ? TypeConditionOperatorRel.GE : TypeConditionOperatorRel.GT;
	}
} public static enum TypeRank {
	FIRST, LAST, ANY;
} public class Utilities {

	public static final Comparator<int[]> lexComparatorInt = (t1, t2) -> {
		for (int i = 0; i < t1.length; i++)
			if (t1[i] < t2[i])
				return -1;
			else if (t1[i] > t2[i])
				return +1;
		return 0;
	};

	public static final Comparator<String[]> lexComparatorString = (t1, t2) -> {
		for (int i = 0; i < t1.length; i++) {
			int res = t1[i].compareTo(t2[i]);
			if (res != 0)
				return res;
		}
		return 0;
	};

	public static <T> T[] buildArray(Class<?> cl, int length) {
		return (T[]) Array.newInstance(cl, length);
	}

	public static <T> T[][] buildArray(Class<?> cl, int length1, int length2) {
		return (T[][]) Array.newInstance(cl, length1, length2);
	}

	public static Object firstNonNull(Object array) {
		if (array != null && array.getClass().isArray())
			return IntStream.range(0, Array.getLength(array)).mapToObj(i -> firstNonNull(Array.get(array, i))).filter(o -> o != null).findFirst().orElse(null);
		return array;
	}

	/**
	 * Builds a one-dimensional array of T with the objects of the specified list. If the list does not contain any
	 * object other than null, null is returned.
	 */
	public static <T> T[] convert(Collection<T> list) {
		Object firstObject = list.stream().filter(o -> o != null).findFirst().orElse(null);
		if (firstObject == null)
			return null;
		T[] ts = buildArray(firstObject.getClass(), list.size());
		int i = 0;
		for (T x : list)
			ts[i++] = x;
		return ts;
	}

	/**
	 * Builds a one-dimensional array of T with the objects of the specified stream. If the stream does not contain any
	 * object other than null, null is returned.
	 */
	public static <T> T[] convert(Stream<T> stream) {
		return convert(stream.collect(Collectors.toList()));
	}

	public static <T> T[] convert(Object[] t) {
		Object firstObject = Stream.of(t).filter(o -> o != null).findFirst().orElse(null);
		Class<?> clazz = firstObject == null ? null
				: Stream.of(t).noneMatch(o -> o != null && o.getClass() != firstObject.getClass()) ? firstObject.getClass() : null;
		if (clazz == null)
			return null; // null is returned if the array has only null or elements of several types
		T[] ts = buildArray(firstObject.getClass(), t.length);
		int i = 0;
		for (Object x : t)
			ts[i++] = (T) x;
		return ts;
	}

	public static <T> T[][] convert(Object[][] t) {
		control(isRegular(t), " pb");
		// other controls to add
		T[][] m = buildArray(t[0][0].getClass(), t.length, t[0].length);
		for (int i = 0; i < t.length; i++)
			for (int j = 0; j < t[i].length; j++)
				m[i][j] = (T) t[i][j];
		return m;
	}

	public static Object[] specificArrayFrom(List<Object> list) {
		Object firstObject = list.stream().filter(o -> o != null).findFirst().orElse(null);
		Class<?> clazz = firstObject == null ? null
				: list.stream().noneMatch(o -> o != null && o.getClass() != firstObject.getClass()) ? firstObject.getClass() : null;
		return clazz == null ? list.toArray() : list.toArray((Object[]) Array.newInstance(clazz, list.size()));
	}

	public static Object[][] specificArray2DFrom(List<Object[]> list) {
		Class<?> clazz = list.stream().noneMatch(o -> o.getClass() != list.get(0).getClass()) ? list.get(0).getClass() : null;
		return clazz == null ? list.toArray(new Object[0][]) : list.toArray((Object[][]) Array.newInstance(clazz, list.size()));
	}

	private static <T> List<T> collectRec(Class<T> clazz, List<T> list, Object src) {
		if (src != null) {
			if (src instanceof Collection)
				collectRec(clazz, list, ((Collection<?>) src).stream());
			else if (src instanceof Stream)
				((Stream<?>) src).forEach(o -> collectRec(clazz, list, o));
			else if (src.getClass().isArray())
				IntStream.range(0, Array.getLength(src)).forEach(i -> collectRec(clazz, list, Array.get(src, i)));
			else if (clazz.isAssignableFrom(src.getClass()))
				list.add(clazz.cast(src));
			else if (src instanceof IntStream)
				((IntStream) src).forEach(o -> collectRec(clazz, list, o));
			else if (src.getClass() == Range.class)
				collectRec(clazz, list, ((Range) src).toArray()); // in order to deal with clazz being Integer.class
		}
		return list;
	}

	/**
	 * Returns a 1-dimensional array of objects of the specified type after collecting any object of this type being
	 * present in the specified objects. The specified objects can be stream (and IntStream), collections and arrays.
	 * The collecting process is made recursively.
	 * 
	 * @param clazz
	 *            the class of the objects to be collected
	 * @param src
	 *            the objects where to collect the objects
	 * @return a 1-dimensional array of objects of the specified type after collecting any object of this type being
	 *         present in the specified objects
	 */
	public static <T> T[] collect(Class<T> clazz, Object... src) {
		List<T> list = new ArrayList<>();
		Stream.of(src).forEach(o -> collectRec(clazz, list, o));
		return convert(list.stream().collect(Collectors.toList()));
	}

	/**
	 * Builds a 1-dimensional array of integers (int) from the specified sequence of parameters. Each parameter can be
	 * an integer, a Range, an array, a stream, a collection, etc. All integers are collected and concatenated to form a
	 * 1-dimensional array.
	 *
	 * @param src
	 *            the objects where to collect the integers
	 * @return a 1-dimensional array of integers after collecting any encountered integer in the specified objects
	 */
	public static int[] collectInt(Object... src) {
		Integer[] t = collect(Integer.class, src);
		return t == null ? new int[0] : Stream.of(t).filter(i -> i != null).mapToInt(i -> i).toArray();
	}

	public static boolean isNumeric(String token) {
		return token.matches("-?\\d+(\\.\\d+)?"); // match a number with optional '-' and decimal.
	}

	public static boolean isNumericInterval(String token) {
		return token.matches("-?\\d+\\.\\.-?\\d+");
	}

	public static int[] splitToInts(String s, String regex) {
		return Arrays.stream(s.trim().split(regex)).filter(tok -> tok.length() > 0).mapToInt(tok -> Integer.parseInt(tok)).toArray();
	}

	public static int[] splitToInts(String s) {
		return splitToInts(s, Constants.REG_WS);
	}

	public static int splitToInt(String s, String regex) {
		int[] t = splitToInts(s, regex);
		control(t.length > 0, "Not possible to extract an int from this call");
		return t[0];
	}

	public static int[] wordAsIntArray(String s) {
		assert s.chars().allMatch(c -> ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'));
		int[] t = new int[s.length()]; // We don't use streams here for efficiency reasons (when dealing with large sets
										// of words)
		for (int i = 0; i < s.length(); i++)
			t[i] = s.charAt(i) - (Character.isUpperCase(s.charAt(i)) ? 'A' : 'a');
		return t;
	}

	public static Boolean toBoolean(String s) {
		s = s.toLowerCase();
		if (s.equals("yes") || s.equals("y") || s.equals("true") || s.equals("t") || s.equals("1"))
			return Boolean.TRUE;
		if (s.equals("no") || s.equals("n") || s.equals("false") || s.equals("f") || s.equals("0"))
			return Boolean.FALSE;
		return null;
	}

	public static boolean isInteger(String token) {
		try {
			Integer.parseInt(token);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	public static boolean isLong(String token) {
		try {
			Long.parseLong(token);
			return true;
		} catch (NumberFormatException e) {
			return false;
		}
	}

	public static Integer toInteger(String token, Predicate<Integer> p) {
		try {
			Integer i = Integer.parseInt(token);
			Utilities.control(p == null || p.test(i), "Value " + i + " not accepted by " + p);
			return i;
		} catch (RuntimeException e) {
			return null;
		}
	}

	public static Integer toInteger(String token) {
		return toInteger(token, null);
	}

	public static int[] toIntegers(String[] tokens) {
		if (tokens == null || tokens.length == 0)
			return null;
		return Stream.of(tokens).map(tok -> toInteger(tok)).mapToInt(i -> i).toArray();
	}

	public static Double toDouble(String token, Predicate<Double> p) {
		try {
			Double d = Double.parseDouble(token);
			Utilities.control(p == null || p.test(d), "Value " + d + " not accepted by " + p);
			return d;
		} catch (RuntimeException e) {
			return null;
		}
	}

	public static double toDouble(String token) {
		return toDouble(token, null);
	}

	public static BigInteger powerBig(long a, int b) {
		return BigInteger.valueOf(a).pow(b);
	}

	public static int power(long a, int b) {
		return powerBig(a, b).intValueExact();
	}

	private static BigInteger recursiveFactorial(long start, long n) {
		long i;
		if (n <= 16) {
			BigInteger r = BigInteger.valueOf(start);
			for (i = start + 1; i < start + n; i++)
				r = r.multiply(BigInteger.valueOf(i));
			return r;
		}
		i = n / 2;
		return recursiveFactorial(start, i).multiply(recursiveFactorial(start + i, n - i));
	}

	public static BigInteger factorialBig(int n) {
		return recursiveFactorial(1, n);
	}

	public static int factorial(int n) {
		return factorialBig(n).intValueExact();
	}

	public static BigInteger binomialBig(int n, int k) {
		if (k < 0 || n < k)
			return BigInteger.ZERO;
		if (k > n - k)
			k = n - k;
		BigInteger i = BigInteger.ONE;
		for (int v = 0; v < k; v++)
			i = i.multiply(BigInteger.valueOf(n - v)).divide(BigInteger.valueOf(v + 1));
		return i;
	}

	public static int binomial(int n, int k) {
		return binomialBig(n, k).intValueExact();
	}

	public static BigInteger nArrangementsFor(int[] nValues) {
		return IntStream.of(nValues).mapToObj(v -> BigInteger.valueOf(v)).reduce(BigInteger.ONE, (acc, v) -> acc.multiply(v));
	}

	public static boolean contains(int[] tab, int v, int from, int to) {
		return IntStream.rangeClosed(from, to).anyMatch(i -> tab[i] == v);
	}

	public static boolean contains(int[] tab, int v) {
		return contains(tab, v, 0, tab.length - 1);
	}

	public static int indexOf(String s, String... t) {
		return IntStream.range(0, t.length).filter(i -> t[i].equals(s)).findFirst().orElse(-1);
	}

	public static int indexOf(String s, List<Object> list) {
		return IntStream.range(0, list.size()).filter(i -> list.get(i).equals(s)).findFirst().orElse(-1);
	}

	public static int indexOf(int value, int[] t) {
		for (int i = 0; i < t.length; i++)
			if (value == t[i])
				return i;
		return -1;
		// return IntStream.range(0, t.length).filter(i -> t[i] == value).findFirst().orElse(-1);
	}

	public static int indexOf(Object value, Object[] t) {
		return IntStream.range(0, t.length).filter(i -> t[i] == value).findFirst().orElse(-1);
	}

	/**
	 * Returns true is the array is regular and matches exactly the specified size. For example, if size is [5,4] then
	 * the specified array must be a 2-dimensional array of 5 x 4 squares.
	 */
	public static boolean hasSize(Object array, int... size) {
		boolean b1 = array != null && array.getClass().isArray(), b2 = size.length > 0;
		if (!b1 && !b2)
			return true;
		if (b1 && !b2 || !b1 && b2 || Array.getLength(array) != size[0])
			return false;
		return IntStream.range(0, size[0]).noneMatch(i -> !hasSize(Array.get(array, i), Arrays.stream(size).skip(1).toArray()));
	}

	/**
	 * Returns true is the array is regular, that is to say has the form of a rectangle for a 2-dimensional array, a
	 * cube for a 3-dimensional array... For example, if the specified array is a 2-dimensional array of 5 x 4 squares,
	 * then it is regular. But it has 3 squares for the first row, and 4 squares for the second row, then it is not
	 * regular.
	 */
	public static boolean isRegular(Object array) {
		List<Integer> list = new ArrayList<>();
		for (Object a = array; a != null && a.getClass().isArray(); a = Array.getLength(a) == 0 ? null : Array.get(a, 0))
			list.add(Array.getLength(a));
		return hasSize(array, list.stream().mapToInt(i -> i).toArray());
	}

	/**
	 * Method that controls that the specified condition is verified. If it is not the case, a message is displayed and
	 * the program is stopped.
	 */
	public static Object control(boolean condition, String message) {
		if (!condition) {
			System.out.println("\n\nFatal Error: " + message);
			throw new RuntimeException();
			// System.exit(1);
		}
		return null;
	}

	public static Object exit(String message) {
		return control(false, message);
	}

	/**
	 * Checks if the specified {@code Runnable} object raises an {@code ArithmeticException} object, when run. The value
	 * {@code true} is returned iff no such exception is raised.
	 * 
	 * @param r
	 *            a {@code Runnable} object to be run
	 * @return {@code true} iff no {@code ArithmeticException} is raised when running the specified code
	 */
	public static boolean checkSafeArithmeticOperation(Runnable r) {
		try {
			r.run();
			return true;
		} catch (ArithmeticException e) {
			return false;
		}
	}

	/**
	 * Method that parses the specified string as a long integer. If the value is too small or too big, an exception is
	 * raised. The specified boolean allows us to indicate if some special values (such as +infinity) must be checked.
	 */
	public static Long safeLong(String s, boolean checkSpecialValues) {
		if (checkSpecialValues) {
			if (s.equals(PLUS_INFINITY_STRING))
				return PLUS_INFINITY;
			if (s.equals(MINUS_INFINITY_STRING))
				return MINUS_INFINITY;
		}
		if (s.length() > 18) { // 18 because MAX_LONG and MIN_LONG are composed of at most 19 characters
			BigInteger big = new BigInteger(s);
			control(big.compareTo(BIG_MIN_SAFE_LONG) >= 0 && big.compareTo(BIG_MAX_SAFE_LONG) <= 0, "Too small or big value for this parser : " + s);
			return big.longValue();
		} else
			return Long.parseLong(s);
	}

	/**
	 * Method that parses the specified string as a long integer. If the value is too small or too big, an exception is
	 * raised.
	 */
	public static Long safeLong(String s) {
		return safeLong(s, false);
	}

	public static boolean isSafeInt(long l, boolean useMargin) {
		return (useMargin ? Constants.MIN_SAFE_INT : Integer.MIN_VALUE) <= l && l <= (useMargin ? Constants.MAX_SAFE_INT : Integer.MAX_VALUE);
	}

	public static boolean isSafeInt(long l) {
		return isSafeInt(l, true);
	}

	/**
	 * Converts the specified long number to int if it is safe to do it. When the specified boolean is set to true, we
	 * control that it is safe according to the constants MIN_SAFE_INT and MAX_SAFE_INT.
	 */
	public static int safeInt(Long l, boolean useMargin) {
		control(isSafeInt(l, useMargin), "Too big integer value " + l);
		return l.intValue();
		// return safeLong2Int(number.longValue(), useMargin);
	}

	/**
	 * Converts the specified long to int if it is safe to do it. We control that it is safe according to the constants
	 * MIN_SAFE_INT and MAX_SAFE_INT.
	 */
	public static int safeInt(Long l) {
		return safeInt(l, true);
	}

	/**
	 * Converts the specified long to int if it is safe to do it. Note that VAL_MINUS_INFINITY will be translated to
	 * VAL_MINUS_INFINITY_INT and that VAL_PLUS_INFINITY will be translated to VAL_PLUS_INFINITY_INT . We control that
	 * it is safe according to the constants MIN_SAFE_INT and MAX_SAFE_INT.
	 */
	public static int safeIntWhileHandlingInfinity(long l) {
		if (l == Constants.MINUS_INFINITY)
			return Constants.MINUS_INFINITY_INT;
		if (l == Constants.PLUS_INFINITY)
			return Constants.PLUS_INFINITY_INT;
		return safeInt(l);
	}

	public static <T> T[] swap(T[] t, int i, int j) {
		T tmp = t[i];
		t[i] = t[j];
		t[j] = tmp;
		return t;
	}

	/** Method that joins the elements of the specified array, using the specified delimiter to separate them. */
	public static String join(Object array, String delimiter) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0, length = Array.getLength(array); i < length; i++) {
			Object item = Array.get(array, i);
			if (item != null && item.getClass().isArray())
				sb.append("[").append(join(item, delimiter)).append("]");
			else
				sb.append(item != null ? item.toString() : "null").append(i < length - 1 ? delimiter : "");
		}
		return sb.toString();
	}

	/** Method that joins the elements of the specified array, using a white-space as delimiter. */
	public static String join(Object array) {
		return join(array, " ");
	}

	public static String join(Collection<? extends Object> c) {
		return join(c.toArray());
	}

	/** Method that joins the elements of the specified map, using the specified separator and delimiter. */
	public static <K, V> String join(Map<K, V> m, String separator, String delimiter) {
		return m.entrySet().stream().map(e -> e.getKey() + separator + e.getValue()).reduce("", (n, p) -> n + (n.length() == 0 ? "" : delimiter) + p);
	}

	/**
	 * Method that joins the elements of the specified two-dimensional array, using the specified separator and
	 * delimiter.
	 */
	public static String join(Object[][] m, String separator, String delimiter) {
		return Arrays.stream(m).map(t -> join(t, delimiter)).reduce("", (n, p) -> n + (n.length() == 0 ? "" : separator) + p);
	}

	public static String join(int[][] m, String separator, String delimiter) {
		return Arrays.stream(m).map(t -> join(t, delimiter)).reduce("", (n, p) -> n + (n.length() == 0 ? "" : separator) + p);
	}

	/**
	 * Returns the specified string in camel case form (with the first letter of the first word in lower case).
	 */
	public static String toCamelCase(String s) {
		String[] words = s.split("_");
		return IntStream.range(0, words.length)
				.mapToObj(i -> i == 0 ? words[i].toLowerCase() : words[i].substring(0, 1).toUpperCase() + words[i].substring(1).toLowerCase())
				.collect(Collectors.joining());
	}

	/**
	 * Returns the specified string in kebab case form.
	 */
	public static String toKebabCase(String s) {
		String[] words = s.split("_");
		return Stream.of(words).map(w -> w.toLowerCase()).collect(Collectors.joining("-"));
	}

	/** Method for converting an array into a string. */
	public static String arrayToString(Object array, final char LEFT, final char RIGHT, final String SEP) {
		assert array.getClass().isArray();

		if (array instanceof boolean[])
			return Arrays.toString((boolean[]) array);
		if (array instanceof byte[])
			return Arrays.toString((byte[]) array);
		if (array instanceof short[])
			return Arrays.toString((short[]) array);
		if (array instanceof int[])
			return Arrays.toString((int[]) array);
		if (array instanceof long[])
			return Arrays.toString((long[]) array);
		if (array instanceof String[])
			return LEFT + String.join(SEP, (String[]) array) + RIGHT;
		if (array instanceof IVar[])
			return LEFT + String.join(SEP, Stream.of((IVar[]) array).map(x -> x.toString()).toArray(String[]::new)) + RIGHT;

		if (array instanceof boolean[][])
			return LEFT + String.join(SEP, Stream.of((boolean[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof byte[][])
			return LEFT + String.join(SEP, Stream.of((byte[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof short[][])
			return LEFT + String.join(SEP, Stream.of((short[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof int[][])
			return LEFT + String.join(SEP, Stream.of((int[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof long[][])
			return LEFT + String.join(SEP, Stream.of((long[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof String[][])
			return LEFT + String.join(SEP, Stream.of((String[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;

		if (array instanceof boolean[][][])
			return LEFT + String.join(SEP, Stream.of((boolean[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof byte[][][])
			return LEFT + String.join(SEP, Stream.of((byte[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof short[][][])
			return LEFT + String.join(SEP, Stream.of((short[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof int[][][])
			return LEFT + String.join(SEP, Stream.of((int[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof long[][][])
			return LEFT + String.join(SEP, Stream.of((long[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;
		if (array instanceof String[][][])
			return LEFT + String.join(SEP, Stream.of((String[][][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;

		if (array instanceof Long[][])
			return LEFT + String.join(SEP, Stream.of((Long[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;

		if (array instanceof IVar[][])
			return LEFT + String.join(SEP, Stream.of((IVar[][]) array).map(t -> arrayToString(t, LEFT, RIGHT, SEP)).toArray(String[]::new)) + RIGHT;

		if (array instanceof Object[][])
			return LEFT + String.join(SEP, Stream.of((Object[][]) array).map(t -> arrayToString(t)).toArray(String[]::new)) + RIGHT;
		// return "(" + String.join(")(", Stream.of((Object[][]) array).map(t ->
		// simplify(Arrays.toString(t))).toArray(String[]::new)) +
		// ")";
		if (array instanceof Object[])
			return String.join(SEP,
					Stream.of((Object[]) array).map(t -> t.getClass().isArray() ? LEFT + arrayToString(t) + RIGHT : t.toString()).toArray(String[]::new));
		return null;
	}

	/** Method for converting an array into a string. */
	public static String arrayToString(Object array) {
		return arrayToString(array, '[', ']', ", ");
	}

	/**
	 * Returns true if inside the specified object, there is an element that checks the predicate. If syntactic trees
	 * are encountered, we check the leaves only.
	 */
	public static boolean check(Object obj, Predicate<Object> p) {
		if (obj instanceof Object[])
			return IntStream.range(0, Array.getLength(obj)).anyMatch(i -> check(Array.get(obj, i), p));
		if (obj instanceof XNode)
			return ((XNode<?>) obj).firstNodeSuchThat(n -> n instanceof XNodeLeaf && p.test(((XNodeLeaf<?>) n).value)) != null;
		// if (obj instanceof XNode)
		// return ((XNode<?>) obj).containsLeafSuchThat(leaf -> p.test(leaf.value));
		return p.test(obj);
	}

	public static class ModifiableBoolean {
		public Boolean value;

		public ModifiableBoolean(Boolean value) {
			this.value = value;
		}
	}

	// ************************************************************************
	// ***** Methods for XML
	// ************************************************************************

	/** Method that loads an XML document, using the specified file name. */
	public static Document loadDocument(String fileName) throws Exception {
		Utilities.control(new File(fileName).exists(), "Filename " + fileName + " not found\n");
		if (fileName.endsWith("xml.bz2") || fileName.endsWith("xml.lzma")) {
			Process p = Runtime.getRuntime().exec((fileName.endsWith("xml.bz2") ? "bunzip2 -c " : "lzma -c -d ") + fileName);
			Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(p.getInputStream());
			p.waitFor();
			return document;
		} else
			return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new FileInputStream(new File(fileName)));
	}

	public static void save(Document document, PrintWriter out) {
		try {
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
			transformer.transform(new DOMSource(document), new StreamResult(out));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static String save(Document document, String fileName) {
		try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)))) {
			save(document, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return fileName;
	}

	/** Method that returns an array with the child elements of the specified element. */
	public static Element[] childElementsOf(Element element) {
		NodeList childs = element.getChildNodes();
		return IntStream.range(0, childs.getLength()).mapToObj(i -> childs.item(i)).filter(e -> e.getNodeType() == Node.ELEMENT_NODE).toArray(Element[]::new);
	}

	/** Determines whether the specified element has the specified type as tag name. */
	public static boolean isTag(Element elt, TypeChild type) {
		return elt.getTagName().equals(type.name());
	}

	public static Element element(Document doc, String tag, List<Element> sons) {
		Element elt = doc.createElement(tag);
		sons.stream().forEach(c -> elt.appendChild(c));
		return elt;
	}

	public static Element element(Document doc, String tag, Element son) {
		return element(doc, tag, Arrays.asList(son));
	}

	public static Element element(Document doc, String tag, Element son, Element... otherSons) {
		return element(doc, tag, IntStream.range(0, 1 + otherSons.length).mapToObj(i -> i == 0 ? son : otherSons[i - 1]).collect(Collectors.toList()));
	}

	public static Element element(Document doc, String tag, Element son, Stream<Element> otherSons) {
		return element(doc, tag, Stream.concat(Stream.of(son), otherSons).collect(Collectors.toList()));
	}

	public static Element element(Document doc, String tag, Object textContent) {
		Element elt = doc.createElement(tag);
		elt.setTextContent(" " + textContent + " ");
		return elt;
	}

	public static Element element(Document doc, String tag, String attName, String attValue, Object textContent) {
		Element elt = element(doc, tag, textContent);
		elt.setAttribute(attName, attValue);
		return elt;
	}

	public static Element element(Document doc, String tag, String attName, String attValue) {
		Element elt = doc.createElement(tag);
		elt.setAttribute(attName, attValue);
		return elt;
	}

	public static Element element(Document doc, String tag, String attName1, String attValue1, String attName2, String attValue2) {
		Element elt = doc.createElement(tag);
		elt.setAttribute(attName1, attValue1);
		elt.setAttribute(attName2, attValue2);
		return elt;
	}

	public static Element element(Document doc, String tag, Stream<Entry<String, Object>> attributes) {
		Element elt = doc.createElement(tag);
		if (attributes != null)
			attributes.forEach(e -> elt.setAttribute(e.getKey(), e.getValue().toString()));
		return elt;
	}

	public static Element element(Document doc, String tag, Collection<Entry<String, Object>> attributes) {
		return element(doc, tag, attributes != null ? attributes.stream() : null);
	}

} public static class ModifiableBoolean {
	public Boolean value;

	public ModifiableBoolean(Boolean value) {
		this.value = value;
	}
} public static final class Dom extends DomBasic {

	public static String compactFormOf(int[] values) {
		StringBuilder sb = new StringBuilder();
		if (values.length == 2)
			return values[0] + " " + values[1];
		int prevVal = values[0], startInterval = prevVal;
		for (int i = 1; i < values.length; i++) {
			int currVal = values[i];
			if (currVal != prevVal + 1) {
				sb.append(prevVal == startInterval ? prevVal : startInterval + (prevVal == startInterval + 1 ? " " : "..") + prevVal).append(" ");
				// when only two values, no need for an interval
				startInterval = currVal;
			}
			prevVal = currVal;
		}
		return sb.append(prevVal == startInterval ? prevVal : startInterval + (prevVal == startInterval + 1 ? " " : "..") + prevVal).toString();
	}

	/**
	 * Builds an integer domain, with the integer values (entities that are either integers or integer intervals) obtained by parsing the
	 * specified string.
	 */
	protected Dom(String seq) {
		super(IntegerEntity.parseSeq(seq)); // must be already sorted.
	}

	/** Builds an integer domain, with the specified integer values. */
	public Dom(int[] values) {
		super(IntStream.of(values).mapToObj(v -> new IntegerValue(v)).toArray(IntegerEntity[]::new));
	}

	/** Builds an integer domain, with the specified integer interval. */
	public Dom(int min, int max) {
		super(new IntegerEntity[] { new IntegerInterval(min, max) });
	}

	/** Returns the first (smallest) value of the domain. It may be VAL_M_INFINITY for -infinity. */
	public long firstValue() {
		return ((IntegerEntity) values[0]).smallest();
	}

	/** Returns the last (greatest) value of the domain. It may be VAL_P_INFINITY for +infinity. */
	public long lastValue() {
		return ((IntegerEntity) values[values.length - 1]).greatest();
	}

	/** Returns true iff the domain contains the specified value. */
	public boolean contains(long v) {
		for (int left = 0, right = values.length - 1; left <= right;) {
			int center = (left + right) / 2;
			int res = ((IntegerEntity) values[center]).compareContains(v);
			if (res == 0)
				return true;
			if (res == -1)
				left = center + 1;
			else
				right = center - 1;
		}
		return false;
	}

	private Object cacheAllValues; // cache for lazy initialization

	/** Returns the number of values in the domain, if the domain is finite. Returns -1 otherwise. */
	public long nValues() {
		if (cacheAllValues == null)
			cacheAllValues = allValues();
		return cacheAllValues == null ? -1 : cacheAllValues instanceof Range ? ((Range) cacheAllValues).length() : ((int[]) cacheAllValues).length;
	}

	/**
	 * Returns the values of the integer domain, either as an object Range or as an array of integers. Returns null if the domain is infinite (or
	 * too large).
	 **/
	public Object allValues() {
		if (cacheAllValues == null) {
			IntegerEntity[] vs = (IntegerEntity[]) values;
			if (values.length == 1 && values[0] instanceof IntegerInterval) {
				IntegerInterval ii = (IntegerInterval) values[0];
				cacheAllValues = ii.width() == -1 ? null : new Range(ii);
			} else
				cacheAllValues = IntegerEntity.toIntArray(vs, Integer.MAX_VALUE);
		}
		assert !(cacheAllValues instanceof int[])
				|| IntStream.range(0, ((int[]) cacheAllValues).length - 1).allMatch(i -> ((int[]) cacheAllValues)[i] < ((int[]) cacheAllValues)[i + 1]);
		return cacheAllValues;
	}

	/**
	 * Returns this object if the condition is evaluated to {@code true}, {@code null} otherwise.
	 * 
	 * @param condition
	 *            a Boolean expression
	 * @return this object if the condition is evaluated to {@code true}, {@code null} otherwise
	 */
	public Dom when(boolean condition) {
		return condition ? this : null;
	}
} public static final class DomSymbolic extends DomBasic {

	/** Builds a symbolic domain, with the symbols obtained by parsing the specified string. */
	protected DomSymbolic(String seq) {
		super(Stream.of(seq.split("\\s+")).sorted().toArray(String[]::new));
	}

	/** Builds a symbolic domain, with the specified symbols. */
	public DomSymbolic(String[] values) {
		super(values);
	}

	/** Returns true iff the domain contains the specified value. */
	public boolean contains(String s) {
		return Arrays.binarySearch(values, s) >= 0;
	}
} public static interface IDom {
} public static interface IntegerEntity extends Comparable<IntegerEntity> {
	/** Returns an integer entity (integer value or integer interval) obtained by parsing the specified string. */
	public static IntegerEntity parse(String s) {
		String[] t = s.split("\\.\\.");
		return t.length == 1 ? new IntegerValue(safeLong(t[0])) : new IntegerInterval(safeLong(t[0], true), safeLong(t[1], true));
	}

	/** Returns an array of integer entities (integer values or integer intervals) obtained by parsing the specified string. */
	public static IntegerEntity[] parseSeq(String seq) {
		return Stream.of(seq.split("\\s+")).map(tok -> IntegerEntity.parse(tok)).toArray(IntegerEntity[]::new);
	}

	/**
	 * Returns the number of values in the specified array of integer entities. Importantly, note that -1 is returned if this number is infinite, or simply
	 * greater than {@code Long.MAX_VALUE}.
	 */
	public static long nValues(IntegerEntity[] pieces) {
		assert IntStream.range(0, pieces.length - 1).noneMatch(i -> pieces[i].greatest() >= pieces[i + 1].smallest());
		if (Stream.of(pieces).anyMatch(p -> p.width() == -1))
			return -1L; // the number of values does not fit within a long
		long cnt = 0;
		try {
			for (IntegerEntity piece : pieces)
				cnt = Math.addExact(cnt, piece.width());
		} catch (ArithmeticException e) {
			return -1L;
		}
		return cnt;
	}

	/**
	 * Returns an array of integers with all values represented by the specified integer entities. Note that null is returned if the number of values is
	 * infinite or greater than the specified limit value.
	 */
	public static int[] toIntArray(IntegerEntity[] pieces, int limit) {
		long l = nValues(pieces);
		if (l == -1L || l > limit)
			return null;
		int[] values = new int[(int) l];
		int i = 0;
		for (IntegerEntity piece : pieces)
			if (piece instanceof IntegerValue)
				values[i++] = Utilities.safeInt(((IntegerValue) piece).v);
			else {
				int min = Utilities.safeInt(((IntegerInterval) piece).inf), max = Utilities.safeInt(((IntegerInterval) piece).sup);
				for (int v = min; v <= max; v++)
					values[i++] = v;
			}
		return values;
	}

	/**
	 * Returns an array of integers with all values represented by the specified integer entities. Note that null is returned if the number of values is
	 * infinite or greater than Integer.MAX_VALUE
	 */
	public static int[] toIntArray(IntegerEntity[] pieces) {
		return toIntArray(pieces, Integer.MAX_VALUE);
	}

	@Override
	public default int compareTo(IntegerEntity p) {
		long l1 = this instanceof IntegerValue ? ((IntegerValue) this).v : ((IntegerInterval) this).inf;
		long l2 = p instanceof IntegerValue ? ((IntegerValue) p).v : ((IntegerInterval) p).inf;
		return l1 < l2 ? -1 : l1 > l2 ? 1 : 0; // correct because pieces do not overlap
	}

	/** Returns true iff the entity is an integer value or an integer interval containing only one value */
	public boolean isSingleton();

	/** Returns the smallest value of the entity (the value itself or the lower bound of the interval). */
	public long smallest();

	/** Returns the greatest value of the entity (the value itself or the upper bound of the interval). */
	public long greatest();

	/** Returns the number of values represented by the entity, or -1 if this number does not fit within a {@code long}. */
	public long width();

	/**
	 * Returns 0 if the entity contains the specified value, -1 if the values of the entity are strictly smaller than the specified value, and +1 if the
	 * values of the entity are strictly greater than the specified value.
	 */
	public int compareContains(long l);

} public class XNodeParent<V extends IVar> extends XNode<V> {

	public static XNodeParent<IVar> build(TypeExpr type, Object... os) {
		os = Stream.of(os).flatMap(o -> o instanceof Stream ? (Stream<?>) o : Stream.of(o)).toArray();
		Utilities.control(type.arityMin <= os.length && os.length <= type.arityMax, "The arity (number of sons) is not valid");
		List<XNode<IVar>> sons = Stream.of(os).map(o -> {
			if (o instanceof XNode)
				return (XNode<IVar>) o;
			if (o instanceof IVar)
				return new XNodeLeaf<IVar>(TypeExpr.VAR, o);
			if (o instanceof Byte || o instanceof Short || o instanceof Integer || o instanceof Long)
				return new XNodeLeaf<IVar>(TypeExpr.LONG, ((Number) o).longValue());
			if (o instanceof String)
				return new XNodeLeaf<IVar>(TypeExpr.SYMBOL, o);
			return (XNode<IVar>) Utilities.control(false,
					"Bad form: have you used equal, different , lessThan,... instead of eq, ne, lt,... ?\n" + "The class causing problem is " + o.getClass());
		}).collect(Collectors.toList()); // toArray(XNode[]::new);
		return new XNodeParent<IVar>(type, sons);
	}

	public static XNodeParent<IVar> abs(Object operand) {
		return build(TypeExpr.ABS, operand);
	}

	public static XNodeParent<IVar> neg(Object operand) {
		return build(TypeExpr.NEG, operand);
	}

	public static XNodeParent<IVar> sqr(Object operand) {
		return build(TypeExpr.SQR, operand);
	}

	public static XNodeParent<IVar> add(Object... operands) {
		return build(TypeExpr.ADD, operands);
	}

	public static XNodeParent<IVar> sub(Object operand1, Object operand2) {
		return build(TypeExpr.SUB, operand1, operand2);
	}

	public static XNodeParent<IVar> mul(Object... operands) {
		return build(TypeExpr.MUL, operands);
	}

	public static XNodeParent<IVar> div(Object operand1, Object operand2) {
		return build(TypeExpr.DIV, operand1, operand2);
	}

	public static XNodeParent<IVar> mod(Object operand1, Object operand2) {
		return build(TypeExpr.MOD, operand1, operand2);
	}

	public static XNodeParent<IVar> pow(Object operand1, Object operand2) {
		return build(TypeExpr.POW, operand1, operand2);
	}

	public static XNodeParent<IVar> min(Object... operands) {
		return build(TypeExpr.MIN, operands);
	}

	public static XNodeParent<IVar> max(Object... operands) {
		return build(TypeExpr.MAX, operands);
	}

	public static XNodeParent<IVar> dist(Object operand1, Object operand2) {
		return build(TypeExpr.DIST, operand1, operand2);
	}

	public static XNodeParent<IVar> lt(Object operand1, Object operand2) {
		return build(TypeExpr.LT, operand1, operand2);
	}

	public static XNodeParent<IVar> le(Object operand1, Object operand2) {
		return build(TypeExpr.LE, operand1, operand2);
	}

	public static XNodeParent<IVar> ge(Object operand1, Object operand2) {
		return build(TypeExpr.GE, operand1, operand2);
	}

	public static XNodeParent<IVar> gt(Object operand1, Object operand2) {
		return build(TypeExpr.GT, operand1, operand2);
	}

	public static XNodeParent<IVar> ne(Object... operands) {
		return build(TypeExpr.NE, operands);
	}

	public static XNodeParent<IVar> eq(Object... operands) {
		return build(TypeExpr.EQ, operands);
	}

	public static XNode<IVar> set(Object... operands) {
		if (operands.length == 0)
			return new XNodeLeaf<IVar>(TypeExpr.SET, null);
		if (operands.length == 1 && operands[0] instanceof Range)
			return set(((Range) operands[0]).toArray());
		if (operands.length == 1 && operands[0] instanceof Collection) {
			Collection<?> coll = (Collection<?>) operands[0];
			if (coll.size() == 0)
				return new XNodeLeaf<IVar>(TypeExpr.SET, null);
			Object first = coll.iterator().next();
			if (first instanceof Byte || first instanceof Short || first instanceof Integer || first instanceof Long)
				return new XNodeParent<IVar>(TypeExpr.SET,
						coll.stream().map(s -> new XNodeLeaf<IVar>(TypeExpr.LONG, ((Number) s).longValue())).collect(toList()));
			if (first instanceof String)
				return new XNodeParent<IVar>(TypeExpr.SET, coll.stream().map(s -> new XNodeLeaf<IVar>(TypeExpr.SYMBOL, s)).collect(toList()));
			throw new RuntimeException();
		}
		return build(TypeExpr.SET, operands);
	}

	public static XNode<IVar> set(int[] operands) {
		if (operands.length == 0)
			return new XNodeLeaf<IVar>(TypeExpr.SET, null);
		return new XNodeParent<IVar>(TypeExpr.SET, IntStream.of(operands).mapToObj(v -> new XNodeLeaf<IVar>(TypeExpr.LONG, (long) v)).collect(toList()));
	}

	public static XNodeParent<IVar> in(Object var, Object set) {
		return build(TypeExpr.IN, var, set);
	}

	public static XNodeParent<IVar> notin(Object var, Object set) {
		return build(TypeExpr.NOTIN, var, set);
	}

	public static XNodeParent<IVar> not(Object operand) {
		return build(TypeExpr.NOT, operand);
	}

	public static XNodeParent<IVar> and(Object... operands) {
		return operands.length == 1 ? (XNodeParent<IVar>) operands[0] : build(TypeExpr.AND, operands); // modeling facility
	}

	public static XNodeParent<IVar> or(Object... operands) {
		return operands.length == 1 ? (XNodeParent<IVar>) operands[0] : build(TypeExpr.OR, operands); // modeling facility
	}

	public static XNodeParent<IVar> xor(Object... operands) {
		return build(TypeExpr.XOR, operands);
	}

	public static XNodeParent<IVar> iff(Object... operands) {
		return build(TypeExpr.IFF, operands);
	}

	public static XNodeParent<IVar> imp(Object operand1, Object operand2) {
		return build(TypeExpr.IMP, operand1, operand2);
	}

	public static XNodeParent<IVar> ifThenElse(Object operand1, Object operand2, Object operand3) {
		return build(TypeExpr.IF, operand1, operand2, operand3);
	}

	public static XNodeParent<IVar> scalar(int[] t1, Object[] t2) {
		Utilities.control(t1.length == t2.length, "Not the same number of elements in the two arrays");
		return new XNodeParent<IVar>(TypeExpr.ADD, IntStream.range(0, t1.length).mapToObj(i -> mul(t1[i], t2[i])).collect(toList()));
	}

	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof XNodeParent))
			return false;
		XNodeParent<?> node = (XNodeParent<?>) obj;
		return type == node.type && sons.length == node.sons.length && IntStream.range(0, sons.length).allMatch(i -> sons[i].equals(node.sons[i]));
	}

	@Override
	public int compareTo(XNode<V> obj) {
		if (type != obj.type)
			return Integer.compare(type.ordinal(), obj.type.ordinal());
		XNodeParent<V> node = (XNodeParent<V>) obj;
		if (sons.length < node.sons.length)
			return -1;
		if (sons.length > node.sons.length)
			return 1;
		return IntStream.range(0, sons.length).map(i -> sons[i].compareTo(node.sons[i])).filter(v -> v != 0).findFirst().orElse(0);
	}

	/** Builds a parent node for a syntactic tree, with the specified type and the specified sons. */
	public XNodeParent(TypeExpr type, XNode<V>[] sons) {
		super(type, sons);
		Utilities.control(sons.length > 0, "Pb with this node that should be a parent");
	}

	/** Builds a parent node for a syntactic tree, with the specified type and the specified sons. */
	public XNodeParent(TypeExpr type, List<XNode<V>> sons) {
		this(type, sons.toArray(new XNode[sons.size()]));
	}

	/** Builds a parent node for a syntactic tree, with the specified type and the specified son. */
	public XNodeParent(TypeExpr type, XNode<V> son) {
		this(type, Arrays.asList(son));
	}

	/** Builds a parent node for a syntactic tree, with the specified type and the two specified sons. */
	public XNodeParent(TypeExpr type, XNode<V> son1, XNode<V> son2) {
		this(type, Arrays.asList(son1, son2));
	}

	@Override
	public int size() {
		return 1 + Stream.of(sons).mapToInt(c -> c.size()).sum();
	}

	@Override
	public int maxParameterNumber() {
		return Stream.of(sons).mapToInt(c -> c.maxParameterNumber()).max().orElse(-1);
	}

	// private Matcher matcher() {
	// return new Matcher((XNodeParent<IVar>) this);
	// }

	private static class Canonizer<W extends IVar> {
		private Matcher abs_sub = new Matcher(node(ABS, node(SUB, any, any)));
		private Matcher not_not = new Matcher(node(NOT, node(NOT, any)));
		private Matcher neg_neg = new Matcher(node(NEG, node(NEG, any)));
		private Matcher any_lt_k = new Matcher(node(LT, any, val));
		private Matcher k_lt_any = new Matcher(node(LT, val, any));
		private Matcher not_logop = new Matcher(node(NOT, anyc), (node, level) -> level == 1 && node.type.isLogicallyInvertible());
		private Matcher not_symrel_any = new Matcher(node(symop, not, any)); // , (node, level) -> level == 0 && node.type.oneOf(EQ, NE));
		private Matcher any_symrel_not = new Matcher(node(symop, any, not)); // , (node, level) -> level == 0 && node.type.oneOf(EQ, NE));
		private Matcher x_mul_k__eq_l = new Matcher(node(EQ, node(MUL, var, val), val));
		private Matcher flattenable = new Matcher(anyc,
				(node, level) -> level == 0 && node.type.oneOf(ADD, MUL, MIN, MAX, AND, OR) && Stream.of(node.sons).anyMatch(s -> s.type == node.type));
		private Matcher mergeable = new Matcher(anyc, (node, level) -> level == 0 && node.type.oneOf(ADD, MUL, MIN, MAX, AND, OR) && node.sons.length >= 2
				&& node.sons[node.sons.length - 1].type == LONG && node.sons[node.sons.length - 2].type == LONG);
		private Matcher sub_relop_sub = new Matcher(node(relop, sub, sub));
		private Matcher any_relop_sub = new Matcher(node(relop, any, sub));
		private Matcher sub_relop_any = new Matcher(node(relop, sub, any));
		private Matcher any_add_val__relop__any_add_val = new Matcher(node(relop, any_add_val, any_add_val));
		private Matcher var_add_val__relop__val = new Matcher(node(relop, var_add_val, val));
		private Matcher val__relop__var_add_val = new Matcher(node(relop, val, var_add_val));

		private Map<Matcher, Function<XNodeParent<W>, XNode<W>>> rules = new LinkedHashMap<>();

		private Canonizer() {
			rules.put(abs_sub, r -> node(DIST, r.sons[0].sons)); // abs(sub(a,b)) => dist(a,b)
			rules.put(not_not, r -> r.sons[0].sons[0]); // not(not(a)) => a
			rules.put(neg_neg, r -> r.sons[0].sons[0]); // neg(neg(a)) => a
			rules.put(any_lt_k, r -> node(LE, r.sons[0], augment(r.sons[1], -1))); // e.g., lt(x,5) => le(x,4)
			rules.put(k_lt_any, r -> node(LE, augment(r.sons[0], 1), r.sons[1])); // e.g., lt(5,x) => le(6,x)
			rules.put(not_logop, r -> node(r.sons[0].type.logicalInversion(), r.sons[0].sons)); // e.g., not(lt(x)) => ge(x)
			rules.put(not_symrel_any, r -> node(r.type.logicalInversion(), r.sons[0].sons[0], r.sons[1])); // e.g., ne(not(x),y) => eq(x,y)
			rules.put(any_symrel_not, r -> node(r.type.logicalInversion(), r.sons[0], r.sons[1].sons[0])); // e.g., ne(x,not(y)) => eq(x,y)
			rules.put(x_mul_k__eq_l, r -> r.val(1) % r.val(0) == 0 ? node(EQ, r.sons[0].sons[0], longLeaf(r.val(1) / r.val(0))) : longLeaf(0));
			// below, e.g., eq(mul(x,4),8) => eq(x,2) and eq(mul(x,4),6) => 0 (false)
			rules.put(flattenable, r -> { // we flatten operators when possible; for example add(add(x,y),z) becomes add(x,y,z)
				int l1 = r.sons.length, pos = IntStream.range(0, l1).filter(i -> r.sons[i].type == r.type).findFirst().getAsInt(), l2 = r.sons[pos].sons.length;
				Stream<XNode<W>> list = IntStream.range(0, l1 - 1 + l2)
						.mapToObj(j -> j < pos ? r.sons[j] : j < pos + l2 ? r.sons[pos].sons[j - pos] : r.sons[j - l2 + 1]);
				return node(r.type, list);
			});
			rules.put(mergeable, r -> { // we merge long when possible. e.g., add(a,3,2) => add(a,5) and max(a,2,1) => max(a,2)
				XNode<W>[] t = Arrays.copyOf(r.sons, r.arity() - 1);
				long v1 = r.sons[r.arity() - 1].val(0), v2 = r.sons[r.arity() - 2].val(0);
				t[r.arity() - 2] = longLeaf(r.type == ADD ? v1 + v2 : r.type == MUL ? v1 * v2 : r.type.oneOf(MIN, AND) ? Math.min(v1, v2) : Math.max(v1, v2));
				return node(r.type, t);
			});
			// We replace sub by add when possible
			rules.put(sub_relop_sub, r -> node(r.type, node(ADD, r.sons[0].sons[0], r.sons[1].sons[1]), node(ADD, r.sons[1].sons[0], r.sons[0].sons[1])));
			rules.put(any_relop_sub, r -> node(r.type, node(ADD, r.sons[0], r.sons[1].sons[1]), r.sons[1].sons[0]));
			rules.put(sub_relop_any, r -> node(r.type, r.sons[0].sons[0], node(ADD, r.sons[1], r.sons[0].sons[1])));
			// We remove add when possible
			rules.put(any_add_val__relop__any_add_val,
					r -> node(r.type, node(ADD, r.sons[0].sons[0], longLeaf(r.sons[0].sons[1].val(0) - r.sons[1].sons[1].val(0))), r.sons[1].sons[0]));
			rules.put(var_add_val__relop__val, r -> node(r.type, r.sons[0].sons[0], longLeaf(r.sons[1].val(0) - r.sons[0].sons[1].val(0))));
			rules.put(val__relop__var_add_val, r -> node(r.type, longLeaf(r.sons[0].val(0) - r.sons[1].sons[1].val(0)), r.sons[1].sons[0]));
			// mul(x,1) = > x, and add(x,0) => x // TODO
		}

		private XNode<W> augment(XNode<W> n, int offset) {
			((XNodeLeaf<?>) n).value = (long) n.val(0) + offset;
			return n;
		}
	}

	private static Canonizer<?> canonizer;

	private Canonizer<V> canonizer() {
		if (XNodeParent.canonizer != null)
			return (Canonizer<V>) XNodeParent.canonizer;
		Canonizer<V> can = new Canonizer<>();
		canonizer = can;
		return can;
	}

	@Override
	public XNode<V> canonization() {
		// We will build the canonized form of the node, with the local variables type and sons
		TypeExpr type = this.type; // possibly, this initial value of type will be modified during canonization
		XNode<V>[] sons = this.sons.clone();
		IntStream.range(0, sons.length).forEach(i -> sons[i] = sons[i].canonization()); // sons are made canonical
		if (type.isSymmetricOperator())
			Arrays.sort(sons); // Sons are sorted if the type of the node is symmetric
		// Now, sons are potentially sorted if the type corresponds to a non-symmetric binary relational operator (in that case, we swap sons and
		// arithmetically inverse the operator provided that the ordinal value of the reverse operator is smaller)
		if (sons.length == 2 && type.isUnsymmetricRelationalOperator() && (type.arithmeticInversion().ordinal() < type.ordinal()
				|| (type.arithmeticInversion().ordinal() == type.ordinal() && sons[0].compareTo(sons[1]) > 0))) {
			type = type.arithmeticInversion();
			Utilities.swap(sons, 0, 1);
		}
		if (sons.length == 1 && type.isIdentityWhenOneOperand()) // add(x) becomes x, min(x) becomes x, ...
			return sons[0]; // certainly can happen during the canonization process
		XNodeParent<V> node = node(type, sons);
		Entry<Matcher, Function<XNodeParent<V>, XNode<V>>> rule = canonizer().rules.entrySet().stream().filter(e -> e.getKey().matches(node)).findFirst()
				.orElse(null);
		return rule != null ? rule.getValue().apply(node).canonization() : node;
	}

	private XNode<V> buildNewTreeUsing(Function<XNode<V>, XNode<V>> f) {
		return new XNodeParent<V>(type, Stream.of(sons).map(f).collect(Collectors.toList()));
	}

	@Override
	public XNode<V> abstraction(List<Object> args, boolean abstractIntegers, boolean multiOccurrences) {
		return buildNewTreeUsing(s -> s.abstraction(args, abstractIntegers, multiOccurrences));
	}

	@Override
	public XNode<V> concretization(Object[] args) {
		return buildNewTreeUsing(s -> s.concretization(args));
	}

	@Override
	public XNode<V> replaceSymbols(Map<String, Integer> mapOfSymbols) {
		return buildNewTreeUsing(s -> s.replaceSymbols(mapOfSymbols));
	}

	@Override
	public XNode<V> replaceLeafValues(Function<Object, Object> f) {
		return new XNodeParent<V>(type, Stream.of(sons).map(s -> s.replaceLeafValues(f)).collect(Collectors.toList()));
	}

	@Override
	public XNode<V> replacePartiallyParameters(Object[] valueParameters) {
		return new XNodeParent<V>(type, Stream.of(sons).map(s -> s.replacePartiallyParameters(valueParameters)).collect(Collectors.toList()));
	}

	@Override
	public XNode<V> firstNodeSuchThat(Predicate<XNode<V>> p) {
		return p.test(this) ? this : Stream.of(sons).map(son -> son.firstNodeSuchThat(p)).filter(o -> o != null).findFirst().orElse(null);
	}

	@Override
	public LinkedList<XNode<V>> allNodesSuchThat(Predicate<XNode<V>> p, LinkedList<XNode<V>> list) {
		if (p.test(this))
			list.add(this);
		Stream.of(sons).forEach(s -> s.allNodesSuchThat(p, list));
		return list;
	}

	@Override
	public String toPostfixExpression(IVar[] scopeForAbstraction) {
		String s = Stream.of(sons).map(c -> c.toPostfixExpression(scopeForAbstraction)).collect(Collectors.joining(" "));
		return s + " " + ((type == TypeExpr.SET || sons.length > 2 && type != TypeExpr.IF ? sons.length : "") + type.toString().toLowerCase());
	}

	@Override
	public String toFunctionalExpression(Object[] argsForConcretization) {
		String s = this instanceof XNodeParentSpecial ? ((XNodeParentSpecial<?>) this).specialName : type.toString().toLowerCase();
		return s + "(" + Stream.of(sons).map(c -> c.toFunctionalExpression(argsForConcretization)).collect(Collectors.joining(",")) + ")";
	}

} public interface AbstractTuple {

	boolean match(int[] tuple);

	public static class OrdinaryTuple implements AbstractTuple {
		public int[] values;

		public OrdinaryTuple(int[] values) {
			this.values = values;
		}

		@Override
		public boolean match(int[] t) {
			assert t.length == values.length;
			return IntStream.range(0, t.length).allMatch(i -> values[i] == Constants.STAR || t[i] == Constants.STAR || values[i] == t[i]);
		}

	}

	public static class SmartTuple implements AbstractTuple {
		public Object[] values; // either Integer or Condition objects inside the array

		public SmartTuple(Object[] values) {
			this.values = values;
			Utilities.control(Stream.of(values).allMatch(v -> v instanceof Integer || v instanceof ConditionVal || v instanceof ConditionSet),
					"Bad form for smart tuple");
		}

		@Override
		public boolean match(int[] t) {
			assert t.length == values.length;
			for (int i = 0; i < t.length; i++) {
				if (values[i] instanceof Integer) {
					int v = ((Integer) values[i]).intValue();
					if (v != Constants.STAR && t[i] != Constants.STAR && v != t[i])
						return false;
				} else {
					Condition condition = (Condition) values[i];
					if (condition instanceof ConditionVal) {
						if (((ConditionVal) condition).operator.isValidFor(t[i], ((ConditionVal) condition).k) == false)
							return false;
					} else if (condition instanceof ConditionIntvl) {
						if (((ConditionIntvl) condition).operator.isValidFor(t[i], ((ConditionIntvl) condition).min, ((ConditionIntvl) condition).max) == false)
							return false;
					} else if (condition instanceof ConditionIntset) {
						if (((ConditionIntset) condition).operator.isValidFor(t[i], ((ConditionIntset) condition).t) == false)
							return false;
					}
				}
			}
			return true;
		}
	}
} public final class Automaton {
	/**
	 * The start (initial) state of the automaton.
	 */
	public final String startState;

	/**
	 * The set (array) of transitions. Each transition is an object composed of a first state, a symbol (that may be an
	 * integer or a string) and a second state that is reached from the first state after reading the symbol.
	 */
	public final Transition[] transitions;

	/**
	 * The set (array) of final states of the automaton, i.e., accepting stated of the automaton.
	 */
	public final String[] finalStates;

	/**
	 * Cache used for storing the fact that the automaton is deterministic or not
	 */
	private Boolean deterministic;

	/**
	 * Constructs an automaton from the specified arguments.
	 * 
	 * @param startState
	 *            the start state of the automaton
	 * @param transitions
	 *            the set (array) of transitions where each transition is an object composed of a first state, a symbol
	 *            (that may be an integer or a string) and a second state that is reached from the first state after
	 *            reading the symbol
	 * @param finalStates
	 *            the set (array) of final states of the automaton, i.e., accepting stated of the automaton
	 */
	public Automaton(String startState, Transition[] transitions, String... finalStates) {
		this.startState = startState;
		this.transitions = transitions;
		this.finalStates = finalStates;
	}

	/**
	 * Constructs an automaton from the specified arguments.
	 * 
	 * @param startState
	 *            the start state of the automaton
	 * @param transitions
	 *            the object encapsulating the list of transitions where each transition is an object composed of a
	 *            first state, a symbol (that may be an integer or a string) and a second state that is reached from the
	 *            first state after reading the symbol
	 * @param finalStates
	 *            the set (array) of final states of the automaton, i.e., accepting stated of the automaton
	 */
	public Automaton(String startState, Transitions transitions, String... finalStates) {
		this(startState, transitions.toArray(), finalStates);
	}

	/**
	 * Constructs an automaton from the specified regular expression, given as a string.
	 * 
	 * @param expression
	 *            the expression representing a regular expression.
	 */
	public Automaton(String expression) {
		Utilities.exit("Unimplemented code; converting regular expressions into an automaton");
		this.startState = null;
		this.transitions = null;
		this.finalStates = null;
	}

	public boolean _isDeterministic() {
		Set<String> encountered = new HashSet<>();
		for (Transition tr : transitions)
			if (tr.value instanceof int[]) {
				for (int v : (int[]) tr.value) {
					if (encountered.contains(tr.start + " " + v))
						return false;
					encountered.add(tr.start + " " + v);
				}
			} else {
				if (encountered.contains(tr.start + " " + tr.value))
					return false;
				encountered.add(tr.start + " " + tr.value);
			}
		return true;
	}

	public boolean isDeterministic() {
		if (deterministic == null)
			deterministic = _isDeterministic();
		return deterministic;
	}

	@Override
	public String toString() {
		return "startState=" + startState + " finalStates={" + Utilities.join(finalStates) + "} " + "\ntransitions={"
				+ Stream.of(transitions).map(t -> t.toString()).collect(Collectors.joining()) + "}";
	}

} public class Table extends TableAbstract {

	/**
	 * Returns an array of tuples in lexicographic order, and without any duplicates.
	 * 
	 * @param tuples
	 *            an array of tuples
	 * @return an array of tuples in lexicographic order, and without any duplicates
	 */
	public static int[][] clean(int[]... tuples) {
		Set<int[]> set = new TreeSet<>(Utilities.lexComparatorInt);
		for (int i = 0; i < tuples.length - 1; i++)
			if (set.size() > 0)
				set.add(tuples[i]);
			else if (Utilities.lexComparatorInt.compare(tuples[i], tuples[i + 1]) >= 0)
				for (int j = 0; j <= i; j++)
					set.add(tuples[j]);
		if (set.size() > 0)
			set.add(tuples[tuples.length - 1]);
		return set.size() == 0 ? tuples : set.stream().toArray(int[][]::new);
	}

	/**
	 * Returns an array of tuples in lexicographic order, and without any duplicates.
	 * 
	 * @param tuples
	 *            a list of tuples
	 * @return an array of tuples in lexicographic order, and without any duplicates
	 */
	public static int[][] clean(List<int[]> tuples) {
		return clean(tuples.stream().toArray(int[][]::new));
	}

	public static int[][] toOrdinaryTable(int[][] shortTable, int[][] values) {
		List<int[]> tuples = new ArrayList<>();
		for (int[] t : shortTable) {
			int[] pos = IntStream.range(0, t.length).filter(i -> t[i] == STAR).toArray();
			if (pos.length == 0)
				tuples.add(t.clone());
			else {
				EnumerationCartesian ec = new EnumerationCartesian(
						IntStream.range(0, t.length).mapToObj(i -> t[i] == STAR ? values[i] : new int[] { t[i] }).toArray(int[][]::new));
				while (ec.hasNext())
					tuples.add(ec.next().clone());
			}
		}
		return tuples.stream().sorted(Utilities.lexComparatorInt).distinct().toArray(int[][]::new);
		// return Kit.intArray2D(tuples);
	}

	public static int[][] toOrdinaryTable(int[][] shortTable, int... nValues) {
		return toOrdinaryTable(shortTable, IntStream.of(nValues).mapToObj(i -> IntStream.range(0, i).toArray()).toArray(int[][]::new));
	}

	@Override
	public Table positive(Boolean positive) {
		this.positive = positive;
		return this;
	}

	private List<int[]> list = new ArrayList<>();

	@Override
	public int size() {
		return list.size();
	}

	private Table addTuple(int[] tuple) {
		Utilities.control(tuple != null && tuple.length > 0, "The tuple has a bad length");
		Utilities.control(list.size() == 0 || list.get(0).length == tuple.length, "The tuple has a different length from those already recorded");
		list.add(tuple);
		return this;
	}

	/**
	 * Adds an integer tuple to the table.
	 * 
	 * @param val
	 *            the first integer of the specified tuple
	 * @param otherVals
	 *            the orther integers of the specified tuple
	 * @return this integer table
	 */
	public Table add(int val, int... otherVals) {
		return addTuple(IntStream.range(0, otherVals.length + 1).map(i -> i == 0 ? val : otherVals[i - 1]).toArray());
	}

	// /**
	// * Adds an integer tuple to the table if the condition evaluates to {@code true}
	// *
	// * @param condition
	// * a Boolean condition
	// * @param tuple
	// * an integer tuple
	// * @return this integer table
	// */
	// public Table addIf(boolean condition, int... tuple) {
	// return condition ? add(tuple) : this;
	// }

	/**
	 * Adds the specified integer tuples to the table.
	 * 
	 * @param tuples
	 *            a sequence of other tuples
	 * @return this integer table
	 */
	public Table add(int[]... tuples) {
		Stream.of(tuples).forEach(t -> addTuple(t));
		return this;
	}

	/**
	 * Adds all tuples of the specified stream to the table.
	 * 
	 * @param stream
	 *            a stream of tuples to be added to the table
	 * @return this integer table
	 */
	public Table add(Stream<int[]> stream) {
		return add(stream.toArray(int[][]::new));
	}

	/**
	 * Adds all tuples of the specified collection to the table.
	 * 
	 * @param tuples
	 *            a collection of tuples to be added to the table
	 * @return this integer table
	 */
	public Table add(Collection<int[]> tuples) {
		return add(tuples.toArray(new int[0][]));
	}

	/**
	 * Adds all tuples of the specified table to this table.
	 * 
	 * @param table
	 *            another table
	 * @return this integer table
	 */
	public Table add(Table table) {
		return add(table.toArray());
	}

	/**
	 * Adds the tuples obtained after parsing the specified string. The string must represent a sequence of tuples as defined in XCSP3. For example,
	 * it could be {@code "(0,0,1)(0,2,0)(1,0,1)(1,1,2)"}.
	 * 
	 * @param s
	 *            a string representing a sequence of integer tuples
	 * @return this integer table
	 */
	public Table add(String s) {
		boolean b = controlStringRepresentationOfTuples(s);
		Utilities.control(b, "The specified string is not correct, as it does not correspond to a sequence of integer tuples");
		int[][] tuples = Stream.of(s.split(Constants.DELIMITER_LISTS)).skip(1)
				.map(tok -> Stream.of(tok.split("\\s*,\\s*")).mapToInt(v -> v.equals(Constants.STAR_SYMBOL) ? STAR : Integer.parseInt(v)).toArray())
				.toArray(int[][]::new);
		Stream.of(tuples).forEach(tuple -> addTuple(tuple));
		return this;
	}

	public Table addFrom(Range r, Function<Integer, int[]> f) {
		r.stream().mapToObj(i -> f.apply(i)).filter(t -> t != null).forEach(t -> add(t));
		return this;
	}

	public Table addFrom(Rangesx2 r2, BiFunction<Integer, Integer, int[]> f) {
		for (int i : r2.items[0])
			for (int j : r2.items[1]) {
				int[] t = f.apply(i, j);
				if (t != null)
					add(t);
			}
		return this;
	}

	private int[] intersectionOf(int[] t1, int[] t2, int[] out) {
		assert t1.length == t2.length;
		for (int i = 0; i < t1.length; i++) {
			if (t1[i] == t2[i])
				out[i] = t1[i];
			else if (t1[i] == STAR)
				out[i] = t2[i];
			else if (t2[i] == STAR)
				out[i] = t1[i];
			else
				return null;
		}
		return out;
	}

	public Table intersectionWith(Table other) {
		Utilities.control(positive && other.positive, "Tables must be both positive");
		int[][] m1 = this.toArray();
		int[][] m2 = other.toArray();
		Utilities.control(m1[0].length == m2[0].length, "Not the same arity");
		if (m1.length > m2.length) {
			int[][] m = m1;
			m1 = m2;
			m2 = m;
		}
		int[] tmp = new int[m1[0].length];
		Set<int[]> setOfSupports = new TreeSet<>(Utilities.lexComparatorInt);
		for (int[] t1 : m1) {
			IntStream.range(0, tmp.length).forEach(i -> tmp[i] = t1[i] == STAR ? Integer.MIN_VALUE : t1[i]); // we compute tmp from t1
			int index = Arrays.binarySearch(m2, tmp, Utilities.lexComparatorInt);
			if (index >= 0)
				setOfSupports.add(t1.clone());
			else {
				for (int i = -index - 1; i < m2.length; i++) {
					// if (Utilities.lexicographicInt.compare(lowerTuple(m2[i], tmp), t1) > 0) break; else
					if (intersectionOf(t1, m2[i], tmp) != null)
						setOfSupports.add(tmp.clone());
				}
			}
		}
		return new Table().add(setOfSupports.stream()); // .toArray(new int[setOfSupports.size()][]);
	}

	public Table addColumnWithValue(int position, int value) {
		int[][] m = this.toArray();
		Utilities.control(0 <= position && position <= m[0].length, "bad value of column position");
		return new Table().add(IntStream.range(0, m.length)
				.mapToObj(i -> IntStream.range(0, m[0].length + 1).map(j -> j < position ? m[i][j] : j == position ? STAR : m[i][j - 1]).toArray()));
	}

	/**
	 * Returns a 2-dimensional array corresponding to the collected tuples. Tuples are sorted and made distinct.
	 * 
	 * @return a 2-dimensional array corresponding to the collected tuples
	 */
	public int[][] toArray() {
		return list.stream().sorted(Utilities.lexComparatorInt).distinct().toArray(int[][]::new);
	}

	public int[][] toOrdinaryTableArray(int[][] values) {
		return Table.toOrdinaryTable(toArray(), values);
	}

	public int[][] toOrdinaryTableArray(int... nValues) {
		return Table.toOrdinaryTable(toArray(), nValues);
	}

	@Override
	public String toString() {
		return list.stream().map(t -> "(" + Utilities.join(t, ",") + ")").collect(Collectors.joining(" "));
	}

} public class Transition {

	/**
	 * The source state, where the transition begins.
	 */
	public String start;

	/**
	 * The value (object) labeling the transition.
	 */
	public Object value;

	/**
	 * The target state, where the transition ends.
	 */
	public String end;

	/**
	 * Constructs a transition from the specified arguments.
	 * 
	 * @param firstState
	 *            the first state, where the transition begins
	 * @param symbol
	 *            the symbol labeling the transition
	 * @param secondState
	 *            the second state, where the transition ends
	 */
	public Transition(String firstState, Object symbol, String secondState) {
		this.start = firstState;
		this.value = symbol;
		this.end = secondState;
	}

	@Override
	public String toString() {
		return "(" + start + "," + value.toString() + "," + end + ")";
	}
} public interface ICtr extends IRootForCtrAndObj {
	String EXTENSION = TypeCtr.extension.name();
	String INTENSION = TypeCtr.intension.name();
	String SMART = TypeCtr.smart.name();
	String REGULAR = TypeCtr.regular.name();
	String GRAMMAR = TypeCtr.grammar.name();
	String MDD = TypeCtr.mdd.name();
	String ALL_DIFFERENT = TypeCtr.allDifferent.name();
	String ALL_EQUAL = TypeCtr.allEqual.name();
	String ALL_DISTANT = TypeCtr.allDistant.name();
	String ORDERED = TypeCtr.ordered.name();
	String LEX = TypeCtr.lex.name();
	String ALL_INCOMPARABLE = TypeCtr.allIncomparable.name();
	String SUM = TypeCtr.sum.name();
	String COUNT = TypeCtr.count.name();
	String NVALUES = TypeCtr.nValues.name();
	String CARDINALITY = TypeCtr.cardinality.name();
	String BALANCE = TypeCtr.balance.name();
	String SPREAD = TypeCtr.spread.name();
	String DEVIATION = TypeCtr.deviation.name();
	String SUM_COSTS = TypeCtr.sumCosts.name();
	String STRETCH = TypeCtr.stretch.name();
	String NO_OVERLAP = TypeCtr.noOverlap.name();
	String CUMULATIVE = TypeCtr.cumulative.name();
	String BIN_PACKING = TypeCtr.binPacking.name();
	String KNAPSACK = TypeCtr.knapsack.name();
	String NETWORK_FLOW = TypeCtr.networkFlow.name();
	String CIRCUIT = TypeCtr.circuit.name();
	String NCIRCUITS = TypeCtr.nCircuits.name();
	String PÄTH = TypeCtr.path.name();
	String NPATHS = TypeCtr.nPaths.name();
	String TREE = TypeCtr.tree.name();
	String NTREES = TypeCtr.nTrees.name();
	String ARBO = TypeCtr.arbo.name();
	String NARBOS = TypeCtr.nArbos.name();
	String NCLIQUES = TypeCtr.nCliques.name();
	String CLAUSE = TypeCtr.clause.name();
	String INSTANTIATION = TypeCtr.instantiation.name();
	String ALL_INTERSECTING = TypeCtr.allIntersecting.name();
	String RANGE = TypeCtr.range.name();
	String ROOTS = TypeCtr.roots.name();
	String PARTITION = TypeCtr.partition.name();
	String MINIMUM = TypeCtr.minimum.name();
	String MAXIMUM = TypeCtr.maximum.name();
	String ELEMENT = TypeCtr.element.name();
	String CHANNEL = TypeCtr.channel.name();
	String PERMUTATION = TypeCtr.permutation.name();
	String PRECEDENCE = TypeCtr.precedence.name();
	String AND = TypeCtr.and.name();
	String OR = TypeCtr.or.name();
	String NOT = TypeCtr.not.name();
	String IF_THEN = TypeCtr.ifThen.name();
	String IF_THEN_ELSE = TypeCtr.ifThenElse.name();
	String SLIDE = TypeCtr.slide.name();
	String SEQBIN = TypeCtr.seqbin.name();

	String LIST = TypeChild.list.name();
	String SET = TypeChild.set.name();
	String MSET = TypeChild.mset.name();
	String MATRIX = TypeChild.matrix.name();
	String FUNCTION = TypeChild.function.name();
	String SUPPORTS = TypeChild.supports.name();
	String CONFLICTS = TypeChild.conflicts.name();
	String EXCEPT = TypeChild.except.name();
	String VALUE = TypeChild.value.name();
	String VALUES = TypeChild.values.name();
	String TOTAL = TypeChild.total.name();
	String COEFFS = TypeChild.coeffs.name();
	String CONDITION = TypeChild.condition.name();
	String COST = TypeChild.cost.name();
	String OPERATOR = TypeChild.operator.name();
	String NUMBER = TypeChild.number.name();
	String TRANSITIONS = TypeChild.transitions.name();
	String START = TypeChild.start.name();
	String FINAL = TypeChild.FINAL.name().toLowerCase(); // upper-cased because a keyword
	String TERMINAL = TypeChild.terminal.name();
	String RULES = TypeChild.rules.name();
	String INDEX = TypeChild.index.name();
	String MAPPING = TypeChild.mapping.name();
	String OCCURS = TypeChild.occurs.name();
	String ROW_OCCURS = TypeChild.rowOccurs.name();
	String COL_OCCURS = TypeChild.colOccurs.name();
	String WIDTHS = TypeChild.widths.name();
	String PATTERNS = TypeChild.patterns.name();
	String ORIGINS = TypeChild.origins.name();
	String LENGTHS = TypeChild.lengths.name();
	String ENDS = TypeChild.ends.name();
	String HEIGHTS = TypeChild.heights.name();
	String MACHINES = TypeChild.machines.name();
	String CONDITIONS = TypeChild.conditions.name();
	String SIZES = TypeChild.sizes.name();
	String WEIGHTS = TypeChild.weights.name();
	String PROFITS = TypeChild.profits.name();
	String LIMIT = TypeChild.limit.name();
	String SIZE = TypeChild.size.name();
	String ROOT = TypeChild.root.name();
	String IMAGE = TypeChild.image.name();
	String GRAPH = TypeChild.graph.name();
	String ROW = TypeChild.row.name();

	String ARITY = "arity";
	String TUPLES = "tuples";
	String POSITIVE = "positive";
	String START_INDEX = "startIndex";
	String START_ROW_INDEX = "startRowIndex";
	String START_COL_INDEX = "startColIndex";
	String RANK = "rank";
	String CLOSED = "closed";
	String START_INDEX2 = "startIndex2";
	String LIST2 = "list2";
	String LISTS = "lists";
	String SETS = "sets";
	String MSETS = "msets";
	String ROWS = "rows";
	String CIRCULAR = "circular";
	String OFFSETS = "offsets";
	String COLLECTS = "collects";
	String ALONES = "alones";
	String ZERO_IGNORED = "zeroIgnored";
	String REC = "rec";

	default Class<?> findInterfaceFor(Class<?> c) {
		if (c == null)
			return null;
		if (c.isInterface() && ICtr.class.isAssignableFrom(c))
			return c;
		Class<?> c1 = Stream.of(c.getInterfaces()).map(cc -> findInterfaceFor(cc)).filter(cc -> cc != null).findFirst().orElse(null);
		if (c1 != null)
			return c1;
		return findInterfaceFor(c.getSuperclass());
	}

	@Override
	default DefXCSP defXCSP() {
		Class<?> cc = findInterfaceFor(this.getClass());
		String s = cc.getSimpleName();
		String name = Character.toLowerCase(s.charAt(4)) + s.substring(5);
		DefXCSP def = def(name);
		Map<String, Object> map = mapXCSP(); // store it to avoid building it several times
		String[] keys = map.keySet().stream().filter(key -> key != null && !key.equals(SCOPE)).toArray(String[]::new);
		// System.out.println("KEYS=" + Utilities.join(keys));
		for (int i = 0; i < keys.length; i++)
			def.addOne(keys[i]);
		return def;
	}

	public interface ICtrIntension extends ICtr {

		static ICtrIntension buildFrom(IVar[] scope, XNodeParent<IVar> tree) {
			return new ICtrIntension() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, FUNCTION, tree); // making it canonical ?
				}
			};
		}
	}

	public interface ICtrExtension extends ICtr {

		static ICtrExtension buildFrom(IVar[] scope, String list, int arity, int[][] tuples, boolean positive) {
			return new ICtrExtension() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, ARITY, arity, TUPLES, tuples, POSITIVE, positive);
				}
			};
		}

		static ICtrExtension buildFrom(IVar[] scope, String list, int arity, String[][] tuples, boolean positive) {
			return new ICtrExtension() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, ARITY, arity, TUPLES, tuples, POSITIVE, positive);
				}
			};
		}

		static String tableAsString(int[][] tuples) {
			if (tuples.length == 0)
				return "";
			if (tuples[0].length == 1)
				return Dom.compactFormOf(Stream.of(tuples).mapToInt(t -> t[0]).toArray()); // * can't be present if 1-ary
			StringBuilder sb = new StringBuilder(); // save cpu time compared to using a stream
			for (int[] t : tuples) {
				sb.append("(");
				for (int i = 0; i < t.length; i++) {
					sb.append(t[i] == Constants.STAR ? "*" : t[i]);
					sb.append(i < t.length - 1 ? "," : "");
				}
				sb.append(")");
			}
			return sb.toString();
			// return Stream.of(tuples).map(t -> "(" + IntStream.of(t).mapToObj(v -> v == Constants.STAR_INT ? "*" : v + "").collect(joining(",")) +
			// ")").collect(joining());
		}

		static String tableAsString(String[][] tuples) {
			if (tuples.length == 0)
				return "";
			if (tuples[0].length == 1)
				return Stream.of(tuples).map(t -> t[0]).collect(joining(" "));
			return Stream.of(tuples).map(t -> "(" + Stream.of(t).collect(joining(",")) + ")").collect(joining());
		}

		default boolean isSimilarTo(ICtrExtension c) {
			Map<String, Object> map = mapXCSP(), mapc = c.mapXCSP();
			if (!map.get(ARITY).equals(mapc.get(ARITY)))
				return false;
			if (!map.get(POSITIVE).equals(mapc.get(POSITIVE)))
				return false;
			if (map.get(TUPLES).getClass() != mapc.get(TUPLES).getClass())
				return false;
			if (map.get(TUPLES) instanceof int[][]) {
				int[][] t1 = (int[][]) map.get(TUPLES), t2 = (int[][]) mapc.get(TUPLES);
				if (t1.length != t2.length)
					return false;
				if (t1 == t2 || t1.length == 0)
					return true; // may be the case when the same array has been associated with several table constraints
				if (t1.length > 50000) // hard coding ; limit for search
					return false;
				// System.out.println(mapXCSP().get(ARITY) + " vs " + c.mapXCSP().get(ARITY) + "\nt1= " + Kit.join(t1) + "\nt2= " +
				// Kit.join(t2));
				int arity = t1[0].length;
				for (int i = t1.length - 1; i >= 0; i--)
					for (int j = arity - 1; j >= 0; j--)
						if (t1[i][j] != t2[i][j])
							return false;
			} else {
				String[][] t1 = (String[][]) map.get(TUPLES), t2 = (String[][]) mapc.get(TUPLES);
				if (t1.length != t2.length)
					return false;
				if (t1 == t2 || t1.length == 0)
					return true; // may be the case when the same array has been associated with several table constraints
				if (t1.length > 50000) // hard coding ; limit for search
					return false;
				// System.out.println(mapXCSP().get(ARITY) + " vs " + c.mapXCSP().get(ARITY) + "\nt1= " + Kit.join(t1) + "\nt2= " +
				// Kit.join(t2));
				int arity = t1[0].length;
				for (int i = t1.length - 1; i >= 0; i--)
					for (int j = arity - 1; j >= 0; j--)
						if (!t1[i][j].equals(t2[i][j]))
							return false;
			}
			return true;
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(EXTENSION).add(LIST);
			Object tuples = def.map.get(TUPLES) instanceof int[][] ? tableAsString((int[][]) def.map.get(TUPLES))
					: tableAsString((String[][]) def.map.get(TUPLES));
			return def.addSon(((Boolean) def.map.get(POSITIVE)) ? SUPPORTS : CONFLICTS, tuples);
		}
	}

	public interface ICtrRegular extends ICtr {
		static ICtrRegular buildFrom(IVar[] scope, String list, String transitions, String startState, String[] finalStates) {
			return new ICtrRegular() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, TRANSITIONS, transitions, START, startState, FINAL, Utilities.join(finalStates));
				}
			};
		}
	}

	public interface ICtrMdd extends ICtr {
		static ICtrMdd buildFrom(IVar[] scope, String list, String transitions) {
			return new ICtrMdd() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, TRANSITIONS, transitions);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			return def(MDD).add(LIST, TRANSITIONS);
		}
	}

	public interface ICtrAllDifferent extends ICtr {
		static ICtrAllDifferent buildFrom(IVar[] scope, String key1, Object value1, String except) {
			Utilities.control(except == null || except.length() > 0, "Pb with except values");
			return new ICtrAllDifferent() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, key1, value1, EXCEPT, except);
				}
			};
		}
	}

	public interface ICtrAllEqual extends ICtr {
		static ICtrAllEqual buildFrom(IVar[] scope, String key, Object value) {
			return new ICtrAllEqual() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, key, value);
				}
			};
		}
	}

	public interface ICtrOrdered extends ICtr {
		static ICtrOrdered buildFrom(IVar[] scope, String key1, Object value1, Object lengths, TypeOperatorRel operator) {
			return new ICtrOrdered() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, key1, value1, LENGTHS, lengths, OPERATOR, operator.name().toLowerCase());
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(mapXCSP().containsKey(LISTS) || mapXCSP().containsKey(MATRIX) ? LEX : ORDERED).addListOrLifted();
			return def.add(LENGTHS, OPERATOR);
		}
	}

	public interface ICtrSum extends ICtr {
		static ICtrSum buildFrom(IVar[] scope, String list, Object coeffs, Condition condition) {
			return new ICtrSum() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, COEFFS, coeffs, CONDITION, condition);
				}
			};
		}
	}

	public interface ICtrCount extends ICtr {
		static ICtrCount buildFrom(IVar[] scope, String list, Object values, Condition condition) {
			return new ICtrCount() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, VALUES, values, CONDITION, condition);
				}
			};
		}
	}

	public interface ICtrNValues extends ICtr {
		static ICtrNValues buildFrom(IVar[] scope, String list, String except, Condition condition) {
			return new ICtrNValues() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, EXCEPT, except, CONDITION, condition);
				}
			};
		}
	}

	public interface ICtrCardinality extends ICtr {
		static ICtrCardinality buildFrom(IVar[] scope, String list, String values, Boolean closed, String occurs) {
			return new ICtrCardinality() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, VALUES, values, CLOSED, closed, OCCURS, occurs);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(CARDINALITY).add(LIST);
			if (def.map.get(CLOSED) == null || ((Boolean) def.map.get(CLOSED)) == false)
				def.add(VALUES);
			else
				def.addSon(VALUES, def.map.get(VALUES), CLOSED, def.map.get(CLOSED));
			return def.add(OCCURS);
		}
	}

	public interface ICtrMaximum extends ICtr {
		static ICtrMaximum buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
			return new ICtrMaximum() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(MAXIMUM);
			if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
				def.add(LIST);
			else
				def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
			if (def.map.containsKey(INDEX))
				if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
					def.add(INDEX);
				else
					def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
			return def.map.containsKey(CONDITION) ? def.add(CONDITION) : def;
		}
	}

	public interface ICtrMinimum extends ICtr {
		static ICtrMinimum buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
			return new ICtrMinimum() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(MINIMUM);
			if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
				def.add(LIST);
			else
				def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
			if (def.map.containsKey(INDEX))
				if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
					def.add(INDEX);
				else
					def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
			return def.map.containsKey(CONDITION) ? def.add(CONDITION) : def;
		}
	}

	public interface ICtrElement extends ICtr {
		static ICtrElement buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
			return new ICtrElement() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(ELEMENT);
			if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
				def.add(LIST);
			else
				def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
			if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
				def.add(INDEX);
			else
				def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
			return def.add(CONDITION);
		}
	}

	public interface ICtrElementMatrix extends ICtr {
		static ICtrElementMatrix buildFrom(IVar[] scope, Object matrix, Integer startRowIndex, Object rowIndex, Integer startColIndex, Object colIndex,
				Object value) {
			return new ICtrElementMatrix() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, MATRIX, matrix, START_ROW_INDEX, startRowIndex, INDEX, rowIndex + " " + colIndex, START_COL_INDEX, startColIndex,
							VALUE, value);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(ELEMENT);
			if (def.map.get(START_ROW_INDEX) == null && def.map.get(START_COL_INDEX) == null || ((Integer) def.map.get(START_ROW_INDEX)) == 0)
				def.add(MATRIX);
			else
				def.addSon(MATRIX, def.map.get(MATRIX), START_ROW_INDEX, def.map.get(START_ROW_INDEX), START_COL_INDEX, def.map.get(START_COL_INDEX));
			def.add(INDEX);
			return def.add(VALUE);
		}
	}

	public interface ICtrChannel extends ICtr {
		static ICtrChannel buildFrom(IVar[] scope, String list, Integer startIndex, String list2, Integer startIndex2, Object value) {
			return new ICtrChannel() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, LIST2, list2, START_INDEX2, startIndex2, VALUE, value);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(CHANNEL);
			if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
				def.add(LIST);
			else
				def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
			if (def.map.containsKey(LIST2))
				if (def.map.get(START_INDEX2) == null || ((Integer) def.map.get(START_INDEX2)) == 0)
					def.addSon(LIST, def.map.get(LIST2));
				else
					def.addSon(LIST, def.map.get(LIST2), START_INDEX, def.map.get(START_INDEX2));
			if (def.map.containsKey(VALUE))
				def.add(VALUE);
			return def;
		}
	}

	public interface ICtrStretch extends ICtr {

		static ICtrStretch buildFrom(IVar[] scope, String list, String values, String widths, String patterns) {
			return new ICtrStretch() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, VALUES, values, WIDTHS, widths, PATTERNS, patterns);
				}
			};
		}
	}

	public interface ICtrNoOverlap extends ICtr {

		static ICtrNoOverlap buildFrom(IVar[] scope, String origins, String lengths, Boolean zeroIgnored) {
			return new ICtrNoOverlap() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, ORIGINS, origins, LENGTHS, lengths, ZERO_IGNORED, zeroIgnored);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(NO_OVERLAP).add(ORIGINS, LENGTHS);
			if (def.map.get(ZERO_IGNORED) != null && !(Boolean) def.map.get(ZERO_IGNORED))
				def.attributes.add(new SimpleEntry<>(ZERO_IGNORED, Boolean.FALSE));
			return def;
		}
	}

	public interface ICtrCumulative extends ICtr {

		static ICtrCumulative buildFrom(IVar[] scope, String origins, String lengths, String ends, String heights, Condition condition) {
			return new ICtrCumulative() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, ORIGINS, origins, LENGTHS, lengths, ENDS, ends, HEIGHTS, heights, CONDITION, condition);
				}
			};
		}
	}

	public interface ICtrCircuit extends ICtr {

		static ICtrCircuit buildFrom(IVar[] scope, String list, Integer startIndex, Object size) {
			return new ICtrCircuit() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, SIZE, size);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(CIRCUIT);
			if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
				def.add(LIST);
			else
				def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
			return def.add(SIZE);
		}
	}

	public interface ICtrClause extends ICtr {

		static ICtrClause buildFrom(IVar[] scope, String list) {
			return new ICtrClause() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list);
				}
			};
		}
	}

	public interface ICtrInstantiation extends ICtr {
		static ICtrInstantiation buildFrom(IVar[] scope, String list, String values) {
			return new ICtrInstantiation() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, VALUES, values);
				}
			};
		}
	}

	public interface ICtrSmart extends ICtr {

		static ICtrSmart buildFrom(IVar[] scope, String list, String[] rows) {
			return new ICtrSmart() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, LIST, list, ROWS, rows);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(SMART).add(LIST);
			String[] rows = (String[]) def.map.get(ROWS);
			Stream.of(rows).forEach(o -> def.addSon(ROW, o));
			return def;
		}
	}

	// ************************************************************************
	// ***** Meta-constraints
	// ************************************************************************

	public interface Meta {
	}

	public interface ICtrSlide extends ICtr, Meta {

		static ICtrSlide buildFrom(IVar[] scope, Boolean circular, IVar[][] lists, int[] offsets, int[] collects, CtrAlone[] cas) {
			return new ICtrSlide() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, CIRCULAR, circular, LISTS, lists, OFFSETS, offsets, COLLECTS, collects, ALONES, cas);
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			// code managed in XCSP3Builder
			throw new RuntimeException();
		}
	}

	public interface ICtrIfThen extends ICtr, Meta {

		static ICtrIfThen buildFrom(IVar[] scope, CtrAlone ca1, CtrAlone ca2) {
			return new ICtrIfThen() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, ALONES, new CtrAlone[] { ca1, ca2 });
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(IF_THEN);
			CtrAlone[] cas = (CtrAlone[]) def.map.get(ALONES);
			def.addSon(REC, cas[0]);
			def.addSon(REC, cas[1]);
			return def;
		}
	}

	public interface ICtrIfThenElse extends ICtr, Meta {

		static ICtrIfThenElse buildFrom(IVar[] scope, CtrAlone ca1, CtrAlone ca2, CtrAlone ca3) {
			return new ICtrIfThenElse() {
				@Override
				public Map<String, Object> mapXCSP() {
					return map(SCOPE, scope, ALONES, new CtrAlone[] { ca1, ca2, ca3 });
				}
			};
		}

		@Override
		default DefXCSP defXCSP() {
			DefXCSP def = def(IF_THEN_ELSE);
			CtrAlone[] cas = (CtrAlone[]) def.map.get(ALONES);
			def.addSon(REC, cas[0]);
			def.addSon(REC, cas[1]);
			def.addSon(REC, cas[2]);
			return def;
		}
	}

} public interface ICtrAllDifferent extends ICtr {
	static ICtrAllDifferent buildFrom(IVar[] scope, String key1, Object value1, String except) {
		Utilities.control(except == null || except.length() > 0, "Pb with except values");
		return new ICtrAllDifferent() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, key1, value1, EXCEPT, except);
			}
		};
	}
} public interface ICtrAllEqual extends ICtr {
	static ICtrAllEqual buildFrom(IVar[] scope, String key, Object value) {
		return new ICtrAllEqual() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, key, value);
			}
		};
	}
} public interface ICtrCardinality extends ICtr {
	static ICtrCardinality buildFrom(IVar[] scope, String list, String values, Boolean closed, String occurs) {
		return new ICtrCardinality() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, VALUES, values, CLOSED, closed, OCCURS, occurs);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(CARDINALITY).add(LIST);
		if (def.map.get(CLOSED) == null || ((Boolean) def.map.get(CLOSED)) == false)
			def.add(VALUES);
		else
			def.addSon(VALUES, def.map.get(VALUES), CLOSED, def.map.get(CLOSED));
		return def.add(OCCURS);
	}
} public interface ICtrChannel extends ICtr {
	static ICtrChannel buildFrom(IVar[] scope, String list, Integer startIndex, String list2, Integer startIndex2, Object value) {
		return new ICtrChannel() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, LIST2, list2, START_INDEX2, startIndex2, VALUE, value);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(CHANNEL);
		if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
			def.add(LIST);
		else
			def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
		if (def.map.containsKey(LIST2))
			if (def.map.get(START_INDEX2) == null || ((Integer) def.map.get(START_INDEX2)) == 0)
				def.addSon(LIST, def.map.get(LIST2));
			else
				def.addSon(LIST, def.map.get(LIST2), START_INDEX, def.map.get(START_INDEX2));
		if (def.map.containsKey(VALUE))
			def.add(VALUE);
		return def;
	}
} public interface ICtrCircuit extends ICtr {

	static ICtrCircuit buildFrom(IVar[] scope, String list, Integer startIndex, Object size) {
		return new ICtrCircuit() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, SIZE, size);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(CIRCUIT);
		if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
			def.add(LIST);
		else
			def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
		return def.add(SIZE);
	}
} public interface ICtrClause extends ICtr {

	static ICtrClause buildFrom(IVar[] scope, String list) {
		return new ICtrClause() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list);
			}
		};
	}
} public interface ICtrCount extends ICtr {
	static ICtrCount buildFrom(IVar[] scope, String list, Object values, Condition condition) {
		return new ICtrCount() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, VALUES, values, CONDITION, condition);
			}
		};
	}
} public interface ICtrCumulative extends ICtr {

	static ICtrCumulative buildFrom(IVar[] scope, String origins, String lengths, String ends, String heights, Condition condition) {
		return new ICtrCumulative() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, ORIGINS, origins, LENGTHS, lengths, ENDS, ends, HEIGHTS, heights, CONDITION, condition);
			}
		};
	}
} public interface ICtrElement extends ICtr {
	static ICtrElement buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
		return new ICtrElement() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(ELEMENT);
		if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
			def.add(LIST);
		else
			def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
		if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
			def.add(INDEX);
		else
			def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
		return def.add(CONDITION);
	}
} public interface ICtrElementMatrix extends ICtr {
	static ICtrElementMatrix buildFrom(IVar[] scope, Object matrix, Integer startRowIndex, Object rowIndex, Integer startColIndex, Object colIndex,
			Object value) {
		return new ICtrElementMatrix() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, MATRIX, matrix, START_ROW_INDEX, startRowIndex, INDEX, rowIndex + " " + colIndex, START_COL_INDEX, startColIndex,
						VALUE, value);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(ELEMENT);
		if (def.map.get(START_ROW_INDEX) == null && def.map.get(START_COL_INDEX) == null || ((Integer) def.map.get(START_ROW_INDEX)) == 0)
			def.add(MATRIX);
		else
			def.addSon(MATRIX, def.map.get(MATRIX), START_ROW_INDEX, def.map.get(START_ROW_INDEX), START_COL_INDEX, def.map.get(START_COL_INDEX));
		def.add(INDEX);
		return def.add(VALUE);
	}
} public interface ICtrExtension extends ICtr {

	static ICtrExtension buildFrom(IVar[] scope, String list, int arity, int[][] tuples, boolean positive) {
		return new ICtrExtension() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, ARITY, arity, TUPLES, tuples, POSITIVE, positive);
			}
		};
	}

	static ICtrExtension buildFrom(IVar[] scope, String list, int arity, String[][] tuples, boolean positive) {
		return new ICtrExtension() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, ARITY, arity, TUPLES, tuples, POSITIVE, positive);
			}
		};
	}

	static String tableAsString(int[][] tuples) {
		if (tuples.length == 0)
			return "";
		if (tuples[0].length == 1)
			return Dom.compactFormOf(Stream.of(tuples).mapToInt(t -> t[0]).toArray()); // * can't be present if 1-ary
		StringBuilder sb = new StringBuilder(); // save cpu time compared to using a stream
		for (int[] t : tuples) {
			sb.append("(");
			for (int i = 0; i < t.length; i++) {
				sb.append(t[i] == Constants.STAR ? "*" : t[i]);
				sb.append(i < t.length - 1 ? "," : "");
			}
			sb.append(")");
		}
		return sb.toString();
		// return Stream.of(tuples).map(t -> "(" + IntStream.of(t).mapToObj(v -> v == Constants.STAR_INT ? "*" : v + "").collect(joining(",")) +
		// ")").collect(joining());
	}

	static String tableAsString(String[][] tuples) {
		if (tuples.length == 0)
			return "";
		if (tuples[0].length == 1)
			return Stream.of(tuples).map(t -> t[0]).collect(joining(" "));
		return Stream.of(tuples).map(t -> "(" + Stream.of(t).collect(joining(",")) + ")").collect(joining());
	}

	default boolean isSimilarTo(ICtrExtension c) {
		Map<String, Object> map = mapXCSP(), mapc = c.mapXCSP();
		if (!map.get(ARITY).equals(mapc.get(ARITY)))
			return false;
		if (!map.get(POSITIVE).equals(mapc.get(POSITIVE)))
			return false;
		if (map.get(TUPLES).getClass() != mapc.get(TUPLES).getClass())
			return false;
		if (map.get(TUPLES) instanceof int[][]) {
			int[][] t1 = (int[][]) map.get(TUPLES), t2 = (int[][]) mapc.get(TUPLES);
			if (t1.length != t2.length)
				return false;
			if (t1 == t2 || t1.length == 0)
				return true; // may be the case when the same array has been associated with several table constraints
			if (t1.length > 50000) // hard coding ; limit for search
				return false;
			// System.out.println(mapXCSP().get(ARITY) + " vs " + c.mapXCSP().get(ARITY) + "\nt1= " + Kit.join(t1) + "\nt2= " +
			// Kit.join(t2));
			int arity = t1[0].length;
			for (int i = t1.length - 1; i >= 0; i--)
				for (int j = arity - 1; j >= 0; j--)
					if (t1[i][j] != t2[i][j])
						return false;
		} else {
			String[][] t1 = (String[][]) map.get(TUPLES), t2 = (String[][]) mapc.get(TUPLES);
			if (t1.length != t2.length)
				return false;
			if (t1 == t2 || t1.length == 0)
				return true; // may be the case when the same array has been associated with several table constraints
			if (t1.length > 50000) // hard coding ; limit for search
				return false;
			// System.out.println(mapXCSP().get(ARITY) + " vs " + c.mapXCSP().get(ARITY) + "\nt1= " + Kit.join(t1) + "\nt2= " +
			// Kit.join(t2));
			int arity = t1[0].length;
			for (int i = t1.length - 1; i >= 0; i--)
				for (int j = arity - 1; j >= 0; j--)
					if (!t1[i][j].equals(t2[i][j]))
						return false;
		}
		return true;
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(EXTENSION).add(LIST);
		Object tuples = def.map.get(TUPLES) instanceof int[][] ? tableAsString((int[][]) def.map.get(TUPLES))
				: tableAsString((String[][]) def.map.get(TUPLES));
		return def.addSon(((Boolean) def.map.get(POSITIVE)) ? SUPPORTS : CONFLICTS, tuples);
	}
} public interface ICtrIfThen extends ICtr, Meta {

	static ICtrIfThen buildFrom(IVar[] scope, CtrAlone ca1, CtrAlone ca2) {
		return new ICtrIfThen() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, ALONES, new CtrAlone[] { ca1, ca2 });
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(IF_THEN);
		CtrAlone[] cas = (CtrAlone[]) def.map.get(ALONES);
		def.addSon(REC, cas[0]);
		def.addSon(REC, cas[1]);
		return def;
	}
} public interface ICtrIfThenElse extends ICtr, Meta {

	static ICtrIfThenElse buildFrom(IVar[] scope, CtrAlone ca1, CtrAlone ca2, CtrAlone ca3) {
		return new ICtrIfThenElse() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, ALONES, new CtrAlone[] { ca1, ca2, ca3 });
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(IF_THEN_ELSE);
		CtrAlone[] cas = (CtrAlone[]) def.map.get(ALONES);
		def.addSon(REC, cas[0]);
		def.addSon(REC, cas[1]);
		def.addSon(REC, cas[2]);
		return def;
	}
} public interface ICtrInstantiation extends ICtr {
	static ICtrInstantiation buildFrom(IVar[] scope, String list, String values) {
		return new ICtrInstantiation() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, VALUES, values);
			}
		};
	}
} public interface ICtrIntension extends ICtr {

	static ICtrIntension buildFrom(IVar[] scope, XNodeParent<IVar> tree) {
		return new ICtrIntension() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, FUNCTION, tree); // making it canonical ?
			}
		};
	}
} public interface ICtrMaximum extends ICtr {
	static ICtrMaximum buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
		return new ICtrMaximum() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(MAXIMUM);
		if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
			def.add(LIST);
		else
			def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
		if (def.map.containsKey(INDEX))
			if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
				def.add(INDEX);
			else
				def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
		return def.map.containsKey(CONDITION) ? def.add(CONDITION) : def;
	}
} public interface ICtrMdd extends ICtr {
	static ICtrMdd buildFrom(IVar[] scope, String list, String transitions) {
		return new ICtrMdd() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, TRANSITIONS, transitions);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		return def(MDD).add(LIST, TRANSITIONS);
	}
} public interface ICtrMinimum extends ICtr {
	static ICtrMinimum buildFrom(IVar[] scope, String list, Integer startIndex, Object index, TypeRank rank, Condition condition) {
		return new ICtrMinimum() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, START_INDEX, startIndex, INDEX, index, RANK, rank, CONDITION, condition);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(MINIMUM);
		if (def.map.get(START_INDEX) == null || ((Integer) def.map.get(START_INDEX)) == 0)
			def.add(LIST);
		else
			def.addSon(LIST, def.map.get(LIST), START_INDEX, def.map.get(START_INDEX));
		if (def.map.containsKey(INDEX))
			if (def.map.get(RANK) == null || ((TypeRank) def.map.get(RANK)) == TypeRank.ANY)
				def.add(INDEX);
			else
				def.addSon(INDEX, def.map.get(INDEX), RANK, ((TypeRank) def.map.get(RANK)).name().toLowerCase());
		return def.map.containsKey(CONDITION) ? def.add(CONDITION) : def;
	}
} public interface ICtrNValues extends ICtr {
	static ICtrNValues buildFrom(IVar[] scope, String list, String except, Condition condition) {
		return new ICtrNValues() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, EXCEPT, except, CONDITION, condition);
			}
		};
	}
} public interface ICtrNoOverlap extends ICtr {

	static ICtrNoOverlap buildFrom(IVar[] scope, String origins, String lengths, Boolean zeroIgnored) {
		return new ICtrNoOverlap() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, ORIGINS, origins, LENGTHS, lengths, ZERO_IGNORED, zeroIgnored);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(NO_OVERLAP).add(ORIGINS, LENGTHS);
		if (def.map.get(ZERO_IGNORED) != null && !(Boolean) def.map.get(ZERO_IGNORED))
			def.attributes.add(new SimpleEntry<>(ZERO_IGNORED, Boolean.FALSE));
		return def;
	}
} public interface ICtrOrdered extends ICtr {
	static ICtrOrdered buildFrom(IVar[] scope, String key1, Object value1, Object lengths, TypeOperatorRel operator) {
		return new ICtrOrdered() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, key1, value1, LENGTHS, lengths, OPERATOR, operator.name().toLowerCase());
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		DefXCSP def = def(mapXCSP().containsKey(LISTS) || mapXCSP().containsKey(MATRIX) ? LEX : ORDERED).addListOrLifted();
		return def.add(LENGTHS, OPERATOR);
	}
} public interface ICtrRegular extends ICtr {
	static ICtrRegular buildFrom(IVar[] scope, String list, String transitions, String startState, String[] finalStates) {
		return new ICtrRegular() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, TRANSITIONS, transitions, START, startState, FINAL, Utilities.join(finalStates));
			}
		};
	}
} public interface ICtrSlide extends ICtr, Meta {

	static ICtrSlide buildFrom(IVar[] scope, Boolean circular, IVar[][] lists, int[] offsets, int[] collects, CtrAlone[] cas) {
		return new ICtrSlide() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, CIRCULAR, circular, LISTS, lists, OFFSETS, offsets, COLLECTS, collects, ALONES, cas);
			}
		};
	}

	@Override
	default DefXCSP defXCSP() {
		// code managed in XCSP3Builder
		throw new RuntimeException();
	}
} public interface ICtrStretch extends ICtr {

	static ICtrStretch buildFrom(IVar[] scope, String list, String values, String widths, String patterns) {
		return new ICtrStretch() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, VALUES, values, WIDTHS, widths, PATTERNS, patterns);
			}
		};
	}
} public interface ICtrSum extends ICtr {
	static ICtrSum buildFrom(IVar[] scope, String list, Object coeffs, Condition condition) {
		return new ICtrSum() {
			@Override
			public Map<String, Object> mapXCSP() {
				return map(SCOPE, scope, LIST, list, COEFFS, coeffs, CONDITION, condition);
			}
		};
	}
} public static class MVarInteger extends MVariable implements Var {
	public MVarInteger(String id, Dom dom) {
		super(id, dom);
	}

	@Override
	public Object allValues() {
		return null; // Not useful for the moment
	}
} public static class MVarSymbolic extends MVariable implements VarSymbolic {
	public MVarSymbolic(String id, DomSymbolic dom) {
		super(id, dom);
	}
}
import static org.xcsp.modeler.definitions.ICtr.LIST;
import static org.xcsp.modeler.definitions.ICtr.LISTS;
import static org.xcsp.modeler.definitions.ICtr.MATRIX;
import java.util.HashMap;
import java.util.Map;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.xcsp.common.Condition;
import org.xcsp.common.IVar;
import org.xcsp.common.IVar.Var;
import org.xcsp.common.IVar.VarSymbolic;
import org.xcsp.common.Range;
import org.xcsp.common.Types.TypeExpr;
import org.xcsp.common.Types.TypeObjective;
import org.xcsp.common.Types.TypeOperatorRel;
import org.xcsp.common.Types.TypeRank;
import org.xcsp.common.Utilities;
import org.xcsp.common.Utilities.ModifiableBoolean;
import org.xcsp.common.domains.Domains.Dom;
import org.xcsp.common.domains.Domains.DomSymbolic;
import org.xcsp.common.domains.Domains.IDom;
import org.xcsp.common.domains.Values.IntegerEntity;
import org.xcsp.common.predicates.XNode;
import org.xcsp.common.predicates.XNodeLeaf;
import org.xcsp.common.predicates.XNodeParent;
import org.xcsp.common.structures.AbstractTuple;
import org.xcsp.common.structures.Automaton;
import org.xcsp.common.structures.Table;
import org.xcsp.common.structures.TableSymbolic;
import org.xcsp.common.structures.Transition;
import org.xcsp.modeler.api.ProblemAPI;
import org.xcsp.modeler.definitions.ICtr;
import org.xcsp.modeler.definitions.ICtr.ICtrAllDifferent;
import org.xcsp.modeler.definitions.ICtr.ICtrAllEqual;
import org.xcsp.modeler.definitions.ICtr.ICtrCardinality;
import org.xcsp.modeler.definitions.ICtr.ICtrChannel;
import org.xcsp.modeler.definitions.ICtr.ICtrCircuit;
import org.xcsp.modeler.definitions.ICtr.ICtrClause;
import org.xcsp.modeler.definitions.ICtr.ICtrCount;
import org.xcsp.modeler.definitions.ICtr.ICtrCumulative;
import org.xcsp.modeler.definitions.ICtr.ICtrElement;
import org.xcsp.modeler.definitions.ICtr.ICtrElementMatrix;
import org.xcsp.modeler.definitions.ICtr.ICtrExtension;
import org.xcsp.modeler.definitions.ICtr.ICtrIfThen;
import org.xcsp.modeler.definitions.ICtr.ICtrIfThenElse;
import org.xcsp.modeler.definitions.ICtr.ICtrInstantiation;
import org.xcsp.modeler.definitions.ICtr.ICtrIntension;
import org.xcsp.modeler.definitions.ICtr.ICtrMaximum;
import org.xcsp.modeler.definitions.ICtr.ICtrMdd;
import org.xcsp.modeler.definitions.ICtr.ICtrMinimum;
import org.xcsp.modeler.definitions.ICtr.ICtrNValues;
import org.xcsp.modeler.definitions.ICtr.ICtrNoOverlap;
import org.xcsp.modeler.definitions.ICtr.ICtrOrdered;
import org.xcsp.modeler.definitions.ICtr.ICtrRegular;
import org.xcsp.modeler.definitions.ICtr.ICtrSlide;
import org.xcsp.modeler.definitions.ICtr.ICtrStretch;
import org.xcsp.modeler.definitions.ICtr.ICtrSum;
import org.xcsp.modeler.definitions.IObj;
import org.xcsp.modeler.definitions.IObj.IObjFunctional;
import org.xcsp.modeler.definitions.IObj.IObjSpecialized;
import org.xcsp.modeler.entities.CtrEntities.CtrAlone;
import org.xcsp.modeler.entities.CtrEntities.CtrEntity;
import org.xcsp.modeler.entities.ObjEntities.ObjEntity;
import org.xcsp.modeler.implementation.ProblemIMP3.MVariable.MVarInteger;
import org.xcsp.modeler.implementation.ProblemIMP3.MVariable.MVarSymbolic;
package org.xcsp.modeler.implementation;




public class ProblemIMP3 extends ProblemIMP {

	private static final Object unimplementedCase(Object... objects) {
		System.out.println("\n\n**********************");
		System.out.println("Missing Implementation");
		StackTraceElement[] t = Thread.currentThread().getStackTrace();
		System.out.println("  Method " + t[2].getMethodName());
		System.out.println("  Class " + t[2].getClassName());
		System.out.println("  Line " + t[2].getLineNumber());
		System.out.println("**********************");
		System.out.println(Stream.of(objects).filter(o -> o != null).map(o -> o.toString()).collect(Collectors.joining("\n")));
		// throw new RuntimeException();
		System.exit(1);
		return null;
	}

	public static class MVariable implements IVar, Comparable<MVariable> {

		@Override
		public int compareTo(MVariable x) {
			int res = idPrefix.compareTo(x.idPrefix);
			if (res != 0)
				return res;
			if (idIndexes == null)
				return 0;
			return Utilities.lexComparatorInt.compare(idIndexes, x.idIndexes);
		}

		protected String id;
		private String idPrefix;
		private int[] idIndexes;
		public IDom dom;

		public MVariable(String id, IDom dom) {
			this.id = id;
			int pos = id.indexOf('[');
			this.idPrefix = pos == -1 ? id : id.substring(0, pos);
			this.idIndexes = pos == -1 ? null : Utilities.splitToInts(id.substring(pos), "\\[|\\]");
			this.dom = dom;
		}

		@Override
		public String id() {
			return id;
		}

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

		public static class MVarSymbolic extends MVariable implements VarSymbolic {
			public MVarSymbolic(String id, DomSymbolic dom) {
				super(id, dom);
			}
		}

		public static class MVarInteger extends MVariable implements Var {
			public MVarInteger(String id, Dom dom) {
				super(id, dom);
			}

			@Override
			public Object allValues() {
				return null; // Not useful for the moment
			}
		}
	}

	@Override
	public Class<MVarInteger> classVI() {
		return MVarInteger.class;
	}

	@Override
	public Class<MVarSymbolic> classVS() {
		return MVarSymbolic.class;
	}

	public ProblemIMP3(ProblemAPI api, String modelVariant, String data, String dataFormat, boolean dataSaving, String[] argsForPb) {
		super(api, modelVariant, argsForPb);
		loadData(data, dataFormat, dataSaving);
		api.model();
	}

	/** A map that gives access to each variable through its id. */
	public final Map<String, MVariable> mapForVars = new HashMap<>();

	/** Adds a variable that has already be built. Should not be called directly when modeling. */
	public final MVariable addVar(MVariable x) {
		Utilities.control(!mapForVars.containsKey(x.id()), x.id() + " duplicated");
		mapForVars.put(x.id(), x);
		return x;
	}

	@Override
	public MVarInteger buildVarInteger(String id, Dom dom) {
		return (MVarInteger) addVar(new MVarInteger(id, dom));
	}

	@Override
	public MVarSymbolic buildVarSymbolic(String id, DomSymbolic dom) {
		return (MVarSymbolic) addVar(new MVarSymbolic(id, dom));
	}

	public CtrAlone post(ICtr c) {
		// StackTraceElement[] t = Thread.currentThread().getStackTrace();
		// for (StackTraceElement s : t)
		// System.out.println(s);
		// System.out.println(t[t.length - 5].getLineNumber());
		return ctrEntities.new CtrAlone(c);
	}

	// ************************************************************************
	// ***** Constraint intension
	// ************************************************************************

	@Override
	public CtrEntity intension(XNodeParent<IVar> tree) {
		return post(ICtrIntension.buildFrom(tree.vars(), tree));
	}

	// ************************************************************************
	// ***** Converting Intension to Extension
	// ************************************************************************

	private Converter converter = new Converter() {
		@Override
		public StringBuilder signatureFor(Var[] scp) {
			StringBuilder sb = new StringBuilder();
			for (MVarInteger x : (MVarInteger[]) scp)
				sb.append(System.identityHashCode(x.dom)).append(' ');
			return sb;
		}

		@Override
		public int[][] domValuesOf(Var[] scp) {
			IntegerEntity[][] ies = Stream.of((MVarInteger[]) scp).map(x -> (IntegerEntity[]) ((Dom) x.dom).values).toArray(IntegerEntity[][]::new);
			Utilities.control(Stream.of(ies).allMatch(t -> IntegerEntity.nValues(t) != -1 && IntegerEntity.nValues(t) < 1000000), "");
			return Stream.of(ies).map(t -> IntegerEntity.toIntArray(t, 1000000)).toArray(int[][]::new);
		}

		@Override
		public ModifiableBoolean mode() {
			return new ModifiableBoolean(null);
		}
	};

	@Override
	protected Converter getConverter() {
		return converter;
	}

	// ************************************************************************
	// ***** Constraint extension
	// ************************************************************************

	@Override
	public CtrAlone extension(Var[] list, int[][] tuples, boolean positive) {
		return post(ICtrExtension.buildFrom(list, varEntities.compactOrdered(list), list.length, Table.clean(tuples), positive));
	}

	@Override
	public CtrAlone extension(VarSymbolic[] list, String[][] tuples, boolean positive) {
		return post(ICtrExtension.buildFrom(list, varEntities.compactOrdered(list), list.length, TableSymbolic.clean(tuples), positive));
	}

	@Override
	public CtrAlone extension(Var[] scp, AbstractTuple[] tuples, boolean positive) {
		return (CtrAlone) unimplementedCase();
	}

	// ************************************************************************
	// ***** Constraint regular
	// ***************************** *******************************************

	@Override
	public CtrAlone regular(Var[] list, Automaton automaton) {
		return post(ICtrRegular.buildFrom(list, varEntities.compactOrdered(list),
				Stream.of(automaton.transitions).map(t -> t.toString()).collect(Collectors.joining()), automaton.startState, automaton.finalStates));
	}

	// ************************************************************************
	// ***** Constraint mdd
	// ************************************************************************

	@Override
	public CtrAlone mdd(Var[] list, Transition[] transitions) {
		return post(ICtrMdd.buildFrom(list, varEntities.compactOrdered(list), Stream.of(transitions).map(t -> t.toString()).collect(Collectors.joining())));
	}

	// ************************************************************************
	// ***** Constraint allDifferent
	// ************************************************************************

	@Override
	public CtrEntity allDifferent(Var[] list) {
		return post(ICtrAllDifferent.buildFrom(list, LIST, varEntities.compact(list), null));
	}

	@Override
	public CtrEntity allDifferent(VarSymbolic[] list) {
		return post(ICtrAllDifferent.buildFrom(list, LIST, varEntities.compact(list), null));
	}

	@Override
	public CtrEntity allDifferent(Var[] list, int[] exceptValues) {
		return post(ICtrAllDifferent.buildFrom(list, LIST, varEntities.compact(list), Utilities.join(exceptValues)));
	}

	@Override
	public CtrEntity allDifferentList(Var[]... lists) {
		return post(ICtrAllDifferent.buildFrom(vars(lists), LISTS, varEntities.compactOrdered(lists), null));
	}

	@Override
	public CtrEntity allDifferentMatrix(Var[][] matrix) {
		return post(ICtrAllDifferent.buildFrom(vars(matrix), MATRIX, varEntities.compactMatrix(matrix), null));
	}

	@Override
	public CtrEntity allDifferent(XNode<IVar>[] trees) {
		String s = Stream.of(trees).map(t -> t.toString()).collect(Collectors.joining(" "));
		return post(ICtrAllDifferent.buildFrom(scope(Stream.of(trees).map(t -> t.vars())), LIST, s, null));
	}

	// ************************************************************************
	// ***** Constraint allEqual
	// ************************************************************************

	@Override
	public CtrEntity allEqual(Var... list) {
		return post(ICtrAllEqual.buildFrom(list, LIST, varEntities.compact(list)));
	}

	@Override
	public CtrEntity allEqual(VarSymbolic... list) {
		return post(ICtrAllEqual.buildFrom(list, LIST, varEntities.compact(list)));
	}

	@Override
	public CtrEntity allEqualList(Var[]... lists) {
		return post(ICtrAllEqual.buildFrom(vars(lists), LISTS, varEntities.compactOrdered(lists)));
	}

	// ************************************************************************
	// ***** Constraint ordered and lex
	// ************************************************************************

	@Override
	public CtrEntity ordered(Var[] list, int[] lengths, TypeOperatorRel operator) {
		return post(ICtrOrdered.buildFrom(list, LIST, varEntities.compactOrdered(list),
				IntStream.of(lengths).allMatch(v -> v == 0) ? null : Utilities.join(lengths), operator));
	}

	@Override
	public CtrEntity ordered(Var[] list, Var[] lengths, TypeOperatorRel operator) {
		return post(ICtrOrdered.buildFrom(list, LIST, varEntities.compactOrdered(list), varEntities.compactOrdered(lengths), operator));
	}

	@Override
	public CtrEntity lex(Var[][] lists, TypeOperatorRel operator) {
		return post(ICtrOrdered.buildFrom(vars(lists), LISTS, varEntities.compactOrdered(lists), null, operator));
	}

	@Override
	public CtrEntity lexMatrix(Var[][] matrix, TypeOperatorRel operator) {
		return post(ICtrOrdered.buildFrom(vars(matrix), MATRIX, varEntities.compactMatrix(matrix), null, operator));
	}

	@Override
	public CtrEntity precedence(Var[] list, int[] values, boolean covered) {
		throw new AssertionError("not implemented");
	}

	// ************************************************************************
	// ***** Constraint sum
	// ************************************************************************

	@Override
	public CtrEntity sum(Var[] list, int[] coeffs, Condition condition) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null), "A variable is null");
		Utilities.control(list.length == coeffs.length, "Pb because the number of variables is different form the number of coefficients");
		Var[] newList = list; // api.select(list, i -> coeffs[i] != 0);
		int[] newCoeffs = coeffs; // pi.selectFromIndexing(coeffs, i -> coeffs[i] != 0);
		Utilities.control(newList.length == newCoeffs.length, "Pb because the number of variables is different form the number of coefficients");
		return post(ICtrSum.buildFrom(scope(newList, condition), varEntities.compactOrdered(newList),
				IntStream.range(0, newCoeffs.length).allMatch(i -> newCoeffs[i] == 1) ? null : Utilities.join(newCoeffs), condition));
	}

	@Override
	public CtrEntity sum(Var[] list, Var[] coeffs, Condition condition) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null) && Stream.of(coeffs).noneMatch(x -> x == null), "A variable is null");
		Utilities.control(list.length == coeffs.length, "Pb because the number of variables is different form the number of coefficients");
		return post(ICtrSum.buildFrom(scope(list, coeffs, condition), varEntities.compactOrdered(list), varEntities.compactOrdered(coeffs), condition));
	}

	@Override
	public CtrEntity sum(XNode<IVar>[] trees, int[] coeffs, Condition condition) {
		Utilities.control(trees.length == coeffs.length, "Pb because the number of trees is different form the number of coefficients");
		String s = Stream.of(trees).map(t -> t.toString()).collect(Collectors.joining(" "));
		return post(ICtrSum.buildFrom(scope(Stream.of(trees).map(t -> t.vars()), condition), s,
				IntStream.range(0, coeffs.length).allMatch(i -> coeffs[i] == 1) ? null : Utilities.join(coeffs), condition));
	}

	// ************************************************************************
	// ***** Constraint count
	// ************************************************************************

	@Override
	public CtrEntity count(Var[] list, int[] values, Condition condition) {
		return post(ICtrCount.buildFrom(scope(list, condition), varEntities.compact(clean(list)), Utilities.join(values), condition));
	}

	@Override
	public CtrEntity count(Var[] list, Var[] values, Condition condition) {
		return post(ICtrCount.buildFrom(scope(list, values, condition), varEntities.compact(clean(list)), varEntities.compact(clean(values)), condition));
	}

	// ************************************************************************
	// ***** Constraint nValues
	// ************************************************************************

	@Override
	public CtrEntity nValues(Var[] list, Condition condition) {
		return post(ICtrNValues.buildFrom(scope(list, condition), varEntities.compact(clean(list)), null, condition));
	}

	@Override
	public CtrEntity nValues(Var[] list, Condition condition, int[] exceptValues) {
		return post(ICtrNValues.buildFrom(scope(list, condition), varEntities.compact(clean(list)), Utilities.join(exceptValues), condition));
	}

	// ************************************************************************
	// ***** Constraint cardinality
	// ************************************************************************

	@Override
	public CtrEntity cardinality(Var[] list, int[] values, boolean mustBeClosed, int[] occurs) {
		Utilities.control(values.length == occurs.length, "Arrays values and occurs have different length.");
		return post(ICtrCardinality.buildFrom(list, varEntities.compact(clean(list)), Utilities.join(values), mustBeClosed, Utilities.join(occurs)));
	}

	@Override
	public CtrEntity cardinality(Var[] list, int[] values, boolean mustBeClosed, Var[] occurs) {
		Utilities.control(values.length == occurs.length, "Arrays values and occurs have different length.");
		Utilities.control(Stream.of(occurs).noneMatch(x -> x == null), "A variable in array occurs is null");
		return post(ICtrCardinality.buildFrom(scope(list, occurs), varEntities.compact(clean(list)), Utilities.join(values), mustBeClosed,
				varEntities.compactOrdered(occurs)));
	}

	@Override
	public CtrEntity cardinality(Var[] list, int[] values, boolean mustBeClosed, int[] minOccurs, int[] maxOccurs) {
		Utilities.control(values.length == minOccurs.length && values.length == maxOccurs.length,
				"Arrays values, minOccurs and maxOccurs have different length.");
		return post(ICtrCardinality.buildFrom(list, varEntities.compact(clean(list)), Utilities.join(values), mustBeClosed,
				intervalAsString(minOccurs, maxOccurs)));
	}

	@Override
	public CtrEntity cardinality(Var[] list, Var[] values, boolean mustBeClosed, int[] occurs) {
		Utilities.control(values.length == occurs.length, "Arrays values and occurs have different length.");
		Utilities.control(Stream.of(values).noneMatch(x -> x == null), "A variable in array values is null");
		return post(ICtrCardinality.buildFrom(scope(list, values), varEntities.compact(clean(list)), varEntities.compactOrdered(values), mustBeClosed,
				Utilities.join(occurs)));
	}

	@Override
	public CtrEntity cardinality(Var[] list, Var[] values, boolean mustBeClosed, Var[] occurs) {
		Utilities.control(values.length == occurs.length, "Arrays values and occurs have different length.");
		Utilities.control(Stream.of(values).noneMatch(x -> x == null) && Stream.of(occurs).noneMatch(x -> x == null),
				"A variable in array values or occurs is null");
		return post(ICtrCardinality.buildFrom(scope(list, values, occurs), varEntities.compact(clean(list)), varEntities.compactOrdered(values), mustBeClosed,
				varEntities.compactOrdered(occurs)));
	}

	@Override
	public CtrEntity cardinality(Var[] list, Var[] values, boolean mustBeClosed, int[] minOccurs, int[] maxOccurs) {
		Utilities.control(values.length == minOccurs.length && values.length == maxOccurs.length,
				"Arrays values, minOccurs and maxOccurs have different length.");
		Utilities.control(Stream.of(values).noneMatch(x -> x == null), "A variable in array values is null");
		return post(ICtrCardinality.buildFrom(scope(list, values), varEntities.compact(clean(list)), varEntities.compactOrdered(values), mustBeClosed,
				intervalAsString(minOccurs, maxOccurs)));
	}

	// ************************************************************************
	// ***** Constraint maximum
	// ************************************************************************

	@Override
	public CtrEntity maximum(Var[] list, Condition condition) {
		return post(ICtrMaximum.buildFrom(scope(list, condition), varEntities.compact(clean(list)), null, null, null, condition));

	}

	@Override
	public CtrEntity maximum(Var[] list, int startIndex, Var index, TypeRank rank) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null), "A variable in array list is null");
		return post(ICtrMaximum.buildFrom(scope(list, index), varEntities.compactOrdered(list), startIndex, index, rank, null));
	}

	@Override
	public CtrEntity maximum(Var[] list, int startIndex, Var index, TypeRank rank, Condition condition) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null), "A variable in array list is null");
		return post(ICtrMaximum.buildFrom(scope(list, index, condition), varEntities.compactOrdered(list), startIndex, index, rank, condition));
	}

	@Override
	public CtrEntity maximum(XNode<IVar>[] trees, Condition condition) {
		return (CtrEntity) unimplementedCase();
	}

	// ************************************************************************
	// ***** Constraint minimum
	// ************************************************************************

	@Override
	public CtrEntity minimum(Var[] list, Condition condition) {
		return post(ICtrMinimum.buildFrom(scope(list, condition), varEntities.compact(clean(list)), null, null, null, condition));

	}

	@Override
	public CtrEntity minimum(Var[] list, int startIndex, Var index, TypeRank rank) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null), "A variable in array list is null");
		return post(ICtrMinimum.buildFrom(scope(list, index), varEntities.compactOrdered(list), startIndex, index, rank, null));
	}

	@Override
	public CtrEntity minimum(Var[] list, int startIndex, Var index, TypeRank rank, Condition condition) {
		Utilities.control(Stream.of(list).noneMatch(x -> x == null), "A variable in array list is null");
		return post(ICtrMinimum.buildFrom(scope(list, index, condition), varEntities.compactOrdered(list), startIndex, index, rank, condition));
	}

	@Override
	public CtrEntity minimum(XNode<IVar>[] trees, Condition condition) {
		return (CtrEntity) unimplementedCase();
	}

	// ************************************************************************
	// ***** Constraint element
	// ************************************************************************

	@Override
	public CtrEntity element(Var[] list, Condition condition) {
		return post(ICtrElement.buildFrom(list, varEntities.compact(list), null, null, null, condition));
	}

	@Override
	public CtrEntity element(Var[] list, int startIndex, Var index, TypeRank rank, Condition condition) {
		return post(ICtrElement.buildFrom(scope(list, index), varEntities.compactOrdered(list), startIndex, index, rank, condition));
	}

	@Override
	public CtrEntity element(int[] list, int startIndex, Var index, TypeRank rank, Condition condition) {
		return post(ICtrElement.buildFrom(scope(index, condition), Utilities.join(list), startIndex, index, rank, condition));
	}

	@Override
	public CtrEntity element(int[][] matrix, int startRowIndex, Var rowIndex, int startColIndex, Var colIndex, Condition condition) {
		return post(ICtrElementMatrix.buildFrom(vars(rowIndex, colIndex, condition), ICtrExtension.tableAsString(matrix), startRowIndex, rowIndex,
				startColIndex, colIndex, condition));
	}

	// ************************************************************************
	// ***** Constraint channel
	// ************************************************************************

	@Override
	public CtrEntity channel(Var[] list, int startIndex) {
		return post(ICtrChannel.buildFrom(list, varEntities.compactOrdered(list), startIndex, null, null, null));