Skip to content

Commit

Permalink
adds bigint methods, Array.choose
Browse files Browse the repository at this point in the history
  • Loading branch information
rgrannell1 committed Aug 6, 2023
1 parent 4beefba commit bf8fd70
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 1 deletion.
48 changes: 48 additions & 0 deletions src/array/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,51 @@ Deno.test({
}
},
});

Deno.test({
name: "Array.choose | returns a subset of the provided collection",
fn() {
const integers = Peach.Array.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.Array.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: "Array.choose | preserves elements",
fn() {
const integers = Peach.Array.from(
0,
Peach.Number.uniform(0, 100));

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

const result = Peach.Array.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}`);
}
}
}
}
})
26 changes: 25 additions & 1 deletion src/array/array.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 @@ -29,3 +29,27 @@ export function concat<T>(...elems: Wrapped<T>[]): Thunk<T[]> {
return elems.map((elem) => unwrap(elem));
};
}

export function choose<T>(elems: Wrapped<T[]>, density: DensityBigInt): Thunk<T[]> {
return () => {
const concreteElems = 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;
}
}
27 changes: 27 additions & 0 deletions src/bigint/bigint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { assertEquals } from "https://deno.land/std@0.160.0/testing/asserts.ts";

import * as Peach from "../mod.ts";

Deno.test({
name: "Peach.BigInt.Uniform | Choosing 0...0 returns 0",
fn() {
assertEquals(Peach.BigInt.uniform(0n, 0n)(), 0n, "uniform 0...0 must be zero");
},
});

Deno.test({
name: "Peach.BigInt.Uniform | Always in range",
fn() {
const upper = Peach.BigInt.uniform(0n, 10n);

for (let idx = 0; idx < 1_000; idx++) {
const sizeTgt = upper();
const random = Peach.BigInt.uniform(0n, sizeTgt);
const val = random();

if (val < 0 || val > sizeTgt) {
throw new Error(`out of range 0...${sizeTgt}: ${val}`);
}
}
},
});
31 changes: 31 additions & 0 deletions src/bigint/bigint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Thunk, Wrapped } from "../types.ts";
import { unwrap } from "../types.ts";

/**
* Return a random integer in the chosen range. The distribution is
* uniform (pseudo RNG willing).
*
* @param from A wrapped integer that represents the lower bound of the range
* @param to A wrapped integer that represents the upper bound of the range
*
* @returns A thunk that returns a random integer in the chosen range
*/
export function uniform(
from: Wrapped<bigint>,
to: Wrapped<bigint>,
): Thunk<bigint> {
return () => {
const lower = unwrap(from);
const upper = unwrap(to);

const diff = upper - lower;

if (diff > Number.MAX_SAFE_INTEGER) {
throw new Error(
`Range too large: ${lower}...${upper} (${diff} > ${Number.MAX_SAFE_INTEGER})`,
)
}

return BigInt(Math.floor(Math.random() * Number(diff))) + lower;
};
}
1 change: 1 addition & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * as Set from "./set/set.ts";
export * as String from "./string/string.ts";
export * as Object from "./object/object.ts";
export * as Boolean from "./boolean/boolean.ts";
export * as BigInt from "./bigint/bigint.ts";
//export * as StateMachine from "./state_machine/state_machine.ts";

export type { Density, Thunk, Wrapped } from "./types.ts";
Expand Down
14 changes: 14 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@ export type Density = (
to: Wrapped<number>,
) => Wrapped<number>;

/*
* Density functions take a lower and upper bound as wrapped values, and return
* a wrapped value that is a number within that range
*
* @param from A wrapped value that represents the lower bound
* @param to A wrapped value that represents the upper bound
*
* @returns A wrapped value that is a number within the range [from, to]
*/
export type DensityBigInt = (
from: Wrapped<bigint>,
to: Wrapped<bigint>,
) => Wrapped<bigint>;

export type StateMachineResult<T> = {
state: string;
value: T;
Expand Down

0 comments on commit bf8fd70

Please sign in to comment.