import type { Stream } from "../Random/Types"; /** * How randomly generated values cluster within a `{ min, max }` range. Without * one, generation is uniform — every value in the range is equally likely. * * Each variant is a tagged object so distributions stay introspectable and * serializable. The `custom` variant is the escape hatch: a shaping function * `(u) => p` mapping a uniform draw in `[0, 1)` to a position in `[0, 1)` * within the range. */ export type Distribution = Distribution.Uniform | Distribution.Normal | Distribution.Skew | Distribution.Triangular | Distribution.Logarithmic | Distribution.Multi | Distribution.Custom; export type Range = { min: number; max: number; }; export declare namespace Distribution { type Uniform = { kind: "uniform"; }; const uniform: () => Uniform; type Normal = { kind: "normal"; mean?: number | undefined; spread?: number | undefined; }; /** * Bell curve truncated to the range. `mean` defaults to the range's center; * `spread` (standard deviation) defaults to a sixth of the span, placing the * bounds at roughly ±3σ before truncation. */ const normal: (params?: { mean?: number; spread?: number; }) => Normal; type Skew = { kind: "skew"; exponent: number; }; /** * Power curve. `exponent > 1` biases toward `min`, `exponent < 1` biases * toward `max`, and `exponent === 1` is uniform. */ const skew: (exponent: number) => Distribution; type Triangular = { kind: "triangular"; mode?: number | undefined; }; /** * Linear ramps peaking at `mode` (defaults to the range's center). */ const triangular: (params?: { mode?: number; }) => Distribution; type Logarithmic = { kind: "logarithmic"; }; /** * Log-uniform (reciprocal): density proportional to `1/x`, so values spread * evenly across orders of magnitude and cluster toward `min`. Requires a * strictly positive range — the logarithm is undefined at or below zero. */ const logarithmic: () => Distribution; type Multi = { kind: "multi"; components: ReadonlyArray<{ weight: number; distribution: Distribution; }>; }; /** * A weighted blend of component distributions, each drawn over the same * range. Localized components with distinct centers (e.g. two `normal`s at * different means) produce the separate peaks of a multimodal distribution. * Weights are relative — they need not sum to 1. */ const multi: (components: ReadonlyArray<{ weight: number; distribution: Distribution; }>) => Distribution; type Custom = { kind: "custom"; shape: (u: number) => number; }; /** * Escape hatch: `shape` maps a uniform draw in `[0, 1)` to a position in `[0, * 1)` within the range (an inverse CDF). The output is clamped to `[0, 1]` so * the result always lands within the bounds. */ const custom: (shape: (u: number) => number) => Distribution; } /** * Integer draw over the inclusive `[min, max]` of `range`. Samples `[min, max + * 1)` and floors, so each integer owns a full-width bucket — avoiding the * half-width endpoint bias that rounding a `[min, max]` draw would produce. An * omitted `distribution` is uniform. `range` is already the effective inclusive * interval; exclusive ends are the caller's to resolve (`effectiveDiscrete`). * * The `Math.min(max, …)` clamp is load-bearing, not defensive. `max + 1` is * exact even at `Number.MAX_SAFE_INTEGER` (it is 2^53), but near there doubles * are spaced 1 apart, so `sampler`'s `(1 - u) * min + u * (max + 1)` can round * up to exactly `max + 1` — e.g. `[MAX_SAFE_INTEGER - 10, MAX_SAFE_INTEGER]` — * which would floor to one past `max`. */ export declare function discreteSampler(distribution: Distribution | undefined, range: Range, stream: Stream): () => number; /** * Build a sampler that draws values within `range` following `distribution`. * Each call consumes one fresh uniform draw and, by construction, returns a * value within `[min, max]` — distributions with mass outside the range (e.g. a * normal's tails) are truncated via their inverse CDF rather than rejected or * clamped. */ export declare function sampler(distribution: Distribution, range: Range, stream: Stream): () => number; /** * Standard normal CDF via the Abramowitz & Stegun 7.1.26 approximation of the * error function (|error| < 1.5e-7). */ export declare function normalCdf(x: number): number; /** * Inverse of the standard normal CDF (quantile function) via Peter Acklam's * rational approximation (relative error < 1.15e-9). */ export declare function normalInv(p: number): number; /** * A uniform pick from `list`. One `stream.next()` per call; `list` is not * mutated. An empty list throws {@link FabricatorError.EmptyItemsError} — there * is no member to return. */ export declare function sample<$T>(list: ReadonlyArray<$T>, stream: Stream): $T; /** * Fisher–Yates (Durstenfeld) shuffle: a new array holding `items` in a * uniformly random order — every permutation is equally likely. Does not mutate * `items`, matching `sample`/`weighted`'s read-only convention. */ export declare function shuffle<$T>(items: ReadonlyArray<$T>, stream: Stream): $T[]; /** * `weighted()`'s own inclusion rule: whether this entry stays in the draw * table. `0` is valid and disables the outcome; negative/`NaN` are rejected * earlier by {@link isValidWeight}. Exposed so `Enumeration/Plan.ts` and the * construction guards share one definition of "will this be drawn" rather than * each writing `weight > 0`. */ export declare function isDrawable(weight: number): boolean; /** * Whether a weight is _expressible_ at all, as opposed to whether it is * drawable. `0` is valid and disables the outcome; a negative weight or `NaN` * is a mistake. `Infinity` is rejected because it cannot be summed into a * usable draw table — the sum becomes `Infinity`, so `weighted()`'s alias * scaling (`weight * n / sum`) yields `NaN` for the infinite entry and `0` for * every other. */ export declare function isValidWeight(weight: number): boolean; /** * Outcomes that still have a positive weight after applying the baseline of `1` * for any unspecified (missing or explicitly `undefined`) key. The one home of * that `?? 1` default, so `assertDrawableKeyedWeights` and * `Enumeration/Plan.ts` cannot drift. */ export declare function drawableOutcomes<$Outcome extends string>(outcomes: ReadonlyArray<$Outcome>, weights: Readonly> | undefined): ReadonlyArray<$Outcome>; /** * Two-stage guard for a `[weight, item]` list (`enum`/`choice`'s `.weighted()` * registries). Stage 1 rejects any entry that is not {@link isValidWeight}; * stage 2 rejects a list with no {@link isDrawable} entry left. `label` names * the call site (`"T.enum.weighted"`/`"T.choice.weighted"`), `noun` the kind of * entry (`"member"`/`"option"`). */ export declare function assertDrawableWeights(label: string, noun: string, items: ReadonlyArray): void; /** * The same two-stage guard as {@link assertDrawableWeights}, for the kinds * whose `.weighted(...)` weighs a _fixed, named_ outcome set (`boolean`'s * `true`/`false`; `nullable`/`nullish`/`undefinable`/ * `object.omittable`/`object.optional`'s presence outcomes) rather than a * caller-supplied list. Stage 2 must see the kind's **full** outcome list — an * omitted key still defaults to `1` — hence `outcomes`. * * An explicitly-`undefined` value means "unspecified" — `Weights`' keys are all * optional and fall back to a baseline of `1` — so it is skipped in stage 1 and * defaulted in stage 2. */ export declare function assertDrawableKeyedWeights<$Outcome extends string>(label: string, outcomes: ReadonlyArray<$Outcome>, weights: Readonly>): void; /** * A pick over `weights`, in O(1) per call regardless of table size. The table * is built once, here, during `new Fabricator(...)`; the returned closure is * what `.fabricate()` calls. * * Two paths, chosen at construction: * * - **Equal weights** (every `.uniform(...)` registry, every default-weighted * presence wrapper) need no table at all: the pick is `discreteSampler`'s * uniform index draw, one `stream.next()` per call. * - **Unequal weights** use Vose's alias method (see {@link toAliasTable}): draw * a column with that same index draw, then a second `stream.next()` as a coin * between the column's own item and its alias. The coin is a separate draw * rather than the fractional remainder of the first, because that remainder * keeps only `53 - log2(n)` bits, which coarsens the probabilities as the * table grows. A column whose probability is `1` returns without drawing the * coin; the draw count stays a pure function of the stream, so this is still * replayable. */ export declare function weighted(weights: ReadonlyArray, stream: Stream, label: string): () => $Item;