/**
 * Arithmetic expressions compute a value from two operands.
 * Both `left` and `right` can be field references or nested expressions.
 * When `right` is omitted, `value` provides a literal operand.
 */
declare type ArithmeticExpression = {
    operator: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow';
    left: Operand;
    right: Operand;
} | {
    operator: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow';
    left: Operand;
    value: any;
};

declare type ArrayObjectType = {
    [s: string]: any[];
};

declare type ArrayTupleType = any[][];

declare type CandidateType = [ScalarType, number][];

/**
 * Custom comparison functions. Each key matches a comparison operator name.
 * When provided, the corresponding function is called instead of the default
 * JS comparison. The function receives two resolved (non-undefined) values
 * and must return a boolean.
 *
 * The `in` comparer receives the field value and the values array.
 *
 * `undefined` values (= field not yet set in the row) are **never** passed
 * to a comparer — the engine returns `null` before reaching the
 * comparer in that case.
 */
declare interface Comparer {
    eq?: (a: any, b: any) => boolean;
    ne?: (a: any, b: any) => boolean;
    gt?: (a: any, b: any) => boolean;
    lt?: (a: any, b: any) => boolean;
    gte?: (a: any, b: any) => boolean;
    lte?: (a: any, b: any) => boolean;
    in?: (value: any, values: Set<any>) => boolean;
}

/**
 * A comparison condition. `left` is the first operand (field reference or
 * expression). The second operand is either `right` (another field/expression)
 * or `value` (a literal). For `in`, use `values` (an array of literals).
 */
declare type ComparisonExpression = {
    operator: 'eq';
    left: Operand;
    value: any;
} | {
    operator: 'eq';
    left: Operand;
    right: Operand;
} | {
    operator: 'ne';
    left: Operand;
    value: any;
} | {
    operator: 'ne';
    left: Operand;
    right: Operand;
} | {
    operator: 'gt';
    left: Operand;
    value: any;
} | {
    operator: 'gt';
    left: Operand;
    right: Operand;
} | {
    operator: 'lt';
    left: Operand;
    value: any;
} | {
    operator: 'lt';
    left: Operand;
    right: Operand;
} | {
    operator: 'gte';
    left: Operand;
    value: any;
} | {
    operator: 'gte';
    left: Operand;
    right: Operand;
} | {
    operator: 'lte';
    left: Operand;
    value: any;
} | {
    operator: 'lte';
    left: Operand;
    right: Operand;
} | {
    operator: 'in';
    left: Operand;
    values: any[];
};

