import { MethodMap } from '../map/MethodMap';
import { StringMap, KeyGetter, Getter, Predicate, Tuple } from '../types';
/**
 * Functions to be used in {@link Array.prototype.reduce} as a callback.
 * @see https://pavel-surinin.github.io/declarativejs/#/?id=reducers
 */
export declare namespace Reducer {
    type OnDuplicateFunction<K> = (v1: K, v2: K, key: string) => K | never;
    function Map<T>(data?: StringMap<T>): MethodMap<T>;
    function ImmutableMap<T>(): MethodMap<T>;
    function ImmutableObject<T>(): Readonly<StringMap<T>>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback to group by provided key.
     * As second parameter in reduce function need to pass
     * Reducer.Map()
     * Reducer.ImmutableMap()
     * Or own implementation of {@link MethodMap}
     * @param {string}  key     objects key to resolve value,to group by it
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=groupby
     *
     */
    function groupBy<T, K extends keyof T>(key: K): <A extends MethodMap<T[]> | StringMap<T[]>>(agr: A, value: T, index: number, array: T[]) => A;
    /**
     * Groups an array by key resolved from callback.
     * Function to be used in {@link Array.prototype.reduce} as a callback to group by provided function.
     * As second parameter in reduce function need to pass
     * Reducer.Map()
     * Reducer.ImmutableMap()
     * Or own implementation of {@link MethodMap}
     * @param {Function} getKey              callback to resolve key,to group by it
     * @throws {Error}                       if resolved key from callback is not a string
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=groupby
     */
    function groupBy<T>(getKey: KeyGetter<T>): <A extends MethodMap<T[]> | StringMap<T[]>>(agr: A, value: T, index: number, array: T[]) => A;
    /**
     * Groups an array by key resolved from callback and transform value to put in new grouped array.
     * Function to be used in {@link Array.prototype.reduce} as a callback to group by provided key.
     * As second parameter in reduce function need to pass
     * {@link Reducer.Map()}, {@link Reducer.ImmutableMap()} or own implementation of {@link MethodMap}
     *
     * @export
     * @template T type of element in array
     * @template TR type of element in grouped array
     * @param {KeyGetter<T>} getKey function to get key, output must be a string
     *                              by this key an array will be grouped
     * @param {Getter<T, TR>} transformer function to transform array element in grouped array
     * @returns {(agr: MethodMap<TR[]>, value: T, index: number, array: T[]) => MethodMap<TR[]>}
     *          function to use in Array.reduce
     * @throws {Error} if resolved key from callback is not a string
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=groupby
     */
    function groupBy<T, TR>(getKey: KeyGetter<T>, transformer: Getter<T, TR>): <A extends MethodMap<TR[]> | StringMap<TR[]>>(agr: A, value: T, index: number, array: T[]) => A;
    /**
     * Groups an array by key and transform value to put in new grouped array.
     * Function to be used in {@link Array.prototype.reduce} as a callback to group by provided key.
     * As second parameter in reduce function need to pass
     * {@link Reducer.Map()}, {@link Reducer.ImmutableMap()} or own implementation of {@link MethodMap}
     *
     * @export
     * @template T type of element in array
     * @template TR type of element in grouped array
     * @param {string} key of an element in array object to group by it
     * @param {Getter<T, TR>} transformer function to transform array element in grouped array
     * @returns {(agr: MethodMap<TR[]>, value: T, index: number, array: T[]) => MethodMap<TR[]>}
     *          function to use in Array.reduce
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=groupby
     */
    function groupBy<T, TR, K extends keyof T>(key: K, transformer: Getter<T, TR>): <A extends MethodMap<TR[]> | StringMap<TR[]>>(agr: A, value: T, index: number, array: T[]) => A;
    function groupByObject<T, K extends keyof T>(key: K): (agr: Partial<Record<Extract<T[K], string>, T[]>>, value: T, index: number, array: T[]) => Partial<Record<Extract<T[K], string>, T[]>>;
    function groupByObject<T, K extends string>(getKey: Getter<T, K>): (agr: Partial<Record<K, T[]>>, value: T, index: number, array: T[]) => Partial<Record<K, T[]>>;
    function groupByObject<T, TR, K extends string>(getKey: Getter<T, K>, transformer: Getter<T, TR>): (agr: Partial<Record<K, TR[]>>, value: T, index: number, array: T[]) => Partial<Record<K, TR[]>>;
    function groupByObject<T, TR, K extends keyof T>(key: K, transformer: Getter<T, TR>): (agr: Partial<Record<Extract<T[K], string>, TR[]>>, value: T, index: number, array: T[]) => Partial<Record<Extract<T[K], string>, TR[]>>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback
     * to make from 2d array simple array
     * As second parameter in reduce function need to pass <code>[]</code>
     * @param {T[]} agr              to collect in
     * @param {T[]} value            to concatenate with
     * @returns {T[]}                concatenated array
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=flat
     */
    const flat: <T>(agr: T[], value: T[]) => T[];
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback to make a Map.
     * Collects items by key, from callback to {@link MethodMap<T>}.
     * If function resolves key, that already exists it will throw an Error
     * As second parameter in reduce function need to pass
     * Reducer.Map(), Reducer.ImmutableMap(), Or own implementation of {@link MethodMap}
     * @type {T}                            value type
     * @type {R}                            value type in map
     * @param {KeyGetter<T>} getKey         callback to get key from value
     * @param {MethodMap<T>} agr            object to collect in
     * @param {T} value                     value that that is passed in function for each iteration
     * @throws Error                        if resolved key from callback is not a string
     * @throws Error                        if map has duplicate keys will thrown error
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=tomap
     */
    function toMap<T>(getKey: KeyGetter<T>): (agr: MethodMap<T>, value: T, index: number, array: T[]) => MethodMap<T>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback to make a Map.
     * Collects items to {@link MethodMap<T>} by key from callback. If function resolves key,
     * that already exists it will throw an Error. Second callback is value mapper.
     * As second parameter in reduce function need to pass
     * Reducer.Map(), Reducer.ImmutableMap(), Or own implementation of {@link MethodMap}
     * @param {Function} getKey             callback to get key from value
     * @param {Function} getValue           callback to get value to put in object
     * @throws {Error}                      if map has duplicate keys will thrown error
     * @throws {Error}                      if resolved key from callback is not a string
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=tomap
     */
    function toMap<T, K>(getKey: KeyGetter<T>, valueGetter: Getter<T, K>): (agr: MethodMap<K>, value: T, index: number, array: T[]) => MethodMap<K>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Collects items to object by key from callback. If function resolves
     * key, that already exists it will throw an Error
     * As second parameter in reduce function need to pass {} or Reducer.ImmutableObject()
     * @param {Function} getKey               callback to get key from value
     * @throws {Error}                        if map has duplicate keys will thrown error
     * @throws {Error}                        if resolved key from callback is not a string      *   *
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=toobject
     */
    function toObject<T>(getKey: KeyGetter<T>): (agr: StringMap<T>, value: T, index: number, array: T[]) => StringMap<T>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback
     * Collects items to object by key from callback. If function resolves key,
     * that already exists it will throw an Error. Second callback is value mapper.
     * As second parameter in reduce function need to pass {} or Reducer.ImmutableObject()
     * @param {Function} getKey             callback to get key from value
     * @param {Function} getValue           callback to get value to put in object
     * @throws {Error}                      if map has duplicate keys will thrown error
     * @throws {Error}                      if resolved key from callback is not a string      *
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=toobject
     */
    function toObject<T, K>(getKey: KeyGetter<T>, valueGetter: Getter<T, K>): (agr: StringMap<K>, value: T, index: number, array: T[]) => StringMap<K>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback
     * Collects items to object by key from callback. If function resolves key,
     * that already exists it will throw an Error. Second callback is value mapper.
     * As second parameter in reduce function need to pass {} or Reducer.ImmutableObject()
     * @param {Function} getKey             callback to get key from value
     * @param {Function} getValue           callback to get value to put in object
     * @param {Function} merge              callback to merge values with duplicate key
     * @throws {Error}                      if resolved key from callback is not a string      *
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=toobject
     */
    function toObject<T, K>(getKey: KeyGetter<T>, valueGetter: Getter<T, K>, merge: (v1: K, v2: K) => K): (agr: StringMap<K>, value: T, index: number, array: T[]) => StringMap<K>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Finds lowest value in array. Array must contain only numbers
     * @returns {number} lowest value in array.
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=min
     */
    function min(agr: number, value: number, index: number, array: number[]): number;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Finds highest value in array. Array must contain only numbers
     * @returns {number} highest value in array.
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=max
     */
    function max(agr: number, value: number, index: number, array: number[]): number;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Finds sum of values in array. Array must contain only numbers
     * @returns {number} sum of values in array.
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=sum
     */
    const sum: (agr: number, value: number) => number;
    /**
     * Object merging strategy used in {@link Reducer#toMergedObject}
     * @see toMergedObject
     */
    const MergeStrategy: Record<'OVERRIDE' | 'UNIQUE' | 'CHECKED', IsMergable>;
    type IsMergable<T = any> = (currentValue: T, aggregatorValue: T, key: string) => boolean;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Reduces array of objects to one object, There is three merge strategies
     * @param merge {@link MergeStrategy} = default is OVERRIDE
     * @see MergeStrategy
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=tomergedobject
     */
    function toMergedObject(isMergable?: IsMergable): <T extends object, R extends object>(agr: R, value: T) => T & R;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Collects two arrays into one array of tuples, two element array([x ,y]).
     * The length of zipped array will be length of shortest array.
     *
     * @param {Array} array array to zip with
     * @returns array with elements from two arrays as tuples
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=zip
     */
    function zip<T1, T2>(array: T2[]): (agr: Array<Tuple<T1, T2>>, value: T1, index: number) => Tuple<T1, T2>[];
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Collects two arrays into one array of aggregated objects by provided function.
     * The length of zipped array will be length of shortest array.
     *
     * @param {Array} array array to zip with
     * @param {Function} withFx function that will combine two elements into one
     * @returns array with elements from two arrays
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=zip
     */
    function zip<T1, T2, R>(array: T2[], withFx: (t1: T1, t2: T2) => R): (agr: Array<R>, value: T1, index: number) => Array<R>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Collects all arrays to arrays of arrays, with elements
     * at being grouped with elements from other arrays by same index.
     * The length of zipped array will be length of shortest array.
     * Almost the same as {@link Reducer.zip}, except zipAll accepts
     * multiple array to zip with.
     *
     * @export
     * @param {...Array[]} arraysToZip
     * @returns function to use in Array.reduce
     * @see Reducer.zip
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=zipall
     */
    function zipAll(...arraysToZip: Array<any>[]): (agr: any[][], currentValue: any, currentValueIndex: number) => any[][];
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * It does the opposite as {@link Reducer.zip} or {@link Reducer.zipAll}.
     * It collects from all zipped arrays one arrays, that was before zip.
     * Takes from each nested arrays and element and for each index will
     * collect to new array.
     * The length of and array will be the shortest length of arrays to unzip
     *
     * @export
     * @returns function to use in Array.reduce
     * @see Reducer.zip
     * @see Reducer.zipAll
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=unzip
     */
    function unzip<T>(): (agr: T[][], value: T[], index: number, arrays: T[][]) => T[][];
    const Partition: <E>() => Tuple<E, E>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * It reduces array in a tuple ([[], []]) with two arrays.
     * First array contains elements, that matches predicate,
     * second array, that does not match.
     * As a second paramter in reduce (callback, initialValue), as an
     * initial value need to pass empty tuple of arrays ([[], []])
     * Or use Reducer.Partition function to create initial value for it.
     * Predicate is a function that takes current element as a parameter
     * and returns boolean.
     *
     * @export
     * @template T                          element type in array
     * @param {(T) => boolean} matches      predicate function that has a a value
     *                                      current element and returns boolean
     * @returns {(agr: [T[], T[]], value: T) => [T[], T[]]} function to pass to Array.reduce
     * @see Reducer.Partition
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=partitionby
     */
    function partitionBy<T>(matches: Predicate<T>): (agr: Tuple<T[], T[]>, value: T) => Tuple<T[], T[]>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * It reduces array in a tuple ([[], []]) with two arrays.
     * First array contains elements, that matches predicate,
     * second array, that does not match.
     * As a second paramter in reduce (callback, initialValue), as an
     * initial value need to pass empty tuple of arrays ([[], []])
     * Or use Reducer.Partition function to create initial value for it.
     * Predicate is an objects key, that will be coerced to boolean with
     * Boolean constructor (Boolean()).
     *
     * @export
     * @template T                          element type in array
     * @param {key of T: string} matches    element key, which value is used to
     *                                      decide in which partition array to add
     *                                      element
     * @returns {(agr: [T[], T[]], value: T) => [T[], T[]]} function to pass to Array.reduce
     * @see Reducer.Partition
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=partitionby
     */
    function partitionBy<T, K extends keyof T>(matches: K): (agr: Tuple<T[], T[]>, value: T) => Tuple<T[], T[]>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * It reduces array in a tuple ([[], []]) with two arrays.
     * First array contains elements, that matches predicate,
     * second array, that does not match.
     * As a second paramter in reduce (callback, initialValue), as an
     * initial value need to pass empty tuple of arrays ([[], []])
     * Or use Reducer.Partition function to create initial value for it.
     * Predicate is an object, which key and values must match current element.
     * For matching all key-value pairs, element will be placed in
     * first partition array.
     *
     * @export
     * @template T                          element type in array
     * @param {T} matches                   object to match key value pairs in current element
     * @returns {(agr: [T[], T[]], value: T) => [T[], T[]]} function to pass to Array.reduce
     * @see Reducer.Partition
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=partitionby
     */
    function partitionBy<T>(matches: Partial<T>): (agr: Tuple<T[], T[]>, value: T) => Tuple<T[], T[]>;
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Groups pairs of consecutive elements together and returns them as an array of two values.
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=pairwise
     */
    function pairwise<T>(): (agr: Tuple<T, T>[], value: T, index: number, array: T[]) => Tuple<T, T>[];
    /**
     * Function to be used in {@link Array.prototype.reduce} as a callback.
     * Applies an accumulator function over the current element
     * and returns each intermediate result for accumulation
     * @param {function} accFunction accumulator function
     * @param {T} initial value
     * @see https://pavel-surinin.github.io/declarativejs/#/?id=scan
     */
    function scan<T, R>(accFunction: (acc: R, current: T) => R, initial: R): (agr: R[], value: T) => R[];
}
