UNPKG

finitedomain

Version:

A fast feature rich finite domain solver

1,071 lines (913 loc) 138 kB
import expect from '../fixtures/mocha_proxy.fixt'; import { fixt_arrdom_nums, fixt_arrdom_range, fixt_arrdom_ranges, stripAnonVarsFromArrays, } from '../fixtures/domain.fixt'; import { SUB, SUP, } from '../../src/helpers'; import { domain__debug, } from '../../src/domain'; import Solver from '../../src/solver'; let INPUT_MAP = { bool: { F: [0, 0], B: [0, 1], T: [1, 1], }, booly: { F: [0, 0], B: [0, 0, 5, 5], T: [5, 5], }, }; let OUTPUT_MAP = { bool: { F: 0, T: 1, }, booly: { F: 0, T: 5, }, }; describe('src/constraint.spec', function() { describe('solver integration', function() { it('should work without constraints (FIX THIS ONE FIRST)', function() { // if this test fails the problem is _probably_ unrelated to constraints... :) let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 100); let solution = solver.solve(); expect(solution).to.eql([{A: 100, B: 100}]); }); describe('eq', function() { it('should work with a simple solved vars', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 100); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}]); }); it('should work with a simple solved and unsolved vars', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}]); }); it('should work with a simple unsolved vars that do not reject', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 101)); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}, {A: 101, B: 101}]); }); it('should work with a simple unsolved vars that reduce but do not reject', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 102)); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}, {A: 101, B: 101}]); }); it('should work with a simple unsolved vars that reject', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(200, 201)); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); describe('pre-computable', function() { function preEq(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.eq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preEq('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 101}]); preEq('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 101}]); preEq('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 101, B: 101}]); preEq('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 101, B: 101}]); preEq('should not create a constraint if A and B solved as number and reject', 100, 99, []); preEq('should not create a constraint if A and B solved as number and pass', 101, 101, [{A: 101, B: 101}]); preEq('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(100, 100), fixt_arrdom_range(99, 99), []); preEq('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(101, 101), fixt_arrdom_range(101, 101), [{A: 101, B: 101}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.eq('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], [{A: 0, B: 0}]); test([0, 1], [0, 0], [{A: 0, B: 0}]); // doe dit voor alle propagators. en zoek uit waarom deze niet werkt. test([1, 1], [0, 0], []); test([0, 0], [0, 1], [{A: 0, B: 0}]); test([0, 1], [0, 1], [{A: 0, B: 0}, {A: 1, B: 1}]); test([1, 1], [0, 1], [{A: 1, B: 1}]); test([0, 0], [1, 1], []); test([0, 1], [1, 1], [{A: 1, B: 1}]); test([1, 1], [1, 1], [{A: 1, B: 1}]); }); }); describe('neq', function() { it('should work with a simple solved vars', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 100); solver.neq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); it('should work with a simple solved and unsolved vars', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.neq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}]); }); it('should work with a simple unsolved vars', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 101)); solver.neq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}, {A: 101, B: 100}]); }); describe('pre-computable', function() { function preNeq(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.neq('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preNeq('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 100}, {A: 101, B: 102}]); preNeq('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 100}, {A: 101, B: 102}]); preNeq('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 100, B: 101}, {A: 102, B: 101}]); preNeq('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 100, B: 101}, {A: 102, B: 101}]); preNeq('should not create a constraint if A and B solved as number and reject', 101, 101, []); preNeq('should not create a constraint if A and B solved as number and pass', 100, 99, [{A: 100, B: 99}]); preNeq('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(101, 101), fixt_arrdom_range(101, 101), []); preNeq('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(100, 100), fixt_arrdom_range(99, 99), [{A: 100, B: 99}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.neq('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], []); test([0, 1], [0, 0], [{A: 1, B: 0}]); test([1, 1], [0, 0], [{A: 1, B: 0}]); test([0, 0], [0, 1], [{A: 0, B: 1}]); test([0, 1], [0, 1], [{A: 0, B: 1}, {A: 1, B: 0}]); test([1, 1], [0, 1], [{A: 1, B: 0}]); test([0, 0], [1, 1], [{A: 0, B: 1}]); test([0, 1], [1, 1], [{A: 0, B: 1}]); test([1, 1], [1, 1], []); }); }); describe('lt', function() { it('should work when A < B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 101); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}]); }); it('should work when A <= B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}]); }); it('should work when A >= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); it('should work when A <= B <= A', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 102)); solver.decl('B', fixt_arrdom_range(101, 101)); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}]); }); it('should work when B <= A <= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 102)); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 102}]); }); it('should work A > B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); describe('pre-computable', function() { function preLt(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.lt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preLt('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 102}]); preLt('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 102}]); preLt('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 100, B: 101}]); preLt('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 100, B: 101}]); preLt('should not create a constraint if A and B solved as number and reject', 100, 99, []); preLt('should not create a constraint if A and B solved as number and pass', 100, 101, [{A: 100, B: 101}]); preLt('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(100, 100), fixt_arrdom_range(99, 99), []); preLt('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(100, 100), fixt_arrdom_range(101, 101), [{A: 100, B: 101}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.lt('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], []); test([0, 1], [0, 0], []); test([1, 1], [0, 0], []); test([0, 0], [0, 1], [{A: 0, B: 1}]); test([0, 1], [0, 1], [{A: 0, B: 1}]); test([1, 1], [0, 1], []); test([0, 0], [1, 1], [{A: 0, B: 1}]); test([0, 1], [1, 1], [{A: 0, B: 1}]); test([1, 1], [1, 1], []); }); }); describe('lte', function() { it('should work when A < B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 101); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}]); }); it('should work when A <= B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}, {A: 100, B: 101}]); }); it('should work when A >= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}]); }); it('should work when A <= B <= A', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 102)); solver.decl('B', fixt_arrdom_range(101, 101)); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101}, {A: 101, B: 101}]); }); it('should work when B <= A <= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 102)); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 101}, {A: 101, B: 102}]); }); it('should work A > B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); describe('pre-computable', function() { function preLte(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.lte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preLte('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 101}, {A: 101, B: 102}]); preLte('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 101}, {A: 101, B: 102}]); preLte('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 100, B: 101}, {A: 101, B: 101}]); preLte('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 100, B: 101}, {A: 101, B: 101}]); preLte('should not create a constraint if A and B solved as number and reject', 100, 99, []); preLte('should not create a constraint if A and B solved as number and pass', 100, 100, [{A: 100, B: 100}]); preLte('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(100, 100), fixt_arrdom_range(99, 99), []); preLte('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(100, 100), fixt_arrdom_range(100, 100), [{A: 100, B: 100}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.lte('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], [{A: 0, B: 0}]); test([0, 1], [0, 0], [{A: 0, B: 0}]); test([1, 1], [0, 0], []); test([0, 0], [0, 1], [{A: 0, B: 0}, {A: 0, B: 1}]); test([0, 1], [0, 1], [{A: 0, B: 0}, {A: 0, B: 1}, {A: 1, B: 1}]); test([1, 1], [0, 1], [{A: 1, B: 1}]); test([0, 0], [1, 1], [{A: 0, B: 1}]); test([0, 1], [1, 1], [{A: 0, B: 1}, {A: 1, B: 1}]); test([1, 1], [1, 1], [{A: 1, B: 1}]); }); }); describe('gt', function() { it('should work when A < B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 101); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); it('should work when A <= B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); it('should work when A >= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 100}]); }); it('should work when A <= B <= A', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 102)); solver.decl('B', fixt_arrdom_range(101, 101)); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 102, B: 101}]); }); it('should work when B <= A <= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 102)); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 100}]); }); it('should work A > B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 100}]); }); describe('pre-computable', function() { function preGt(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.gt('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preGt('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 100}]); preGt('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 100}]); preGt('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 102, B: 101}]); preGt('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 102, B: 101}]); preGt('should not create a constraint if A and B solved as number and reject', 99, 100, []); preGt('should not create a constraint if A and B solved as number and pass', 101, 100, [{A: 101, B: 100}]); preGt('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(99, 99), fixt_arrdom_range(100, 100), []); preGt('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 100), [{A: 101, B: 100}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.gt('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], []); test([0, 1], [0, 0], [{A: 1, B: 0}]); test([1, 1], [0, 0], [{A: 1, B: 0}]); test([0, 0], [0, 1], []); test([0, 1], [0, 1], [{A: 1, B: 0}]); test([1, 1], [0, 1], [{A: 1, B: 0}]); test([0, 0], [1, 1], []); test([0, 1], [1, 1], []); test([1, 1], [1, 1], []); }); }); describe('gte', function() { it('should work when A < B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 101); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([]); }); it('should work when A <= B', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', fixt_arrdom_range(100, 101)); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}]); }); it('should work when A >= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 100}, {A: 101, B: 100}]); }); it('should work when A <= B <= A', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 102)); solver.decl('B', fixt_arrdom_range(101, 101)); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 101}, {A: 102, B: 101}]); }); it('should work when B <= A <= B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 102)); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 100}, {A: 101, B: 101}]); }); it('should work A > B', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(101, 101)); solver.decl('B', fixt_arrdom_range(100, 100)); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql([{A: 101, B: 100}]); }); describe('pre-computable', function() { function preGte(desc, A, B, out) { it(desc, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.gte('A', 'B'); let solution = solver.solve({}); expect(solution).to.eql(out); expect(solver.config.allConstraints.length, 'constraint count').to.eql(0); }); } preGte('should not create a constraint if A is solved as number', 101, fixt_arrdom_range(100, 102), [{A: 101, B: 100}, {A: 101, B: 101}]); preGte('should not create a constraint if A is solved as array', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 102), [{A: 101, B: 100}, {A: 101, B: 101}]); preGte('should not create a constraint if B is solved as number', fixt_arrdom_range(100, 102), 101, [{A: 101, B: 101}, {A: 102, B: 101}]); preGte('should not create a constraint if B is solved as array', fixt_arrdom_range(100, 102), fixt_arrdom_range(101, 101), [{A: 101, B: 101}, {A: 102, B: 101}]); preGte('should not create a constraint if A and B solved as number and reject', 99, 100, []); preGte('should not create a constraint if A and B solved as number and pass', 101, 100, [{A: 101, B: 100}]); preGte('should not create a constraint if A and B solved as array and reject', fixt_arrdom_range(99, 99), fixt_arrdom_range(100, 100), []); preGte('should not create a constraint if A and B solved as array and pass', fixt_arrdom_range(101, 101), fixt_arrdom_range(100, 100), [{A: 101, B: 100}]); }); describe('brute force bool table', function() { function test(A, B, out) { it('test: A=[' + A + '] B=[' + B + '] out=' + JSON.stringify(out).replace(/"/g, ''), function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.gte('A', 'B'); solver.solve({max: 5}); // only 4 possible outcomes; 00 01 10 11 or none expect(solver.solutions).to.eql(out); }); } test([0, 0], [0, 0], [{A: 0, B: 0}]); test([0, 1], [0, 0], [{A: 0, B: 0}, {A: 1, B: 0}]); test([1, 1], [0, 0], [{A: 1, B: 0}]); test([0, 0], [0, 1], [{A: 0, B: 0}]); test([0, 1], [0, 1], [{A: 0, B: 0}, {A: 1, B: 0}, {A: 1, B: 1}]); test([1, 1], [0, 1], [{A: 1, B: 0}, {A: 1, B: 1}]); test([0, 0], [1, 1], []); test([0, 1], [1, 1], [{A: 1, B: 1}]); test([1, 1], [1, 1], [{A: 1, B: 1}]); }); }); describe('reifier (conceptual)', function() { it('should find two solutions with a constant left', function() { let solver = new Solver(); solver.decl('A', 0); solver.decl('B', [0, 1]); solver.decl('C', [0, 1]); solver.isLt('A', 'B', 'C'); let solution = solver.solve({}); expect(solution).to.eql([{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}]); }); it('should find two solutions with a constant right', function() { let solver = new Solver(); solver.decl('A', [0, 1]); solver.decl('B', 0); solver.decl('C', [0, 1]); solver.isLt('A', 'B', 'C'); let solution = solver.solve({}); expect(solution).to.eql([{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}]); }); describe('exhaustive bool tables to check optimizations in propagator_addReified', function() { describe('eq', function() { function test(domain1, domain2, domain3, out, desc) { it('should solve despite optimizations. ' + [domain__debug(domain1), '==', domain__debug(domain2), '->', domain__debug(domain3)] + ' solves to: ' + (JSON.stringify(out).replace(/"/g, '')) + (desc ? '; ' + desc : ''), function() { let solver = new Solver(); let A = solver.decl('A', domain1); let B = solver.decl('B', domain2); let C = solver.decl('C', domain3); solver.isEq(A, B, C); solver.solve({vars: ['A', 'B', 'C']}); expect(solver.solutions).to.eql(out); }); } [ // pure bools {A: [0, 0], B: [0, 0], C: [0, 0], out: []}, {A: [0, 0], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 0], B: [0, 0], C: [1, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [0, 1], C: [1, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [1, 1], C: [1, 1], out: []}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [1, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 0}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [0, 1], C: [1, 1], out: [{A: 0, B: 0, C: 1}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 0}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [1, 1], C: [1, 1], out: [{A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 1], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [1, 1], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 1], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [0, 1], C: [1, 1], out: [{A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: []}, {A: [1, 1], B: [1, 1], C: [0, 1], out: [{A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [1, 1], C: [1, 1], out: [{A: 1, B: 1, C: 1}]}, // booly, same but with C = [0 0 5 5] {A: [0, 0], B: [0, 0], C: [0, 0], out: []}, {A: [0, 0], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 0], B: [0, 0], C: [5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [0, 1], C: [5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 0], B: [1, 1], C: [5, 5], out: []}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 0}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [0, 1], C: [5, 5], out: [{A: 0, B: 0, C: 5}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 0, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 0}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [1, 1], C: [5, 5], out: [{A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [5, 5], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [0, 1], C: [5, 5], out: [{A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: []}, {A: [1, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [1, 1], C: [5, 5], out: [{A: 1, B: 1, C: 5}]}, ].forEach(testCase => test(testCase.A, testCase.B, testCase.C, testCase.out)); }); describe('neq', function() { function test(domain1, domain2, domain3, out, desc) { it('should solve despite optimizations. ' + [domain__debug(domain1), '!=', domain__debug(domain2), '->', domain__debug(domain3)] + ' solves to: ' + (JSON.stringify(out).replace(/"/g, '')) + (desc ? '; ' + desc : ''), function() { let solver = new Solver(); let A = solver.decl('A', domain1); let B = solver.decl('B', domain2); let C = solver.decl('C', domain3); solver.isNeq(A, B, C); solver.solve({vars: ['A', 'B', 'C']}); expect(solver.solutions).to.eql(out); }); } [ // pure bools {A: [0, 0], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [1, 1], out: []}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [0, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 1}]}, {A: [0, 1], B: [0, 0], C: [1, 1], out: [{A: 1, B: 0, C: 1}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 1}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 1}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: []}, {A: [1, 1], B: [0, 0], C: [0, 1], out: [{A: 1, B: 0, C: 1}]}, {A: [1, 1], B: [0, 0], C: [1, 1], out: [{A: 1, B: 0, C: 1}]}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 1], out: [{A: 1, B: 0, C: 1}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [1, 1], out: [{A: 1, B: 0, C: 1}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [0, 1], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [1, 1], out: []}, // booly, same but with C = [0 0 5 5] {A: [0, 0], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [5, 5], out: []}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [0, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 5}]}, {A: [0, 1], B: [0, 0], C: [5, 5], out: [{A: 1, B: 0, C: 5}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 5}, {A: 1, B: 0, C: 5}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}, {A: 1, B: 0, C: 5}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: []}, {A: [1, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 5}]}, {A: [1, 1], B: [0, 0], C: [5, 5], out: [{A: 1, B: 0, C: 5}]}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 5}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [5, 5], out: [{A: 1, B: 0, C: 5}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [5, 5], out: []}, ].forEach(testCase => test(testCase.A, testCase.B, testCase.C, testCase.out)); }); describe('lt-', function() { function test(domain1, domain2, domain3, out, desc) { it('should solve despite optimizations. ' + [domain__debug(domain1), '<', domain__debug(domain2), '->', domain__debug(domain3)] + ' solves to: ' + (JSON.stringify(out).replace(/"/g, '')) + (desc ? '; ' + desc : ''), function() { let solver = new Solver(); let A = solver.decl('A', domain1); let B = solver.decl('B', domain2); let C = solver.decl('C', domain3); solver.isLt(A, B, C); solver.solve({vars: ['A', 'B', 'C']}); expect(solver.solutions).to.eql(out); }); } [ // pure bools {A: [0, 0], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [1, 1], out: []}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [0, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [1, 1], out: []}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 1], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [1, 1], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 1], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [1, 1], out: []}, {A: [1, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [0, 1], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [1, 1], out: []}, // booly, same but with C = [0 0 5 5] {A: [0, 0], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 0], C: [5, 5], out: []}, {A: [0, 0], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}]}, {A: [0, 0], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [0, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [5, 5], out: []}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 0, B: 0, C: 0}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 0}, {A: 0, B: 1, C: 5}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [0, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}, {A: 1, B: 1, C: 0}]}, {A: [0, 1], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [5, 5], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [0, 1], C: [5, 5], out: []}, {A: [1, 1], B: [1, 1], C: [0, 0], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 1, C: 0}]}, {A: [1, 1], B: [1, 1], C: [5, 5], out: []}, ].forEach(testCase => test(testCase.A, testCase.B, testCase.C, testCase.out)); }); describe('lte', function() { function test(domain1, domain2, domain3, out, desc) { it('should solve despite optimizations. ' + [domain__debug(domain1), '<=', domain__debug(domain2), '->', domain__debug(domain3)] + ' solves to: ' + (JSON.stringify(out).replace(/"/g, '')) + (desc ? '; ' + desc : ''), function() { let solver = new Solver(); let A = solver.decl('A', domain1); let B = solver.decl('B', domain2); let C = solver.decl('C', domain3); solver.isLte(A, B, C); solver.solve({vars: ['A', 'B', 'C']}); expect(solver.solutions).to.eql(out); }); } [ // pure bools {A: [0, 0], B: [0, 0], C: [0, 0], out: []}, {A: [0, 0], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 0], B: [0, 0], C: [1, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 0], B: [0, 1], C: [0, 0], out: []}, {A: [0, 0], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [0, 1], C: [1, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 0], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [1, 1], out: [{A: 0, B: 0, C: 1}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 1}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [0, 1], C: [1, 1], out: [{A: 0, B: 0, C: 1}, {A: 0, B: 1, C: 1}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: []}, {A: [0, 1], B: [1, 1], C: [0, 1], out: [{A: 0, B: 1, C: 1}, {A: 1, B: 1, C: 1}]}, {A: [0, 1], B: [1, 1], C: [1, 1], out: [{A: 0, B: 1, C: 1}, {A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 1], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [1, 1], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 1], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [0, 1], C: [1, 1], out: [{A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: []}, {A: [1, 1], B: [1, 1], C: [0, 1], out: [{A: 1, B: 1, C: 1}]}, {A: [1, 1], B: [1, 1], C: [1, 1], out: [{A: 1, B: 1, C: 1}]}, // booly, same but with C = [0 0 5 5] {A: [0, 0], B: [0, 0], C: [0, 0], out: []}, {A: [0, 0], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 0], B: [0, 0], C: [5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 0], B: [0, 1], C: [0, 0], out: []}, {A: [0, 0], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [0, 1], C: [5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [0, 0], out: []}, {A: [0, 0], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 0], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}]}, {A: [0, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 0], C: [5, 5], out: [{A: 0, B: 0, C: 5}]}, {A: [0, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [0, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 5}, {A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [0, 1], C: [5, 5], out: [{A: 0, B: 0, C: 5}, {A: 0, B: 1, C: 5}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [1, 1], C: [0, 0], out: []}, {A: [0, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 0, B: 1, C: 5}, {A: 1, B: 1, C: 5}]}, {A: [0, 1], B: [1, 1], C: [5, 5], out: [{A: 0, B: 1, C: 5}, {A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [0, 0], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 0], C: [5, 5], out: []}, {A: [1, 1], B: [0, 1], C: [0, 0], out: [{A: 1, B: 0, C: 0}]}, {A: [1, 1], B: [0, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 0, C: 0}, {A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [0, 1], C: [5, 5], out: [{A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [1, 1], C: [0, 0], out: []}, {A: [1, 1], B: [1, 1], C: [0, 0, 5, 5], out: [{A: 1, B: 1, C: 5}]}, {A: [1, 1], B: [1, 1], C: [5, 5], out: [{A: 1, B: 1, C: 5}]}, ].forEach(testCase => test(testCase.A, testCase.B, testCase.C, testCase.out)); }); // note: gt and gte map to lt and lte so there's no real need to test them as well... but we could :) }); }); describe('plus', function() { it('should work with simple case', function() { let solver = new Solver(); solver.decl('A', 100); solver.decl('B', 101); solver.decl('C', fixt_arrdom_range(150, 250)); solver.plus('A', 'B', 'C'); let solution = solver.solve({}); expect(solution).to.eql([{A: 100, B: 101, C: 201}]); }); function testABC(A, B, C, solves) { it(`should work with A=${A} B=${B} C=${C} -> ${solves}`, function() { let solver = new Solver(); solver.decl('A', A); solver.decl('B', B); solver.decl('C', C); solver.plus('A', 'B', 'C'); let solution = solver.solve({}); expect(solution).to.eql(solves); }); } testABC(0, 1, 1, [{A: 0, B: 1, C: 1}]); testABC(1, 0, 1, [{A: 1, B: 0, C: 1}]); testABC(0, 0, 0, [{A: 0, B: 0, C: 0}]); testABC(1, 1, 2, [{A: 1, B: 1, C: 2}]); testABC(fixt_arrdom_range(100, 110), fixt_arrdom_range(50, 60), fixt_arrdom_range(150, 151), [ {A: 100, B: 50, C: 150}, {A: 100, B: 51, C: 151}, {A: 101, B: 50, C: 151}, ]); it('should work without C', function() { let solver = new Solver(); solver.decl('A', fixt_arrdom_range(100, 102)); solver.decl('B', fixt_arrdom_range(50, 52)); solver.plus('A', 'B'); let solution = solver.solve({}); expect(stripAnonVarsFromArrays(solution)).to.eql([ // Note: order is not relevant to the test! { A