/**
* Statistical-distribution core — pure and engine-agnostic.
*
* A `number` generator can draw from a named distribution instead of a uniform
* range (``), so a
* column looks like real data (heights, salaries, inter-event times, wealth).
*
* Design: `docs/superpowers/specs/2026-07-15-statistical-distributions-design.md`.
*
* Two invariants make this work with the streaming/disk engines:
* - **Fixed draws.** Each distribution consumes a FIXED number of uniform
* draws (`spec.draws`), so a row's value is seekable — computable from its
* index without generating the others. Only inverse-CDF / Box–Muller here;
* no rejection sampling (which would consume a variable number of draws).
* - **No dependency.** The math is hand-rolled and portable. `sampleDistribution`
* is covered by golden vectors — the exact-number contract every language
* port must reproduce from the same uniforms.
*
* `sampleDistribution` takes the uniforms as input; the ENGINE supplies them
* (the render PRNG in memory, a seekable draw per row when streaming). Uniforms
* MUST lie in the open interval (0, 1) — never exactly 0 or 1.
*/
interface Base {
/** How many uniform draws in (0,1) `sampleDistribution` needs for this spec. */
readonly draws: number;
/** Output precision — digits after the decimal point (0 ⇒ integer). */
readonly decimals: number;
/** Optional inclusive clip of the sampled value. */
readonly min?: number;
readonly max?: number;
}
export type DistributionSpec = Base & ({
readonly name: 'normal';
readonly mean: number;
readonly sd: number;
} | {
readonly name: 'lognormal';
readonly meanlog: number;
readonly sdlog: number;
} | {
readonly name: 'exponential';
readonly rate: number;
} | {
readonly name: 'pareto';
readonly alpha: number;
readonly xmin: number;
} | {
readonly name: 'weibull';
readonly shape: number;
readonly scale: number;
} | {
readonly name: 'poisson';
readonly lambda: number;
readonly cdf: readonly number[];
} | {
readonly name: 'zipf';
readonly n: number;
readonly s: number;
readonly cum: readonly number[];
} | {
readonly name: 'gamma';
readonly shape: number;
readonly scale: number;
} | {
readonly name: 'beta';
readonly alpha: number;
readonly beta: number;
});
export type DistributionName = DistributionSpec['name'];
/**
* Sample the distribution from `uniforms` (length ≥ `spec.draws`, each in
* (0,1)). Returns the RAW value — clipping and rounding happen in
* `formatSample`.
*/
export declare function sampleDistribution(spec: DistributionSpec, uniforms: readonly number[]): number;
/** Clip the raw sample to `[min, max]` (if set), then round to `decimals`. */
export declare function formatSample(x: number, spec: DistributionSpec): string;
/**
* Parse and validate the distribution attributes of a `number` gen. Throws a
* plain `Error` (callers add gen/source context). Only reads distribution
* attributes — the incompatibility of `distribution` with range/percent/uniq is
* enforced at the validation layer, which sees the whole gen.
*/
export declare function parseDistribution(attrs: Record): DistributionSpec;
/**
* Every attribute a distribution reads as a NUMBER — the ones that may instead
* be an expression over the row's other columns.
*
* Kept as one list so "which parameters can follow a column" has a single
* answer, rather than each caller remembering a different subset.
*/
export declare const DISTRIBUTION_PARAMS: readonly string[];
/**
* The parameters this generator wrote as an EXPRESSION rather than a number.
*
* A bare number stays the degenerate case, so a config that never wanted this
* pays nothing: the spec is parsed once, exactly as before, and only a config
* that actually names a column takes the per-row path.
*
* This does NOT change how many uniforms a row consumes — `draws` depends only
* on WHICH distribution, never on its parameters — so the property every engine
* is built on (a row computable without its predecessors) is untouched. That is
* the difference from `repeat=`, where a per-row count would have changed the
* draw budget and was therefore refused; see `sequence/repeat.ts`.
*/
/**
* How many uniforms a row of this distribution consumes, known from the NAME
* alone — no parameters needed.
*
* Wanted by a row that cannot be drawn at all (a parameter read an empty cell),
* which must still spend what a drawn row would spend. Otherwise blanking one
* cell would slide every value after it, and a `parent=` filter would quietly
* rewrite the rest of the column.
*/
export declare function distributionDraws(attrs: Record): number;
export declare function expressionParams(attrs: Record): readonly string[];
export {};
//# sourceMappingURL=distribution.d.ts.map