import { Environment, ValueType } from './types'; import * as _ from 'lodash'; // takes an array of arrays, and outputs an array containing all possibilities to select values from the original sub-arrays function crossProduct(arrays: T[][]): T[][]; // type variant 1: variable length, same type function crossProduct(arrays: [T1[], T2[]]): [T1, T2][]; // type variant 1: length 2, varying types function crossProduct(arrays: any[]): any[] { // eslint-disable-line @typescript-eslint/no-explicit-any if (_.some(arrays, (array) => array.length === 0)) return []; const res = []; const indexes = _.fill(new Array(arrays.length), 0); while (true) { // add the current combination to res res.push(_.zipWith(arrays, indexes, (array, index) => array[index])); // find the lexicographically next indexes const countIndex = _.findLastIndex(_.zip(arrays, indexes), ([array, index]) => array!.length > index! + 1); if (countIndex < 0) break; indexes[countIndex]++; // all indexes to the right wrap around to 0 for (let i = countIndex + 1; i < arrays.length; i++) indexes[i] = 0; } return res; } const mergeEnvironments = function (...environments: Environment[]): Environment | null { // merges multiple environments if they are compatible, or returns null if they are not const environment: Environment = {}; if (_.some(environments, (partialEnvironment) => { return _.some(partialEnvironment, (value: ValueType, identifier) => { if (_.has(environment, identifier)) { if (_.isEqual(value, environment[identifier])) { // identifier was set before, but is compatible – all is well return false; } else { // identifier is conflicting return true; // raise conflict } } else { environment[identifier] = value; // identifier not set before – just copy return false; } }); })) return null; // found inconsistency return environment; }; class NameSuggester { private usedNames: Set = new Set(); constructor() { this.usedNames = new Set(); } suggestName(preferredName: string): string { if (!this.usedNames.has(preferredName)) { this.usedNames.add(preferredName); return preferredName; } else { // naming conflict // dumb resolution strategy: append something random return this.suggestName(preferredName + '-' + Math.random().toString(36).substring(2,3)); } } } const simpleToStableJSON = (o: ValueType): string => { if (o instanceof Array) { return `[${o.map(simpleToStableJSON).join()}]`; } else if (typeof o === "object") { const propBinders = Object.keys(o).sort().map((k) => `${JSON.stringify(k)}:${simpleToStableJSON(o[k])}`); return `{${propBinders.join()}}`; } else { return JSON.stringify(o); } }; export { crossProduct, mergeEnvironments, NameSuggester, simpleToStableJSON };