UNPKG

covertable

Version:

Efficient TypeScript library for pairwise testing, generating minimal covering arrays with constraint support.

150 lines (149 loc) 6.11 kB
import { FactorsType, ScalarType, PairByKeyType, CandidateType, RowType, OptionsType, PairType, SuggestRowType } from "./types"; import { UncoveredPair } from "./exceptions"; export 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; } export 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>>; } export 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; 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>; }