UNPKG

finitedomain

Version:

A fast feature rich finite domain solver

1,005 lines (841 loc) 34.8 kB
import expect from '../../fixtures/mocha_proxy.fixt'; import { fixt_arrdom_ranges, fixt_arrdom_solved, fixt_arrdom_nums, } from '../../fixtures/domain.fixt'; import distribution_getNextVarIndex, { BETTER, SAME, WORSE, distribution_varByMin, distribution_varByMax, distribution_varByMinSize, distribution_varByMarkov, distribution_varByList, } from '../../../src/distribution/var'; import Solver from '../../../src/solver'; import { config_addVarDomain, config_addVarRange, config_create, config_setOption, } from '../../../src/config'; import { space_createRoot, space_initFromConfig, } from '../../../src/space'; describe('distribution/var.spec', function() { describe('distribution_var_by_throw', function() { it('should throw', function() { expect(_ => distribution_getNextVarIndex({}, {varStratConfig: {type: 'throw'}})).to.throw('not expecting to pick this distributor'); }); it('should throw', function() { expect(_ => distribution_getNextVarIndex({}, {varStratConfig: {type: 'unknown'}})).to.throw('unknown next var func'); }); }); function itAvsB(type, range_a, range_b, out, desc) { let stack = new Error('from').stack; // mocha wont tell us which line called itAvsB :( if (stack && stack.slice) stack = stack.slice(0, stack.indexOf('._compile')) + ' ...'; // dont need the whole thing it(`itAvsB: ${desc}; type: ${type}, rangeA: ${range_a}, rangeB: ${range_b}, out: ${out}`, function() { let solver = new Solver(); solver.declRange('A', ...range_a); solver.declRange('B', ...range_b); solver._prepare({ distribute: { varStrategy: { type: type, }, targeted_var_names: ['A', 'B'], // otherwise nothing happens }, }); let varIndex = distribution_getNextVarIndex(solver._space, solver.config); expect(varIndex, stack).to.equal(solver.config.allVarNames.indexOf(out)); }); } describe('by_min', function() { describe('unit', function() { it('should return BETTER if lo(v1) < lo(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(10)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMin(space, config, A, B)).to.equal(BETTER); }); it('should return SAME if lo(v1) = lo(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(11)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMin(space, config, A, B)).to.equal(SAME); }); it('should return WORSE if lo(v1) > lo(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(12)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMin(space, config, A, B)).to.equal(WORSE); }); }); describe('integration', function() { itAvsB('min', [0, 1], [10, 11], 'A', 'should decide on lowest vars first A'); itAvsB('min', [20, 30], [5, 8], 'B', 'should decide on lowest vars first B'); itAvsB('min', [9, 21], [10, 20], 'A', 'should base decision on lowest lo, not lowest hi'); }); }); describe('by_max', function() { describe('unit', function() { it('should return BETTER if hi(v1) > hi(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(12)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMax(space, config, A, B)).to.equal(BETTER); }); it('should return SAME if hi(v1) = hi(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(11)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMax(space, config, A, B)).to.equal(SAME); }); it('should return WORSE if hi(v1) < hi(v2)', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_solved(10)); config_addVarDomain(config, 'B', fixt_arrdom_solved(11)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMax(space, config, A, B)).to.equal(WORSE); }); }); describe('integration', function() { itAvsB('max', [0, 1], [10, 11], 'B', 'should decide on highest vars first A'); itAvsB('max', [20, 30], [5, 8], 'A', 'should decide on highest vars first B'); itAvsB('max', [9, 21], [10, 20], 'A', 'should base decision on highest hi, not highest lo'); }); }); describe('by_size', function() { describe('unit', function() { // note: further tests should be unit tests on domain_size instead it('should return BETTER if size(v1) < size(v2)', function() { let config = config_create(); config_addVarRange(config, 'A', 5, 5); config_addVarRange(config, 'B', 11, 12); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMinSize(space, config, A, B)).to.equal(BETTER); }); it('should return SAME if size(v1) = size(v2) with single range', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 11); config_addVarRange(config, 'B', 8, 8); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMinSize(space, config, A, B)).to.equal(SAME); }); it('should return SAME if size(v1) = size(v2) with multiple ranges', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_nums(11, 15, 16, 17, 18, 19)); config_addVarDomain(config, 'B', fixt_arrdom_nums(8, 9, 10, 12, 13, 14)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMinSize(space, config, A, B)).to.equal(SAME); }); it('should return SAME if size(v1) = size(v2) with different range count', function() { let config = config_create(); config_addVarDomain(config, 'A', fixt_arrdom_nums(11, 13, 14, 18, 19)); config_addVarDomain(config, 'B', fixt_arrdom_nums(8, 9, 10, 13, 14)); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMinSize(space, config, A, B)).to.equal(SAME); }); it('should return WORSE if size(v1) > size(v2)', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_addVarRange(config, 'B', 11, 11); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMinSize(space, config, A, B)).to.equal(WORSE); }); }); describe('integration', function() { itAvsB('size', [0, 1], [10, 12], 'A', 'should decide on largest domain first A'); itAvsB('size', [20, 30], [50, 55], 'B', 'should decide on largest domain first B'); it('should count actual elements in the domain A', function() { // note: further tests should be unit tests on domain_size instead let solver = new Solver(); solver.declRange('A', 30, 100); // 71 elements solver.decl('B', fixt_arrdom_ranges([0, 50], [60, 90])); // 82 elements solver._prepare({ distribute: { varStrategy: { type: 'size', }, targeted_var_names: ['A', 'B'], // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName).to.eql(solver.config.allVarNames.indexOf('A')); }); it('should count actual elements in the domain B', function() { // note: further tests should be unit tests on domain_size instead let solver = new Solver(); solver.decl('A', fixt_arrdom_ranges([0, 5], [10, 15], [20, 25], [30, 35], [40, 45], [50, 55], [60, 65], [70, 75], [80, 100])); // 69 elements solver.decl('B', fixt_arrdom_ranges([0, 10], [30, 40], [50, 60], [670, 700])); // 64 elements solver._prepare({ distribute: { varStrategy: { type: 'size', }, targeted_var_names: ['A', 'B'], // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName).to.eql(solver.config.allVarNames.indexOf('B')); }); }); }); describe('by_markov', function() { describe('unit', function() { it('should say v1 is BETTER if v1 is a markov var', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_addVarRange(config, 'B', 11, 11); config_setOption(config, 'varValueStrat', { valtype: 'markov', expandVectorsWith: 1, }, 'A'); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, {})).to.equal(BETTER); }); it('should say v1 is WORSE if v1 not a markov but v2 is', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_addVarRange(config, 'B', 11, 11); config_setOption(config, 'varValueStrat', { valtype: 'markov', expandVectorsWith: 1, }, 'B'); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, {})).to.equal(WORSE); }); it('should say v1 is BETTER if v1 and v2 are both markov vars', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_setOption(config, 'varValueStrat', { valtype: 'markov', expandVectorsWith: 1, }, 'A'); config_addVarRange(config, 'B', 11, 11); config_setOption(config, 'varValueStrat', { valtype: 'markov', expandVectorsWith: 1, }, 'B'); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, {})).to.equal(BETTER); }); it('should say v1 is SAME as v2 if neither is a markov var', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_addVarRange(config, 'B', 11, 11); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, {})).to.equal(SAME); }); it('should use fallback if available and vars are SAME and then return BETTER', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 11); config_addVarRange(config, 'B', 11, 12); let space = space_createRoot(); space_initFromConfig(space, config); let fallback = {fallback: {type: 'size'}}; let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, fallback)).to.equal(BETTER); }); it('should use fallback if available and vars are SAME but then still return SAME', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 11); config_addVarRange(config, 'B', 11, 11); let space = space_createRoot(); space_initFromConfig(space, config); let fallback = {fallback: {type: 'size'}}; let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, fallback)).to.equal(SAME); }); it('should use fallback if available and vars are SAME and then return WORSE', function() { let config = config_create(); config_addVarRange(config, 'A', 11, 12); config_addVarRange(config, 'B', 11, 11); let space = space_createRoot(); space_initFromConfig(space, config); let fallback = {fallback: {type: 'size'}}; let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); expect(distribution_varByMarkov(space, config, A, B, fallback)).to.equal(WORSE); }); }); describe('integration', function() { it('should prioritize markov vars', function() { let solver = new Solver(); solver.declRange('A', 0, 1); solver.declRange('B', 10, 12, { valtype: 'markov', expandVectorsWith: 1, }); solver.declRange('C', 5, 17); solver.decl('D', 13); solver._prepare({ distribute: { varStrategy: {type: 'markov'}, }, }); let varIndex = distribution_getNextVarIndex(solver._space, solver.config); expect(varIndex).to.eql(solver.config.allVarNames.indexOf('B')); }); it('should get markov vars front to back', function() { // it's not really a hard requirement but that's how it works let solver = new Solver(); solver.declRange('A', 0, 1); solver.declRange('B', 10, 12, { valtype: 'markov', expandVectorsWith: 1, }); solver.declRange('C', 5, 17, { valtype: 'markov', expandVectorsWith: 1, }); solver.decl('D', 13); solver._prepare({ distribute: { varStrategy: {type: 'markov'}, }, }); let varIndex = distribution_getNextVarIndex(solver._space, solver.config); // (either way, the next var _must_ be a markov var!) expect(varIndex).to.eql(solver.config.allVarNames.indexOf('B')); }); }); }); describe('by_list', function() { describe('unit', function() { it('should return BETTER if the priority hash says A is higher than B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [A]: 2, [B]: 1, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should return WORSE if the inverted priority hash says A is higher than B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: { [A]: 2, [B]: 1, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(WORSE); }); it('should THROW if the priority hash says A is equal to B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [A]: 2, [B]: 2, }, }; expect(() => distribution_varByList(space, config, A, B, nvconfig)).to.throw('A_CANNOT_GET_SAME_INDEX_FOR_DIFFERENT_NAME'); }); it('should return WORSE if the priority hash says A is lower than B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [A]: 1, [B]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(WORSE); }); it('should return BETTER if the inverted priority hash says A is lower than B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: { [A]: 1, [B]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should return BETTER if A is in the hash but B is not', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [A]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should return WORSE if A is in the inverted hash but B is not', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: { [A]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(WORSE); }); it('should return WORSE if B is in the hash but A is not', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [B]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(WORSE); }); it('should return BETTER if B is in the inverted hash but A is not', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: { [B]: 2, }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should throw if A gets value 0 from the hash', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [A]: 0, }, }; let f = _ => distribution_varByList(space, config, A, B, nvconfig); expect(f).to.throw('SHOULD_NOT_USE_INDEX_ZERO'); }); it('should throw if B gets value 0 from the hash', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: { [B]: 0, }, }; let f = _ => distribution_varByList(space, config, A, B, nvconfig); expect(f).to.throw('SHOULD_NOT_USE_INDEX_ZERO'); }); it('should return SAME if neither A nor B is in the hash without fallback', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: {}, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(SAME); }); it('should return SAME if neither A nor B is in the inverted hash without fallback', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: {}, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(SAME); }); it('should return BETTER if neither is in the hash and fallback is size with A smaller', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 10); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: {}, fallback: { type: 'size', }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should return BETTER if neither is in the inverted hash and fallback is size with A smaller', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 0); config_addVarRange(config, 'B', 0, 10); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { inverted: true, _priorityByIndex: {}, fallback: { type: 'size', }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(BETTER); }); it('should return SAME if neither is in the hash and fallback is size with A same size as B', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 10); config_addVarRange(config, 'B', 0, 10); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: {}, fallback: { type: 'size', }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(SAME); }); it('should return WORSE if neither is in the hash and fallback is size with A larger', function() { let config = config_create(); config_addVarRange(config, 'A', 0, 10); config_addVarRange(config, 'B', 0, 0); let space = space_createRoot(); space_initFromConfig(space, config); let A = config.allVarNames.indexOf('A'); let B = config.allVarNames.indexOf('B'); let nvconfig = { _priorityByIndex: {}, fallback: { type: 'size', }, }; expect(distribution_varByList(space, config, A, B, nvconfig)).to.equal(WORSE); }); }); describe('integration', function() { it('should solve vars in the explicit order of the list A', function() { let solver = new Solver(); solver.decl('A'); solver.decl('B'); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: ['A', 'B'], }, targeted_var_names: ['A', 'B'], // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName).to.eql(solver.config.allVarNames.indexOf('A')); }); it('should solve vars in the explicit order of the list B', function() { let solver = new Solver(); solver.decl('A'); solver.decl('B'); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: ['B', 'A'], }, targeted_var_names: ['A', 'B'], // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName).to.eql(solver.config.allVarNames.indexOf('B')); }); it('should not crash if a var is not on the list or when list is empty', function() { let solver = new Solver(); solver.decl('A'); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: [], }, targeted_var_names: ['A'], // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName).to.eql(solver.config.allVarNames.indexOf('A')); }); function unlistedTest(desc, targetNames, expectingName) { let solver = new Solver(); solver.decl('A'); solver.decl('B'); solver.decl('C'); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: ['A', 'C'], }, targeted_var_names: targetNames, // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName, desc).to.eql(solver.config.allVarNames.indexOf(expectingName)); } it('should assume unlisted vars come after listed vars', function() { unlistedTest('A and C should go before B', ['A', 'B', 'C'], 'A'); unlistedTest('A should go before B', ['A', 'B'], 'A'); unlistedTest('C should go before B', ['B', 'C'], 'C'); unlistedTest('B is only one left', ['B'], 'B'); }); function fallbackTest(desc, targetNames, expectedName) { let solver = new Solver(); solver.decl('A'); solver.decl('B'); solver.decl('C'); solver._prepare({ distribute: { varStrategy: { type: 'markov', // there are no markov vars so it will fallback immediately fallback: { type: 'list', priorityList: ['A', 'C'], }, }, targeted_var_names: targetNames, // else nothing happens }, }); let varName = distribution_getNextVarIndex(solver._space, solver.config); expect(varName, desc).to.eql(solver.config.allVarNames.indexOf(expectedName)); } it('should work with list as fallback dist', function() { fallbackTest('A and C should go before B', ['A', 'B', 'C'], 'A'); fallbackTest('A should go before B', ['A', 'B'], 'A'); fallbackTest('C should go before B', ['B', 'C'], 'C'); fallbackTest('B is only one left', ['B'], 'B'); }); }); }); describe('fallback list -> markov -> size', function() { // each test will ask for a var and supply a more limited list of vars function test(targetNames, resultName) { let solver; solver = new Solver(); solver.declRange('A_list', 0, 10); solver.declRange('B_list', 0, 20); solver.declRange('C_markov', 0, 100, { valtype: 'markov', expandVectorsWith: 1, }); solver.declRange('D_markov', 0, 50, { valtype: 'markov', expandVectorsWith: 1, }); solver.declRange('E_pleb', 0, 100); solver.declRange('F_pleb', 0, 75); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: ['B_list', 'A_list'], fallback: { type: 'markov', fallback: { type: 'size', }, }, }, targeted_var_names: targetNames, // else nothing happens }, }); let varIndex = distribution_getNextVarIndex(solver._space, solver.config); if (resultName !== undefined) { expect(varIndex).to.equal(solver.config.allVarNames.indexOf(resultName)); } } it('base test: should get highest priority on the list; A_list', function() { test(['A_list', 'B_list', 'C_markov', 'D_markov', 'E_pleb', 'F_pleb'], 'B_list'); }); it('missing first item from list', function() { test(['A_list', 'C_markov', 'D_markov', 'E_pleb', 'F_pleb'], 'A_list'); }); it('nothing on list, fallback to markov, get last markov', function() { test(['C_markov', 'D_markov', 'E_pleb', 'F_pleb'], 'D_markov'); }); it('nothing on list, fallback to markov, get only markov', function() { test(['C_markov', 'E_pleb', 'F_pleb'], 'C_markov'); }); it('nothing on list, no markov vars, pick smallest by size', function() { test(['E_pleb', 'F_pleb'], 'F_pleb'); }); it('nothing on list, no markov vars, pick only var left', function() { test(['E_pleb'], 'E_pleb'); }); it('dont crash on randomized inclusion and order', function() { let all = ['A_list', 'B_list', 'C_markov', 'D_markov', 'E_pleb', 'F_pleb']; for (let i = 0; i < 20; ++i) { // randomly remove elements from the list let names = all.filter(() => Math.random() > 0.3); // shuffle list the ugly way names.sort(() => Math.random() - 0.5); // must have some names or assertions are broken if (names.length) test(names); } }); }); describe('list -> inverted list -> min', function() { function test(targetNames, expectName) { let solver = new Solver(); solver.declRange('A', 0, 10); solver.declRange('B', 10, 20); solver.declRange('C', 10, 20); solver.declRange('D', 10, 20); solver.declRange('E', 0, 20); solver.declRange('F', 10, 20); solver._prepare({ distribute: { varStrategy: { type: 'list', priorityList: ['B', 'A'], fallback: { type: 'list', inverted: true, priorityList: ['D', 'C'], fallback: { type: 'min', }, }, }, targeted_var_names: targetNames, // else nothing happens }, }); let varIndex = distribution_getNextVarIndex(solver._space, solver.config); expect(varIndex).to.equal(solver.config.allVarNames.indexOf(expectName)); } it('should prioritize list over rest A', function() { test(['D', 'C', 'A', 'E', 'F'], 'A'); }); it('should prioritize list over rest B', function() { test(['D', 'C', 'B', 'E', 'F'], 'B'); }); it('should prioritize un-blacklisted over rest E', function() { test(['D', 'E', 'C'], 'E'); }); it('should prioritize un-blacklisted over rest F', function() { test(['D', 'F', 'C'], 'F'); }); it('should prioritize C over D in blacklist', function() { test(['D', 'C'], 'C'); }); }); });