001package org.clafer.ir.compiler;
002
003import gnu.trove.set.hash.TIntHashSet;
004import java.util.ArrayList;
005import java.util.Arrays;
006import java.util.Collections;
007import java.util.HashMap;
008import java.util.HashSet;
009import java.util.List;
010import java.util.Map;
011import java.util.Map.Entry;
012import java.util.Set;
013import org.clafer.choco.constraint.Constraints;
014import org.clafer.collection.Pair;
015import org.clafer.collection.Triple;
016import org.clafer.common.Check;
017import org.clafer.common.Util;
018import org.clafer.ir.IrAcyclic;
019import org.clafer.ir.IrAdd;
020import org.clafer.ir.IrAllDifferent;
021import org.clafer.ir.IrAnd;
022import org.clafer.ir.IrArrayToSet;
023import org.clafer.ir.IrBoolChannel;
024import org.clafer.ir.IrBoolDomain;
025import org.clafer.ir.IrBoolExpr;
026import org.clafer.ir.IrBoolExprVisitor;
027import org.clafer.ir.IrBoolVar;
028import org.clafer.ir.IrCard;
029import org.clafer.ir.IrCompare;
030import org.clafer.ir.IrCount;
031import org.clafer.ir.IrDiv;
032import org.clafer.ir.IrDomain;
033import org.clafer.ir.IrElement;
034import org.clafer.ir.IrExpr;
035import org.clafer.ir.IrFilterString;
036import org.clafer.ir.IrIfOnlyIf;
037import org.clafer.ir.IrIfThenElse;
038import org.clafer.ir.IrImplies;
039import org.clafer.ir.IrIntChannel;
040import org.clafer.ir.IrIntConstant;
041import org.clafer.ir.IrIntExpr;
042import org.clafer.ir.IrIntExprVisitor;
043import org.clafer.ir.IrIntVar;
044import org.clafer.ir.IrJoinFunction;
045import org.clafer.ir.IrJoinRelation;
046import org.clafer.ir.IrLone;
047import org.clafer.ir.IrMask;
048import org.clafer.ir.IrMember;
049import org.clafer.ir.IrMinus;
050import org.clafer.ir.IrModule;
051import org.clafer.ir.IrMul;
052import org.clafer.ir.IrNot;
053import org.clafer.ir.IrNotImplies;
054import org.clafer.ir.IrNotMember;
055import org.clafer.ir.IrNotWithin;
056import org.clafer.ir.IrOffset;
057import org.clafer.ir.IrOne;
058import org.clafer.ir.IrOr;
059import org.clafer.ir.IrSelectN;
060import org.clafer.ir.IrSetDifference;
061import org.clafer.ir.IrSetExpr;
062import org.clafer.ir.IrSetExprVisitor;
063import org.clafer.ir.IrSetIntersection;
064import org.clafer.ir.IrSetSum;
065import org.clafer.ir.IrSetTernary;
066import org.clafer.ir.IrSetTest;
067import org.clafer.ir.IrSetUnion;
068import org.clafer.ir.IrSetVar;
069import org.clafer.ir.IrSingleton;
070import org.clafer.ir.IrSortSets;
071import org.clafer.ir.IrSortStrings;
072import org.clafer.ir.IrSortStringsChannel;
073import org.clafer.ir.IrSubsetEq;
074import org.clafer.ir.IrTernary;
075import org.clafer.ir.IrUnreachable;
076import org.clafer.ir.IrUtil;
077import org.clafer.ir.IrVar;
078import org.clafer.ir.IrWithin;
079import org.clafer.ir.IrXor;
080import org.clafer.ir.analysis.AnalysisUtil;
081import org.clafer.ir.analysis.Canonicalizer;
082import org.clafer.ir.analysis.Coalescer;
083import org.clafer.ir.analysis.CommonSubexpression;
084import org.clafer.ir.analysis.DuplicateConstraints;
085import org.clafer.ir.analysis.Optimizer;
086import solver.Solver;
087import solver.constraints.Constraint;
088import solver.constraints.ICF;
089import solver.constraints.Operator;
090import solver.constraints.set.SCF;
091import solver.variables.BoolVar;
092import solver.variables.IntVar;
093import solver.variables.SetVar;
094import solver.variables.VF;
095import solver.variables.view.SetConstantView;
096
097/**
098 * Compile from IR to Choco.
099 *
100 * @author jimmy
101 */
102public class IrCompiler {
103
104    private final Solver solver;
105    private final boolean coalesceVariables;
106    private int varNum = 0;
107
108    private IrCompiler(Solver solver, boolean coalesceVariables) {
109        this.solver = Check.notNull(solver);
110        this.coalesceVariables = coalesceVariables;
111    }
112
113    public static IrSolutionMap compile(IrModule in, Solver out) {
114        return compile(in, out, true);
115    }
116
117    public static IrSolutionMap compile(IrModule in, Solver out, boolean coalesceVariables) {
118        IrCompiler compiler = new IrCompiler(out, coalesceVariables);
119        return compiler.compile(in);
120    }
121
122    private IrSolutionMap compile(IrModule module) {
123        IrModule optModule = Optimizer.optimize(Canonicalizer.canonical(module));
124
125        List<IrBoolExpr> constraints = optModule.getConstraints();
126        Map<IrIntVar, IrIntVar> coalescedIntVars = Collections.emptyMap();
127        Map<IrSetVar, IrSetVar> coalescedSetVars = Collections.emptyMap();
128        if (coalesceVariables) {
129            Triple<Map<IrIntVar, IrIntVar>, Map<IrSetVar, IrSetVar>, IrModule> coalesceTriple = Coalescer.coalesce(optModule);
130            coalescedIntVars = coalesceTriple.getFst();
131            coalescedSetVars = coalesceTriple.getSnd();
132            optModule = coalesceTriple.getThd();
133            while (!coalesceTriple.getFst().isEmpty()
134                    || !coalesceTriple.getSnd().isEmpty()) {
135                coalesceTriple = Coalescer.coalesce(optModule);
136                coalescedIntVars = compose(coalescedIntVars, coalesceTriple.getFst());
137                coalescedSetVars = compose(coalescedSetVars, coalesceTriple.getSnd());
138                optModule = coalesceTriple.getThd();
139            }
140            optModule = DuplicateConstraints.removeDuplicates(optModule);
141
142            constraints = new ArrayList<>(optModule.getConstraints().size());
143            for (IrBoolExpr constraint : optModule.getConstraints()) {
144                Pair<IrIntExpr, IrSetVar> cardinality = AnalysisUtil.getAssignCardinality(constraint);
145                if (cardinality != null && cardinality.getFst() instanceof IrIntVar) {
146                    IrIntVar leftInt = (IrIntVar) cardinality.getFst();
147                    SetVar rightSet = getSetVar(cardinality.getSnd());
148
149                    CSet duplicate = cardIntVarMap.get(leftInt);
150                    if (duplicate == null) {
151                        cardIntVarMap.put(leftInt,
152                                new CSet(rightSet,
153                                IrUtil.intersection(leftInt.getDomain(), cardinality.getSnd().getCard())));
154                    } else {
155                        /*
156                         * Case where
157                         *     c = |A|
158                         *     c = |B|
159                         */
160                        cardIntVarMap.put(leftInt, new CSet(rightSet, duplicate.getCard()));
161                        post(SCF.cardinality(rightSet, duplicate.getCard()));
162                    }
163                } else {
164                    constraints.add(constraint);
165                }
166            }
167        }
168
169        commonSubexpressions.addAll(CommonSubexpression.findCommonSubexpressions(optModule));
170
171        for (IrBoolExpr constraint : constraints) {
172            post(compileAsConstraint(constraint));
173        }
174        for (IrVar variable : optModule.getVariables()) {
175            if (variable instanceof IrBoolVar) {
176                compile((IrBoolVar) variable);
177            } else if (variable instanceof IrIntVar) {
178                compile((IrIntVar) variable);
179            } else {
180                compile((IrSetVar) variable);
181            }
182        }
183        return new IrSolutionMap(
184                coalescedIntVars, intVarMap,
185                coalescedSetVars, setVarMap);
186    }
187
188    private static <T> Map<T, T> compose(Map<T, T> f1, Map<T, T> f2) {
189        if (f1.isEmpty()) {
190            return f2;
191        }
192        if (f2.isEmpty()) {
193            return f1;
194        }
195        Map<T, T> composed = new HashMap<>(f1.size() + f2.size());
196        composed.putAll(f2);
197        for (Entry<T, T> e : f1.entrySet()) {
198            T key = e.getKey();
199            T value = f2.get(e.getValue());
200            if (value == null) {
201                value = e.getValue();
202            }
203            composed.put(key, value);
204        }
205        return composed;
206    }
207    private final Map<TIntHashSet, SetVar> cachedSetConstants = new HashMap<>();
208    private final Map<SetVar, IntVar> cachedSetCardVars = new HashMap<>();
209    private final Map<IntVar, IntVar> cachedMinus = new HashMap<>();
210    private final Map<Pair<IntVar, Integer>, IntVar> cachedOffset = new HashMap<>();
211    private final Set<IrExpr> commonSubexpressions = new HashSet<>();
212    private final Map<IrIntExpr, IntVar> cachedCommonIntSubexpressions = new HashMap<>();
213    private final Map<IrSetExpr, CSet> cachedCommonSetSubexpressions = new HashMap<>();
214
215    private void post(Constraint constraint) {
216        if (!solver.TRUE.equals(constraint)) {
217            solver.post(constraint);
218        }
219    }
220
221    private BoolVar boolVar(String name, IrBoolDomain domain) {
222        switch (domain) {
223            case TrueDomain:
224                return VF.one(solver);
225            case FalseDomain:
226                return VF.zero(solver);
227            default:
228                return VF.bool(name, solver);
229        }
230    }
231
232    private IntVar intVar(String name, IrDomain domain) {
233        if (domain.size() == 1) {
234            int constant = domain.getLowBound();
235            switch (domain.getLowBound()) {
236                case 0:
237                    return VF.zero(solver);
238                case 1:
239                    return VF.one(solver);
240                default:
241                    return VF.fixed(constant, solver);
242            }
243        }
244        if (domain.getLowBound() == 0 && domain.getHighBound() == 1) {
245            return VF.bool(name, solver);
246        }
247        if (domain.isBounded()) {
248            return VF.enumerated(name, domain.getLowBound(), domain.getHighBound(), solver);
249        }
250        return VF.enumerated(name, domain.getValues(), solver);
251    }
252
253    private SetVar setVar(String name, IrDomain env, IrDomain ker) {
254        assert IrUtil.isSubsetOf(ker, env);
255        if (env.size() == ker.size()) {
256            int[] values = ker.getValues();
257            TIntHashSet valueSet = new TIntHashSet(values);
258            SetVar var = cachedSetConstants.get(valueSet);
259            if (var == null) {
260                var = new SetConstantView(Arrays.toString(values), valueSet, solver);
261                cachedSetConstants.put(valueSet, var);
262            }
263            return var;
264        }
265        return VF.set(name, env.getValues(), ker.getValues(), solver);
266    }
267
268    private CSet cset(String name, IrDomain env, IrDomain ker, IrDomain card) {
269        SetVar set = setVar(name, env, ker);
270        return new CSet(set, card);
271    }
272
273    private IntVar setCardVar(SetVar set, IrDomain card) {
274        IntVar setCardVar = cachedSetCardVars.get(set);
275        if (setCardVar == null) {
276            setCardVar = intVar("|" + set.getName() + "|", card);
277            if (!(set.instantiated() && card.size() == 1 && card.getLowBound() == set.getKernelSize())) {
278                post(SCF.cardinality(set, setCardVar));
279            }
280            assert !cachedSetCardVars.containsKey(set);
281            cachedSetCardVars.put(set, setCardVar);
282        }
283        return setCardVar;
284    }
285
286    private BoolVar numBoolVar(String name) {
287        return boolVar(name + "#" + varNum++, IrBoolDomain.BoolDomain);
288    }
289
290    private IntVar numIntVar(String name, IrDomain domain) {
291        return intVar(name + "#" + varNum++, domain);
292    }
293
294    private SetVar numSetVar(String name, IrDomain env, IrDomain ker) {
295        return setVar(name + "#" + varNum++, env, ker);
296    }
297
298    private CSet numCset(String name, IrDomain env, IrDomain ker, IrDomain card) {
299        return cset(name + "#" + varNum++, env, ker, card);
300    }
301
302    private BoolVar getBoolVar(IrBoolVar var) {
303        BoolVar bool = (BoolVar) intVarMap.get(var);
304        if (bool == null) {
305            CSet set = cardIntVarMap.get(var);
306            bool = set == null ? boolVar(var.getName(), var.getDomain()) : (BoolVar) set.getCard();
307            intVarMap.put(var, bool);
308        }
309        return bool;
310    }
311
312    private IntVar getIntVar(IrIntVar var) {
313        IntVar iint = intVarMap.get(var);
314        if (iint == null) {
315            CSet set = cardIntVarMap.get(var);
316            iint = set == null ? intVar(var.getName(), var.getDomain()) : set.getCard();
317            intVarMap.put(var, iint);
318        }
319        return iint;
320    }
321    private final Map<IrIntVar, IntVar> intVarMap = new HashMap<>();
322    private final Map<IrIntVar, CSet> cardIntVarMap = new HashMap<>();
323
324    private SetVar getSetVar(IrSetVar var) {
325        SetVar set = setVarMap.get(var);
326        if (set == null) {
327            set = setVar(var.getName(), var.getEnv(), var.getKer());
328            setVarMap.put(var, set);
329        }
330        return set;
331    }
332    private final Map<IrSetVar, SetVar> setVarMap = new HashMap<>();
333
334    private BoolVar asBoolVar(Object obj) {
335        if (obj instanceof Constraint) {
336            return asBoolVar((Constraint) obj);
337        }
338        return (BoolVar) obj;
339    }
340
341    private BoolVar asBoolVar(Constraint op) {
342        return op.reif();
343    }
344
345    private BoolVar compileAsBoolVar(IrBoolExpr expr) {
346        return asBoolVar(expr.accept(boolExprCompiler, BoolVarNoReify));
347    }
348
349    private BoolVar[] compileAsBoolVars(IrBoolExpr[] exprs) {
350        BoolVar[] vars = new BoolVar[exprs.length];
351        for (int i = 0; i < vars.length; i++) {
352            vars[i] = compileAsBoolVar(exprs[i]);
353        }
354        return vars;
355    }
356
357    private IntVar compileAsIntVar(IrBoolExpr expr) {
358        return asBoolVar(expr.accept(boolExprCompiler, BoolVarNoReify));
359    }
360
361    private IntVar[] compileAsIntVars(IrBoolExpr[] exprs) {
362        IntVar[] vars = new IntVar[exprs.length];
363        for (int i = 0; i < vars.length; i++) {
364            vars[i] = compileAsIntVar(exprs[i]);
365        }
366        return vars;
367    }
368
369    private Constraint asConstraint(Object obj) {
370        if (obj instanceof BoolVar) {
371            return asConstraint((BoolVar) obj);
372        }
373        return (Constraint) obj;
374    }
375
376    private Constraint asConstraint(BoolVar var) {
377        return _arithm(var, "=", 1);
378    }
379
380    private Constraint compileAsConstraint(IrBoolExpr expr) {
381        return asConstraint(expr.accept(boolExprCompiler, ConstraintNoReify));
382    }
383
384    private Constraint compileAsConstraint(IrBoolExpr expr, BoolVar reify) {
385        BoolArg arg = new BoolArg(reify, Preference.Constraint);
386        Object result = expr.accept(boolExprCompiler, arg);
387        if (result instanceof Constraint) {
388            Constraint constraint = (Constraint) result;
389            if (arg.hasReify()) {
390                // The compliation failed to reify, explicitly reify now.
391                return _arithm(arg.useReify(), "=", constraint.reif());
392            }
393            return constraint;
394        }
395        BoolVar var = (BoolVar) result;
396        if (arg.hasReify()) {
397            return _arithm(arg.useReify(), "=", var);
398        }
399        return asConstraint(var);
400    }
401
402    private Constraint compileAsEqual(IrIntExpr expr, IntVar value, BoolVar reify) {
403        Object result = commonSubexpressions.contains(expr)
404                ? compile(expr)
405                : expr.accept(intExprCompiler, value);
406        if (result instanceof IntVar) {
407            // The compliation failed to reify, explicitly reify now.
408            return _reify_equal(reify, value, (IntVar) result);
409        }
410        return _arithm(((Constraint) result).reif(), "=", reify);
411    }
412
413    private Constraint compileAsNotEqual(IrIntExpr expr, IntVar value, BoolVar reify) {
414        Object result = commonSubexpressions.contains(expr)
415                ? compile(expr)
416                : expr.accept(intExprCompiler, value);
417        if (result instanceof IntVar) {
418            // The compliation failed to reify, explicitly reify now.
419            return _reify_not_equal(reify, value, (IntVar) result);
420        }
421        return _arithm(((Constraint) result).reif(), "!=", reify);
422    }
423
424    private Constraint compileAsConstraint(IrIntExpr expr, IntVar reify) {
425        Object result = commonSubexpressions.contains(expr)
426                ? compile(expr)
427                : expr.accept(intExprCompiler, reify);
428        if (result instanceof IntVar) {
429            // The compliation failed to reify, explicitly reify now.
430            return _arithm(reify, "=", (IntVar) result);
431        }
432        return (Constraint) result;
433    }
434
435    private Constraint compileAsConstraint(IrSetExpr expr, CSet reify) {
436        Object result = commonSubexpressions.contains(expr)
437                ? compile(expr)
438                : expr.accept(setExprCompiler, reify);
439        if (result instanceof CSet) {
440            CSet set = (CSet) result;
441            // The compliation failed to reify, explicitly reify now.
442            return _equal(reify, set);
443        }
444        return (Constraint) result;
445    }
446
447    private Constraint compileArithm(IrIntExpr a, Rel op, IrIntExpr b) {
448        if (a instanceof IrMinus) {
449            IrMinus minus = (IrMinus) a;
450            // -a ◁ b <=> a + b ▷ 0
451            return compileArithm(minus.getExpr(), Arithm.ADD, b, op.reverse(), 0);
452        }
453        if (b instanceof IrMinus) {
454            IrMinus minus = (IrMinus) b;
455            // a ◁ -b <=> a + b ◁ 0
456            return compileArithm(a, Arithm.ADD, minus.getExpr(), op, 0);
457        }
458        if (a instanceof IrNot) {
459            IrNot not = (IrNot) a;
460            // !a ◁ b <=> 1 - a ◁ b <=> a + b ▷ 1
461            return compileArithm(not.getExpr(), Arithm.ADD, b, op.reverse(), 1);
462        }
463        if (b instanceof IrNot) {
464            IrNot not = (IrNot) b;
465            // a ◁ !b <=> a ◁ 1 - b <=> a + b ◁ 1
466            return compileArithm(a, Arithm.ADD, not.getExpr(), op, 1);
467        }
468        if (a instanceof IrIntConstant) {
469            IrIntConstant constant = (IrIntConstant) a;
470            return _arithm(compile(b), op.reverse().getSyntax(), constant.getValue());
471        }
472        if (b instanceof IrIntConstant) {
473            IrIntConstant constant = (IrIntConstant) b;
474            return _arithm(compile(a), op.getSyntax(), constant.getValue());
475        }
476        switch (op) {
477            case EQ:
478                if (b instanceof IrBoolVar) {
479                    return compileAsConstraint(a, compile(b));
480                }
481                return compileAsConstraint(b, compile(a));
482            case NQ:
483                if (a instanceof IrVar && b instanceof IrVar) {
484                    return _arithm(compile(a), "!=", compile(b));
485                }
486                if (a instanceof IrBoolExpr && b instanceof IrBoolExpr) {
487                    IrBoolExpr boolA = (IrBoolExpr) a;
488                    IrBoolExpr boolB = (IrBoolExpr) b;
489                    if (boolA instanceof IrCompare) {
490                        return compileAsConstraint(boolA.negate(), compileAsIntVar(boolB));
491                    }
492                    if (boolB instanceof IrCompare) {
493                        return compileAsConstraint(boolB.negate(), compileAsIntVar(boolA));
494                    }
495                    if (boolA instanceof IrBoolVar) {
496                        return compileAsConstraint(boolB, compileAsIntVar(boolA.negate()));
497                    }
498                    return compileAsConstraint(boolA, compileAsIntVar(boolB.negate()));
499                }
500        }
501        return _arithm(compile(a), op.getSyntax(), compile(b));
502    }
503
504    private Constraint compileArithm(IrIntExpr a, Rel op1, IrIntExpr b, Arithm op2, int c) {
505        if (c == 0) {
506            return compileArithm(a, op1, b);
507        }
508        if (a instanceof IrMinus) {
509            IrMinus minus = (IrMinus) a;
510            // -a ◁ b + c <=> a + b ▷ -c
511            // -a ◁ b - c <=> a + b ▷ c
512            return compileArithm(minus.getExpr(), Arithm.ADD, b, op1.reverse(),
513                    Arithm.ADD.equals(op2) ? -c : c);
514        }
515        if (b instanceof IrMinus) {
516            IrMinus minus = (IrMinus) b;
517            // a ◁ -b + c <=> a + b ▷ c
518            // a ◁ -b - c <=> a + b ▷ -c
519            return compileArithm(a, Arithm.ADD, minus.getExpr(), op1.reverse(),
520                    Arithm.ADD.equals(op2) ? c : -c);
521        }
522        if (a instanceof IrAdd) {
523            IrAdd add = (IrAdd) a;
524            IrIntExpr[] addends = add.getAddends();
525            if (addends.length == 1) {
526                // a + d ◁ b + c <=> a ◁ b + (c - d)
527                // a + d ◁ b - c <=> a ◁ b - (c + d)
528                return compileArithm(addends[0], op1, b, op2,
529                        Arithm.ADD.equals(op2) ? c - add.getOffset() : c + add.getOffset());
530            }
531        }
532        if (b instanceof IrAdd) {
533            IrAdd add = (IrAdd) b;
534            IrIntExpr[] addends = add.getAddends();
535            if (addends.length == 1) {
536                // a ◁ b + d + c <=> a ◁ b + (c + d)
537                // a ◁ b + d - c <=> a ◁ b - (c - d)
538                return compileArithm(a, op1, addends[0], op2,
539                        Arithm.ADD.equals(op2) ? c + add.getOffset() : c - add.getOffset());
540            }
541        }
542        if (a instanceof IrNot) {
543            IrNot not = (IrNot) a;
544            // !a ◁ b + c <=> 1 - a ◁ b + c <=> a + b ▷ 1 - c
545            // !a ◁ b - c <=> 1 - a ◁ b - c <=> a + b ▷ 1 + c
546            return compileArithm(not.getExpr(), Arithm.ADD, b, op1.reverse(),
547                    Arithm.ADD.equals(op2) ? 1 - c : 1 + c);
548        }
549        if (b instanceof IrNot) {
550            IrNot not = (IrNot) b;
551            // a ◁ !b + c <=> a ◁ 1 - b + c <=> a + b ▷ 1 + c
552            // a ◁ !b - c <=> a ◁ 1 - b - c <=> a + b ▷ 1 - c
553            return compileArithm(a, Arithm.ADD, not.getExpr(), op1.reverse(),
554                    Arithm.ADD.equals(op2) ? 1 + c : 1 - c);
555        }
556        return _arithm(compile(a), op1.getSyntax(), compile(b), op2.getSyntax(), c);
557    }
558
559    private Constraint compileArithm(IrIntExpr a, Arithm op1, IrIntExpr b, Rel op2, int c) {
560        if (b instanceof IrMinus) {
561            IrMinus minus = (IrMinus) b;
562            // a + (-b) ◁ c <=> a - b ◁ c
563            // a - (-b) ◁ c <=> a + b ◁ c
564            return compileArithm(a, op1.negate(), minus.getExpr(), op2, c);
565        }
566        if (a instanceof IrAdd) {
567            IrAdd add = (IrAdd) a;
568            IrIntExpr[] addends = add.getAddends();
569            if (addends.length == 1) {
570                // a + d + b ◁ c <=> a + b ◁ (c - d)
571                // a + d - b ◁ c <=> a - b ◁ (c - d)
572                return compileArithm(addends[0], op1, b, op2, c - add.getOffset());
573            }
574        }
575        if (b instanceof IrAdd) {
576            IrAdd add = (IrAdd) b;
577            IrIntExpr[] addends = add.getAddends();
578            if (addends.length == 1) {
579                // a + b + d ◁ c <=> a + b ◁ (c - d)
580                // a - (b + d) ◁ c <=> a - b ◁ (c + d)
581                return compileArithm(a, op1, addends[0], op2,
582                        Arithm.ADD.equals(op2) ? c - add.getOffset() : c + add.getOffset());
583            }
584        }
585        switch (op1) {
586            case ADD:
587                if (a instanceof IrNot) {
588                    IrNot not = (IrNot) a;
589                    // !a + b ◁ c <=> 1 - a + b ◁ c <=> b ◁ a + c - 1
590                    return compileArithm(b, op2, not.getExpr(), Arithm.ADD, c - 1);
591                }
592                if (b instanceof IrNot) {
593                    IrNot not = (IrNot) b;
594                    // a + !b ◁ c <=> a + 1 - b ◁ c <=> a ◁ b + c - 1
595                    return compileArithm(a, op2, not.getExpr(), Arithm.ADD, c - 1);
596                }
597                if (a instanceof IrMinus) {
598                    IrMinus minus = (IrMinus) a;
599                    // (-a) + b ◁ c <=> b - a ◁ c
600                    return compileArithm(b, Arithm.MINUS, minus.getExpr(), op2, c);
601                }
602                break;
603            case MINUS:
604                if (a instanceof IrNot) {
605                    IrNot not = (IrNot) a;
606                    // !a - b ◁ c <=> 1 - a - b ◁ c <=> -a - b ◁ c - 1 <=> a + b ▷ 1 - c
607                    return compileArithm(not.getExpr(), Arithm.ADD, b, op2.reverse(), 1 - c);
608                }
609                if (b instanceof IrNot) {
610                    IrNot not = (IrNot) b;
611                    // a - !b ◁ c <=> a - 1 + b ◁ c <=> a + b ◁ c + 1
612                    return compileArithm(a, Arithm.ADD, not.getExpr(), op2, c + 1);
613                }
614                if (a instanceof IrMinus) {
615                    IrMinus minus = (IrMinus) a;
616                    // (-a) - b ◁ c <=> a + b ▷ -c
617                    return compileArithm(minus.getExpr(), Arithm.ADD, b, op2.reverse(), -c);
618                }
619                break;
620        }
621        return _arithm(compile(a), op1.getSyntax(), compile(b), op2.getSyntax(), c);
622    }
623
624    private Constraint compileArithm(IrIntExpr a, IrCompare.Op op, IrIntExpr b) {
625        return compileArithm(a, Rel.from(op), b);
626    }
627
628    private Constraint compileArithm(IrIntExpr a, IrCompare.Op op1, IrIntExpr b, Arithm op2, int c) {
629        return compileArithm(a, Rel.from(op1), b, op2, c);
630    }
631
632    private Constraint compileArithm(IrIntExpr a, Arithm op1, IrIntExpr b, IrCompare.Op op2, int c) {
633        return compileArithm(a, op1, b, Rel.from(op2), c);
634    }
635
636    private Constraint compileArithm(int c, IrCompare.Op op1, IrIntExpr a, Arithm op2, IrIntExpr b) {
637        return compileArithm(b, op2, a, Rel.from(op1).reverse(), c);
638    }
639
640    private Constraint compileArithm(int c, Arithm op1, IrIntExpr a, IrCompare.Op op2, IrIntExpr b) {
641        return compileArithm(b, Rel.from(op2).reverse(), a, op1, c);
642    }
643
644    private Object compile(IrBoolExpr expr) {
645        return expr.accept(boolExprCompiler, ConstraintNoReify);
646    }
647
648    private IntVar compile(IrIntExpr expr) {
649        IntVar var = cachedCommonIntSubexpressions.get(expr);
650        if (var == null) {
651            var = (IntVar) expr.accept(intExprCompiler, null);
652            if (commonSubexpressions.contains(expr)) {
653                cachedCommonIntSubexpressions.put(expr, var);
654            }
655        }
656        return var;
657    }
658
659    private IntVar[] compile(IrIntExpr[] exprs) {
660        IntVar[] vars = new IntVar[exprs.length];
661        for (int i = 0; i < vars.length; i++) {
662            vars[i] = compile(exprs[i]);
663        }
664        return vars;
665    }
666
667    private CSet compile(IrSetExpr expr) {
668        CSet set = cachedCommonSetSubexpressions.get(expr);
669        if (set == null) {
670            set = (CSet) expr.accept(setExprCompiler, null);
671            if (commonSubexpressions.contains(expr)) {
672                cachedCommonSetSubexpressions.put(expr, set);
673            }
674        }
675        return set;
676    }
677
678    private CSet[] compile(IrSetExpr[] exprs) {
679        CSet[] vars = new CSet[exprs.length];
680        for (int i = 0; i < vars.length; i++) {
681            vars[i] = compile(exprs[i]);
682        }
683        return vars;
684    }
685    private final IrBoolExprVisitor<BoolArg, Object> boolExprCompiler = new IrBoolExprVisitor<BoolArg, Object>() {
686        @Override
687        public Object visit(IrBoolVar ir, BoolArg a) {
688            return getBoolVar(ir);
689        }
690
691        @Override
692        public Object visit(IrNot ir, BoolArg a) {
693            BoolVar var = compileAsBoolVar(ir.getExpr());
694            if (a.hasReify()) {
695                return _arithm(a.useReify(), "!=", var);
696            }
697            return Preference.Constraint.equals(a.getPreference())
698                    ? _arithm(var, "=", 0)
699                    : var.not();
700        }
701
702        @Override
703        public Object visit(IrAnd ir, BoolArg a) {
704            IrBoolExpr[] operands = ir.getOperands();
705            switch (operands.length) {
706                case 1:
707                    // delegate
708                    return operands[0].accept(this, a);
709                case 2:
710                    return compileArithm(operands[0], Arithm.ADD, operands[1], Rel.EQ, 2);
711                default:
712                    return Constraints.and(compileAsBoolVars(ir.getOperands()));
713            }
714        }
715
716        @Override
717        public Object visit(IrLone ir, BoolArg a) {
718            IrBoolExpr[] operands = ir.getOperands();
719            switch (operands.length) {
720                case 1:
721                    return solver.TRUE;
722                case 2:
723                    return compileArithm(operands[0], Arithm.ADD, operands[1], Rel.LE, 1);
724                default:
725                    return Constraints.lone(compileAsBoolVars(ir.getOperands()));
726            }
727        }
728
729        @Override
730        public Object visit(IrOne ir, BoolArg a) {
731            IrBoolExpr[] operands = ir.getOperands();
732            switch (operands.length) {
733                case 1:
734                    // delegate
735                    return operands[0].accept(this, a);
736                case 2:
737                    return compileArithm(operands[0], Arithm.ADD, operands[1], Rel.EQ, 1);
738                default:
739                    return Constraints.one(compileAsBoolVars(ir.getOperands()));
740            }
741        }
742
743        @Override
744        public Object visit(IrOr ir, BoolArg a) {
745            IrBoolExpr[] operands = ir.getOperands();
746            switch (operands.length) {
747                case 1:
748                    // delegate
749                    return operands[0].accept(this, a);
750                case 2:
751                    return compileArithm(operands[0], Arithm.ADD, operands[1], Rel.GE, 1);
752                default:
753                    return Constraints.or(compileAsBoolVars(ir.getOperands()));
754            }
755        }
756
757        @Override
758        public Object visit(IrImplies ir, BoolArg a) {
759            return compileArithm(ir.getAntecedent(), Rel.LE, ir.getConsequent());
760        }
761
762        @Override
763        public Object visit(IrNotImplies ir, BoolArg a) {
764            return compileArithm(ir.getAntecedent(), Rel.GT, ir.getConsequent());
765        }
766
767        @Override
768        public Object visit(IrIfThenElse ir, BoolArg a) {
769            BoolVar antecedent = compileAsBoolVar(ir.getAntecedent());
770            BoolVar consequent = compileAsBoolVar(ir.getConsequent());
771            BoolVar alternative = compileAsBoolVar(ir.getAlternative());
772            return _ifThenElse(antecedent, consequent, alternative);
773        }
774
775        @Override
776        public Object visit(IrIfOnlyIf ir, BoolArg a) {
777            return compileArithm(ir.getLeft(), Rel.EQ, ir.getRight());
778        }
779
780        @Override
781        public Object visit(IrXor ir, BoolArg a) {
782            return compileArithm(ir.getLeft(), Rel.NQ, ir.getRight());
783        }
784
785        @Override
786        public Object visit(IrWithin ir, BoolArg a) {
787            IntVar var = compile(ir.getValue());
788            IrDomain range = ir.getRange();
789            if (range.isBounded()) {
790                return _within(var, range.getLowBound(), range.getHighBound());
791            }
792            return _within(var, range.getValues());
793        }
794
795        @Override
796        public Object visit(IrNotWithin ir, BoolArg a) {
797            IntVar var = compile(ir.getValue());
798            IrDomain range = ir.getRange();
799            if (range.isBounded()) {
800                return _not_within(var, range.getLowBound(), range.getHighBound());
801            }
802            return _not_within(var, range.getValues());
803        }
804
805        @Override
806        public Object visit(IrCompare ir, BoolArg a) {
807            IrCompare.Op op = ir.getOp();
808            IrIntExpr left = ir.getLeft();
809            IrIntExpr right = ir.getRight();
810            if (left instanceof IrAdd) {
811                IrAdd add = (IrAdd) left;
812                IrIntExpr[] addends = add.getAddends();
813                if (addends.length == 1) {
814                    return compileArithm(addends[0], Arithm.MINUS, right, op, -add.getOffset());
815                }
816                if (addends.length == 2) {
817                    Integer constant = IrUtil.getConstant(right);
818                    if (constant != null) {
819                        return compileArithm(addends[0], Arithm.ADD, addends[1], op, constant.intValue() - add.getOffset());
820                    }
821                }
822            }
823            if (right instanceof IrAdd) {
824                IrAdd add = (IrAdd) right;
825                IrIntExpr[] addends = add.getAddends();
826                if (addends.length == 1) {
827                    return compileArithm(left, Arithm.MINUS, addends[0], op, add.getOffset());
828                }
829                if (addends.length == 2) {
830                    Integer constant = IrUtil.getConstant(left);
831                    if (constant != null) {
832                        return compileArithm(constant.intValue() - add.getOffset(), op, addends[0], Arithm.ADD, addends[1]);
833                    }
834                }
835            }
836            if (IrCompare.Op.Equal.equals(op)) {
837                if (a.hasReify()) {
838                    BoolVar reify = a.useReify();
839                    return right instanceof IrIntVar
840                            ? compileAsEqual(left, compile(right), reify)
841                            : compileAsEqual(right, compile(left), reify);
842                }
843                if (a.getPreference().equals(Preference.BoolVar)) {
844                    BoolVar reify = numBoolVar("ReifyEqual");
845                    post(right instanceof IrIntVar
846                            ? compileAsEqual(left, compile(right), reify)
847                            : compileAsEqual(right, compile(left), reify));
848                    return reify;
849                }
850            } else if (IrCompare.Op.NotEqual.equals(op)) {
851                if (a.hasReify()) {
852                    BoolVar reify = a.useReify();
853                    return right instanceof IrIntVar
854                            ? compileAsNotEqual(left, compile(right), reify)
855                            : compileAsNotEqual(right, compile(left), reify);
856                }
857                if (a.getPreference().equals(Preference.BoolVar)) {
858                    BoolVar reify = numBoolVar("ReifyNotEqual");
859                    post(right instanceof IrIntVar
860                            ? compileAsNotEqual(left, compile(right), reify)
861                            : compileAsNotEqual(right, compile(left), reify));
862                    return reify;
863                }
864            }
865            return compileArithm(left, op, right);
866        }
867
868        private Triple<String, IrIntExpr, Integer> getOffset(IrIntExpr expr) {
869            if (expr instanceof IrAdd) {
870                IrAdd add = (IrAdd) expr;
871                IrIntExpr[] addends = add.getAddends();
872                if (addends.length == 1) {
873                    return new Triple<>("-", addends[0], add.getOffset());
874                }
875            }
876            return null;
877        }
878
879        @Override
880        public Object visit(IrSetTest ir, BoolArg a) {
881            switch (ir.getOp()) {
882                case Equal:
883                    if (ir.getRight() instanceof IrSetVar) {
884                        return compileAsConstraint(ir.getLeft(), compile(ir.getRight()));
885                    }
886                    return compileAsConstraint(ir.getRight(), compile(ir.getLeft()));
887                case NotEqual:
888                    return _not_equal(compile(ir.getLeft()), compile(ir.getRight()));
889                default:
890                    throw new IllegalArgumentException("Unexpected operator.");
891            }
892        }
893
894        @Override
895        public Object visit(IrMember ir, BoolArg a) {
896            return _member(compile(ir.getElement()), compile(ir.getSet()).getSet());
897        }
898
899        @Override
900        public Object visit(IrNotMember ir, BoolArg a) {
901            return _not_member(compile(ir.getElement()), compile(ir.getSet()).getSet());
902        }
903
904        @Override
905        public Object visit(IrSubsetEq ir, BoolArg a) {
906            return _subset_eq(compile(ir.getSubset()), compile(ir.getSuperset()));
907        }
908
909        @Override
910        public Constraint visit(IrBoolChannel ir, BoolArg a) {
911            BoolVar[] bools = compileAsBoolVars(ir.getBools());
912            CSet set = compile(ir.getSet());
913            return SCF.bool_channel(bools, set.getSet(), 0);
914        }
915
916        @Override
917        public Constraint visit(IrIntChannel ir, BoolArg a) {
918            IntVar[] ints = compile(ir.getInts());
919            CSet[] sets = compile(ir.getSets());
920            return Constraints.intChannel(mapSet(sets), ints);
921        }
922
923        @Override
924        public Object visit(IrSortStrings ir, BoolArg a) {
925            IntVar[][] strings = new IntVar[ir.getStrings().length][];
926            for (int i = 0; i < strings.length; i++) {
927                strings[i] = compile(ir.getStrings()[i]);
928            }
929            return ir.isStrict() ? _lex_chain_less(strings) : _lex_chain_less_eq(strings);
930        }
931
932        @Override
933        public Object visit(IrSortSets ir, BoolArg a) {
934            CSet[] sets = compile(ir.getSets());
935            return Constraints.sortedSets(mapSet(sets), mapCard(sets));
936        }
937
938        @Override
939        public Object visit(IrSortStringsChannel ir, BoolArg a) {
940            IntVar[][] strings = new IntVar[ir.getStrings().length][];
941            for (int i = 0; i < strings.length; i++) {
942                strings[i] = compile(ir.getStrings()[i]);
943            }
944            return _lex_chain_channel(strings, compile(ir.getInts()));
945        }
946
947        @Override
948        public Constraint visit(IrAllDifferent ir, BoolArg a) {
949            IrIntExpr[] operands = ir.getOperands();
950
951            IntVar[] $operands = new IntVar[operands.length];
952            for (int i = 0; i < $operands.length; i++) {
953                $operands[i] = compile(operands[i]);
954            }
955            return _all_different($operands);
956        }
957
958        @Override
959        public Constraint visit(IrSelectN ir, BoolArg a) {
960            BoolVar[] bools = compileAsBoolVars(ir.getBools());
961            IntVar n = compile(ir.getN());
962            return Constraints.selectN(bools, n);
963        }
964
965        @Override
966        public Object visit(IrAcyclic ir, BoolArg a) {
967            IntVar[] edges = compile(ir.getEdges());
968            return Constraints.acyclic(edges);
969        }
970
971        @Override
972        public Object visit(IrUnreachable ir, BoolArg a) {
973            IntVar[] edges = compile(ir.getEdges());
974            return Constraints.unreachable(edges, ir.getFrom(), ir.getTo());
975        }
976
977        @Override
978        public Object visit(IrFilterString ir, BoolArg a) {
979            CSet set = compile(ir.getSet());
980            int offset = ir.getOffset();
981            IntVar[] string = compile(ir.getString());
982            IntVar[] result = compile(ir.getResult());
983            return Constraints.filterString(set.getSet(), set.getCard(), offset, string, result);
984        }
985    };
986    private final IrIntExprVisitor<IntVar, Object> intExprCompiler = new IrIntExprVisitor<IntVar, Object>() {
987        @Override
988        public IntVar visit(IrIntVar ir, IntVar reify) {
989            return getIntVar(ir);
990        }
991
992        @Override
993        public IntVar visit(IrMinus ir, IntVar reify) {
994            IntVar expr = compile(ir.getExpr());
995            IntVar minus = cachedMinus.get(expr);
996            if (minus == null) {
997                minus = VF.minus(compile(ir.getExpr()));
998                cachedMinus.put(expr, minus);
999            }
1000            return minus;
1001        }
1002
1003        @Override
1004        public Object visit(IrCard ir, IntVar reify) {
1005            CSet set = compile(ir.getSet());
1006            return set.getCard();
1007        }
1008
1009        private Pair<Integer, IrIntExpr> getCoefficient(IrIntExpr e) {
1010            if (e instanceof IrMinus) {
1011                return new Pair<>(-1, ((IrMinus) e).getExpr());
1012            } else if (e instanceof IrMul) {
1013                IrMul mul = (IrMul) e;
1014                Integer constant = IrUtil.getConstant(mul.getMultiplicand());
1015                if (constant != null) {
1016                    return new Pair<>(constant, mul.getMultiplier());
1017                }
1018                constant = IrUtil.getConstant(mul.getMultiplier());
1019                if (constant != null) {
1020                    return new Pair<>(constant, mul.getMultiplicand());
1021                }
1022            }
1023            return new Pair<>(1, e);
1024        }
1025
1026        private Pair<int[], IrIntExpr[]> getCoefficients(IrIntExpr[] es) {
1027            int[] coefficients = new int[es.length];
1028            IrIntExpr[] exprs = new IrIntExpr[es.length];
1029            for (int i = 0; i < es.length; i++) {
1030                Pair<Integer, IrIntExpr> coef = getCoefficient(es[i]);
1031                coefficients[i] = coef.getFst();
1032                exprs[i] = coef.getSnd();
1033            }
1034            return new Pair<>(coefficients, exprs);
1035        }
1036
1037        @Override
1038        public Object visit(IrAdd ir, IntVar reify) {
1039            int offset = ir.getOffset();
1040            IrIntExpr[] addends = ir.getAddends();
1041            switch (addends.length) {
1042                case 0:
1043                    // This case should have already been optimized earlier.
1044                    return VF.fixed(offset, solver);
1045                case 1:
1046                    if (reify == null) {
1047                        return _offset(compile(addends[0]), offset);
1048                    }
1049                    return _arithm(reify, "-", compile(addends[0]), "=", offset);
1050                default:
1051                    Pair<int[], IrIntExpr[]> coeffs = getCoefficients(addends);
1052                    int[] coefficients = coeffs.getFst();
1053                    IntVar[] operands = compile(coeffs.getSnd());
1054                    if (reify == null) {
1055                        IntVar sum = numIntVar("Sum", IrUtil.offset(ir.getDomain(), -offset));
1056                        post(_scalar(sum, coefficients, operands));
1057                        return _offset(sum, offset);
1058                    }
1059                    if (offset != 0) {
1060                        coefficients = Util.cons(1, coefficients);
1061                        operands = Util.cons(VF.fixed(offset, solver), operands);
1062                    }
1063                    return _scalar(reify, coefficients, operands);
1064            }
1065        }
1066
1067        @Override
1068        public Object visit(IrMul ir, IntVar reify) {
1069            IrIntExpr multiplicand = ir.getMultiplicand();
1070            IrIntExpr multiplier = ir.getMultiplier();
1071            Integer multiplicandConstant = IrUtil.getConstant(multiplicand);
1072            Integer multiplierConstant = IrUtil.getConstant(multiplier);
1073            if (multiplicandConstant != null) {
1074                switch (multiplicandConstant.intValue()) {
1075                    case 0:
1076                        return compileAsConstraint(multiplicand, reify);
1077                    case 1:
1078                        return compileAsConstraint(multiplier, reify);
1079                    default:
1080                        if (multiplicandConstant.intValue() >= -1) {
1081                            return VF.scale(compile(multiplier), multiplicandConstant.intValue());
1082                        }
1083                }
1084            }
1085            if (multiplierConstant != null) {
1086                switch (multiplierConstant.intValue()) {
1087                    case 0:
1088                        return compileAsConstraint(multiplier, reify);
1089                    case 1:
1090                        return compileAsConstraint(multiplicand, reify);
1091                    default:
1092                        if (multiplierConstant.intValue() >= -1) {
1093                            return VF.scale(compile(multiplicand), multiplierConstant.intValue());
1094                        }
1095                }
1096            }
1097            if (reify == null) {
1098                IntVar product = numIntVar("Mul", ir.getDomain());
1099                post(_times(compile(multiplicand), compile(multiplier), product));
1100                return product;
1101            }
1102            return _times(compile(multiplicand), compile(multiplier), reify);
1103        }
1104
1105        @Override
1106        public Object visit(IrDiv ir, IntVar reify) {
1107            IrIntExpr dividend = ir.getDividend();
1108            IrIntExpr divisor = ir.getDivisor();
1109            if (reify == null) {
1110                IntVar quotient = numIntVar("Div", ir.getDomain());
1111                post(_div(compile(dividend), compile(divisor), quotient));
1112                return quotient;
1113            }
1114            return _div(compile(dividend), compile(divisor), reify);
1115        }
1116
1117        @Override
1118        public Object visit(IrElement ir, IntVar reify) {
1119            if (reify == null) {
1120                IntVar element = numIntVar("Element", ir.getDomain());
1121                post(_element(compile(ir.getIndex()), compile(ir.getArray()), element));
1122                return element;
1123            }
1124            return _element(compile(ir.getIndex()), compile(ir.getArray()), reify);
1125        }
1126
1127        @Override
1128        public Object visit(IrCount ir, IntVar reify) {
1129            IntVar[] array = compile(ir.getArray());
1130            if (reify == null) {
1131                IntVar count = numIntVar("Count", ir.getDomain());
1132                post(_count(ir.getValue(), array, count));
1133                return count;
1134            }
1135            return _count(ir.getValue(), array, reify);
1136        }
1137
1138        @Override
1139        public Object visit(IrSetSum ir, IntVar reify) {
1140            CSet set = compile(ir.getSet());
1141            if (reify == null) {
1142                IntVar sum = numIntVar("SetSum", ir.getDomain());
1143                post(Constraints.setSum(set.getSet(), set.getCard(), sum));
1144                return sum;
1145            }
1146            return Constraints.setSum(set.getSet(), set.getCard(), reify);
1147        }
1148
1149        @Override
1150        public Object visit(IrTernary ir, IntVar reify) {
1151            BoolVar antecedent = compileAsBoolVar(ir.getAntecedent());
1152            IntVar consequent = compile(ir.getConsequent());
1153            IntVar alternative = compile(ir.getAlternative());
1154            if (reify == null) {
1155                IntVar ternary = numIntVar("Ternary", ir.getDomain());
1156                BoolVar reifyConsequent = numBoolVar("ReifyEqual");
1157                BoolVar reifyAlternative = numBoolVar("ReifyEqual");
1158                solver.post(_reify_equal(reifyConsequent, consequent, ternary));
1159                solver.post(_reify_equal(reifyAlternative, alternative, ternary));
1160                post(_ifThenElse(antecedent, reifyConsequent, reifyAlternative));
1161                return ternary;
1162            }
1163            BoolVar reifyConsequent = numBoolVar("ReifyEqual");
1164            BoolVar reifyAlternative = numBoolVar("ReifyEqual");
1165            solver.post(_reify_equal(reifyConsequent, consequent, reify));
1166            solver.post(_reify_equal(reifyAlternative, alternative, reify));
1167            return _ifThenElse(antecedent, reifyConsequent, reifyAlternative);
1168        }
1169
1170        private Object compileBool(IrBoolExpr expr, IntVar a) {
1171            if (a instanceof BoolVar) {
1172                return compileAsConstraint(expr, (BoolVar) a);
1173            }
1174            IntVar var = compileAsIntVar(expr);
1175            return a == null ? var : _arithm(var, "=", a);
1176        }
1177
1178        @Override
1179        public Object visit(IrBoolVar ir, IntVar a) {
1180            return compileBool(ir, a);
1181        }
1182
1183        @Override
1184        public Object visit(IrNot ir, IntVar a) {
1185            return compileBool(ir, a);
1186        }
1187
1188        @Override
1189        public Object visit(IrAnd ir, IntVar a) {
1190            return compileBool(ir, a);
1191        }
1192
1193        @Override
1194        public Object visit(IrLone ir, IntVar a) {
1195            return compileBool(ir, a);
1196        }
1197
1198        @Override
1199        public Object visit(IrOne ir, IntVar a) {
1200            return compileBool(ir, a);
1201        }
1202
1203        @Override
1204        public Object visit(IrOr ir, IntVar a) {
1205            return compileBool(ir, a);
1206        }
1207
1208        @Override
1209        public Object visit(IrImplies ir, IntVar a) {
1210            return compileBool(ir, a);
1211        }
1212
1213        @Override
1214        public Object visit(IrNotImplies ir, IntVar a) {
1215            return compileBool(ir, a);
1216        }
1217
1218        @Override
1219        public Object visit(IrIfThenElse ir, IntVar a) {
1220            return compileBool(ir, a);
1221        }
1222
1223        @Override
1224        public Object visit(IrIfOnlyIf ir, IntVar a) {
1225            return compileBool(ir, a);
1226        }
1227
1228        @Override
1229        public Object visit(IrXor ir, IntVar a) {
1230            return compileBool(ir, a);
1231        }
1232
1233        @Override
1234        public Object visit(IrWithin ir, IntVar a) {
1235            return compileBool(ir, a);
1236        }
1237
1238        @Override
1239        public Object visit(IrNotWithin ir, IntVar a) {
1240            return compileBool(ir, a);
1241        }
1242
1243        @Override
1244        public Object visit(IrCompare ir, IntVar a) {
1245            return compileBool(ir, a);
1246        }
1247
1248        @Override
1249        public Object visit(IrSetTest ir, IntVar a) {
1250            return compileBool(ir, a);
1251        }
1252
1253        @Override
1254        public Object visit(IrMember ir, IntVar a) {
1255            return compileBool(ir, a);
1256        }
1257
1258        @Override
1259        public Object visit(IrNotMember ir, IntVar a) {
1260            return compileBool(ir, a);
1261        }
1262
1263        @Override
1264        public Object visit(IrSubsetEq ir, IntVar a) {
1265            return compileBool(ir, a);
1266        }
1267
1268        @Override
1269        public Object visit(IrBoolChannel ir, IntVar a) {
1270            return compileBool(ir, a);
1271        }
1272
1273        @Override
1274        public Object visit(IrIntChannel ir, IntVar a) {
1275            return compileBool(ir, a);
1276        }
1277
1278        @Override
1279        public Object visit(IrSortStrings ir, IntVar a) {
1280            return compileBool(ir, a);
1281        }
1282
1283        @Override
1284        public Object visit(IrSortSets ir, IntVar a) {
1285            return compileBool(ir, a);
1286        }
1287
1288        @Override
1289        public Object visit(IrSortStringsChannel ir, IntVar a) {
1290            return compileBool(ir, a);
1291        }
1292
1293        @Override
1294        public Object visit(IrAllDifferent ir, IntVar a) {
1295            return compileBool(ir, a);
1296        }
1297
1298        @Override
1299        public Object visit(IrSelectN ir, IntVar a) {
1300            return compileBool(ir, a);
1301        }
1302
1303        @Override
1304        public Object visit(IrAcyclic ir, IntVar a) {
1305            return compileBool(ir, a);
1306        }
1307
1308        @Override
1309        public Object visit(IrUnreachable ir, IntVar a) {
1310            return compileBool(ir, a);
1311        }
1312
1313        @Override
1314        public Object visit(IrFilterString ir, IntVar a) {
1315            return compileBool(ir, a);
1316        }
1317    };
1318    private final IrSetExprVisitor<CSet, Object> setExprCompiler = new IrSetExprVisitor<CSet, Object>() {
1319        @Override
1320        public Object visit(IrSetVar ir, CSet reify) {
1321            return new CSet(getSetVar(ir), ir.getCard());
1322        }
1323
1324        @Override
1325        public Object visit(IrSingleton ir, CSet reify) {
1326            IntVar value = compile(ir.getValue());
1327            if (reify == null) {
1328                SetVar singleton = numSetVar("Singleton", ir.getEnv(), ir.getKer());
1329                post(Constraints.singleton(value, singleton));
1330                return new CSet(singleton, VF.one(solver));
1331            }
1332            return Constraints.singleton(value, reify.getSet(), reify.getCard());
1333        }
1334
1335        @Override
1336        public Object visit(IrArrayToSet ir, CSet reify) {
1337            IntVar[] array = compile(ir.getArray());
1338            if (reify == null) {
1339                CSet set = numCset("ArrayToSet", ir.getEnv(), ir.getKer(), ir.getCard());
1340                post(Constraints.arrayToSet(array, set.getSet(), set.getCard(), ir.getGlobalCardinality()));
1341                return set;
1342            }
1343            return Constraints.arrayToSet(array, reify.getSet(), reify.getCard(), ir.getGlobalCardinality());
1344        }
1345
1346        @Override
1347        public Object visit(IrJoinRelation ir, CSet reify) {
1348            CSet take = compile(ir.getTake());
1349            CSet[] children = compile(ir.getChildren());
1350            if (reify == null) {
1351                CSet joinRelation = numCset("JoinRelation", ir.getEnv(), ir.getKer(), ir.getCard());
1352                if (ir.isInjective()) {
1353                    post(Constraints.joinInjectiveRelation(take.getSet(), take.getCard(),
1354                            mapSet(children), mapCard(children), joinRelation.getSet(), joinRelation.getCard()));
1355                    return joinRelation;
1356                } else {
1357                    post(Constraints.joinRelation(take.getSet(), mapSet(children), joinRelation.getSet()));
1358                    return joinRelation;
1359                }
1360            }
1361            if (ir.isInjective()) {
1362                return Constraints.joinInjectiveRelation(take.getSet(), take.getCard(),
1363                        mapSet(children), mapCard(children), reify.getSet(), reify.getCard());
1364            }
1365            return Constraints.joinRelation(take.getSet(), mapSet(children), reify.getSet());
1366        }
1367
1368        @Override
1369        public Object visit(IrJoinFunction ir, CSet reify) {
1370            CSet take = compile(ir.getTake());
1371            IntVar[] refs = compile(ir.getRefs());
1372            if (reify == null) {
1373                CSet joinFunction = numCset("JoinFunction", ir.getEnv(), ir.getKer(), ir.getCard());
1374                post(Constraints.joinFunction(take.getSet(), take.getCard(), refs, joinFunction.getSet(), joinFunction.getCard(), ir.getGlobalCardinality()));
1375                return joinFunction;
1376            }
1377            return Constraints.joinFunction(take.getSet(), take.getCard(), refs, reify.getSet(), reify.getCard(), ir.getGlobalCardinality());
1378        }
1379
1380        @Override
1381        public Object visit(IrSetDifference ir, CSet reify) {
1382            CSet minuend = compile(ir.getMinuend());
1383            CSet subtrahend = compile(ir.getSubtrahend());
1384            if (reify == null) {
1385                CSet difference = numCset("Difference", ir.getEnv(), ir.getKer(), ir.getCard());
1386                post(_difference(minuend, subtrahend, difference));
1387                return difference;
1388            }
1389            return _difference(minuend, subtrahend, reify);
1390        }
1391
1392        @Override
1393        public Object visit(IrSetIntersection ir, CSet reify) {
1394            CSet[] operands = compile(ir.getOperands());
1395            if (reify == null) {
1396                CSet intersection = numCset("Intersection", ir.getEnv(), ir.getKer(), ir.getCard());
1397                post(_intersection(operands, intersection));
1398                return intersection;
1399            }
1400            return _intersection(operands, reify);
1401        }
1402
1403        @Override
1404        public Object visit(IrSetUnion ir, CSet reify) {
1405            CSet[] operands = compile(ir.getOperands());
1406            if (reify == null) {
1407                CSet union = numCset("Union", ir.getEnv(), ir.getKer(), ir.getCard());
1408                post(_union(operands, union, ir.isDisjoint()));
1409                return union;
1410            }
1411            return _union(operands, reify, ir.isDisjoint());
1412        }
1413
1414        @Override
1415        public Object visit(IrOffset ir, CSet reify) {
1416            CSet set = compile(ir.getSet());
1417            if (reify == null) {
1418                SetVar offset = numSetVar("Offset", ir.getEnv(), ir.getKer());
1419                post(_offset(set.getSet(), offset, ir.getOffset()));
1420                return new CSet(offset, set.getCard());
1421            }
1422            return _offset(set.getSet(), reify.getSet(), ir.getOffset());
1423        }
1424
1425        @Override
1426        public Object visit(IrMask ir, CSet reify) {
1427            CSet set = compile(ir.getSet());
1428            if (reify == null) {
1429                CSet mask = numCset("Mask", ir.getEnv(), ir.getKer(), ir.getCard());
1430                post(_mask(set, mask, ir.getFrom(), ir.getTo()));
1431                return mask;
1432            }
1433            return _mask(set, reify, ir.getFrom(), ir.getTo());
1434        }
1435
1436        @Override
1437        public Object visit(IrSetTernary ir, CSet reify) {
1438            BoolVar antecedent = compileAsBoolVar(ir.getAntecedent());
1439            CSet consequent = compile(ir.getConsequent());
1440            CSet alternative = compile(ir.getAlternative());
1441            if (reify == null) {
1442                CSet ternary = numCset("Ternary", ir.getEnv(), ir.getKer(), ir.getCard());
1443                post(_ifThenElse(antecedent,
1444                        _equal(ternary, consequent),
1445                        _equal(ternary, alternative)));
1446                return ternary;
1447            }
1448            return _ifThenElse(antecedent,
1449                    _equal(reify, consequent),
1450                    _equal(reify, alternative));
1451        }
1452    };
1453
1454    private static Constraint _implies(BoolVar antecedent, Constraint consequent) {
1455        return _implies(antecedent, consequent.reif());
1456    }
1457
1458    private static Constraint _ifThenElse(BoolVar antecedent, BoolVar consequent, BoolVar alternative) {
1459        return Constraints.ifThenElse(antecedent, consequent, alternative);
1460    }
1461
1462    private static Constraint _ifThenElse(BoolVar antecedent, Constraint consequent, Constraint alternative) {
1463        Constraint thenClause = _implies(antecedent, consequent);
1464        Constraint elseClause = _implies(antecedent.not(), alternative);
1465        return _arithm(thenClause.reif(), "+", elseClause.reif(), "=", 2);
1466    }
1467
1468    private static Constraint _sum(IntVar sum, IntVar... vars) {
1469        for (IntVar var : vars) {
1470            if (!(var instanceof BoolVar)) {
1471                return ICF.sum(vars, sum);
1472            }
1473        }
1474        return _sum(sum, Arrays.copyOf(vars, vars.length, BoolVar[].class));
1475    }
1476
1477    private static Constraint _sum(IntVar sum, BoolVar... vars) {
1478        return ICF.sum(vars, sum);
1479    }
1480
1481    private static Constraint _scalar(IntVar sum, int[] coefficients, IntVar[] vars) {
1482        boolean allOnes = true;
1483        for (int coefficient : coefficients) {
1484            if (coefficient != 1) {
1485                allOnes = false;
1486                break;
1487            }
1488        }
1489        if (allOnes) {
1490            return _sum(sum, vars);
1491        }
1492        return ICF.scalar(vars, coefficients, sum);
1493    }
1494
1495    private static Constraint _times(IntVar multiplicand, IntVar multiplier, IntVar product) {
1496        return ICF.times(multiplicand, multiplier, product);
1497    }
1498
1499    private static Constraint _div(IntVar dividend, IntVar divisor, IntVar quotient) {
1500        return ICF.eucl_div(dividend, divisor, quotient);
1501    }
1502
1503    private static Constraint _arithm(IntVar var1, String op1, IntVar var2, String op2, int cste) {
1504        if (cste == 0) {
1505            switch (Operator.get(op2)) {
1506                case PL:
1507                    return ICF.arithm(var1, op1, var2);
1508                case MN:
1509                    return ICF.arithm(var1, op1, var2);
1510            }
1511        }
1512        return ICF.arithm(var1, op1, var2, op2, cste);
1513    }
1514
1515    private static Constraint _implies(BoolVar antecedent, IntVar consequent) {
1516        return _arithm(antecedent, "<=", consequent);
1517    }
1518
1519    private static Constraint _arithm(IntVar var1, String op, IntVar var2) {
1520        if (var2.instantiated()) {
1521            return ICF.arithm(var1, op, var2.getValue());
1522        }
1523        return ICF.arithm(var1, op, var2);
1524    }
1525
1526    private static Constraint _arithm(IntVar var1, String op, int c) {
1527        return ICF.arithm(var1, op, c);
1528    }
1529
1530    private Constraint _reify_equal(BoolVar reify, IntVar var1, IntVar var2) {
1531        if (var1.instantiated()) {
1532            if (var2.instantiated()) {
1533                return _arithm(reify, "=", var1.getValue() == var2.getValue() ? 1 : 0);
1534            }
1535            return Constraints.reifyEqual(reify, var2, var1.getValue());
1536        } else if (var2.instantiated()) {
1537            return Constraints.reifyEqual(reify, var1, var2.getValue());
1538        } else {
1539            return Constraints.reifyEqual(reify, var1, var2);
1540        }
1541    }
1542
1543    private Constraint _reify_not_equal(BoolVar reify, IntVar var1, IntVar var2) {
1544        if (var1.instantiated()) {
1545            if (var2.instantiated()) {
1546                return _arithm(reify, "=", var1.getValue() != var2.getValue() ? 1 : 0);
1547            }
1548            return Constraints.reifyNotEqual(reify, var2, var1.getValue());
1549        } else if (var2.instantiated()) {
1550            return Constraints.reifyNotEqual(reify, var1, var2.getValue());
1551        } else {
1552            return Constraints.reifyNotEqual(reify, var1, var2);
1553        }
1554    }
1555
1556    private static Constraint _element(IntVar index, IntVar[] array, IntVar value) {
1557        return ICF.element(value, array, index, 0);
1558    }
1559
1560    private static Constraint _count(int value, IntVar[] array, IntVar count) {
1561        return ICF.count(value, array, count);
1562    }
1563
1564    private static Constraint _equal(CSet var1, CSet var2) {
1565        return Constraints.equal(var1.getSet(), var1.getCard(), var2.getSet(), var2.getCard());
1566    }
1567
1568    private static Constraint _not_equal(CSet var1, CSet var2) {
1569        if (var1.getSet().instantiated()) {
1570            return Constraints.notEqual(var2.getSet(), var1.getSet().getValue());
1571        }
1572        if (var2.getSet().instantiated()) {
1573            return Constraints.notEqual(var1.getSet(), var2.getSet().getValue());
1574        }
1575        return Constraints.notEqual(var1.getSet(), var1.getCard(), var2.getSet(), var2.getCard());
1576    }
1577
1578    private static Constraint _all_different(IntVar... vars) {
1579        return ICF.alldifferent(vars, "AC");
1580    }
1581
1582    private static Constraint _within(IntVar var, int low, int high) {
1583        return ICF.member(var, low, high);
1584    }
1585
1586    private static Constraint _within(IntVar var, int[] values) {
1587        return ICF.member(var, values);
1588    }
1589
1590    private static Constraint _not_within(IntVar var, int low, int high) {
1591        return ICF.not_member(var, low, high);
1592    }
1593
1594    private static Constraint _not_within(IntVar var, int[] values) {
1595        return ICF.not_member(var, values);
1596    }
1597
1598    private static Constraint _member(IntVar element, SetVar set) {
1599        return SCF.member(element, set);
1600    }
1601
1602    private static Constraint _not_member(IntVar element, SetVar set) {
1603        return Constraints.notMember(element, set);
1604    }
1605
1606    private static Constraint _lex_chain_less(IntVar[]... vars) {
1607        if (vars.length == 2) {
1608            return ICF.lex_less(vars[0], vars[1]);
1609        }
1610        return ICF.lex_chain_less(vars);
1611    }
1612
1613    private static Constraint _lex_chain_less_eq(IntVar[]... vars) {
1614        if (vars.length == 2) {
1615            return ICF.lex_less_eq(vars[0], vars[1]);
1616        }
1617        return ICF.lex_chain_less_eq(vars);
1618    }
1619
1620    private static Constraint _lex_chain_channel(IntVar[][] strings, IntVar[] ints) {
1621        return Constraints.lexChainChannel(strings, ints);
1622    }
1623
1624    private static Constraint _difference(CSet minuend, CSet subtrahend, CSet difference) {
1625        return Constraints.difference(
1626                minuend.getSet(), minuend.getCard(),
1627                subtrahend.getSet(), subtrahend.getCard(),
1628                difference.getSet(), difference.getCard());
1629    }
1630
1631    private static Constraint _intersection(CSet[] operands, CSet intersection) {
1632        return Constraints.intersection(mapSet(operands), mapCard(operands), intersection.getSet(), intersection.getCard());
1633    }
1634
1635    private static Constraint _union(CSet[] operands, CSet union, boolean disjoint) {
1636        return Constraints.union(
1637                mapSet(operands), mapCard(operands),
1638                union.getSet(), union.getCard(),
1639                disjoint);
1640    }
1641
1642    private IntVar _offset(IntVar var, int offset) {
1643        Pair<IntVar, Integer> pair = new Pair<>(var, offset);
1644        IntVar cache = cachedOffset.get(pair);
1645        if (cache == null) {
1646            cache = VF.offset(var, offset);
1647            cachedOffset.put(pair, cache);
1648        }
1649        return cache;
1650    }
1651
1652    private static Constraint _offset(SetVar set, SetVar offseted, int offset) {
1653        return SCF.offSet(set, offseted, offset);
1654    }
1655
1656    private static Constraint _mask(CSet set, CSet masked, int from, int to) {
1657        return Constraints.mask(set.getSet(), set.getCard(), masked.getSet(), masked.getCard(), from, to);
1658    }
1659
1660    private static Constraint _subset_eq(CSet sub, CSet sup) {
1661        return Constraints.subsetEq(sub.getSet(), sub.getCard(), sup.getSet(), sup.getCard());
1662    }
1663
1664    private static enum Rel {
1665
1666        EQ("="),
1667        NQ("!="),
1668        LT("<"),
1669        LE("<="),
1670        GT(">"),
1671        GE(">=");
1672        private final String syntax;
1673
1674        private Rel(String syntax) {
1675            this.syntax = syntax;
1676        }
1677
1678        private String getSyntax() {
1679            return syntax;
1680        }
1681
1682        private static Rel from(IrCompare.Op op) {
1683            switch (op) {
1684                case Equal:
1685                    return EQ;
1686                case NotEqual:
1687                    return NQ;
1688                case LessThan:
1689                    return LT;
1690                case LessThanEqual:
1691                    return LE;
1692                default:
1693                    throw new IllegalArgumentException();
1694            }
1695        }
1696
1697        private Rel reverse() {
1698            switch (this) {
1699                case LT:
1700                    return GT;
1701                case LE:
1702                    return GE;
1703                case GT:
1704                    return LT;
1705                case GE:
1706                    return LE;
1707                default:
1708                    return this;
1709            }
1710        }
1711    }
1712
1713    private static enum Arithm {
1714
1715        ADD("+"),
1716        MINUS("-");
1717        private final String syntax;
1718
1719        private Arithm(String syntax) {
1720            this.syntax = syntax;
1721        }
1722
1723        private String getSyntax() {
1724            return syntax;
1725        }
1726
1727        private Arithm negate() {
1728            switch (this) {
1729                case ADD:
1730                    return MINUS;
1731                case MINUS:
1732                    return ADD;
1733                default:
1734                    throw new IllegalStateException();
1735            }
1736        }
1737    }
1738    private static final BoolArg ConstraintNoReify = new BoolArg(null, Preference.Constraint);
1739    private static final BoolArg BoolVarNoReify = new BoolArg(null, Preference.BoolVar);
1740
1741    private static class BoolArg {
1742
1743        // The solution needs to be reified in this variable.
1744        // Set to null if no reification needed.
1745        private BoolVar reify;
1746        // The prefered type of solution.
1747        private final Preference preference;
1748
1749        BoolArg(BoolVar reify, Preference preference) {
1750            this.reify = reify;
1751            this.preference = preference;
1752        }
1753
1754        boolean hasReify() {
1755            return reify != null;
1756        }
1757
1758        BoolVar useReify() {
1759            BoolVar tmp = reify;
1760            reify = null;
1761            return tmp;
1762        }
1763
1764        Preference getPreference() {
1765            return preference;
1766        }
1767    }
1768
1769    private static enum Preference {
1770
1771        Constraint,
1772        BoolVar;
1773    }
1774
1775    private class CSet {
1776
1777        private final SetVar set;
1778        private final IrDomain cardDomain;
1779        private IntVar card;
1780
1781        CSet(SetVar set, IrDomain cardDomain) {
1782            this.set = Check.notNull(set);
1783            this.cardDomain = Check.notNull(cardDomain);
1784            this.card =
1785                    cardDomain.getLowBound() > set.getKernelSize() || cardDomain.getHighBound() < set.getEnvelopeSize()
1786                    ? setCardVar(set, cardDomain)
1787                    : null;
1788        }
1789
1790        CSet(SetVar set, IntVar card) {
1791            this.set = Check.notNull(set);
1792            this.cardDomain = null;
1793            this.card = Check.notNull(card);
1794        }
1795
1796        SetVar getSet() {
1797            return set;
1798        }
1799
1800        boolean hasCardCached() {
1801            return card != null;
1802        }
1803
1804        IntVar getCard() {
1805            if (card == null) {
1806                assert cardDomain != null;
1807                card = setCardVar(set, cardDomain);
1808            }
1809            return card;
1810        }
1811    }
1812
1813    private static SetVar[] mapSet(CSet[] sets) {
1814        SetVar[] vars = new SetVar[sets.length];
1815        for (int i = 0; i < sets.length; i++) {
1816            vars[i] = sets[i].getSet();
1817        }
1818        return vars;
1819    }
1820
1821    private static IntVar[] mapCard(CSet[] sets) {
1822        IntVar[] vars = new IntVar[sets.length];
1823        for (int i = 0; i < sets.length; i++) {
1824            vars[i] = sets[i].getCard();
1825        }
1826        return vars;
1827    }
1828}