import { ConstraintSystem } from 'consys';
import Domain from './Domain';
/**
 * Model domain object. Should have the same keys as the result model, which
 * each key having a specific domain to be searched for a solution.
 */
export interface ModelDomain {
    [key: string]: Domain<any> | ModelDomain;
}
/**
 * Solver configuration.
 *
 * @param maxIterations Maximum number of iterations until the algorithm stops.
 * The default number is 10000 iterations.
 *
 * @param retryIterations The number of iterations until the algorithm starts
 * again with random values. Useful to get the algorithm out of dead ends.
 *
 * @param lookAheadModels Determines how many models should be considered for
 * the next iteration. Set to the maximum domain size by default.
 *
 * @param randomnessFactor Value between 0.0 and 1.0, determines how often the
 * algorithm chooses a random model for the next iteration. A value of 0.0
 * means that it never chooses randomly, and a value of 1.0 means it always
 * chooses randomly. This is useful to avoid local extrema and plateaus.
 * The default value is 0.3.
 *
 * @param preferenceFactor Value between 0.0 and 1.0, determines how much the
 * domain preference value is weighted when choosing the model for the next
 * iteration. A value of 0.0 means that preferences are not considered, whereas
 * a value of 1.0 means that they heavily influence the decision. The default
 * value is 0.1.
 *
 */
export interface SolverConfig {
    maxIterations?: number;
    retryIterations?: number;
    lookAheadModels?: number;
    randomnessFactor?: number;
    preferenceFactor?: number;
}
/**
 * Solver class, used to find solutions for a specific set of constraints and
 * domains.
 */
export default class Solver<M, S> {
    private readonly system;
    private readonly config;
    /**
     * Create a new solver instance for a given constraint system and config.
     *
     * @param system constraint system
     * @param config configuration
     */
    constructor(system: ConstraintSystem<M, S>, config?: SolverConfig);
    /**
     * Initializes parameters based on the given config.
     *
     * @param config configuration
     * @private
     */
    private initConfig;
    /**
     * Flatten a model domain object to have keys as strings, seperated by a dot
     * if the key is nested.
     *
     * @param modelDomain initial domain object
     * @param parent parent key
     * @param res result map
     * @private
     */
    private static flattenModelDomain;
    /**
     * Creates a model domains object with domain values and current search index.
     *
     * @param modelDomain model domain
     * @private
     */
    private static getModelDomains;
    /**
     * Inserts a value into an object. Key is a dot separated string, which will
     * result in a nested value.
     *
     * @param object object where value should be inserted
     * @param key key of the value
     * @param value value to be inserted
     * @private
     */
    private static insertValue;
    /**
     * Randomizes the current search index of each domain.
     *
     * @param modelDomains model domains
     * @private
     */
    private static randomizeModel;
    /**
     * Creates a new model object from the values of the current search indices.
     *
     * @param modelDomains model domains
     * @private
     */
    private getCurrentModel;
    /**
     * Calculates a logarithmic score between 0 (bad) and 1 (perfect) for a given
     * model and state.
     *
     * @param model model instance
     * @param state state instance
     * @private
     */
    private getLogScore;
    /**
     * Returns a normalized preference score from 0 to 1
     *
     * @param preference preference of the domain value
     * @private
     */
    private getPreferenceScore;
    /**
     * Calculates the harmonic mean of the log score and preference score. This
     * penalizes low values for the log score, as well as low values for the
     * preference score. Only if both values are high, the harmonic mean will be
     * high as well.
     *
     * @param logScore log score
     * @param prefScore preference score
     * @private
     */
    private static getHarmonicMean;
    /**
     * Calculates the total score of a given model and state.
     *
     * @param instance model with preference value
     * @param state state
     * @private
     */
    private getScore;
    /**
     * Checks if a model is consistent with a given state.
     *
     * @param model model to be checked
     * @param state state to be checked
     * @private
     */
    private isModelFeasible;
    /**
     * Shuffles an array of values.
     *
     * @param array array to be shuffled
     * @private
     */
    private static shuffle;
    /**
     * Randomly chooses a key based on their counts as weights.
     *
     * @param domains key domains
     * @param keyCounts key counts
     * @private
     */
    private static chooseKey;
    /**
     * Returns an array of values to be considered as the next value for a model
     * domain.
     *
     * @param domains model domains
     * @param key key to be searched for values
     * @private
     */
    private getNextValuesForKey;
    /**
     * Checks if two models have equal keys and values.
     *
     * @param model0 first model
     * @param model1 second model
     * @private
     */
    private static modelsEqual;
    /**
     * Checks if a model is already in an array of solutions.
     *
     * @param solutions solutions
     * @param model model
     * @private
     */
    private isModelInSolutions;
    /**
     * Returns an array of models to be considered as the next best model, along
     * with their preference value.
     *
     * @param solutions current solutions
     * @param domains model domains
     * @param key key to be changed for the next models
     * @param nextValues array of possible next values for the key
     * @private
     */
    private getNextModels;
    /**
     * Returns the next best model given a current model, state and solutions.
     *
     * @param solutions current solutions
     * @param domains model domains
     * @param currentModel current model
     * @param state state
     * @private
     */
    private getNextBestModel;
    /**
     * Chooses a random element from array.
     *
     * @param values array
     * @private
     */
    private static chooseRandom;
    /**
     * Returns the model with the minimum amount of conflicts (heuristic) and
     * factors in the preference value.
     *
     * @param models models to be considered
     * @param state state
     * @private
     */
    private minConflicts;
    /**
     * Returns the maximum look ahead value based on the size of the largest
     * domain.
     *
     * @param domains model domains
     * @private
     */
    private static getMaxLookAhead;
    private static initializePreferredDomains;
    /**
     * Searches for solutions with a given configuration. Returns an array of
     * solutions as well as the number of iterations it took to find them.
     *
     * @param maxSolutions maximum number of solutions to be found before stopping
     * @param modelDomain model domain to be searched
     * @param state state
     * @param config solver configuration
     */
    solve(maxSolutions: number, modelDomain: ModelDomain, state: S, config?: SolverConfig): {
        iterations: number;
        solutions: M[];
    };
    /**
     * Searches for solutions with a given configuration. Returns an array of
     * solutions.
     *
     * @param maxSolutions maximum number of solutions to be found before stopping
     * @param modelDomain model domain to be searched
     * @param state state
     * @param config solver configuration
     */
    find(maxSolutions: number, modelDomain: ModelDomain, state: S, config?: SolverConfig): M[];
}
