const fc = require('fast-check');
function binaryAssociativeFunction(arb) {
return fc.tuple(
fc.set(arb, {minLength: 100, maxLength: 100, compare: (a, b) => fc.stringify(a) === fc.stringify(b)}),
fc.constantFrom(
length => (a, b) => (a + b) % length,
length => (a, b) => (a * b) % length,
// Come up with more...
)
)
.map(([dataset, createAssociativeF]) => {
const associativeF = createAssociativeF(dataset.length);
const stringifiedDataset = new Map(dataset.map((d, index) => [fc.stringify(d), index]))
return (a, b) => {
// compute index for a
const stringifiedA = fc.stringify(a);
const indexA = stringifiedDataset.has(stringifiedA)
? stringifiedDataset.get(stringifiedA)
: fc.hash(stringifiedA) % dataset.length;
// compute index for b
const stringifiedB = fc.stringify(b);
const indexB = stringifiedDataset.has(stringifiedB)
? stringifiedDataset.get(stringifiedB)
: fc.hash(stringifiedB) % dataset.length;
// compute answer
const indexResult = associativeF(indexA, indexB);
if (!(indexA in dataset)) throw new Error('unexpected: invalid indexA');
if (!(indexB in dataset)) throw new Error('unexpected: invalid indexB');
if (!(indexResult in dataset)) throw new Error('unexpected: invalid indexResult');
return dataset[indexResult];
};
})
}
// Just trying with one of the generated functions
const a = 'a';
const b = 'b';
const c = 'c';
const f1 = fc.sample(binaryAssociativeFunction(fc.string()), 1)[0];
console.log('a,b -> ' + f1(a, b));
console.log('(a,b),c -> ' + f1(f1(a, b), c));
console.log('b,c -> ' + f1(b, c));
console.log('a,(b,c) -> ' + f1(a, f1(b, c)));
// Property to confirm it works
fc.assert(fc.property(
binaryAssociativeFunction(fc.string()), fc.string(), fc.string(), fc.string(),
(f, a, b, c) => f(f(a,b),c) === f(a,f(b,c))));
console.log('Done!');