opusdev/vector-similarity-api
1
1/*!2 * to-regex-range <https://github.com/micromatch/to-regex-range>3 *4 * Copyright (c) 2015-present, Jon Schlinkert.5 * Released under the MIT License.6 */7 8'use strict';9 10const isNumber = require('is-number');11 12const toRegexRange = (min, max, options) => {13 if (isNumber(min) === false) {14 throw new TypeError('toRegexRange: expected the first argument to be a number');15 }16 17 if (max === void 0 || min === max) {18 return String(min);19 }20 21 if (isNumber(max) === false) {22 throw new TypeError('toRegexRange: expected the second argument to be a number.');23 }24 25 let opts = { relaxZeros: true, ...options };26 if (typeof opts.strictZeros === 'boolean') {27 opts.relaxZeros = opts.strictZeros === false;28 }29 30 let relax = String(opts.relaxZeros);31 let shorthand = String(opts.shorthand);32 let capture = String(opts.capture);33 let wrap = String(opts.wrap);34 let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap;35 36 if (toRegexRange.cache.hasOwnProperty(cacheKey)) {37 return toRegexRange.cache[cacheKey].result;38 }39 40 let a = Math.min(min, max);41 let b = Math.max(min, max);42 43 if (Math.abs(a - b) === 1) {44 let result = min + '|' + max;45 if (opts.capture) {46 return `(${result})`;47 }48 if (opts.wrap === false) {49 return result;50 }51 return `(?:${result})`;52 }53 54 let isPadded = hasPadding(min) || hasPadding(max);55 let state = { min, max, a, b };56 let positives = [];57 let negatives = [];58 59 if (isPadded) {60 state.isPadded = isPadded;61 state.maxLen = String(state.max).length;62 }63 64 if (a < 0) {65 let newMin = b < 0 ? Math.abs(b) : 1;66 negatives = splitToPatterns(newMin, Math.abs(a), state, opts);67 a = state.a = 0;68 }69 70 if (b >= 0) {71 positives = splitToPatterns(a, b, state, opts);72 }73 74 state.negatives = negatives;75 state.positives = positives;76 state.result = collatePatterns(negatives, positives, opts);77 78 if (opts.capture === true) {79 state.result = `(${state.result})`;80 } else if (opts.wrap !== false && (positives.length + negatives.length) > 1) {81 state.result = `(?:${state.result})`;82 }83 84 toRegexRange.cache[cacheKey] = state;85 return state.result;86};87 88function collatePatterns(neg, pos, options) {89 let onlyNegative = filterPatterns(neg, pos, '-', false, options) || [];90 let onlyPositive = filterPatterns(pos, neg, '', false, options) || [];91 let intersected = filterPatterns(neg, pos, '-?', true, options) || [];92 let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);93 return subpatterns.join('|');94}95 96function splitToRanges(min, max) {97 let nines = 1;98 let zeros = 1;99 100 let stop = countNines(min, nines);101 let stops = new Set([max]);102 103 while (min <= stop && stop <= max) {104 stops.add(stop);105 nines += 1;106 stop = countNines(min, nines);107 }108 109 stop = countZeros(max + 1, zeros) - 1;110 111 while (min < stop && stop <= max) {112 stops.add(stop);113 zeros += 1;114 stop = countZeros(max + 1, zeros) - 1;115 }116 117 stops = [...stops];118 stops.sort(compare);119 return stops;120}121 122/**123 * Convert a range to a regex pattern124 * @param {Number} `start`125 * @param {Number} `stop`126 * @return {String}127 */128 129function rangeToPattern(start, stop, options) {130 if (start === stop) {131 return { pattern: start, count: [], digits: 0 };132 }133 134 let zipped = zip(start, stop);135 let digits = zipped.length;136 let pattern = '';137 let count = 0;138 139 for (let i = 0; i < digits; i++) {140 let [startDigit, stopDigit] = zipped[i];141 142 if (startDigit === stopDigit) {143 pattern += startDigit;144 145 } else if (startDigit !== '0' || stopDigit !== '9') {146 pattern += toCharacterClass(startDigit, stopDigit, options);147 148 } else {149 count++;150 }151 }152 153 if (count) {154 pattern += options.shorthand === true ? '\\d' : '[0-9]';155 }156 157 return { pattern, count: [count], digits };158}159 160function splitToPatterns(min, max, tok, options) {161 let ranges = splitToRanges(min, max);162 let tokens = [];163 let start = min;164 let prev;165 166 for (let i = 0; i < ranges.length; i++) {167 let max = ranges[i];168 let obj = rangeToPattern(String(start), String(max), options);169 let zeros = '';170 171 if (!tok.isPadded && prev && prev.pattern === obj.pattern) {172 if (prev.count.length > 1) {173 prev.count.pop();174 }175 176 prev.count.push(obj.count[0]);177 prev.string = prev.pattern + toQuantifier(prev.count);178 start = max + 1;179 continue;180 }181 182 if (tok.isPadded) {183 zeros = padZeros(max, tok, options);184 }185 186 obj.string = zeros + obj.pattern + toQuantifier(obj.count);187 tokens.push(obj);188 start = max + 1;189 prev = obj;190 }191 192 return tokens;193}194 195function filterPatterns(arr, comparison, prefix, intersection, options) {196 let result = [];197 198 for (let ele of arr) {199 let { string } = ele;200 201 // only push if _both_ are negative...202 if (!intersection && !contains(comparison, 'string', string)) {203 result.push(prefix + string);204 }205 206 // or _both_ are positive207 if (intersection && contains(comparison, 'string', string)) {208 result.push(prefix + string);209 }210 }211 return result;212}213 214/**215 * Zip strings216 */217 218function zip(a, b) {219 let arr = [];220 for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]);221 return arr;222}223 224function compare(a, b) {225 return a > b ? 1 : b > a ? -1 : 0;226}227 228function contains(arr, key, val) {229 return arr.some(ele => ele[key] === val);230}231 232function countNines(min, len) {233 return Number(String(min).slice(0, -len) + '9'.repeat(len));234}235 236function countZeros(integer, zeros) {237 return integer - (integer % Math.pow(10, zeros));238}239 240function toQuantifier(digits) {241 let [start = 0, stop = ''] = digits;242 if (stop || start > 1) {243 return `{${start + (stop ? ',' + stop : '')}}`;244 }245 return '';246}247 248function toCharacterClass(a, b, options) {249 return `[${a}${(b - a === 1) ? '' : '-'}${b}]`;250}251 252function hasPadding(str) {253 return /^-?(0+)\d/.test(str);254}255 256function padZeros(value, tok, options) {257 if (!tok.isPadded) {258 return value;259 }260 261 let diff = Math.abs(tok.maxLen - String(value).length);262 let relax = options.relaxZeros !== false;263 264 switch (diff) {265 case 0:266 return '';267 case 1:268 return relax ? '0?' : '0';269 case 2:270 return relax ? '0{0,2}' : '00';271 default: {272 return relax ? `0{0,${diff}}` : `0{${diff}}`;273 }274 }275}276 277/**278 * Cache279 */280 281toRegexRange.cache = {};282toRegexRange.clearCache = () => (toRegexRange.cache = {});283 284/**285 * Expose `toRegexRange`286 */287 288module.exports = toRegexRange;289 