All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.chocosolver.solver.constraints.ISetConstraintFactory Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2023, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.solver.constraints;

import org.chocosolver.solver.ISelf;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.set.*;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.SetVar;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.tools.ArrayUtils;

import java.util.Arrays;

/**
 * Interface to make constraints over SetVar
 * 

* A kind of factory relying on interface default implementation to allow (multiple) inheritance * * @author Jean-Guillaume FAGES * @since 4.0.0 */ public interface ISetConstraintFactory extends ISelf { //*********************************************************************************** // BASICS : union/inter/subset/card //*********************************************************************************** /** * Creates a constraint ensuring that union is exactly the union of values taken by ints, * * @param ints an array of integer variables * @param union a set variable * @return a constraint ensuring that union = {x | x in ints} */ default Constraint union(IntVar[] ints, SetVar union) { return new Constraint(ConstraintsName.SETINTVALUESUNION , new PropSetIntValuesUnion(ints, union) , new PropSetIntValuesUnion(ints, union) ); } /** * Creates a constraint which ensures that the union of sets is equal to unionSet * * @param sets an array of set variables * @param unionSet set variable representing the union of sets * @return A constraint ensuring that the union of sets is equal to unionSet */ default Constraint union(SetVar[] sets, SetVar unionSet) { return new Constraint(ConstraintsName.SETUNION, new PropUnion(sets, unionSet), new PropUnion(sets, unionSet)); } /** * Creates a constraint which ensures that the union of sets_i, where i in indices, * is equal to unionSet. *
* U = \cup_{i \in I} S_{i - o} *

* where U is unionSet, I is indices, o is iOffset and S is sets. * * @param unionSet set variable representing the union of sets * @param indices set variable representing the indices of selected variables in sets * @param iOffset index offset * @param sets an array of set variables * @return A constraint ensuring that the indices-union of sets is equal to unionSet */ default Constraint union(SetVar unionSet, SetVar indices, int iOffset, SetVar[] sets) { return new Constraint(ConstraintsName.SETUNION, new PropUnionVar(unionSet, indices, iOffset, sets)); } /** * Creates a constraint which ensures that the union of sets_i, where i in indices, * is equal to unionSet. *
* U = \cup_{i \in I} S_{i} *

* where U is unionSet, I is indices and S is sets. * * @param unionSet set variable representing the union of sets * @param indices set variable representing the indices of selected variables in sets * @param sets an array of set variables * @return A constraint ensuring that the indices-union of sets is equal to unionSet */ default Constraint union(SetVar unionSet, SetVar indices, SetVar[] sets) { return union(unionSet, indices, 0, sets); } /** * Creates a constraint which ensures that the intersection of sets is equal to intersectionSet * * @param sets an array of set variables * @param intersectionSet a set variable representing the intersection of sets * @return A constraint ensuring that the intersection of sets is equal to intersectionSet */ default Constraint intersection(SetVar[] sets, SetVar intersectionSet) { return intersection(sets, intersectionSet, false); } /** * Creates a constraint which ensures that the intersection of sets is equal to intersectionSet * * @param sets an array of set variables * @param intersectionSet a set variable representing the intersection of sets * @param boundConsistent adds an additional propagator to guarantee BC * @return A constraint ensuring that the intersection of sets is equal to intersectionSet * @throws IllegalArgumentException when the array of variables is either null or empty. */ default Constraint intersection(SetVar[] sets, SetVar intersectionSet, boolean boundConsistent) { if (sets.length == 0) { throw new IllegalArgumentException("The intersection of zero sets is undefined."); } if (boundConsistent) { return new Constraint(ConstraintsName.SETINTERSECTION, new PropIntersection(sets, intersectionSet), sets.length == 1 ? new PropAllEqual(new SetVar[]{sets[0], intersectionSet}) : new PropIntersectionFilterSets(sets, intersectionSet)); } else { return new Constraint(ConstraintsName.SETINTERSECTION, new PropIntersection(sets, intersectionSet)); } } /** * Creates a constraint establishing that sets[i] is a subset of sets[j] if isets[i] is a subset of sets[j] if i[] props = new Propagator[sets.length - 1]; for (int i = 0; i < sets.length - 1; i++) { props[i] = new PropSubsetEq(sets[i], sets[i + 1]); } return new Constraint(ConstraintsName.SETSUBSETEQ, props); } /** * Creates a constraint counting the number of empty sets sets * |{s in sets where |s|=0}| = nbEmpty * * @param sets an array of set variables * @param nbEmpty integer restricting the number of empty sets in sets * @return A constraint ensuring that |{s in sets where |s|=0}| = nbEmpty */ default Constraint nbEmpty(SetVar[] sets, int nbEmpty) { return nbEmpty(sets, ref().intVar(nbEmpty)); } /** * Creates a constraint counting the number of empty sets sets * |{s in sets where |s|=0}| = nbEmpty * * @param sets an array of set variables * @param nbEmpty integer variable restricting the number of empty sets in sets * @return A constraint ensuring that |{s in sets where |s|=0}| = nbEmpty */ default Constraint nbEmpty(SetVar[] sets, IntVar nbEmpty) { return new Constraint(ConstraintsName.SETNBEMPTY, new PropNbEmpty(sets, nbEmpty)); } /** * Creates a constraint linking set1 and set2 with an index offset : * x in set1 <=> x+offset in set2 * * @param set1 a set variable * @param set2 a set variable * @param offset offset index * @return a constraint ensuring that x in set1 <=> x+offset in set2 */ default Constraint offSet(SetVar set1, SetVar set2, int offset) { return new Constraint(ConstraintsName.SETOFFSET, new PropOffSet(set1, set2, offset)); } /** * Creates a constraint preventing set to be empty * * @param set a SetVar which should not be empty * @return a constraint ensuring that set is not empty */ default Constraint notEmpty(SetVar set) { return new Constraint(ConstraintsName.SETNOTEMPTY, new PropNotEmpty(set)); } //*********************************************************************************** // SUM //*********************************************************************************** /** * Creates a constraint summing elements of set * sum{i | i in set} = sum * * @param set a set variable * @param sum an integer variable representing sum{i | i in set} * @return a constraint ensuring that sum{i | i in set} = sum */ default Constraint sum(SetVar set, IntVar sum) { return sumElements(set, null, 0, sum); } /** * Creates a constraint summing weights given by a set of indices: * sum{weights[i] | i in indices} = sum *

* Also ensures that elements in indices belong to [0, weights.length-1] * * @param indices a set variable * @param weights integers representing the weight of each element in indices * @param sum an integer variable representing sum{weights[i] | i in indices} * @return a constraint ensuring that sum{weights[i] | i in indices} = sum */ default Constraint sumElements(SetVar indices, int[] weights, IntVar sum) { return sumElements(indices, weights, 0, sum); } /** * Creates a constraint summing weights given by a set of indices: * sum{weights[i-offset] | i in indices} = sum *

* Also ensures that elements in indices belong to [offset, offset+weights.length-1] * * @param indices a set variable * @param weights integers representing the weight of each element in indices * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param sum an integer variable representing sum{weights[i-offset] | i in indices} * @return a constraint ensuring that sum{weights[i-offset] | i in indices} = sum */ default Constraint sumElements(SetVar indices, int[] weights, int offset, IntVar sum) { return new Constraint(ConstraintsName.SETSUM, new PropSumOfElements(indices, weights, offset, sum)); } //*********************************************************************************** // MAX - MIN //*********************************************************************************** /** * Creates a constraint over the maximum element in a set: * max{i | i in set} = maxElementValue * * @param set a set variable * @param maxElementValue an integer variable representing the maximum element in set * @param notEmpty true : the set variable cannot be empty * false : the set may be empty (if set is empty, this constraint is not applied) * @return a constraint ensuring that max{i | i in set} = maxElementValue */ default Constraint max(SetVar set, IntVar maxElementValue, boolean notEmpty) { return max(set, null, 0, maxElementValue, notEmpty); } /** * Creates a constraint over the maximum element induces by a set: * max{weights[i-offset] | i in indices} = maxElementValue * * @param indices a set variable containing elements in range [offset,weights.length-1+offset] * @param weights integers representing the weight of each element in indices * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param maxElementValue an integer variable representing the maximum weight induced by indices * @param notEmpty true : the set variable cannot be empty * false : the set may be empty (if indices is empty, this constraint is not applied) * @return a constraint ensuring that max{weights[i-offset] | i in indices} = maxElementValue */ default Constraint max(SetVar indices, int[] weights, int offset, IntVar maxElementValue, boolean notEmpty) { if (notEmpty) { return new Constraint(ConstraintsName.SETMAX, new PropNotEmpty(indices), new PropMaxElement(indices, weights, offset, maxElementValue, true)); } else { return new Constraint(ConstraintsName.SETMAX, new PropMaxElement(indices, weights, offset, maxElementValue, false)); } } /** * Creates a constraint over the minimum element in a set: * min{i | i in set} = minElementValue * * @param set a set variable * @param minElementValue an integer variable representing the minimum element in set * @param notEmpty true : the set variable cannot be empty * false : the set may be empty (if set is empty, this constraint is not applied) * @return a constraint ensuring that min{i | i in set} = minElementValue */ default Constraint min(SetVar set, IntVar minElementValue, boolean notEmpty) { return min(set, null, 0, minElementValue, notEmpty); } /** * Creates a constraint over the minimum element induces by a set: * min{weights[i-offset] | i in indices} = minElementValue * * @param indices a set variable containing elements in range [offset,weights.length-1+offset] * @param weights integers representing the weight of each element in indices * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param minElementValue an integer variable representing the minimum weight induced by indices * @param notEmpty true : the set variable cannot be empty * false : the set may be empty (if indices is empty, this constraint is not applied) * @return a constraint ensuring that min{weights[i-offset] | i in indices} = minElementValue */ default Constraint min(SetVar indices, int[] weights, int offset, IntVar minElementValue, boolean notEmpty) { if (notEmpty) { return new Constraint(ConstraintsName.SETMIN, new PropNotEmpty(indices), new PropMinElement(indices, weights, offset, minElementValue, true)); } else { return new Constraint(ConstraintsName.SETMIN, new PropMinElement(indices, weights, offset, minElementValue, false)); } } //*********************************************************************************** // CHANNELING CONSTRAINTS : bool/int/graph //*********************************************************************************** /** * Creates a constraint channeling a set variable with boolean variables : * i in set <=> bools[i] = TRUE * * @param bools an array of boolean variables * @param set a set variable * @return a constraint ensuring that i in set <=> bools[i] = TRUE */ default Constraint setBoolsChanneling(BoolVar[] bools, SetVar set) { return setBoolsChanneling(bools, set, 0); } /** * Creates a constraint channeling a set variable with boolean variables : * i in set <=> bools[i-offset] = TRUE * * @param bools an array of boolean variables * @param set a set variable * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @return a constraint ensuring that i in set <=> bools[i-offset] = TRUE */ default Constraint setBoolsChanneling(BoolVar[] bools, SetVar set, int offset) { return new Constraint(ConstraintsName.SETBOOLCHANNELING, new PropBoolChannel(set, bools, offset)); } /** * Creates a constraint channeling set variables and integer variables : * x in sets[y] <=> ints[x] = y * * @param sets an array of set variables * @param ints an array of integer variables * @return a constraint ensuring that x in sets[y] <=> ints[x] = y */ default Constraint setsIntsChanneling(SetVar[] sets, IntVar[] ints) { return setsIntsChanneling(sets, ints, 0, 0); } /** * Creates a constraint channeling set variables and integer variables : * x in sets[y-offset1] <=> ints[x-offset2] = y * * @param sets an array of set variables * @param ints an array of integer variables * @param offset1 offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param offset2 offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @return a constraint ensuring that x in sets[y-offset1] <=> ints[x-offset2] = y */ default Constraint setsIntsChanneling(SetVar[] sets, IntVar[] ints, int offset1, int offset2) { return new Constraint(ConstraintsName.SETINTCHANNELING, new PropIntChannel(sets, ints, offset1, offset2), new PropIntChannel(sets, ints, offset1, offset2), new PropAllDisjoint(sets) ); } /** * Channeling constraint stating that int[i] = v + offset, where v is the ith element of the sorted elements of set. * If i is out of bounds with set, int[i] = nullValue. * @param set A set variable. * @param ints An array of integer variables. * @param nullValue An int. * @param offset An int. * @return A sortedSetIntsChanneling constraint. */ default Constraint sortedSetIntsChanneling(SetVar set, IntVar[] ints, int nullValue, int offset) { return new Constraint(ConstraintsName.SETORDEREDINTCHANNELING, new PropSortedIntChannel(set, ints, nullValue, offset)); } //*********************************************************************************** // MINIZINC API //*********************************************************************************** /** * Creates a constraint stating that the intersection of set1 and set2 should be empty * Note that they can be both empty * * @param set1 a set variable * @param set2 a set variable * @return a constraint ensuring that set1 and set2 are disjoint (empty intersection) */ default Constraint disjoint(SetVar set1, SetVar set2) { return allDisjoint(set1, set2); } /** * Creates a constraint stating that the intersection of sets should be empty * Note that there can be multiple empty sets * * @param sets an array of disjoint set variables * @return a constraint ensuring that sets are all disjoint (empty intersection) * @throws IllegalArgumentException when the array of variables is either null or empty. */ default Constraint allDisjoint(SetVar... sets) { if (sets == null || sets.length == 0) { throw new IllegalArgumentException("The array of variables cannot be null or empty"); } if (sets.length == 1) return ref().trueConstraint(); return new Constraint(ConstraintsName.SETALLDISJOINT, new PropAllDisjoint(sets)); } /** * Creates a constraint stating that sets should all be different (not necessarily disjoint) * Note that there cannot be more than one empty set * * @param sets an array of different set variables * @return a constraint ensuring that sets are all different * @throws IllegalArgumentException when the array of variables is either null or empty. */ default Constraint allDifferent(SetVar... sets) { if (sets == null || sets.length == 0) { throw new IllegalArgumentException("The array of variables cannot be null or empty"); } if (sets.length == 1) return ref().trueConstraint(); return new Constraint(ConstraintsName.SETALLDIFFERENT, new PropAllDiff(sets), new PropAllDiff(sets), new PropAtMost1Empty(sets) ); } /** * Creates a constraint stating that sets should be all equal * * @param sets an array of set variables to be equal * @return a constraint ensuring that all sets in sets are equal * @throws IllegalArgumentException when the array of variables is either null or empty. */ default Constraint allEqual(SetVar... sets) { if (sets == null || sets.length == 0) { throw new IllegalArgumentException("The array of variables cannot be null or empty"); } if (sets.length == 1) return ref().trueConstraint(); return new Constraint(ConstraintsName.SETALLEQUAL, new PropAllEqual(sets)); } /** * Creates a constraint stating that partitions universe into sets: * union(sets) = universe * intersection(sets) = {} * * @param sets an array of set variables whose values are subsets of universe * @param universe a set variable representing the union of sets * @return a constraint which ensures that sets forms a partition of universe */ default Constraint partition(SetVar[] sets, SetVar universe) { Constraint allDisjoint = allDisjoint(sets); allDisjoint.ignore(); return new Constraint(ConstraintsName.SETPARTITION, ArrayUtils.append( allDisjoint.getPropagators(), new Propagator[]{new PropUnion(sets, universe), new PropUnion(sets, universe)} )); } /** * Creates a constraint stating that : * x in sets[y-offset1] <=> y in invSets[x-offset2] * * @param sets an array of set variables * @param invSets an array of set variables * @param offset1 offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param offset2 offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @return a constraint ensuring that x in sets[y-offset1] <=> y in invSets[x-offset2] */ default Constraint inverseSet(SetVar[] sets, SetVar[] invSets, int offset1, int offset2) { return new Constraint(ConstraintsName.SETINVERSE, new PropInverse(sets, invSets, offset1, offset2)); } /** * Creates a constraint stating that sets are symmetric sets: * x in sets[y] <=> y in sets[x] * * @param sets an array of set variables * @return a constraint ensuring that x in sets[y] <=> y in sets[x] * @throws IllegalArgumentException when the array of variables is either null or empty. */ default Constraint symmetric(SetVar... sets) { if (sets == null || sets.length == 0) { throw new IllegalArgumentException("The array of variables cannot be null or empty"); } if (sets.length == 1) return ref().trueConstraint(); return symmetric(sets, 0); } /** * Creates a constraint stating that sets are symmetric sets: * x in sets[y-offset] <=> y in sets[x-offset] * * @param sets an array of set variables * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @return a constraint ensuring that x in sets[y-offset] <=> y in sets[x-offset] */ default Constraint symmetric(SetVar[] sets, int offset) { return new Constraint(ConstraintsName.SETSYMMETRIC, new PropSymmetric(sets, offset)); } /** * Creates a constraint enabling to retrieve an element set in sets: * sets[index] = set * * @param index an integer variable pointing to set's index into array sets * @param sets an array of set variables representing possible values for set * @param set a set variable equal to sets[index] * @return a constraint ensuring that sets[index] = set */ default Constraint element(IntVar index, SetVar[] sets, SetVar set) { return element(index, sets, 0, set); } /** * Creates a constraint enabling to retrieve an element set in sets: * sets[index-offset] = set * * @param index an integer variable pointing to set's index into array sets * @param sets an array of set variables representing possible values for set * @param offset offset index : should be 0 by default * but generally 1 with MiniZinc API * which counts from 1 to n instead of counting from 0 to n-1 (Java standard) * @param set a set variable equal to sets[index-offset] * @return a constraint ensuring that sets[index-offset] = set */ default Constraint element(IntVar index, SetVar[] sets, int offset, SetVar set) { return new Constraint(ConstraintsName.SETELEMENT, new PropElement(index, sets, offset, set), new PropElement(index, sets, offset, set)); } /** * Creates a member constraint stating that set belongs to sets * * @param sets set variables representing possible values for set * @param set a set variable which takes its value in sets * @return a constraint ensuring that set belongs to sets */ default Constraint member(SetVar[] sets, SetVar set) { IntVar index = ref().intVar("idx_tmp", 0, sets.length - 1, false); return element(index, sets, 0, set); } /** * Creates a member constraint stating that the value of intVar is in set * * @param intVar an integer variables which takes its value in set * @param set a set variables containing possible values for intVar * @return a constraint ensuring that the value of intVar is in set */ default Constraint member(final IntVar intVar, final SetVar set) { if (intVar.isInstantiated()) { return member(intVar.getValue(), set); } else { return new Constraint(ConstraintsName.SETMEMBER, intVar.hasEnumeratedDomain() ? new PropIntEnumMemberSet(set, intVar) : new PropIntBoundedMemberSet(set, intVar)) { @Override public Constraint makeOpposite() { return notMember(intVar, set); } }; } } /** * Creates a member constraint stating that the constant cst is in set * * @param cst an integer * @param set a set variable * @return a constraint ensuring that cst is in set */ default Constraint member(final int cst, final SetVar set) { return new Constraint(ConstraintsName.SETMEMBER, new PropIntCstMemberSet(set, cst)) { @Override public Constraint makeOpposite() { return notMember(cst, set); } }; } /** * Creates a member constraint stating that the value of intVar is not in set * * @param intVar an integer variables which does not take its values in set * @param set a set variables representing impossible values for intVar * @return a constraint ensuring that the value of intVar is not in set */ default Constraint notMember(final IntVar intVar, final SetVar set) { if (intVar.isInstantiated()) { return notMember(intVar.getValue(), set); } else { IntVar integer = intVar; if (!intVar.hasEnumeratedDomain()) { integer = ref().intVar("enumViewOf(" + intVar.getName() + ")", intVar.getLB(), intVar.getUB(), false); ref().arithm(integer, "=", intVar).post(); } return new Constraint(ConstraintsName.SETNOTMEMBER, new PropNotMemberIntSet(integer, set), new PropNotMemberSetInt(integer, set)) { @Override public Constraint makeOpposite() { return member(intVar, set); } }; } } /** * Creates a member constraint stating that the constant cst is not in set * * @param cst an integer * @param set a set variable * @return a constraint ensuring that cst is not in set */ default Constraint notMember(final int cst, final SetVar set) { return new Constraint(ConstraintsName.SETNOTMEMBER, new PropIntCstNotMemberSet(set, cst)) { @Override public Constraint makeOpposite() { return member(cst, set); } }; } /** * Creates a "less or equal" constraint stating that the constant ab. *

Lexicographic order of the sorted lists of elements. * * @param a a SetVar * @param b a SetVar * @return a constraint ensuring that a and b are lexicographically ordered. * @implSpec This is based on {@link org.chocosolver.solver.variables.IViewFactory#setBoolsView(SetVar, int, int)} * and {@link IIntConstraintFactory#lexLessEq(IntVar[], IntVar[])} */ default Constraint setLe(final SetVar a, final SetVar b) { IntIterableRangeSet union = new IntIterableRangeSet(); for (int v : a.getUB()) { union.add(v); } for (int v : b.getUB()) { union.add(v); } int size = union.size(); int min = union.min(); int offset = 0; if (min <= 0) { offset = 1 - min; } int finalOffset = offset; int[] ubA = Arrays.stream(a.getUB().toArray()).map(i -> i + finalOffset).toArray(); int[] ubB = Arrays.stream(b.getUB().toArray()).map(i -> i + finalOffset).toArray(); IntVar[] intsA = ref().intVarArray(size, ArrayUtils.concat(ubA, 0)); IntVar[] intsB = ref().intVarArray(size, ArrayUtils.concat(ubB, 0)); ref().sortedSetIntsChanneling(a, intsA, 0, offset).post(); ref().sortedSetIntsChanneling(b, intsB, 0, offset).post(); return ref().lexLessEq(intsA, intsB); } /** * Creates a "strictly less" constraint stating that the constant ab. *

Lexicographic order of the sorted lists of elements. * * @param a a SetVar * @param b a SetVar * @return a constraint ensuring that a and b are lexicographically ordered. * @implSpec This is based on {@link org.chocosolver.solver.variables.IViewFactory#setBoolsView(SetVar, int, int)} * and {@link IIntConstraintFactory#lexLess(IntVar[], IntVar[])} */ default Constraint setLt(final SetVar a, final SetVar b) { IntIterableRangeSet union = new IntIterableRangeSet(); for (int v : a.getUB()) { union.add(v); } for (int v : b.getUB()) { union.add(v); } int size = union.size(); int min = union.min(); int offset = 0; if (min <= 0) { offset = 1 - min; } int finalOffset = offset; int[] ubA = Arrays.stream(a.getUB().toArray()).map(i -> i + finalOffset).toArray(); int[] ubB = Arrays.stream(b.getUB().toArray()).map(i -> i + finalOffset).toArray(); IntVar[] intsA = ref().intVarArray(size, ArrayUtils.concat(ubA, 0)); IntVar[] intsB = ref().intVarArray(size, ArrayUtils.concat(ubB, 0)); ref().sortedSetIntsChanneling(a, intsA, 0, offset).post(); ref().sortedSetIntsChanneling(b, intsB, 0, offset).post(); return ref().lexLess(intsA, intsB); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy