import { Native } from '@stemcmicro/native';
export { NATIVE_MAX, NATIVE_MIN, Native, code_from_native_sym, is_native_sym, native_sym } from '@stemcmicro/native';
import { Sym, Cell, Tensor } from '@stemcmicro/atoms';
import { ExprHandler, LambdaExpr, ExprContext } from '@stemcmicro/context';
import { Shareable, U, Cons, Atom } from '@stemcmicro/tree';

interface ProgramStack extends Shareable {
    get length(): number;
    set length(length: number);
    concat(exprs: U[]): void;
    get isatom(): boolean;
    get iscons(): boolean;
    get istrue(): boolean;
    dupl(): void;
    peek(): U;
    pop(): U;
    push(expr: U): void;
    head(): void;
    rest(): void;
    rotateL(n: number): void;
    rotateR(n: number): void;
    swap(): void;
    getAt(i: number): U;
    setAt(i: number, expr: U): void;
    splice(start: number, deleteCount?: number): U[];
}

interface ProgramEnv extends Shareable {
    clearBindings(): void;
    getBinding(opr: Sym, target: Cons): U;
    getUserFunction(name: Sym): U;
    hasBinding(opr: Sym, target: Cons): boolean;
    hasUserFunction(name: Sym): boolean;
    setBinding(opr: Sym, binding: U): void;
    setUserFunction(name: Sym, userfunc: U): void;
    defineUserSymbol(name: Sym): void;
    handlerFor<T extends U>(expr: T): ExprHandler<T>;
    /**
     * If a stack is provided, the computed value is pushed onto the stack and nil is returned.
     */
    valueOf(expr: U, stack?: Pick<ProgramStack, "push">): U;
    hasState(key: string): boolean;
    getState(key: string): Shareable;
    setState(key: string, value: Shareable): void;
}

/**
 * Corresponds to the 'name' property on an Atom.
 */
type FEATURE = "Blade" | "Boo" | "Cell" | "Flt" | "Imu" | "Map" | "Rat" | "Sym" | "Tensor" | "Uom";

interface ParseConfig {
    useCaretForExponentiation: boolean;
    useParenForTensors: boolean;
    explicitAssocAdd: boolean;
    explicitAssocExt: boolean;
    explicitAssocMul: boolean;
}
interface RenderConfig {
    format: "Ascii" | "Human" | "Infix" | "LaTeX" | "SExpr" | "SVG";
    useCaretForExponentiation: boolean;
    useParenForTensors: boolean;
}
declare enum Concept {
    Last = 1,
    TTY = 2
}
interface AtomListener {
    reset(from: U, to: U, source: Cell): void;
}
interface ExprEngineListener {
    output(output: string): void;
}
interface ExprHandlerBuilder<T extends U> {
    create(): ExprHandler<T>;
}
interface ExprEngine extends Pick<ProgramEnv, "clearBindings"> {
    clearBindings(): void;
    defineAtomHandler<T extends Atom>(builder: ExprHandlerBuilder<T>, type: string, guard: (expr: Atom) => boolean): void;
    defineFunction(name: Sym, lambda: LambdaExpr): void;
    simplify(expr: U): U;
    valueOf(expr: U): U;
    getBinding(opr: Sym, target: Cons): U;
    hasBinding(opr: Sym, target: Cons): boolean;
    setBinding(opr: Sym, binding: U): void;
    hasUserFunction(name: Sym): boolean;
    getUserFunction(name: Sym): U;
    setUserFunction(name: Sym, userfunc: U): void;
    symbol(concept: Concept): Sym;
    renderAsString(expr: U, config?: Partial<RenderConfig>): string;
    addAtomListener(listener: AtomListener): void;
    removeAtomListener(listener: AtomListener): void;
    addListener(listener: ExprEngineListener): void;
    removeListener(listener: ExprEngineListener): void;
    release(): void;
}
/**
 * Determines the action upon attempts to access an undeclared variable.
 */
declare enum UndeclaredVars {
    Err = 1,
    Nil = 2
}
interface EngineConfig {
    allowUndeclaredVars: UndeclaredVars;
    prolog: string[];
    useCaretForExponentiation: boolean;
    useDerivativeShorthandLowerD: boolean;
    useIntegersForPredicates: boolean;
}
declare function create_engine(options?: Partial<EngineConfig>): ExprEngine;

declare class Stack<T> {
    #private;
    tos: number;
    constructor(elements?: T[]);
    get length(): number;
    set length(length: number);
    get top(): T;
    getAt(i: number): T;
    setAt(i: number, element: T): void;
    peek(index: number): T;
    push(element: T): void;
    pushItems(items: T[]): void;
    pop(): T;
    popItems(n: number): T[];
    /**
     * [a,b,c,d,e] => [a,b,d,e,c] (n=3)
     */
    rotateL(n: number): void;
    /**
     * [a,b,c,d,e] => [a,b,e,c,d] (n=3)
     */
    rotateR(n: number): void;
    copy(): Stack<T>;
    some(predicate: (value: T, index: number, array: T[]) => boolean): boolean;
    splice(start: number, deleteCount?: number): T[];
    /**
     * Changes the order of the top two elements on the stack.
     */
    swap(): void;
}

declare class Thing {
    readonly proto: unknown;
    getter: unknown;
    setter: unknown;
    properties: unknown;
    constructor(proto: unknown);
}
interface Scope {
    thing: Thing;
    evaluate(opr: Native, ...args: U[]): U;
    hasBinding(sym: Sym, target: Cons): boolean;
    getBinding(sym: Sym, target: Cons): U;
    setBinding(sym: Sym, binding: U): void;
    hasUserFunction(sym: Sym): boolean;
    getUserFunction(sym: Sym): U;
    setUserFunction(sym: Sym, usrfunc: U): void;
    valueOf(expr: U): U;
}
declare class State {
    readonly input: U;
    readonly $: Scope;
    /**
     * For use by evaluators. Let's the evaluator know it is being called for the first time.
     * The evaluator is responsible for updating the value to false if it chooses to use it.
     */
    firstTime: boolean;
    /**
     * MUST be initialized to false.
     */
    done: boolean;
    doneArg: boolean[];
    /**
     * For use by evaluators to keep track of evaluated arguments.
     */
    argValues: U[];
    /**
     * Contains the value from the previous invocation of the evaluator.
     */
    value: U;
    /**
     * The inputs from the invocation of the module.
     */
    inputs: U[];
    /**
     * The values from the invocation of the module.
     */
    values: U[];
    doneCallee: number;
    doneArgs: boolean;
    funcThis: unknown;
    func: unknown;
    arguments: unknown;
    constructor(input: U, $: Scope);
}
interface StepperHandler {
    atom(after: U, before: U): void;
}
interface StepperConfig {
    allowUndeclaredVars: boolean;
}
declare class Stepper {
    #private;
    POLYFILL_TIMEOUT: number;
    /**
     * @param module
     * @param options
     * @param initFunc
     */
    constructor(module: Cons, options?: Partial<StepperConfig>, initFunc?: (runner: Stepper, globalObject: Thing) => void);
    createScope(node: unknown, parentScope: Scope): Scope;
    createObjectProto(proto: unknown | null): Thing;
    defineFunction(name: Sym, lambda: LambdaExpr): void;
    initGlobal(globalObject: Thing): void;
    run(handler?: StepperHandler): boolean;
    /**
     * Execute one step of the interpreter.
     * @returns true if there are more instructions to execute.
     */
    next(handler?: StepperHandler): boolean;
    get stack(): Stack<State>;
    addListener(listener: ExprEngineListener): void;
    removeListener(listener: ExprEngineListener): void;
}

/**
 * @param p
 * @param x
 * @returns
 */
declare function roots(p: U, x: U, $: ExprContext): Tensor;

export { type AtomListener, Concept, type EngineConfig, type ExprEngine, type ExprEngineListener, type ExprHandlerBuilder, type FEATURE, type ParseConfig, type RenderConfig, type Scope, Stack, State, Stepper, type StepperConfig, type StepperHandler, Thing, UndeclaredVars, create_engine, roots };