declare class Controller<T extends FactorsType> {
    factors: FactorsType;
    options: OptionsType<T>;
    factorLength: number;
    factorIsArray: Boolean;
    private serials;
    private parents;
    private indices;
    incomplete: PairByKeyType;
    private rejected;
    row: Row;
    private _totalPairs;
    private _prunedPairs;
    private _rowCount;
    private _uncoveredPairs;
    private _completions;
    get stats(): ControllerStats_2;
    private constraints;
    private constraintsByKey;
    private comparer;
    /**
     * Indices into `constraints` that have already evaluated to `true`
     * against the **current** row. Cleared whenever the row is reset or
     * yielded. Safe because the row only grows and each condition is
     * deterministic over its declared keys.
     */
    private passedIndexes;
    constructor(factors: FactorsType, options?: OptionsType<T>);
    /** Normalize `in` conditions: convert `values` arrays to Sets for O(1) lookup. */
    private static normalizeCondition;
    private resolveConstraints;
    private serialize;
    private setIncomplete;
    /**
     * Try to add a candidate pair to the current row. Evaluates constraints
     * against a snapshot (row + pair) without mutating `this.row`. If all
     * constraints pass (or are unknown), the pair is committed to `this.row`
     * and `true` is returned. If any constraint definitively fails, `this.row`
     * is unchanged and `false` is returned.
     */
    private setPair;
    private consumePairs;
    getCandidate(pair: PairType): CandidateType;
    isCompatible(pair: PairType): number | null;
    /**
     * Check whether adding `candidate` to `row` would violate any constraint.
     * Returns `true` (OK), `false` (definitively violated), or `null`
     * (some dependency is still missing — defer).
     *
     * Constraints already in `passedIndexes` are skipped.
     */
    private storableCheck;
    /**
     * Returns the number of new keys this candidate would add to `row`, or
     * `null` if the candidate is incompatible or would definitively violate a
     * constraint. `null` results from three-valued evaluation are treated
     * as "OK for now" — they will be rechecked once more keys are known.
     */
    storable(candidate: CandidateType, row?: Row): number | null;
    /**
     * Evaluate constraints against `row` and mark those that pass as done.
     * Returns `false` if any constraint definitively fails (= the row is
     * unsalvageable and should be abandoned), `true` otherwise.
     */
    private markPassedConstraints;
    /**
     * Forward checking: given a snapshot row, propagate constraints to prune
     * domains of unfilled factors. If any factor's domain becomes empty, the
     * current assignment is unsolvable — return false.
     *
     * This is read-only: it does NOT modify this.row. It builds a temporary
     * domain map and iteratively narrows it by evaluating constraints with
     * each candidate value.
     */
    private forwardCheck;
    isFilled(row: Row): boolean;
    private toMap;
    private toObject;
    private reset;
    private restore;
    /**
     * Fill the remaining unfilled factors of `this.row` and check constraints.
     * Uses depth-first backtracking: each unfilled factor tries its values in
     * order (weight-sorted on the first pass). When a value causes a
     * constraint to evaluate to `false` (via three-valued `storable`), the
     * next candidate is tried; if all candidates are exhausted, the previous
     * factor is backtracked.
     *
     * Returns `true` when a valid completion is found (the row is updated in
     * place), or `false` when no valid completion exists.
     */
    /**
     * Result of close(): `true` = valid completion found; `false` = failed;
     * or an object with the conflict keys from the first failing constraint.
     */
    private close;
    get strength(): number;
    get allStrengths(): number[];
    private valueToSerial;
    private applyPreset;
    private orderByWeight;
    get isComplete(): boolean;
    /**
     * Find the keys of the first failing constraint on the current row.
     * Returns the set of factor keys that participate in the conflict,
     * or null if the row passes all constraints.
     */
    private findConflictKeys;
    /**
     * Analyse remaining incomplete pairs and identify which constraint(s)
     * make each pair infeasible. Used to build a diagnostic when throwing
     * NeverMatch.
     */
    private diagnoseUncoveredPairs;
    /**
     * Record which factor values were filled by close() (completion) rather
     * than by greedy. `greedyKeys` are the keys that were already in the row
     * before close() ran.
     */
    private recordCompletions;
    get progress(): number;
    make<T extends FactorsType>(): SuggestRowType<T>[];
    makeAsync<T extends FactorsType>(): Generator<SuggestRowType<T>, void, unknown>;
}

declare interface ControllerStats {
    /** Total pairs before any pruning. */
    totalPairs: number;
    /** Number of pairs pruned by constraints (infeasible). */
    prunedPairs: number;
    /** Number of pairs consumed so far. */
    coveredPairs: number;
    /** Coverage ratio: coveredPairs / (totalPairs - prunedPairs). */
    progress: number;
    /** Number of generated rows. */
    rowCount: number;
    /** Pairs that could not be covered. Populated after make/makeAsync completes. */
    uncoveredPairs: UncoveredPair[];
    /** Counts of values filled by close() (completion), keyed by factor then value. */
    completions: Record<string, Record<string, number>>;
}

