import { type StreamSource } from '@rimbu/stream';
/**
 * Interface used to hash objects for hashed collections.
 * @typeparam UK - the upper type limit for which the hasher is assumed to be valid
 * @note A hashcode is a 32-bit JS integer and therefore signed.
 * You can obtain the 32-bit version of any JS number by applying a
 * bitwise operator to it, e.g. if x is a number, use x | 0.
 */
export interface Hasher<UK> {
    /**
     * Returns true if this hasher can be applied to the given `obj` object.
     * @param obj - the object to check
     * @example
     * ```ts
     * const h = Hasher.numberHasher()
     * console.log(h.isValid(5))
     * // => true
     * console.log(h.isValid('a'))
     * // => false
     * ```
     */
    isValid(obj: unknown): obj is UK;
    /**
     * Returns the 32-bit hash code for the given `value`.
     * @param value - the value to hash
     * @note it is assumed that the caller has verified that the given object
     * is valid, either by knowing the types up front, or by using the `isValid` function.
     * @example
     * ```ts
     * const h = Hasher.anyHasher()
     * h.hash([1, 3, 2])
     * ```
     */
    hash(value: UK): number;
}
export declare namespace Hasher {
    /**
     * Returns the default `Hasher` instance used by hashed collections.
     * @example
     * ```ts
     * const h = Hasher.defaultHasher()
     * h.hash({ a: 1, b: 2 })
     * ```
     */
    function defaultHasher(): Hasher<any>;
    /**
     * Returns a `Hasher` instance for string values.
     * @example
     * ```ts
     * const h = Hasher.stringHasher()
     * h.hash('abc')
     * ```
     */
    function stringHasher(): Hasher<string>;
    /**
     * Returns a `Hasher` instance that hashes the string representation of any value.
     * @param maxStepBits - the maximum amount of samples to take from the string
     * @example
     * ```ts
     * const h = Hasher.anyToStringHasher()
     * h.hash([1, 3, 'a'])
     * ```
     */
    function anyToStringHasher(maxStepBits?: number): Hasher<any>;
    /**
     * Returns a `Hasher` instance that hashes any value by hashing the string resulting from
     * applying JSON.stringify to the value.
     * @example
     * ```ts
     * const h = Hasher.anyJsonStringHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => false
     * ```
     */
    function anyJsonStringHasher(): Hasher<any>;
    /**
     * Returns a `Hasher` instance for case-insensitive string values.
     * @example
     * ```ts
     * const h = Hasher.stringCaseInsensitiveHasher()
     * console.log(h.hash('Abc') === h.hash('aBC'))
     * // => true
     * ```
     */
    function stringCaseInsensitiveHasher(): Hasher<string>;
    /**
     * Returns a `Hasher` that hashes arrays of elements by sampling the array and using
     * the given `itemHasher` to hash the sampled elements.
     * @typeparam T - the array element type
     * @param options - (optional) an object containing the following items:<br/>
     * - itemHasher: (optional) a Hasher instance used to hash elements in the array<br/>
     * - maxStepBits: (optional) the amount of bits to determine the maximum amount of array
     * elements to process
     * @example
     * ```ts
     * const h = Hasher.arrayHasher()
     * console.log(h.hash([1, 2, 3] === h.hash([1, 3, 2])))
     * // => false
     * ```
     */
    function arrayHasher<T = any>(options?: {
        itemHasher?: Hasher<T>;
        maxStepBits?: number;
    }): Hasher<readonly T[]>;
    /**
     * Returns a `Hasher` instance that hashes any `StreamSource` limited to a certain amount
     * of elements to prevent hanging on infinite streams.
     * @typeparam T - the StreamSource element type
     * @param options - (optional) an object containing the following items:<br/>
     * - itemHasher: (optional) a Hasher instance used to hash elements in the array<br/>
     * - maxStepBits: (optional) the amount of bits to determine the maximum amount of array
     * elements to process
     * @example
     * ```ts
     * const h = Hasher.streamSourceHasher()
     * h.hash(Stream.random())
     * // infinite stream but will not hang due to the max step limit
     * ```
     */
    function streamSourceHasher<T = any>(options?: {
        itemHasher?: Hasher<T>;
        maxStepBits?: number;
    }): Hasher<StreamSource<T>>;
    /**
     * Returns a `Hasher` instance that hashes numbers, including 'special' values like `NaN` and infinities.
     * @example
     * ```ts
     * const h = Hasher.numberHasher()
     * console.log(h.hash(Number.POSITIVE_INFINITY) === h.hash(Number.NEGATIVE_INFINITY))
     * // => false
     * console.log(h.hash(Number.NaN) === h.hash(Number.NaN))
     * // => true
     * ```
     */
    function numberHasher(): Hasher<number>;
    /**
     * Returns a `Hasher` instance that hashes booleans.
     * @example
     * ```ts
     * const h = Hasher.booleanHasher()
     * console.log(h.hash(true) === h.hash(false))
     * // => false
     * ```
     */
    function booleanHasher(): Hasher<boolean>;
    /**
     * Returns a `Hasher` instance that hashes bigints.
     * @example
     * ```ts
     * const h = Hasher.bigintHasher()
     * console.log(h.hash(BigInt(5)) === h.hash(BigInt(10)))
     * // => false
     * ```
     */
    function bigintHasher(): Hasher<bigint>;
    /**
     * Returns a `Hasher` instance that hashes the `.valueOf` value of the given
     * object using the given `valueHasher` for instances of the given `cls` class.
     * @typeparam T - the input object type
     * @typeparam V - the .valueOf property type
     * @param cls - the class containing the constructor to check for validity of a given object
     * @param valueHasher - the `Hasher` instance to use for the `.valueOf` values
     * @example
     * ```ts
     * const h = Hasher.createValueOfHasher(Date)
     * console.log(h.isValid(new Boolean(true)))
     * // => false
     * const d1 = new Date()
     * const d2 = new Date(d1)
     * console.log(h.hash(d1) === h.hash(d2))
     * // => true
     * ```
     */
    function createValueOfHasher<T extends {
        valueOf(): V;
    }, V>(cls: {
        new (): T;
    }, valueHasher?: Hasher<V>): Hasher<T>;
    /**
     * Returns a `Hasher` instance that hashes `Date` values.
     * @example
     * ```ts
     * const h = Hasher.dateHasher()
     * const d1 = new Date()
     * const d2 = new Date(d1)
     * console.log(h.hash(d1) === h.hash(d2))
     * // => true
     * ```
     */
    function dateHasher(): Hasher<Date>;
    /**
     * Returns a `Hasher` instance that hashes objects of key type K and value type V.
     * @typeparam K - the key type
     * @typeparam V - the value type
     * @param options - (optional) an object containing:<br/>
     * - keyHasher: (optional) a Hasher instance that is used to hash object keys<br/>
     * - valueHasher: (optional) a Hasher instance that is used to hash object values
     * @example
     * ```ts
     * const h = Hasher.objectHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => true
     * ```
     */
    function objectHasher<K extends string | number | symbol, V = any>(options?: {
        keyHasher: Hasher<K>;
        valueHasher: Hasher<V>;
    }): Hasher<Record<K, V>>;
    /**
     * Returns a `Hasher` instance that hashes objects of key type K and value type V.
     * If a value is an object or array, it will convert those values to a string.
     * @typeparam K - the key type
     * @typeparam V - the value type
     * @example
     * ```ts
     * const h = Hasher.objectShallowHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => true
     * ```
     */
    function objectShallowHasher<K extends string | number | symbol, V = any>(): Hasher<Record<K, V>>;
    /**
     * Returns a `Hasher` instance that hashes objects of key type K and value type V.
     * If a value is an object or array, it will recursively hash its values.
     * @note be careful with circular structures, they can cause an infinite loop
     * @typeparam K - the key type
     * @typeparam V - the value type
     * @example
     * ```ts
     * const h = Hasher.objectDeepHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => true
     * ```
     */
    function objectDeepHasher<K extends string | number | symbol, V = any>(): Hasher<Record<K, V>>;
    /**
     * Returns a `Hasher` instance that hashes any value, but never traverses into an object
     * or array to hash its elements. In those cases it will use toString.
     * @example
     * ```ts
     * const h = Hasher.anyFlatHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => false
     * ```
     */
    function anyFlatHasher(): Hasher<any>;
    /**
     * Returns a `Hasher` instance that hashes any value, but only traverses into an object
     * or array to hash its elements one level deep. After one level, it will use toString.
     * @example
     * ```ts
     * const h = Hasher.anyShallowHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => true
     * console.log(h.hash([{ a: 1, b: 2 }]) === h.hash([{ b: 2, a: 1 }]))
     * // => false
     * ```
     */
    function anyShallowHasher(): Hasher<any>;
    /**
     * Returns a `Hasher` instance that hashes any value, and traverses into an object
     * or array to hash its elements.
     * @example
     * ```ts
     * const h = Hasher.anyDeepHasher()
     * console.log(h.hash({ a: 1, b: 2 }) === h.hash({ b: 2, a: 1 }))
     * // => true
     * console.log(h.hash([{ a: 1, b: 2 }]) === h.hash([{ b: 2, a: 1 }]))
     * // => true
     * ```
     */
    function anyDeepHasher(): Hasher<any>;
    /**
     * Returns a `Hasher` that will return equal hash values for values in a tuple regardless
     * of their order, and uses the given `hasher` function to hash the tuple elements.
     * @param hasher - the `Hasher` instance to use for tuple elements
     * @example
     * ```ts
     * const h = Hasher.tupleSymmetric()
     * console.log(h.hash(['abc', 'def']) === h.hash(['def', 'abc']))
     * ```
     */
    function tupleSymmetric<T>(hasher?: Hasher<T>): Hasher<readonly [T, T]>;
}
