UNPKG

luhn-generator

Version:

A generator of numbers that passes the validation of Luhn algorithm or Luhn formula, also known as the 'modulus 10' or 'mod 10' algorithm

456 lines (417 loc) 14.9 kB
'use strict'; /* globals Symbol: false, Uint8Array: false, WeakMap: false */ /*! * deep-eql * Copyright(c) 2013 Jake Luer <jake@alogicalparadox.com> * MIT Licensed */ var type = require('type-detect'); function FakeMap() { this._key = 'chai/deep-eql__' + Math.random() + Date.now(); } FakeMap.prototype = { get: function getMap(key) { return key[this._key]; }, set: function setMap(key, value) { if (Object.isExtensible(key)) { Object.defineProperty(key, this._key, { value: value, configurable: true, }); } }, }; var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; /*! * Check to see if the MemoizeMap has recorded a result of the two operands * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {MemoizeMap} memoizeMap * @returns {Boolean|null} result */ function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { // Technically, WeakMap keys can *only* be objects, not primitives. if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { return null; } var leftHandMap = memoizeMap.get(leftHandOperand); if (leftHandMap) { var result = leftHandMap.get(rightHandOperand); if (typeof result === 'boolean') { return result; } } return null; } /*! * Set the result of the equality into the MemoizeMap * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {MemoizeMap} memoizeMap * @param {Boolean} result */ function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { // Technically, WeakMap keys can *only* be objects, not primitives. if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { return; } var leftHandMap = memoizeMap.get(leftHandOperand); if (leftHandMap) { leftHandMap.set(rightHandOperand, result); } else { leftHandMap = new MemoizeMap(); leftHandMap.set(rightHandOperand, result); memoizeMap.set(leftHandOperand, leftHandMap); } } /*! * Primary Export */ module.exports = deepEqual; module.exports.MemoizeMap = MemoizeMap; /** * Assert deeply nested sameValue equality between two objects of any type. * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {Object} [options] (optional) Additional options * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular references to blow the stack. * @return {Boolean} equal match */ function deepEqual(leftHandOperand, rightHandOperand, options) { // If we have a comparator, we can't assume anything; so bail to its check first. if (options && options.comparator) { return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); } var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); if (simpleResult !== null) { return simpleResult; } // Deeper comparisons are pushed through to a larger function return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); } /** * Many comparisons can be canceled out early via simple equality or primitive checks. * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @return {Boolean|null} equal match */ function simpleEqual(leftHandOperand, rightHandOperand) { // Equal references (except for Numbers) can be returned early if (leftHandOperand === rightHandOperand) { // Handle +-0 cases return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; } // handle NaN cases if ( leftHandOperand !== leftHandOperand && // eslint-disable-line no-self-compare rightHandOperand !== rightHandOperand // eslint-disable-line no-self-compare ) { return true; } // Anything that is not an 'object', i.e. symbols, functions, booleans, numbers, // strings, and undefined, can be compared by reference. if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { // Easy out b/c it would have passed the first equality check return false; } return null; } /*! * The main logic of the `deepEqual` function. * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {Object} [options] (optional) Additional options * @param {Array} [options.comparator] (optional) Override default algorithm, determining custom equality. * @param {Array} [options.memoize] (optional) Provide a custom memoization object which will cache the results of complex objects for a speed boost. By passing `false` you can disable memoization, but this will cause circular references to blow the stack. * @return {Boolean} equal match */ function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { options = options || {}; options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); var comparator = options && options.comparator; // Check if a memoized result exists. var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); if (memoizeResultLeft !== null) { return memoizeResultLeft; } var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); if (memoizeResultRight !== null) { return memoizeResultRight; } // If a comparator is present, use it. if (comparator) { var comparatorResult = comparator(leftHandOperand, rightHandOperand); // Comparators may return null, in which case we want to go back to default behavior. if (comparatorResult === false || comparatorResult === true) { memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); return comparatorResult; } // To allow comparators to override *any* behavior, we ran them first. Since it didn't decide // what to do, we need to make sure to return the basic tests first before we move on. var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); if (simpleResult !== null) { // Don't memoize this, it takes longer to set/retrieve than to just compare. return simpleResult; } } var leftHandType = type(leftHandOperand); if (leftHandType !== type(rightHandOperand)) { memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); return false; } // Temporarily set the operands in the memoize object to prevent blowing the stack memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); return result; } function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { switch (leftHandType) { case 'String': case 'Number': case 'Boolean': case 'Date': // If these types are their instance types (e.g. `new Number`) then re-deepEqual against their values return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); case 'Promise': case 'Symbol': case 'function': case 'WeakMap': case 'WeakSet': case 'Error': return leftHandOperand === rightHandOperand; case 'Arguments': case 'Int8Array': case 'Uint8Array': case 'Uint8ClampedArray': case 'Int16Array': case 'Uint16Array': case 'Int32Array': case 'Uint32Array': case 'Float32Array': case 'Float64Array': case 'Array': return iterableEqual(leftHandOperand, rightHandOperand, options); case 'RegExp': return regexpEqual(leftHandOperand, rightHandOperand); case 'Generator': return generatorEqual(leftHandOperand, rightHandOperand, options); case 'DataView': return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); case 'ArrayBuffer': return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); case 'Set': return entriesEqual(leftHandOperand, rightHandOperand, options); case 'Map': return entriesEqual(leftHandOperand, rightHandOperand, options); default: return objectEqual(leftHandOperand, rightHandOperand, options); } } /*! * Compare two Regular Expressions for equality. * * @param {RegExp} leftHandOperand * @param {RegExp} rightHandOperand * @return {Boolean} result */ function regexpEqual(leftHandOperand, rightHandOperand) { return leftHandOperand.toString() === rightHandOperand.toString(); } /*! * Compare two Sets/Maps for equality. Faster than other equality functions. * * @param {Set} leftHandOperand * @param {Set} rightHandOperand * @param {Object} [options] (Optional) * @return {Boolean} result */ function entriesEqual(leftHandOperand, rightHandOperand, options) { // IE11 doesn't support Set#entries or Set#@@iterator, so we need manually populate using Set#forEach if (leftHandOperand.size !== rightHandOperand.size) { return false; } if (leftHandOperand.size === 0) { return true; } var leftHandItems = []; var rightHandItems = []; leftHandOperand.forEach(function gatherEntries(key, value) { leftHandItems.push([ key, value ]); }); rightHandOperand.forEach(function gatherEntries(key, value) { rightHandItems.push([ key, value ]); }); return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); } /*! * Simple equality for flat iterable objects such as Arrays, TypedArrays or Node.js buffers. * * @param {Iterable} leftHandOperand * @param {Iterable} rightHandOperand * @param {Object} [options] (Optional) * @return {Boolean} result */ function iterableEqual(leftHandOperand, rightHandOperand, options) { var length = leftHandOperand.length; if (length !== rightHandOperand.length) { return false; } if (length === 0) { return true; } var index = -1; while (++index < length) { if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { return false; } } return true; } /*! * Simple equality for generator objects such as those returned by generator functions. * * @param {Iterable} leftHandOperand * @param {Iterable} rightHandOperand * @param {Object} [options] (Optional) * @return {Boolean} result */ function generatorEqual(leftHandOperand, rightHandOperand, options) { return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); } /*! * Determine if the given object has an @@iterator function. * * @param {Object} target * @return {Boolean} `true` if the object has an @@iterator function. */ function hasIteratorFunction(target) { return typeof Symbol !== 'undefined' && typeof target === 'object' && typeof Symbol.iterator !== 'undefined' && typeof target[Symbol.iterator] === 'function'; } /*! * Gets all iterator entries from the given Object. If the Object has no @@iterator function, returns an empty array. * This will consume the iterator - which could have side effects depending on the @@iterator implementation. * * @param {Object} target * @returns {Array} an array of entries from the @@iterator function */ function getIteratorEntries(target) { if (hasIteratorFunction(target)) { try { return getGeneratorEntries(target[Symbol.iterator]()); } catch (iteratorError) { return []; } } return []; } /*! * Gets all entries from a Generator. This will consume the generator - which could have side effects. * * @param {Generator} target * @returns {Array} an array of entries from the Generator. */ function getGeneratorEntries(generator) { var generatorResult = generator.next(); var accumulator = [ generatorResult.value ]; while (generatorResult.done === false) { generatorResult = generator.next(); accumulator.push(generatorResult.value); } return accumulator; } /*! * Gets all own and inherited enumerable keys from a target. * * @param {Object} target * @returns {Array} an array of own and inherited enumerable keys from the target. */ function getEnumerableKeys(target) { var keys = []; for (var key in target) { keys.push(key); } return keys; } /*! * Determines if two objects have matching values, given a set of keys. Defers to deepEqual for the equality check of * each key. If any value of the given key is not equal, the function will return false (early). * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {Array} keys An array of keys to compare the values of leftHandOperand and rightHandOperand against * @param {Object} [options] (Optional) * @return {Boolean} result */ function keysEqual(leftHandOperand, rightHandOperand, keys, options) { var length = keys.length; if (length === 0) { return true; } for (var i = 0; i < length; i += 1) { if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { return false; } } return true; } /*! * Recursively check the equality of two Objects. Once basic sameness has been established it will defer to `deepEqual` * for each enumerable key in the object. * * @param {Mixed} leftHandOperand * @param {Mixed} rightHandOperand * @param {Object} [options] (Optional) * @return {Boolean} result */ function objectEqual(leftHandOperand, rightHandOperand, options) { var leftHandKeys = getEnumerableKeys(leftHandOperand); var rightHandKeys = getEnumerableKeys(rightHandOperand); if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { leftHandKeys.sort(); rightHandKeys.sort(); if (iterableEqual(leftHandKeys, rightHandKeys) === false) { return false; } return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); } var leftHandEntries = getIteratorEntries(leftHandOperand); var rightHandEntries = getIteratorEntries(rightHandOperand); if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { leftHandEntries.sort(); rightHandEntries.sort(); return iterableEqual(leftHandEntries, rightHandEntries, options); } if (leftHandKeys.length === 0 && leftHandEntries.length === 0 && rightHandKeys.length === 0 && rightHandEntries.length === 0) { return true; } return false; } /*! * Returns true if the argument is a primitive. * * This intentionally returns true for all objects that can be compared by reference, * including functions and symbols. * * @param {Mixed} value * @return {Boolean} result */ function isPrimitive(value) { return value === null || typeof value !== 'object'; }