declare interface ControllerStats_2 {
    /** Total pairs before any pruning. */
    totalPairs: number;
    /** Number of pairs pruned by constraints (infeasible). */
    prunedPairs: number;
    /** Number of pairs consumed so far. */
    coveredPairs: number;
    /** Coverage ratio: coveredPairs / (totalPairs - prunedPairs). */
    progress: number;
    /** Number of generated rows. */
    rowCount: number;
    /** Pairs that could not be covered. Populated after make/makeAsync completes. */
    uncoveredPairs: UncoveredPair_2[];
    /** Counts of values filled by close() (completion), keyed by factor then value. */
    completions: Record<string, Record<string, number>>;
}

declare type Expression = ComparisonExpression | LogicalExpression | FnExpression;

declare type FactorsType = ArrayTupleType | ArrayObjectType;

declare type FilterRowType = {
    [key: string]: any;
    [index: number]: any;
};

declare type FilterType = (row: FilterRowType) => boolean;

/**
 * Escape hatch for constraints that cannot be expressed declaratively.
 * The engine cannot perform three-valued reasoning on these — when a
 * dependency key is missing the condition is treated as `null`.
 * Provide `requires` so the engine knows when it is safe to call `evaluate`.
 */
declare type FnExpression = {
    operator: 'fn';
    requires: string[];
    evaluate: (row: {
        [key: string]: any;
    }) => boolean;
};

declare type IndicesType = Map<number, number>;

export declare type IssueSeverity = "error" | "warning";

export declare type IssueSource = "factor" | "subModel" | "constraint";

declare type LogicalExpression = {
    operator: 'not';
    condition: Expression;
} | {
    operator: 'and';
    conditions: Expression[];
} | {
    operator: 'or';
    conditions: Expression[];
};

/**
 * An operand is either a field reference (string, supports dot notation
 * like `"payment.method"`) or an arithmetic expression.
 */
declare type Operand = string | ArithmeticExpression;

declare interface OptionsType<T extends FactorsType> {
    strength?: number;
    subModels?: SubModelType[];
    weights?: WeightsType;
    presets?: PresetRowType[];
    sorter?: SorterType;
    criterion?: (ctrl: Controller<T>) => IterableIterator<PairType>;
    salt?: ScalarType;
    tolerance?: number;
    /**
     * Declarative constraints. Each entry is a `Condition` tree that the
     * generator evaluates under Kleene three-valued logic: when a referenced
     * field is not yet present in the row the result is `null` (deferred)
     * rather than `false`, so the generator can prune early without
     * discarding viable combinations.
     *
     * The top-level array is an implicit AND: every condition must be
     * satisfied for a row to be accepted.
     */
    constraints?: Expression[];
    /**
     * Custom comparison functions. See `Comparer` for details.
     */
    comparer?: Comparer;
}

declare type PairByKeyType = Map<ScalarType, PairType>;

declare type PairType = number[];

/**
 * Parse a PICT-format model string into its constituent parts: parameters,
 * sub-models, and constraints. Issues are collected rather than thrown so the
 * caller can decide how to handle them.
 */
export declare function parse(input: string, options?: ParseOptions): ParseResult;

export declare interface ParseOptions {
    /** Match constraint comparisons and alias lookups case-insensitively. Default: true. */
    caseInsensitive?: boolean;
}

export declare interface ParseResult {
    factors: PictFactorsType;
    aliases: Map<string, string>;
    negatives: Map<string, Set<string | number>>;
    weights: {
        [factorKey: string]: {
            [index: number]: number;
        };
    };
    subModels: SubModelType[];
    lexer: PictConstraintsLexer | null;
    issues: PictModelIssue[];
}

declare class PictConstraintsLexer {
    private input;
    private debug;
    private aliases;
    private caseInsensitive;
    private startLine;
    private tokens;
    filters: (FilterType | null)[];
    errors: (string | null)[];
    filterLines: number[];
    filterKeys: Set<string>[];
    constructor(input: string, debug?: boolean, aliases?: Map<string, string>, caseInsensitive?: boolean, startLine?: number);
    private tokenize;
    private analyze;
    filter: (row: FilterRowType, ...additionalFilters: FilterType[]) => boolean;
}

