UNPKG

fdlib

Version:

Shared library files for the fdq package

285 lines (248 loc) 9.08 kB
// FDlib Helpers // Note: this file is post processed to remove the ASSERTs // A grunt cli (`grunt string-replace:perf`, which is also triggered // in `grunt perf`) will replace all lines that start with `ASSERT` // with a `1`, which acts as a noop to prevent syntax errors for // sub-statements (like condiditions). Additionally, there is a macro // `__REMOVE_BELOW_FOR_DIST__` and `__REMOVE_ABOVE_FOR_DIST__` which // act like barriers. Anything in between is removed and replaced with // an `x` so the result is `x=1` (just easier than the clean version). // We need to wipe these lines because we won't use them and when we // strip the ASSERT lines, syntax errors would happen in this file. // The export is preserved so the constants are still exported but // the method exports are stripped with the ASSERT replacement... // BODY_START let SUB = 0; // WARNING: don't change this. It's mostly a magic number thing. let SUP = 100000000; // Don't let this max exceed 30 bits or stuff will break let NOT_FOUND = -1; let LOG_NONE = 0; let LOG_STATS = 1; let LOG_SOLVES = 2; let LOG_MIN = LOG_NONE; let LOG_MAX = LOG_SOLVES; // different from NOT_FOUND in that NOT_FOUND must be -1 because of the indexOf api // while NO_SUCH_VALUE must be a value that cannot be a legal domain value (<SUB or >SUP) let NO_SUCH_VALUE = Math.min(0, SUB) - 1; // make sure NO_SUCH_VALUE is a value that may be neither valid in a domain nor >=0 let ARR_RANGE_SIZE = 2; // magic number const SMALL_MAX_NUM = 30; // there are SMALL_MAX_NUM flags. if they are all on, this is the number value // (oh and; 1<<31 is negative. >>>0 makes it unsigned. this is why 30 is max.) const SOLVED_FLAG = 1 << 31 >>> 0; // the >>> makes it unsigned, we dont really need it but it may help perf a little (unsigned vs signed) const $STABLE = 0; const $CHANGED = 1; const $SOLVED = 2; const $REJECTED = 3; let TERM = console; // __REMOVE_BELOW_FOR_ASSERTS__ ASSERT(SMALL_MAX_NUM <= 30, 'cant be larger because then shifting fails above and elsewhere'); ASSERT(NOT_FOUND === NO_SUCH_VALUE, 'keep not found constants equal to prevent confusion bugs'); // For unit tests // Should be removed in production. Obviously. function ASSERT(bool, msg = '', ...args) { if (bool) { return; } if (!msg) msg = '(no desc)'; //msg = new Error('trace').stack; TERM.error(`Assertion fail: ${msg}`); if (args) { TERM.log('Error args:', args); } // TERM.trace() // process.exit() # uncomment for quick error access :) let suffix = ''; if (args && args.length) { suffix = `Args (${args.length}x): \`${_stringify(args)}\``; } THROW(`Assertion fail: ${msg} ${suffix}`); } function _stringify(o) { if (o instanceof Array) { return `[ ${o.map(e => _stringify(e)).join(', ')} ]`; } return `${o}`; } // Simple function to completely validate a domain // Should be removed in production. Obviously. function ASSERT_STRDOM(domain, expectSmallest, domain__debug) { let s = domain__debug && domain__debug(domain); const strdomValueLen = 2; const strdomRangeLen = 2 * strdomValueLen; ASSERT(typeof domain === 'string', 'ONLY_STRDOM', s); ASSERT((domain.length % strdomRangeLen) === 0, 'SHOULD_CONTAIN_RANGES', s); let lo = (domain.charCodeAt(0) << 16) | domain.charCodeAt(1); let hi = (domain.charCodeAt(domain.length - strdomValueLen) << 16) | domain.charCodeAt(domain.length - strdomValueLen + 1); ASSERT(lo >= SUB, 'SHOULD_BE_GTE ' + SUB, s); ASSERT(hi <= SUP, 'SHOULD_BE_LTE ' + SUP, s); ASSERT(!expectSmallest || lo !== hi || domain.length > strdomRangeLen, 'SHOULD_NOT_BE_SOLVED', s); return true; } function ASSERT_SOLDOM(domain, value) { ASSERT(typeof domain === 'number', 'ONLY_SOLDOM'); ASSERT(domain >= 0, 'ALL_SOLDOMS_SHOULD_BE_UNSIGNED'); ASSERT(domain >= SOLVED_FLAG, 'SOLDOMS_MUST_HAVE_FLAG_SET'); ASSERT((domain ^ SOLVED_FLAG) >= SUB, 'SOLVED_NUMDOM_SHOULD_BE_MIN_SUB'); ASSERT((domain ^ SOLVED_FLAG) <= SUP, 'SOLVED_NUMDOM_SHOULD_BE_MAX_SUP'); if (value !== undefined) ASSERT((domain ^ SOLVED_FLAG) === value, 'SHOULD_BE_SOLVED_TO:' + value); return true; } function ASSERT_BITDOM(domain) { ASSERT(typeof domain === 'number', 'ONLY_BITDOM'); ASSERT(domain >= 0, 'ALL_BITDOMS_SHOULD_BE_UNSIGNED'); ASSERT(domain < SOLVED_FLAG, 'SOLVED_FLAG_NOT_SET'); ASSERT(SMALL_MAX_NUM < 31, 'next assertion relies on this'); ASSERT(domain >= 0 && domain < ((1 << (SMALL_MAX_NUM + 1)) >>> 0), 'NUMDOM_SHOULD_BE_VALID_RANGE'); return true; } function ASSERT_ARRDOM(domain, min, max) { ASSERT(domain instanceof Array, 'ONLY_ARRDOM'); if (domain.length === 0) return; ASSERT(domain.length % 2 === 0, 'SHOULD_CONTAIN_RANGES'); ASSERT(domain[0] >= (min || SUB), 'SHOULD_BE_GTE ' + (min || SUB)); ASSERT(domain[domain.length - 1] <= (max === undefined ? SUP : max), 'SHOULD_BE_LTE ' + (max === undefined ? SUP : max)); return true; } function ASSERT_NORDOM(domain, expectSmallest, domain__debug) { let s = domain__debug && domain__debug(domain); ASSERT(typeof domain === 'string' || typeof domain === 'number', 'ONLY_NORDOM', s); if (typeof domain === 'string') { ASSERT(domain.length > 0, 'empty domains are always numdoms'); if (expectSmallest) { let lo = (domain.charCodeAt(0) << 16) | domain.charCodeAt(1); let hi = ((domain.charCodeAt(domain.length - 2) << 16) | domain.charCodeAt(domain.length - 1)); ASSERT(hi > SMALL_MAX_NUM, 'EXPECTING_STRDOM_TO_HAVE_NUMS_GT_BITDOM', s); ASSERT(domain.length > 4 || lo !== hi, 'EXPECTING_STRDOM_NOT_TO_BE_SOLVED'); } return ASSERT_STRDOM(domain, undefined, undefined, s); } if (expectSmallest) ASSERT(!domain || domain >= SOLVED_FLAG || (domain & (domain - 1)) !== 0, 'EXPECTING_SOLVED_NUMDOM_TO_BE_SOLDOM', s); ASSERT_NUMDOM(domain, s); return true; } function ASSERT_NUMDOM(domain, expectSmallest, domain__debug) { let s = domain__debug && domain__debug(domain); ASSERT(typeof domain === 'number', 'ONLY_NUMDOM', s); if (expectSmallest) ASSERT(!domain || domain >= SOLVED_FLAG || (domain & (domain - 1)) !== 0, 'EXPECTING_SOLVED_NUMDOM_TO_BE_SOLDOM', s); if (domain >= SOLVED_FLAG) ASSERT_SOLDOM(domain); else ASSERT_BITDOM(domain); return true; } function ASSERT_ANYDOM(domain) { ASSERT(typeof domain === 'string' || typeof domain === 'number' || domain instanceof Array, 'ONLY_VALID_DOM_TYPE'); } function ASSERT_VARDOMS_SLOW(vardoms, domain__debug) { for (let varIndex = 0, len = vardoms.length; varIndex < len; varIndex++) { let domain = vardoms[varIndex]; ASSERT_NORDOM(domain, true, domain__debug); } } const LOG_FLAG_NONE = 0; const LOG_FLAG_PROPSTEPS = 1; const LOG_FLAG_CHOICE = 2; const LOG_FLAG_SEARCH = 4; const LOG_FLAG_SOLUTIONS = 8; let LOG_FLAGS = LOG_FLAG_NONE; //LOG_FLAG_PROPSTEPS|LOG_FLAG_CHOICE|LOG_FLAG_SOLUTIONS|LOG_FLAG_SEARCH; function ASSERT_SET_LOG(level) { LOG_FLAGS = level; } function helper_logger() { TERM.log('LOG', ...arguments); } function ASSERT_LOG(flags, func) { if (flags & LOG_FLAGS) { ASSERT(typeof func === 'function'); func(helper_logger); } } function TRACE_SILENT(...args) { TRACE('\x1b[90m', ...args, '\x1b[0m'); } let TRACING = false; function isTracing() { return TRACING; } function setTracing(b) { TRACING = b; } function TRACE(...args) { if (args.length === 1 && args[0] === '') return false; if (TRACING) TERM.log(...args); return false; } function TRACE_MORPH(from, to, desc, names, indexes) { TRACE(' ### Morphing; ', from, ' ==> ', to); } // __REMOVE_ABOVE_FOR_ASSERTS__ function setTerm(newTerm) { if (!newTerm.log) newTerm.log = TERM.log.bind(TERM); if (!newTerm.error) newTerm.error = TERM.error.bind(TERM); if (!newTerm.warn) newTerm.warn = TERM.warn.bind(TERM); if (!newTerm.trace) newTerm.trace = TERM.trace.bind(TERM); if (!newTerm.time) newTerm.time = TERM.time.bind(TERM); if (!newTerm.timeEnd) newTerm.timeEnd = TERM.timeEnd.bind(TERM); TERM = newTerm; } function getTerm() { return TERM; } function SUSH() { let t = TERM; setTerm({ log: _ => _, warn: _ => _, error: _ => _, trace: _ => _, time: _ => _, timeEnd: _ => _, }); return t; } // Abstraction for throwing because throw statements cause deoptimizations // All explicit throws should use this function. Also helps with tooling // later, catching and reporting explicits throws and what not. function THROW(...msg) { throw new Error(msg.join(': ')); } // BODY_STOP export { $CHANGED, $REJECTED, $SOLVED, $STABLE, LOG_FLAG_CHOICE, LOG_FLAG_NONE, LOG_FLAG_PROPSTEPS, LOG_FLAG_SEARCH, LOG_FLAG_SOLUTIONS, LOG_NONE, LOG_STATS, LOG_SOLVES, LOG_MAX, LOG_MIN, NOT_FOUND, NO_SUCH_VALUE, ARR_RANGE_SIZE, SMALL_MAX_NUM, SOLVED_FLAG, SUB, SUP, ASSERT, ASSERT_ANYDOM, ASSERT_ARRDOM, ASSERT_BITDOM, ASSERT_LOG, ASSERT_NORDOM, ASSERT_NUMDOM, ASSERT_SET_LOG, ASSERT_SOLDOM, ASSERT_STRDOM, ASSERT_VARDOMS_SLOW, getTerm, isTracing, setTerm, setTracing, SUSH, THROW, TRACE, TRACE_MORPH, TRACE_SILENT, };