import random from "random-js" ; export type OptionsMatrix < T extends Record < string , any > > = Required < { [ K in keyof T ] : readonly ( T [ K ] ) [ ] ; } > ; export const booleanCases = [ true , false ] ; export const numberCases = [ undefined ] ; type PartialWithKeyCount < T extends Record < string , any > > = ( Partial < T > & { __partialKeyCount ? : number ; } ) ; function applyPairToPartial < T extends Record < string , any > > ( randEng , keyCount , partials , pair ) { const matchingPartials = [ ] ; for ( const partial of partials ) { if ( pair . iKey in partial && pair . jKey in partial && partial [ pair . iKey ] === pair . iVal && partial [ pair . jKey ] === pair . jVal ) { return ; } if ( partial . __partialKeyCount ! < keyCount ) { if ( ( pair . iKey in partial && ! ( pair . jKey in partial ) && partial [ pair . iKey ] === pair . iVal ) || ( pair . jKey in partial && ! ( pair . iKey in partial ) && partial [ pair . jKey ] === pair . jVal ) ) { matchingPartials . push ( partial ) ; } } } if ( matchingPartials . length === 0 ) { const partial = { } ; partial . __partialKeyCount = 2 ; partial [ pair . iKey ] = pair . iVal ; partial [ pair . jKey ] = pair . jVal ; partials . push ( partial ) ; } else { const found = random . pick ( randEng , matchingPartials ) ; if ( pair . iKey in found ) { found [ pair . jKey ] = pair . jVal ; } else { found [ pair . iKey ] = pair . iVal ; } found . __partialKeyCount ! ++ ; } } export function generatePairwiseOptions < T extends Record < string , any > > ( optionsMatrix , randomSeed = 0x35843 ) { const randEng = random . engines . mt19937 ( ) ; randEng . seed ( randomSeed ) ; const matrixKeys = Object . keys ( optionsMatrix ) . filter ( ( k ) => optionsMatrix [ k ] . length > 1 || optionsMatrix [ k ] [ 0 ] !== undefined ) . sort ( ( a , b ) => optionsMatrix [ b ] . length - optionsMatrix [ a ] . length ) ; if ( matrixKeys . length === 0 ) { return [ ] ; } if ( matrixKeys . length === 1 ) { const values = optionsMatrix [ matrixKeys [ 0 ] ] ; return values . map < T > ( ( v ) => { return { [ matrixKeys [ 0 ] ] : v } as T ; } ) ; } const partials = [ ] ; for ( let i = 0 ; i < matrixKeys . length - 1 ; i ++ ) { const iKey = matrixKeys [ i ] ; for ( let j = i + 1 ; j < matrixKeys . length ; j ++ ) { const jKey = matrixKeys [ j ] ; for ( const iVal of optionsMatrix [ iKey ] ) { for ( const jVal of optionsMatrix [ jKey ] ) { applyPairToPartial ( randEng , matrixKeys . length , partials , { iKey , iVal , jKey , jVal } ) ; } } } } for ( const partial of partials ) { if ( partial . __partialKeyCount !== matrixKeys . length ) { for ( const key of matrixKeys ) { if ( ! ( key in partial ) ) { partial [ key ] = random . pick ( randEng , optionsMatrix [ key ] as any [ ] ) ; } } } delete partial . __partialKeyCount ; } return partials as T [ ] ; }