export declare type PictFactorsType = {
    [key: string]: (string | number)[];
};

export declare class PictModel {
    private _parameters;
    private _subModels;
    private _negatives;
    private _weights;
    private _lexer;
    private _controller;
    issues: PictModelIssue[];
    constructor(input: string, options?: PictModelOptions);
    get parameters(): PictFactorsType;
    get subModels(): SubModelType[];
    get constraints(): Expression[];
    get negatives(): Map<string, Set<string | number>>;
    get weights(): {
        [factorKey: string]: {
            [index: number]: number;
        };
    };
    get progress(): number;
    get stats(): ControllerStats | null;
    /**
     * Evaluate all constraints and the negative-value rule against a
     * (typically complete) row. Returns true if the row passes.
     */
    filter: (row: FilterRowType) => boolean;
    /**
     * Convert this model's constraints into `Expression[]` for the controller.
     * Each lexer filter becomes a `custom` condition with its dependency keys.
     * The negative-value rule also becomes a `custom` condition.
     */
    private _modelConstraints;
    private _buildOptions;
    private _applyNegativePrefix;
    make(options?: OptionsType<PictFactorsType>): any[];
    makeAsync(options?: OptionsType<PictFactorsType>): Generator<any, void, unknown>;
}

export declare class PictModelError extends Error {
    readonly issues: PictModelIssue[];
    constructor(issues: PictModelIssue[]);
}

export declare interface PictModelIssue {
    severity: IssueSeverity;
    source: IssueSource;
    index: number;
    line: number;
    message: string;
}

export declare interface PictModelOptions {
    caseInsensitive?: boolean;
    strict?: boolean;
}

declare type PresetRowType = {
    [key: string]: any;
    [index: number]: any;
};

declare class Row extends Map<ScalarType, number> implements RowType {
    /** Pair keys that failed constraint checks for this row attempt. */
    invalidPairs: Set<ScalarType>;
    constructor(row: CandidateType);
    getPairKey(...newPair: number[]): ScalarType;
    copy(row: Row): void;
}

declare interface RowType {
}

declare type ScalarType = number | string;

declare interface SortArgsType {
    salt: ScalarType;
    indices: IndicesType;
}

declare type SorterType = (pairs: PairType[], sortArgs: SortArgsType) => PairType[];

declare interface SubModelType {
    fields: ScalarType[];
    strength: number;
}

declare type SuggestRowType<T extends FactorsType> = T extends ArrayTupleType ? T[number][number][] : T extends ArrayObjectType ? {
    [K in keyof T]: T[K][number];
} : unknown;

declare interface UncoveredPair {
    /** The pair expressed as factor-key → value entries. */
    pair: Record<string, any>;
    /** The constraint(s) that made this pair infeasible, if identifiable. */
    constraints: number[];
}

declare interface UncoveredPair_2 {
    /** The pair expressed as factor-key → value entries. */
    pair: Record<string, any>;
    /** The constraint(s) that made this pair infeasible, if identifiable. */
    constraints: number[];
}

/**
 * Convert value-keyed weights to index-keyed weights for use with `OptionsType.weights`.
 * Useful when you want to specify weights by value rather than by index position.
 *
 * @example
 *   weightsByValue(
 *     { Browser: ["Chrome", "Firefox", "Safari"] },
 *     { Browser: { Chrome: 10, Safari: 5 } },
 *   )
 *   // → { Browser: { 0: 10, 2: 5 } }
 */
export declare function weightsByValue<T extends PictFactorsType>(factors: T, valueWeights: {
    [factorKey: string]: {
        [value: string]: number;
    };
}): {
    [factorKey: string]: {
        [index: number]: number;
    };
};

declare type WeightsType = {
    [factorKey: string]: {
        [index: number]: number;
    };
};

export { }
