Skip to content

Commit

Permalink
Adds set.choose
Browse files Browse the repository at this point in the history
  • Loading branch information
rgrannell1 committed Aug 6, 2023
1 parent bf8fd70 commit 9b534c1
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
8 changes: 8 additions & 0 deletions src/array/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ export function concat<T>(...elems: Wrapped<T>[]): Thunk<T[]> {
};
}

/*
* Given a list of fuzzers, and a density function, retrieve a subset of elements
*
* @param elems A list of fuzzers
* @param density A function that takes a min and max, and returns a BigInt
*
* @returns A thunk that returns a subset of the umwrapped fuzzers
*/
export function choose<T>(elems: Wrapped<T[]>, density: DensityBigInt): Thunk<T[]> {
return () => {
const concreteElems = unwrap(elems);
Expand Down
49 changes: 49 additions & 0 deletions src/set/set.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,52 @@ Deno.test({
}
},
});


Deno.test({
name: "Set.choose | returns a subset of the provided collection",
fn() {
const integers = Peach.Set.from(
Peach.Number.uniform(0, 100),
Peach.Number.uniform(0, 100));

for (let idx = 0; idx < 1_000; idx++) {
const sample = integers();

const result = Peach.Set.choose(sample, Peach.BigInt.uniform);
if (result.length > 100) {
throw new Error(`out of range 0...100: ${result}`);
}

for (const elem of sample) {
if (elem < 0 || elem > 100) {
throw new Error(`out of range 0...100: ${elem}`);
}
}
}
}
});

Deno.test({
name: "Set.choose | preserves elements",
fn() {
const integers = Peach.Set.from(
0,
Peach.Number.uniform(0, 100));

for (let idx = 0; idx < 1_000; idx++) {
const sample = integers();

const result = Peach.Set.choose(sample, Peach.BigInt.uniform);
if (result.length > 100) {
throw new Error(`out of range 0...100: ${result}`);
}

for (const elem of sample) {
if (elem !== 0) {
throw new Error(`elem was not zero: ${elem}`);
}
}
}
}
})
35 changes: 34 additions & 1 deletion src/set/set.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Thunk, Wrapped } from "../types.ts";
import type { Thunk, Wrapped, DensityBigInt } from "../types.ts";
import { unwrap } from "../types.ts";

/**
Expand Down Expand Up @@ -26,3 +26,36 @@ export function from<T>(
return elems;
};
}


/*
* Given a list of fuzzers, and a density function, retrieve a subset of elements
*
* @param elems A list of fuzzers
* @param density A function that takes a min and max, and returns a BigInt
*
* @returns A thunk that returns a subset of the umwrapped fuzzers
*/
export function choose<T>(elems: Wrapped<Set<T>>, density: DensityBigInt): Thunk<T[]> {
return () => {
const concreteElems = Array.from(unwrap(elems));
const subsetCount = BigInt(2) ^ BigInt(concreteElems.length);
const index = unwrap(density(BigInt(0), subsetCount));

// bits correspond to a include-or-don't for each element
const bits = index
.toString(2)
.padStart(concreteElems.length, "0")
.split("");

const output: T[] = [];

for (let idx = 0; idx < bits.length; idx++) {
if (bits[idx] === "1") {
output.push(concreteElems[idx]);
}
}

return output;
}
}

0 comments on commit 9b534c1

Please sign in to comment.