export declare abstract class AbstractCollection<TElement> extends AbstractReadonlyCollection<TElement> implements ICollection<TElement> {
    protected constructor(comparator?: EqualityComparator<TElement>);
    addAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    toReadonlyCollection(): IReadonlyCollection<TElement>;
    abstract add(element: TElement): boolean;
    abstract clear(): void;
}

declare abstract class AbstractDictionary<TKey, TValue> extends AbstractReadonlyDictionary<TKey, TValue> implements IDictionary<TKey, TValue> {
    protected constructor(valueComparator: EqualityComparator<TValue>, keyValueComparator: EqualityComparator<KeyValuePair<TKey, TValue>>);
    put(key: TKey, value: TValue): TValue | null;
    tryAdd(key: TKey, value: TValue): boolean;
    abstract add(key: TKey, value: TValue): TValue;
    abstract clear(): void;
    abstract remove(key: TKey): TValue | null;
    abstract set(key: TKey, value: TValue): void;
}

export declare abstract class AbstractEnumerable<TElement> implements IEnumerable<TElement> {
    protected readonly comparer: EqualityComparator<TElement>;
    protected constructor(comparator?: EqualityComparator<TElement>);
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): TAccumulate | TResult;
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, TAccumulate>>;
    all(predicate: Predicate<TElement>): boolean;
    any(predicate?: Predicate<TElement>): boolean;
    append(element: TElement): IEnumerable<TElement>;
    average(selector?: Selector<TElement, number>): number;
    cast<TResult>(): IEnumerable<TResult>;
    chunk(size: number): IEnumerable<IEnumerable<TElement>>;
    combinations(size?: number): IEnumerable<IEnumerable<TElement>>;
    concat(iterable: Iterable<TElement>): IEnumerable<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    count(predicate?: Predicate<TElement>): number;
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, number>>;
    cycle(count?: number): IEnumerable<TElement>;
    defaultIfEmpty(value?: TElement | null): IEnumerable<TElement | null>;
    distinct(keyComparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    elementAt(index: number): TElement;
    elementAtOrDefault(index: number): TElement | null;
    except(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    exceptBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    first(predicate?: Predicate<TElement>): TElement;
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    forEach(action: IndexedAction<TElement>): void;
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<IGroup<TKey, TElement>>;
    groupJoin<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TResult>;
    index(): IEnumerable<[number, TElement]>;
    intersect(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    intersectBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    intersperse<TSeparator = TElement>(separator: TSeparator): IEnumerable<TElement | TSeparator>;
    join<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IEnumerable<TResult>;
    last(predicate?: Predicate<TElement>): TElement;
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    max(selector?: Selector<TElement, number>): number;
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    min(selector?: Selector<TElement, number>): number;
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    none(predicate?: Predicate<TElement>): boolean;
    ofType<TResult extends ObjectType>(type: TResult): IEnumerable<InferredType<TResult>>;
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    pairwise(resultSelector: PairwiseSelector<TElement, TElement>): IEnumerable<[TElement, TElement]>;
    partition(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    permutations(size?: number): IEnumerable<IEnumerable<TElement>>;
    prepend(element: TElement): IEnumerable<TElement>;
    product(selector?: Selector<TElement, number>): number;
    reverse(): IEnumerable<TElement>;
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IEnumerable<TAccumulate>;
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IEnumerable<TResult>;
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IEnumerable<TResult>;
    sequenceEqual(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): boolean;
    shuffle(): IEnumerable<TElement>;
    single(predicate?: Predicate<TElement>): TElement;
    singleOrDefault(predicate?: Predicate<TElement>): TElement | null;
    skip(count: number): IEnumerable<TElement>;
    skipLast(count: number): IEnumerable<TElement>;
    skipWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    span(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    step(stepNumber: number): IEnumerable<TElement>;
    sum(selector?: Selector<TElement, number>): number;
    take(count: number): IEnumerable<TElement>;
    takeLast(count: number): IEnumerable<TElement>;
    takeWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    toArray(): TElement[];
    toCircularLinkedList(comparator?: EqualityComparator<TElement, TElement>): CircularLinkedList<TElement>;
    toDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): Dictionary<TKey, TValue>;
    toEnumerableSet(): EnumerableSet<TElement>;
    toImmutableDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    toImmutableList(comparator?: EqualityComparator<TElement>): ImmutableList<TElement>;
    toImmutablePriorityQueue(comparator?: OrderComparator<TElement>): ImmutablePriorityQueue<TElement>;
    toImmutableQueue(comparator?: EqualityComparator<TElement>): ImmutableQueue<TElement>;
    toImmutableSet(): ImmutableSet<TElement>;
    toImmutableSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    toImmutableSortedSet(comparator?: OrderComparator<TElement>): ImmutableSortedSet<TElement>;
    toImmutableStack(comparator?: EqualityComparator<TElement>): ImmutableStack<TElement>;
    toLinkedList(comparator?: EqualityComparator<TElement>): LinkedList<TElement>;
    toList(comparator?: EqualityComparator<TElement>): List<TElement>;
    toLookup<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>): ILookup<TKey, TValue>;
    toMap<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Map<TKey, TValue>;
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Record<TKey, TValue>;
    toPriorityQueue(comparator?: OrderComparator<TElement>): PriorityQueue<TElement>;
    toQueue(comparator?: EqualityComparator<TElement>): Queue<TElement>;
    toSet(): Set<TElement>;
    toSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): SortedDictionary<TKey, TValue>;
    toSortedSet(comparator?: OrderComparator<TElement>): SortedSet<TElement>;
    toStack(comparator?: EqualityComparator<TElement>): Stack<TElement>;
    union(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    unionBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    where(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    windows(size: number): IEnumerable<IEnumerable<TElement>>;
    zip<TSecond>(iterable: Iterable<TSecond>): IEnumerable<[TElement, TSecond]>;
    zip<TSecond, TResult = [TElement, TSecond]>(iterable: Iterable<TSecond>, zipper?: Zipper<TElement, TSecond, TResult>): IEnumerable<TResult>;
    protected getIterableSize(iterable: Iterable<TElement>): number;
    abstract [Symbol.iterator](): Iterator<TElement>;
}

export declare abstract class AbstractImmutableCollection<TElement> extends AbstractReadonlyCollection<TElement> implements IImmutableCollection<TElement> {
    protected constructor(comparator?: EqualityComparator<TElement>);
    abstract add(element: TElement): IImmutableCollection<TElement>;
    abstract addAll<TSource extends TElement>(collection: Iterable<TSource>): IImmutableCollection<TElement>;
    abstract clear(): IImmutableCollection<TElement>;
}

declare abstract class AbstractImmutableDictionary<TKey, TValue> extends AbstractReadonlyDictionary<TKey, TValue> implements IImmutableDictionary<TKey, TValue> {
    protected constructor(valueComparator: EqualityComparator<TValue>, keyValueComparator: EqualityComparator<KeyValuePair<TKey, TValue>>);
    abstract add(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
    abstract clear(): IImmutableDictionary<TKey, TValue>;
    abstract put(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
    abstract remove(key: TKey): IImmutableDictionary<TKey, TValue>;
    abstract set(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
    abstract get length(): number;
}

export declare abstract class AbstractList<TElement> extends AbstractRandomAccessCollection<TElement> implements IList<TElement> {
    protected constructor(comparator?: EqualityComparator<TElement>);
    add(element: TElement): boolean;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    elementAt(index: number): TElement;
    elementAtOrDefault(index: number): TElement | null;
    entries(): IterableIterator<[number, TElement]>;
    first(predicate?: Predicate<TElement>): TElement;
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    indexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    last(predicate?: Predicate<TElement>): TElement;
    lastIndexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    removeIf(predicate: Predicate<TElement>): boolean;
    abstract addAt(element: TElement, index: number): boolean;
    abstract get(index: number): TElement;
    abstract getRange(index: number, count: number): IList<TElement>;
    abstract removeAt(index: number): TElement;
    abstract set(index: number, element: TElement): TElement;
    abstract sort(comparator?: OrderComparator<TElement>): void;
}

export declare abstract class AbstractRandomAccessCollection<TElement> extends AbstractCollection<TElement> implements IRandomAccessCollection<TElement> {
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    abstract remove(element: TElement): boolean;
    abstract removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    abstract removeIf(predicate: Predicate<TElement>): boolean;
}

declare abstract class AbstractRandomAccessImmutableCollection<TElement> extends AbstractImmutableCollection<TElement> implements IRandomAccessImmutableCollection<TElement> {
    abstract remove(element: TElement): IRandomAccessImmutableCollection<TElement>;
    abstract removeAll<TSource extends TElement>(collection: Iterable<TSource>): IRandomAccessImmutableCollection<TElement>;
    abstract removeIf(predicate: (element: TElement) => boolean): IRandomAccessImmutableCollection<TElement>;
    abstract retainAll<TSource extends TElement>(collection: Iterable<TSource>): IRandomAccessImmutableCollection<TElement>;
}

export declare abstract class AbstractReadonlyCollection<TElement> extends AbstractEnumerable<TElement> implements IReadonlyCollection<TElement> {
    protected constructor(comparator?: EqualityComparator<TElement>);
    any(predicate?: Predicate<TElement>): boolean;
    containsAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    count(predicate?: Predicate<TElement>): number;
    isEmpty(): boolean;
    toString(): string;
    toString(separator?: string): string;
    toString(separator?: string, selector?: Selector<TElement, string>): string;
    get comparator(): EqualityComparator<TElement> | OrderComparator<TElement>;
    abstract size(): number;
    abstract get length(): number;
}

declare abstract class AbstractReadonlyDictionary<TKey, TValue> implements IReadonlyDictionary<TKey, TValue> {
    protected readonly keyValueComparer: EqualityComparator<KeyValuePair<TKey, TValue>>;
    protected readonly valueComparer: EqualityComparator<TValue>;
    protected constructor(valueComparator: EqualityComparator<TValue>, keyValueComparator: EqualityComparator<KeyValuePair<TKey, TValue>>);
    aggregate<TAccumulate = KeyValuePair<TKey, TValue>, TResult = TAccumulate>(accumulator: Accumulator<KeyValuePair<TKey, TValue>, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): TAccumulate | TResult;
    aggregateBy<TAggregateKey, TAccumulate = KeyValuePair<TKey, TValue>>(keySelector: Selector<KeyValuePair<TKey, TValue>, TAggregateKey>, seedSelector: Selector<TAggregateKey, TAccumulate> | TAccumulate, accumulator: Accumulator<KeyValuePair<TKey, TValue>, TAccumulate>, keyComparator?: EqualityComparator<TAggregateKey>): IEnumerable<KeyValuePair<TAggregateKey, TAccumulate>>;
    all(predicate: Predicate<KeyValuePair<TKey, TValue>>): boolean;
    any(predicate?: Predicate<KeyValuePair<TKey, TValue>>): boolean;
    append(element: KeyValuePair<TKey, TValue>): IEnumerable<KeyValuePair<TKey, TValue>>;
    asObject<TObjectKey extends string | number | symbol>(): Record<TObjectKey, TValue>;
    average(selector?: Selector<KeyValuePair<TKey, TValue>, number>): number;
    cast<TResult>(): IEnumerable<TResult>;
    chunk(size: number): IEnumerable<IEnumerable<KeyValuePair<TKey, TValue>>>;
    combinations(size?: number): IEnumerable<IEnumerable<KeyValuePair<TKey, TValue>>>;
    concat(iterable: Iterable<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    contains(element: KeyValuePair<TKey, TValue>, comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): boolean;
    count(predicate?: Predicate<KeyValuePair<TKey, TValue>>): number;
    countBy<TCountKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TCountKey>, comparator?: EqualityComparator<TCountKey>): IEnumerable<KeyValuePair<TCountKey, number>>;
    cycle(count?: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    defaultIfEmpty(value?: KeyValuePair<TKey, TValue> | null): IEnumerable<KeyValuePair<TKey, TValue> | null>;
    distinct(keyComparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    distinctBy<TDistinctKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TDistinctKey>, comparator?: EqualityComparator<TDistinctKey>): IEnumerable<KeyValuePair<TKey, TValue>>;
    elementAt(index: number): KeyValuePair<TKey, TValue>;
    elementAtOrDefault(index: number): KeyValuePair<TKey, TValue> | null;
    except(iterable: Iterable<KeyValuePair<TKey, TValue>>, comparator?: EqualityComparator<KeyValuePair<TKey, TValue>> | OrderComparator<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    exceptBy<TExceptKey>(iterable: Iterable<KeyValuePair<TKey, TValue>>, keySelector: Selector<KeyValuePair<TKey, TValue>, TExceptKey>, keyComparator?: EqualityComparator<TExceptKey> | OrderComparator<TExceptKey>): IEnumerable<KeyValuePair<TKey, TValue>>;
    first(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue>;
    firstOrDefault(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue> | null;
    forEach(action: IndexedAction<KeyValuePair<TKey, TValue>>): void;
    groupBy<TGroupKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TGroupKey>, keyComparator?: EqualityComparator<TGroupKey>): IEnumerable<IGroup<TGroupKey, KeyValuePair<TKey, TValue>>>;
    groupJoin<TInner, TGroupKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<KeyValuePair<TKey, TValue>, TGroupKey>, innerKeySelector: Selector<TInner, TGroupKey>, resultSelector: JoinSelector<KeyValuePair<TKey, TValue>, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TGroupKey>): IEnumerable<TResult>;
    index(): IEnumerable<[number, KeyValuePair<TKey, TValue>]>;
    intersect(iterable: Iterable<KeyValuePair<TKey, TValue>>, comparator?: EqualityComparator<KeyValuePair<TKey, TValue>> | OrderComparator<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    intersectBy<TIntersectKey>(iterable: Iterable<KeyValuePair<TKey, TValue>>, keySelector: Selector<KeyValuePair<TKey, TValue>, TIntersectKey>, keyComparator?: EqualityComparator<TIntersectKey> | OrderComparator<TIntersectKey>): IEnumerable<KeyValuePair<TKey, TValue>>;
    intersperse<TSeparator = KeyValuePair<TKey, TValue>>(separator: TSeparator): IEnumerable<KeyValuePair<TKey, TValue> | TSeparator>;
    isEmpty(): boolean;
    join<TInner, TGroupKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<KeyValuePair<TKey, TValue>, TGroupKey>, innerKeySelector: Selector<TInner, TGroupKey>, resultSelector: JoinSelector<KeyValuePair<TKey, TValue>, TInner, TResult>, keyComparator?: EqualityComparator<TGroupKey>, leftJoin?: boolean): IEnumerable<TResult>;
    last(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue>;
    lastOrDefault(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue> | null;
    max(selector?: Selector<KeyValuePair<TKey, TValue>, number>): number;
    maxBy<TMaxKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TMaxKey>, comparator?: OrderComparator<TMaxKey>): KeyValuePair<TKey, TValue>;
    min(selector?: Selector<KeyValuePair<TKey, TValue>, number>): number;
    minBy<TMinKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TMinKey>, comparator?: OrderComparator<TMinKey>): KeyValuePair<TKey, TValue>;
    none(predicate?: Predicate<KeyValuePair<TKey, TValue>>): boolean;
    ofType<TResult extends ObjectType>(type: TResult): IEnumerable<InferredType<TResult>>;
    orderBy<TOrderKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TOrderKey>, comparator?: OrderComparator<TOrderKey>): IOrderedEnumerable<KeyValuePair<TKey, TValue>>;
    orderByDescending<TOrderKey>(keySelector: Selector<KeyValuePair<TKey, TValue>, TOrderKey>, comparator?: OrderComparator<TOrderKey>): IOrderedEnumerable<KeyValuePair<TKey, TValue>>;
    pairwise(resultSelector?: PairwiseSelector<KeyValuePair<TKey, TValue>, KeyValuePair<TKey, TValue>>): IEnumerable<[KeyValuePair<TKey, TValue>, KeyValuePair<TKey, TValue>]>;
    partition(predicate: Predicate<KeyValuePair<TKey, TValue>>): [IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>];
    permutations(size?: number): IEnumerable<IEnumerable<KeyValuePair<TKey, TValue>>>;
    prepend(element: KeyValuePair<TKey, TValue>): IEnumerable<KeyValuePair<TKey, TValue>>;
    product(selector?: Selector<KeyValuePair<TKey, TValue>, number>): number;
    reverse(): IEnumerable<KeyValuePair<TKey, TValue>>;
    scan<TAccumulate = KeyValuePair<TKey, TValue>>(accumulator: Accumulator<KeyValuePair<TKey, TValue>, TAccumulate>, seed?: TAccumulate): IEnumerable<TAccumulate>;
    select<TResult>(selector: IndexedSelector<KeyValuePair<TKey, TValue>, TResult>): IEnumerable<TResult>;
    selectMany<TResult>(selector: IndexedSelector<KeyValuePair<TKey, TValue>, Iterable<TResult>>): IEnumerable<TResult>;
    sequenceEqual(iterable: Iterable<KeyValuePair<TKey, TValue>>, comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): boolean;
    shuffle(): IEnumerable<KeyValuePair<TKey, TValue>>;
    single(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue>;
    singleOrDefault(predicate?: Predicate<KeyValuePair<TKey, TValue>>): KeyValuePair<TKey, TValue> | null;
    skip(count: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    skipLast(count: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    skipWhile(predicate: IndexedPredicate<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    span(predicate: Predicate<KeyValuePair<TKey, TValue>>): [IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>];
    step(stepNumber: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    sum(selector?: Selector<KeyValuePair<TKey, TValue>, number>): number;
    take(count: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    takeLast(count: number): IEnumerable<KeyValuePair<TKey, TValue>>;
    takeWhile(predicate: IndexedPredicate<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    toArray(): KeyValuePair<TKey, TValue>[];
    toCircularLinkedList(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): CircularLinkedList<KeyValuePair<TKey, TValue>>;
    toDictionary<TDictKey, TDictValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TDictKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TDictValue>, valueComparator?: EqualityComparator<TDictValue>): Dictionary<TDictKey, TDictValue>;
    toEnumerableSet(): EnumerableSet<KeyValuePair<TKey, TValue>>;
    toImmutableDictionary<TDictKey, TDictValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TDictKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TDictValue>, valueComparator?: EqualityComparator<TDictValue>): ImmutableDictionary<TDictKey, TDictValue>;
    toImmutableList(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): ImmutableList<KeyValuePair<TKey, TValue>>;
    toImmutablePriorityQueue(comparator?: OrderComparator<KeyValuePair<TKey, TValue>>): ImmutablePriorityQueue<KeyValuePair<TKey, TValue>>;
    toImmutableQueue(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): ImmutableQueue<KeyValuePair<TKey, TValue>>;
    toImmutableSet(): ImmutableSet<KeyValuePair<TKey, TValue>>;
    toImmutableSortedDictionary<TDictKey, TDictValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TDictKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TDictValue>, keyComparator?: OrderComparator<TDictKey>, valueComparator?: EqualityComparator<TDictValue>): ImmutableSortedDictionary<TDictKey, TDictValue>;
    toImmutableSortedSet(comparator?: OrderComparator<KeyValuePair<TKey, TValue>>): ImmutableSortedSet<KeyValuePair<TKey, TValue>>;
    toImmutableStack(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): ImmutableStack<KeyValuePair<TKey, TValue>>;
    toLinkedList(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): LinkedList<KeyValuePair<TKey, TValue>>;
    toList(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): List<KeyValuePair<TKey, TValue>>;
    toLookup<TLookupKey, TLookupValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TLookupKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TLookupValue>, keyComparator?: OrderComparator<TLookupKey>): ILookup<TLookupKey, TLookupValue>;
    toMap<TMapKey, TMapValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TMapKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TMapValue>): Map<TMapKey, TMapValue>;
    toObject<TObjectKey extends string | number | symbol, TObjectValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TObjectKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TObjectValue>): Record<TObjectKey, TObjectValue>;
    toPriorityQueue(comparator?: OrderComparator<KeyValuePair<TKey, TValue>>): PriorityQueue<KeyValuePair<TKey, TValue>>;
    toQueue(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): Queue<KeyValuePair<TKey, TValue>>;
    toSet(): Set<KeyValuePair<TKey, TValue>>;
    toSortedDictionary<TDictKey, TDictValue>(keySelector: Selector<KeyValuePair<TKey, TValue>, TDictKey>, valueSelector: Selector<KeyValuePair<TKey, TValue>, TDictValue>, keyComparator?: OrderComparator<TDictKey>, valueComparator?: EqualityComparator<TDictValue>): SortedDictionary<TDictKey, TDictValue>;
    toSortedSet(comparator?: OrderComparator<KeyValuePair<TKey, TValue>>): SortedSet<KeyValuePair<TKey, TValue>>;
    toStack(comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): Stack<KeyValuePair<TKey, TValue>>;
    toString(): string;
    toString(selector?: Selector<KeyValuePair<TKey, TValue>, string>): string;
    union(iterable: Iterable<KeyValuePair<TKey, TValue>>, comparator?: EqualityComparator<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    unionBy<TUnionKey>(iterable: Iterable<KeyValuePair<TKey, TValue>>, keySelector: Selector<KeyValuePair<TKey, TValue>, TUnionKey>, comparator?: EqualityComparator<TUnionKey>): IEnumerable<KeyValuePair<TKey, TValue>>;
    where(predicate: IndexedPredicate<KeyValuePair<TKey, TValue>>): IEnumerable<KeyValuePair<TKey, TValue>>;
    windows(size: number): IEnumerable<IEnumerable<KeyValuePair<TKey, TValue>>>;
    zip<TSecond, TResult = [KeyValuePair<TKey, TValue>, TSecond]>(iterable: Iterable<TSecond>, zipper?: Zipper<KeyValuePair<TKey, TValue>, TSecond, TResult>): IEnumerable<[KeyValuePair<TKey, TValue>, TSecond]> | IEnumerable<TResult>;
    get keyValueComparator(): EqualityComparator<KeyValuePair<TKey, TValue>>;
    get valueComparator(): EqualityComparator<TValue>;
    abstract [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    abstract containsKey(key: TKey): boolean;
    abstract containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    abstract entries(): IterableIterator<[TKey, TValue]>;
    abstract get(key: TKey): TValue | null;
    abstract keys(): ISet<TKey>;
    abstract size(): number;
    abstract values(): ICollection<TValue>;
    abstract get length(): number;
}

export declare abstract class AbstractSet<TElement> extends AbstractRandomAccessCollection<TElement> implements ISet<TElement> {
    protected constructor(comparator?: EqualityComparator<TElement>);
    exceptWith(other: Iterable<TElement>): void;
    intersectWith(other: Iterable<TElement>): void;
    isProperSubsetOf(other: Iterable<TElement>): boolean;
    isProperSupersetOf(other: Iterable<TElement>): boolean;
    isSubsetOf(other: Iterable<TElement>): boolean;
    isSupersetOf(other: Iterable<TElement>): boolean;
    overlaps(other: Iterable<TElement>): boolean;
}

export declare abstract class AbstractTree<TElement> extends AbstractRandomAccessCollection<TElement> implements ITree<TElement> {
    protected readonly orderComparator: OrderComparator<TElement>;
    protected root: INode<TElement> | null;
    protected treeSize: number;
    protected constructor(comparator?: OrderComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    find(predicate: Predicate<TElement>): TElement | null;
    findBy<TKey>(key: TKey, selector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement | null;
    forEach(action: IndexedAction<TElement>): void;
    getRootData(): TElement | null;
    isEmpty(): boolean;
    remove(element: TElement): boolean;
    removeBy<TKey>(key: TKey, selector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement | null;
    size(): number;
    toArray(): TElement[];
    traverseToArray(direction?: TraverseType): TElement[];
    get length(): number;
    protected toInorderArray(root: INode<TElement> | null, target: TElement[]): void;
    protected toPostorderArray(root: INode<TElement> | null, target: TElement[]): void;
    protected toPreorderArray(root: INode<TElement> | null, target: TElement[]): void;
    private containsRecursive;
    private findByRecursive;
    private findRecursive;
    private removeByRecursive;
    private toArrayRecursive;
    abstract add(element: TElement): boolean;
    abstract delete(element: TElement): void;
    abstract insert(element: TElement): void;
    abstract search(element: TElement): boolean;
}

export declare interface Accumulator<TElement, TAccumulate> {
    (acc: TAccumulate, item: TElement): TAccumulate;
}

/**
 * Applies an accumulator function over the sequence. If seed is specified, it is used as the initial value.
 * If the resultSelector function is specified, it will be used to select the result value.
 * @template TAccumulate
 * @template TResult
 * @param source The source iterable.
 * @param accumulator The accumulator function that will be applied over the sequence.
 * @param seed The value that will be used as the initial value. If not specified, the first element of the sequence will be used as the seed value.
 * @param resultSelector The function that will be used to select the result value.
 * @returns {TAccumulate|TResult} The final accumulator value.
 * @throws {NoElementsException} If the source is empty and seed is not provided.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const sum = numbers.aggregate((acc, current) => acc + current); // sum = 15
 *      const productWithSeed = numbers.aggregate((acc, current) => acc * current, 10); // productWithSeed = 1200 (10 * 1 * 2 * 3 * 4 * 5)
 *      const sumAsString = numbers.aggregate(
 *          (acc, current) => acc + current, // Accumulator
 *          0,                             // Seed
 *          (finalResult) => `Sum: ${finalResult}` // Result selector
 *      ); // sumAsString = "Sum: 15"
 */
export declare const aggregate: <TElement, TAccumulate = TElement, TResult = TAccumulate>(source: Iterable<TElement>, accumulator: (accumulator: TAccumulate, element: TElement) => TAccumulate, seed?: TAccumulate, resultSelector?: (accumulator: TAccumulate) => TResult) => TAccumulate | TResult;

/**
 * Applies an accumulator function over the sequence, grouping the results by a key from the key selector function.
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param seedSelector Initial accumulator value or a function that will be used to select the initial accumulator value.
 * @param accumulator The accumulator function that will be applied over the sequence.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @example
 *      interface Product { category: string; price: number; }
 *      const products = new List<Product>([
 *           { category: 'Electronics', price: 700 },
 *           { category: 'Books', price: 120 },
 *           { category: 'Electronics', price: 200 },
 *           { category: 'Books', price: 90 }
 *      ]);
 *      const totalValuePerCategory = products.aggregateBy(
 *           p => p.category, // keySelector: group by category
 *           0,               // seedSelector: start sum at 0 for each category
 *           (sum, p) => sum + p.price // accumulator: add product price to sum
 *      ).toArray();
 *      // totalValuePerCategory: [{ key: 'Electronics', value: 900 }, { key: 'Books', value: 210 }]
 */
export declare const aggregateBy: <TElement, TKey, TAccumulate = TElement>(source: IEnumerable<TElement>, keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>) => IEnumerable<KeyValuePair<TKey, TAccumulate>>;

/**
 * Determines if all elements of the sequence satisfy the specified predicate.
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition.
 * @returns {boolean} true if all elements of the sequence satisfy the specified predicate; otherwise, false.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const allPositive = numbers.all(n => n > 0); // allPositive = true
 *      const allEven = numbers.all(n => n % 2 === 0); // allEven = false
 */
export declare const all: <TElement>(source: Iterable<TElement>, predicate: Predicate<TElement>) => boolean;

/**
 * Determines if any element of the sequence satisfies the specified predicate.
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition.
 * If not specified, it will return true if the sequence has elements, otherwise false.
 * @returns {boolean} true if any element of the sequence satisfies the specified predicate; otherwise, false.
 * @example
 *      const numbers = new List([1, 2, -3, 4, 5]);
 *      const hasNegative = numbers.any(n => n < 0); // hasNegative = true
 *      const hasGreaterThanTen = numbers.any(n => n > 10); // hasGreaterThanTen = false
 *      const isEmpty = new List<number>().any(); // isEmpty = false
 *      const isNotEmpty = numbers.any(); // isNotEmpty = true
 */
export declare const any: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => boolean;

/**
 * Appends the specified element to the end of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param element The element that will be appended to the end of the sequence
 * @returns {IEnumerable<TElement>} A new enumerable sequence that ends with the specified element.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const appended = numbers.append(4).toArray(); // appended = [1, 2, 3, 4]
 */
export declare const append: <TElement>(source: Iterable<TElement>, element: TElement) => IEnumerable<TElement>;

export declare class AsyncEnumerable<TElement> implements IAsyncEnumerable<TElement> {
    #private;
    private readonly iterable;
    constructor(iterable: AsyncIterable<TElement>);
    static empty<TSource>(): IAsyncEnumerable<TSource>;
    static from<TSource>(source: AsyncIterable<TSource>): IAsyncEnumerable<TSource>;
    static range(start: number, count: number): IAsyncEnumerable<number>;
    static repeat<TSource>(element: TSource, count: number): IAsyncEnumerable<TSource>;
    [Symbol.asyncIterator](): AsyncIterator<TElement>;
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): Promise<TAccumulate | TResult>;
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<KeyValuePair<TKey, TAccumulate>>;
    all(predicate: Predicate<TElement>): Promise<boolean>;
    any(predicate?: Predicate<TElement>): Promise<boolean>;
    append(element: TElement): IAsyncEnumerable<TElement>;
    average(selector?: Selector<TElement, number>): Promise<number>;
    cast<TResult>(): IAsyncEnumerable<TResult>;
    chunk(count: number): IAsyncEnumerable<IEnumerable<TElement>>;
    combinations(size?: number): IAsyncEnumerable<IEnumerable<TElement>>;
    concat(other: AsyncIterable<TElement>): IAsyncEnumerable<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): Promise<boolean>;
    count(predicate?: Predicate<TElement>): Promise<number>;
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IAsyncEnumerable<KeyValuePair<TKey, number>>;
    cycle(count?: number): IAsyncEnumerable<TElement>;
    defaultIfEmpty(defaultValue?: TElement | null): IAsyncEnumerable<TElement | null>;
    distinct(keyComparator?: EqualityComparator<TElement>): IAsyncEnumerable<TElement>;
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<TElement>;
    elementAt(index: number): Promise<TElement>;
    elementAtOrDefault(index: number): Promise<TElement | null>;
    except(iterable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IAsyncEnumerable<TElement>;
    exceptBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IAsyncEnumerable<TElement>;
    first(predicate?: Predicate<TElement>): Promise<TElement>;
    firstOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    forEach(action: IndexedAction<TElement>): Promise<void>;
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<IGroup<TKey, TElement>>;
    groupJoin<TInner, TKey, TResult>(inner: IAsyncEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<TResult>;
    index(): IAsyncEnumerable<[number, TElement]>;
    intersect(iterable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IAsyncEnumerable<TElement>;
    intersectBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IAsyncEnumerable<TElement>;
    intersperse<TSeparator = TElement>(separator: TSeparator): IAsyncEnumerable<TElement | TSeparator>;
    join<TInner, TKey, TResult>(inner: IAsyncEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IAsyncEnumerable<TResult>;
    last(predicate?: Predicate<TElement>): Promise<TElement>;
    lastOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    max(selector?: Selector<TElement, number>): Promise<number>;
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): Promise<TElement>;
    min(selector?: Selector<TElement, number>): Promise<number>;
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): Promise<TElement>;
    none(predicate?: Predicate<TElement>): Promise<boolean>;
    ofType<TResult extends ObjectType>(type: TResult): IAsyncEnumerable<InferredType<TResult>>;
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
    pairwise(resultSelector: PairwiseSelector<TElement, TElement>): IAsyncEnumerable<[TElement, TElement]>;
    partition(predicate: Predicate<TElement>): Promise<[IEnumerable<TElement>, IEnumerable<TElement>]>;
    permutations(size?: number): IAsyncEnumerable<IEnumerable<TElement>>;
    prepend(element: TElement): IAsyncEnumerable<TElement>;
    product(selector?: Selector<TElement, number>): Promise<number>;
    reverse(): IAsyncEnumerable<TElement>;
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IAsyncEnumerable<TAccumulate>;
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IAsyncEnumerable<TResult>;
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IAsyncEnumerable<TResult>;
    sequenceEqual(iterable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement>): Promise<boolean>;
    shuffle(): IAsyncEnumerable<TElement>;
    single(predicate?: Predicate<TElement>): Promise<TElement>;
    singleOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    skip(count: number): IAsyncEnumerable<TElement>;
    skipLast(count: number): IAsyncEnumerable<TElement>;
    skipWhile(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    span(predicate: Predicate<TElement>): Promise<[IEnumerable<TElement>, IEnumerable<TElement>]>;
    step(step: number): IAsyncEnumerable<TElement>;
    sum(selector?: Selector<TElement, number>): Promise<number>;
    take(count: number): IAsyncEnumerable<TElement>;
    takeLast(count: number): IAsyncEnumerable<TElement>;
    takeWhile(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    toArray(): Promise<TElement[]>;
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Promise<Record<TKey, TValue>>;
    union(iterable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement>): IAsyncEnumerable<TElement>;
    unionBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IAsyncEnumerable<TElement>;
    where(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    windows(size: number): IAsyncEnumerable<IEnumerable<TElement>>;
    zip<TSecond>(iterable: AsyncIterable<TSecond>): IAsyncEnumerable<[TElement, TSecond]>;
    zip<TSecond, TResult = [TElement, TSecond]>(iterable: AsyncIterable<TSecond>, zipper: Zipper<TElement, TSecond, TResult>): IAsyncEnumerable<TResult>;
}

/**
 * Computes the average of the sequence. The sequence should be either a sequence consisting of numbers, or an appropriate selector function should be provided.
 * @param source The source iterable.
 * @param selector The selector function that will select a numeric value from the sequence elements.
 * @returns {number} The average of the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const avg = numbers.average(); // avg = 3
 *
 *      interface Item { value: number; }
 *      const items = new List<Item>([{ value: 10 }, { value: 20 }, { value: 60 }]);
 *      const avgValue = items.average(item => item.value); // avgValue = 30
 */
export declare const average: <TElement>(source: Iterable<TElement>, selector?: Selector<TElement, number>) => number;

declare type BigIntType = bigint | BigInt | BigIntConstructor | "bigint";

declare type BooleanType = boolean | Boolean | BooleanConstructor | "boolean";

/**
 * Casts the elements of the sequence to the specified type.
 * @template TResult
 * @param source The source iterable.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are of the specified type.
 * @example
 *      const mixedList = new List([1, 'two', 3, 'four', 5]);
 *      const numbersOnly = mixedList
 *          .where(item => typeof item === 'number')
 *          .cast<number>();
 *      // numbersOnly = [1, 3, 5]
 *
 *      // Note: Cast doesn't perform type conversion, only type assertion.
 *      // If an element cannot be cast, it may lead to runtime errors later.
 *      // Example of a potential issue (if not pre-filtered):
 *      // const potentialError = mixedList.cast<number>();
 *      // Iterating potentialError might throw errors when 'two' or 'four' are accessed as numbers.
 */
export declare const cast: <TResult, TElement = unknown>(source: Iterable<TElement>) => IEnumerable<TResult>;

/**
 * Splits the elements of the sequence into chunks of size at most the specified size.
 * @template TElement
 * @param source The source iterable.
 * @param size The maximum size of each chunk.
 * @return {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence whose elements are chunks of the original sequence.
 * @throws {InvalidArgumentException} If size is less than or equal to 0.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8]);
 *      const chunks = numbers.chunk(3).select(chunk => chunk.toArray()).toArray();
 *      // chunks = [[1, 2, 3], [4, 5, 6], [7, 8]]
 */
export declare const chunk: <TElement>(source: Iterable<TElement>, size: number) => IEnumerable<IEnumerable<TElement>>;

/**
 * Represents a circular doubly linked list.
 * In a circular linked list, the last node points to the first node (head.prev -> tail),
 * and the first node points to the last node (tail.next -> head), forming a circle.
 * This implementation uses a single 'head' reference, where head.prev gives the tail.
 */
export declare class CircularLinkedList<TElement> extends AbstractList<TElement> {
    #private;
    /**
     * Initializes a new instance of the CircularLinkedList class.
     * @param iterable An optional iterable to populate the list with.
     * @param comparator An optional equality comparator for elements.
     */
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    /**
     * Gets an iterator for the circular linked list.
     * Iterates through the list starting from the head node.
     * @returns {Iterator<TElement>} An iterator for the list.
     */
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds an element to the end of the list.
     * @param element The element to add.
     * @returns {boolean} Always returns true as the add operation succeeds.
     */
    add(element: TElement): boolean;
    /**
     * Adds an element at the specified index in the list.
     * @param element The element to add.
     * @param index The zero-based index at which the element should be inserted.
     * @returns {boolean} Always returns true as the add operation succeeds.
     * @throws {IndexOutOfBoundsException} If the index is out of range (index < 0 or index > size).
     */
    addAt(element: TElement, index: number): boolean;
    /**
     * Adds an element to the beginning of the list.
     * @param element The element to add.
     */
    addFirst(element: TElement): void;
    /**
     * Adds an element to the end of the list.
     * @param element The element to add.
     */
    addLast(element: TElement): void;
    /**
     * Removes all elements from the list.
     */
    clear(): void;
    /**
     * Determines whether the list contains a specific element.
     * @param element The element to locate.
     * @param comparator An optional equality comparator.
     * @returns {boolean} True if the element is found; otherwise, false.
     */
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    /**
     * Gets the element at the specified zero-based index.
     * @param index The index of the element to retrieve.
     * @returns {TElement} The element at the specified index.
     * @throws {IndexOutOfBoundsException} If the index is out of range (index < 0 or index >= size).
     */
    get(index: number): TElement;
    /**
     * Gets a range of elements starting from the specified index.
     * The range wraps around the list if necessary.
     * @param index The zero-based starting index of the range.
     * @param count The number of elements in the range.
     * @returns {CircularLinkedList<TElement>} A new CircularLinkedList containing the specified range.
     * @throws {IndexOutOfBoundsException} If the index is out of range (index < 0 or index >= size, or index > 0 for an empty list).
     * @throws {InvalidArgumentException} If count is negative.
     */
    getRange(index: number, count: number): CircularLinkedList<TElement>;
    /**
     * Removes the first occurrence of the specified element from the list.
     * @param element The element to remove.
     * @returns {boolean} True if the element was found and removed; otherwise, false.
     */
    remove(element: TElement): boolean;
    /**
     * Removes the element at the specified index.
     * @param index The zero-based index of the element to remove.
     * @returns {TElement} The element that was removed.
     * @throws {IndexOutOfBoundsException} If the index is out of range.
     */
    removeAt(index: number): TElement;
    /**
     * Removes and returns the first element from the list.
     * @returns {TElement} The removed element.
     * @throws {NoElementsException} If the list is empty.
     */
    removeFirst(): TElement;
    /**
     * Removes and returns the last element from the list.
     * @returns {TElement} The removed element.
     * @throws {NoElementsException} If the list is empty.
     */
    removeLast(): TElement;
    /**
     * Replaces the element at the specified index with a new element.
     * @param index The zero-based index of the element to replace.
     * @param element The new element.
     * @returns {TElement} The original element that was replaced.
     * @throws {IndexOutOfBoundsException} If the index is out of range.
     */
    set(index: number, element: TElement): TElement;
    /**
     * Gets the number of elements contained in the list.
     * @returns {number} The number of elements.
     */
    size(): number;
    /**
     * Sorts the elements in the list in place.
     * Note: This converts the list to an array, sorts it, and rebuilds the list.
     * @param comparator An optional order comparator.
     */
    sort(comparator?: OrderComparator<TElement>): void;
    /**
     * Checks if the index is valid for accessing an element (0 <= index < size).
     * @param index The index to check.
     * @throws {IndexOutOfBoundsException} If the index is invalid.
     */
    private checkElementIndex;
    /**
     * Checks if the index is valid for insertion (0 <= index <= size).
     * @param index The index to check.
     * @throws {IndexOutOfBoundsException} If the index is invalid.
     */
    private checkPositionIndex;
    /**
     * Helper to check if an index is within the bounds for element access.
     * @param index The index.
     * @returns {boolean} True if valid for element access.
     */
    private isElementIndex;
    /**
     * Helper to check if an index is within the bounds for insertion.
     * @param index The index.
     * @returns {boolean} True if valid for insertion.
     */
    private isPositionIndex;
    /**
     * Retrieves the node at the specified index. Traverses from the closer end.
     * @param index The zero-based index.
     * @returns {Node<TElement>} The node at the index.
     * @throws {IndexOutOfBoundsException} If index is invalid (handled by caller).
     */
    private node;
    /**
     * Removes a specified node from the list and updates links.
     * @param node The node to remove.
     * @returns {TElement} The element of the removed node.
     */
    private unlink;
    /**
     * Gets the equality comparator used by the list.
     * @returns {EqualityComparator<TElement>} The comparator.
     */
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * A circular queue is a queue that uses a fixed-size queue as its underlying data structure.
 * When the queue is full, adding a new element to the queue causes the oldest element in the queue to be removed.
 * The oldest element is the one that has been in the queue the longest, which is the element at the front of the queue.
 *
 * This is a first-in-first-out (FIFO) data structure.
 * @see https://en.wikipedia.org/wiki/Circular_buffer
 */
export declare class CircularQueue<TElement> extends Queue<TElement> {
    #private;
    /**
     * Constructs a circular queue with the default capacity of 32.
     */
    constructor();
    /**
     * Constructs a circular queue with the given capacity.
     * @param capacity The capacity of the queue.
     */
    constructor(capacity: number);
    /**
     * Adds an element to the queue.
     * If the queue is full, the oldest element in the queue is removed.
     * @param element The element to add to the queue.
     * @returns true if the element was added to the queue, false otherwise.
     */
    add(element: TElement): boolean;
    /**
     * Adds an element to the queue.
     * If the queue is full, the oldest element in the queue is removed.
     * @param element The element to add to the queue.
     */
    enqueue(element: TElement): void;
    /**
     * Returns if the queue is full.
     * @returns true if the queue is full, false otherwise.
     */
    isFull(): boolean;
    get comparator(): EqualityComparator<TElement>;
}

/**
 * @remarks
 * For the type of args, do not use `unknown[]` since it breaks the return type of `ofType` methods.
 * Since I did not use this anywhere in a situation where it requires a parameter,
 * I've opted for the ` never ` type, but in a situation where it requires a parameter, this should be changed to `any[]`.
 */
declare type Class<T> = new (...args: never[]) => T;

export declare enum CollectionChangedAction {
    Add = 0,
    Remove = 1,
    Replace = 2,
    Move = 3,
    Reset = 4
}

export declare abstract class Collections {
    private constructor();
    /**
     * Add all the given elements to the collection
     * @template TElement The type of the elements
     * @param {ICollection} collection The collection to which the elements will be added.
     * @param {TElement} elements The elements that will be added to the given collection.
     * @return {boolean} true if collection is modified, false otherwise.
     */
    static addAll<TElement, TSource extends TElement>(collection: ICollection<TSource>, ...elements: TElement[]): boolean;
    /**
     * Performs a binary search on the given sequence and returns the index of the element. The sequence must be sorted prior to calling this method.
     * If the searched element exists multiple times in the sequence, the returned index is arbitrary.
     * @template TElement The type of the elements
     * @param {IList|Array} sequence The list or array in which the element will be binary-searched for. It must be sorted.
     * @param {TElement} element The element that will be searched for.
     * @param {OrderComparator} comparator The comparator method that will be used to compare the elements. It should always be provided if the sequence is of a complex type.
     * @return {number} The index of the element that is equal to the searched element. If the element is not found, returns -1.
     */
    static binarySearch<TElement>(sequence: IList<TElement> | Array<TElement>, element: TElement, comparator?: OrderComparator<TElement>): number;
    /**
     * Returns true if the two specified iterables have no elements in common.
     * @param {Iterable} iterable1 First collection of items
     * @param {Iterable} iterable2 Second collection of items
     * @param {Function} comparator The comparator method that will be used to compare the elements. It should always be provided if the sequence is of a complex type.
     * @return {boolean} true if the two specified iterables have no elements in common.
     */
    static disjoint<TFirst, TSecond = TFirst>(iterable1: Iterable<TFirst>, iterable2: Iterable<TSecond>, comparator?: EqualityComparator<TFirst, TSecond>): boolean;
    /**
     * Returns an array of distinct element from a given iterable source.
     * @param {Iterable} iterable Collection of items
     * @param {Function} selector A method that will be used to return a key which will be used to determine the distinctness. If not provided,
     *                 then the item itself will be used as the key.
     * @param {Function} comparator A method that will be used to compare the equality of the selector keys.
     * @return An array of distinct items.
     */
    static distinct<TElement, TKey>(iterable: Iterable<TElement>, selector?: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    /**
     * Replaces all the elements of the list with the given element.
     * @template TElement The type of the elements
     * @param {IList} list The list whose elements will be replaced
     * @param {TElement} element The element which will replace the elements of the list
     */
    static fill<TElement>(list: IList<TElement>, element: TElement): void;
    /**
     * Finds the count of the given element in the source iterable.
     * @template TElement The type of the elements
     * @param {Iterable} source The iterable source in which the given element will be counted.
     * @param {TElement} element The element that will be counted.
     * @param {EqualityComparator} comparator The comparator method that will be used to compare the equality of the elements.
     */
    static frequency<TElement>(source: Iterable<TElement>, element: TElement, comparator?: EqualityComparator<TElement>): number;
    /**
     * Finds the maximum item in an iterable source.
     * @param {Iterable} iterable The data source
     * @param {Function} selector A selector method which will return a key that will be used for comparison.
     * @return The item which has the maximum value according to the selector method, or null if iterable is empty.
     * @throws {NoElementsException} If the iterable is empty.
     */
    static max<TElement>(iterable: Iterable<TElement>, selector?: Selector<TElement, number>): TElement;
    /**
     * Finds the minimum item in an iterable source.
     * @param {Iterable} iterable The data source
     * @param {Function} selector A selector method which will return a key that will be used for comparison.
     * @return The item which has the minimum value according to the selector method, or null if iterable is empty.
     * @throws {NoElementsException} If the iterable is empty.
     */
    static min<TElement>(iterable: Iterable<TElement>, selector?: Selector<TElement, number>): TElement;
    /**
     * Replaces the old element with the new element in a given sequence.
     * @template TElement The type of the elements
     * @param {IList|Array} sequence The sequence whose old elements will be replaced.
     * @param {TElement} oldElement The element that will be replaced with the new element.
     * @param {TElement} newElement The element that will replace the old element.
     * @param {EqualityComparator} comparator The comparator method that will be used to compare the equality of the elements.
     */
    static replaceAll<TElement>(sequence: IList<TElement> | Array<TElement>, oldElement: TElement, newElement: TElement, comparator?: EqualityComparator<TElement>): boolean;
    /**
     * Reverse the order of the elements in a given sequence. Reversing is done in place.
     * @param {IList|Array} sequence The sequence whose elements will be reversed.
     */
    static reverse<TElement>(sequence: IList<TElement> | Array<TElement>): void;
    /**
     * Rotation of the elements in a given sequence.
     *
     * Example:
     *  - If the sequence is [1, 2, 3, 4, 5] and the distance is 2, the result is [4, 5, 1, 2, 3].
     *  - If the sequence is [1, 2, 3, 4, 5] and the distance is -2, the result is [3, 4, 5, 1, 2].
     *
     * @param {IList|Array} sequence The sequence whose elements will be rotated.
     * @param {number} distance The distance of the rotation. This value can be positive or negative.
     */
    static rotate<TElement>(sequence: IList<TElement> | Array<TElement>, distance: number): void;
    /**
     * Shuffles the elements of a sequence. The elements are shuffled using the Fisher-Yates shuffle algorithm.
     *
     * <b>Note:</b> The result of the shuffling is not guaranteed to be different from the original sequence, especially if the size of the sequence is very small.
     * @param {IList|Array} sequence The sequence whose elements will be shuffled.
     */
    static shuffle<TElement>(sequence: IList<TElement> | Array<TElement>): void;
    /**
     * Swaps the elements of the list at the given indices.
     * @param {IList|Array} sequence The list or array whose two elements will be swapped
     * @param {number} firstIndex The first index of the swap operation
     * @param {number} secondIndex The second index of the swap operation
     * @throws {IndexOutOfBoundsException} If the given indices are out of bounds.
     */
    static swap<TElement>(sequence: IList<TElement> | Array<TElement>, firstIndex: number, secondIndex: number): void;
    private static binarySearchArray;
    private static binarySearchArrayCore;
    private static binarySearchList;
    private static binarySearchListCore;
    private static replaceAllArray;
    private static replaceAllList;
    private static rotateArray;
    private static rotateList;
}

/**
 * Returns all combinations of the elements of the sequence.
 * The outputs will not include duplicate combinations.
 * @template TElement
 * @param source The source iterable.
 * @param size The size of the combinations. If not specified, it will return all possible combinations.
 * @returns {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence whose elements are combinations of the source sequence.
 * @throws {InvalidArgumentException} If size is less than or equal to 0.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const combinationsOfTwo = letters.combinations(2)
 *          .select(c => c.toArray())
 *          .toArray();
 *      // combinationsOfTwo = [['a', 'b'], ['a', 'c'], ['b', 'c']]
 *
 *      const allCombinations = letters.combinations()
 *          .select(c => c.toArray())
 *          .toArray();
 *      // allCombinations = [['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']]
 */
export declare const combinations: <TElement>(source: Iterable<TElement>, size?: number) => IEnumerable<IEnumerable<TElement>>;

/**
 * Concatenates two sequences.
 * @template TElement
 * @param source The source iterable.
 * @param other The iterable sequence that will be concatenated to the first sequence.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements of both sequences.
 * @example
 *      const list1 = new List([1, 2]);
 *      const list2 = new List([3, 4]);
 *      const concatenated = list1.concat(list2).toArray();
 *      // concatenated = [1, 2, 3, 4]
 */
export declare const concat: <TElement>(source: Iterable<TElement>, other: Iterable<TElement>) => IEnumerable<TElement>;

/**
 * Determines whether the sequence contains the specified element.
 * @param source The source iterable.
 * @param element The element whose existence will be checked.
 * @param comparator The comparator function that will be used for equality comparison. If not provided, the default equality comparison is used.
 * @returns {boolean} true if the sequence contains the specified element; otherwise, false.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const hasThree = numbers.contains(3);
 *      // hasThree = true
 *      const hasTen = numbers.contains(10);
 *      // hasTen = false
 *
 *      // Using a custom comparator for objects
 *      interface Person { id: number; name: string; }
 *      const people = new List<Person>([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
 *      const bob = { id: 2, name: 'Bob' };
 *      const hasBobById = people.contains(bob, (p1, p2) => p1.id === p2.id);
 *      // hasBobById = true
 */
export declare const contains: <TElement>(source: Iterable<TElement>, element: TElement, comparator?: EqualityComparator<TElement>) => boolean;

/**
 * Returns the number of elements in the sequence.
 *
 * <b>Note:</b> If you want to check whether a sequence contains any elements, do not use <code>sequence.count() > 0</code>. Use <code>sequence.any()</code> instead.
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition.
 * @returns {number} The number of elements in the sequence.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *      const totalCount = numbers.count();
 *      // totalCount = 6
 *
 *      const evenCount = numbers.count(n => n % 2 === 0);
 *      // evenCount = 3
 */
export declare const count: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => number;

/**
 * Returns an enumerable sequence of key value pair objects that contain the key and the number of occurrences of the key in the source sequence.
 * @template TKey
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param comparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<KeyValuePair<TKey, number>>} A new enumerable sequence that contains key value pair objects.
 * @example
 *      const fruits = new List(['apple', 'banana', 'apple', 'orange', 'banana', 'apple']);
 *      const fruitCounts = fruits.countBy(fruit => fruit).toArray();
 *      // fruitCounts = [
 *      //   { key: 'apple', value: 3 },
 *      //   { key: 'banana', value: 2 },
 *      //   { key: 'orange', value: 1 }
 *      // ]
 *
 *      // Example with objects and key selector
 *      interface Item { type: string; }
 *      const items = new List<Item>([{ type: 'A' }, { type: 'B' }, { type: 'A' }]);
 *      const typeCounts = items.countBy(item => item.type).toArray();
 *      // typeCounts = [ { key: 'A', value: 2 }, { key: 'B', value: 1 } ]
 */
export declare const countBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>) => IEnumerable<KeyValuePair<TKey, number>>;

/**
 * Returns a new enumerable sequence that repeats the elements of the source sequence a specified number of times.
 * If the count is not specified, the sequence will be repeated indefinitely.
 * If the sequence is empty, an error will be thrown.
 * @template TElement
 * @param source The source iterable.
 * @param count The number of times the source sequence will be repeated.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that repeats the elements of the source sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const pattern = new List([1, 2]);
 *      const repeatedFinite = pattern.cycle(3).toArray();
 *      // repeatedFinite = [1, 2, 1, 2, 1, 2]
 *
 *      // Infinite cycle (use with caution, typically with take)
 *      // const repeatedInfinite = pattern.cycle();
 *      // const firstTen = repeatedInfinite.take(10).toArray();
 *      // firstTen = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
 *
 *      // Throws error if the source is empty
 *      // new List<number>().cycle(); // Throws NoElementsException
 */
export declare const cycle: <TElement>(source: Iterable<TElement>, count?: number) => IEnumerable<TElement>;

/**
 * Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
 * @template TElement
 * @param source The source iterable.
 * @param value The value to return if the sequence is empty. Defaults to null if not provided.
 * @returns {IEnumerable<TElement | null>} The specified sequence or the specified value in a singleton collection if the sequence is empty.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const resultNotEmpty = numbers.defaultIfEmpty(0).toArray();
 *      // resultNotEmpty = [1, 2, 3]
 *
 *      const emptyList = new List<number>();
 *      const resultEmptyWithDefault = emptyList.defaultIfEmpty(0).toArray();
 *      // resultEmptyWithDefault = [0]
 *
 *      const resultEmptyNull = emptyList.defaultIfEmpty().toArray(); // No value specified, defaults to null
 *      // resultEmptyNull = [null]
 */
export declare const defaultIfEmpty: <TElement>(source: Iterable<TElement>, value?: TElement | null) => IEnumerable<TElement | null>;

export declare class Dictionary<TKey, TValue> extends AbstractDictionary<TKey, TValue> {
    #private;
    constructor();
    constructor(iterable: Iterable<KeyValuePair<TKey, TValue>>, valueComparator?: EqualityComparator<TValue>);
    constructor(iterable: Iterable<[TKey, TValue]>, valueComparator?: EqualityComparator<TValue>);
    constructor(iterable: Iterable<KeyValuePair<TKey, TValue>> | Iterable<[TKey, TValue]>, valueComparator?: EqualityComparator<TValue>);
    [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    add(key: TKey, value: TValue): TValue;
    clear(): void;
    containsKey(key: TKey): boolean;
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    entries(): IterableIterator<[TKey, TValue]>;
    get(key: TKey): TValue | null;
    keys(): ISet<TKey>;
    remove(key: TKey): TValue | null;
    set(key: TKey, value: TValue): void;
    size(): number;
    values(): ICollection<TValue>;
    get length(): number;
}

/**
 * Returns distinct elements from the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains distinct elements from the source sequence.
 * @example
 *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
 *      const distinctNumbers = numbers.distinct().toArray();
 *      // distinctNumbers = [1, 2, 3, 4, 5]
 *
 *      // Using a custom comparator for objects (compares based on id)
 *      interface Item { id: number; value: string; }
 *      const items = new List<Item>([
 *          { id: 1, value: 'A' },
 *          { id: 2, value: 'B' },
 *          { id: 1, value: 'C' }
 *      ]);
 *      const distinctItemsById = items.distinct((a, b) => a.id === b.id).toArray();
 *      // distinctItemsById = [{ id: 1, value: 'A' }, { id: 2, value: 'B' }]
 */
export declare const distinct: <TElement>(source: Iterable<TElement>, keyComparator?: EqualityComparator<TElement>) => IEnumerable<TElement>;

/**
 * Returns distinct elements from the sequence based on a key selector.
 * @template TElement, TKey
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used for selecting a key which will be used for distinctness comparison.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains distinct elements from the source sequence.
 * @example
 *      interface Product { category: string; name: string; }
 *      const products = new List<Product>([
 *          { category: 'Electronics', name: 'Laptop' },
 *          { category: 'Books', name: 'TypeScript Guide' },
 *          { category: 'Electronics', name: 'Mouse' },
 *          { category: 'Books', name: 'Another Book' }
 *      ]);
 *
 *      // Get one product from each distinct category
 *      const distinctByCategory = products.distinctBy(p => p.category).toArray();
 *      // distinctByCategory might be:
 *      // [
 *      //   { category: 'Electronics', name: 'Laptop' },
 *      //   { category: 'Books', name: 'TypeScript Guide' }
 *      // ]
 *      // (The specific element kept from duplicates is the first one encountered)
 *
 *      // Using a custom key comparator (case-insensitive category)
 *      const productsMixedCase = new List<Product>([
 *          { category: 'Electronics', name: 'Laptop' },
 *          { category: 'electronics', name: 'Keyboard' },
 *          { category: 'Books', name: 'Guide' }
 *      ]);
 *      const distinctCaseInsensitive = productsMixedCase.distinctBy(
 *          p => p.category,
 *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase()
 *      ).toArray();
 *      // distinctCaseInsensitive might be:
 *      // [
 *      //   { category: 'Electronics', name: 'Laptop' },
 *      //   { category: 'Books', name: 'Guide' }
 *      // ]
 */
export declare const distinctBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>) => IEnumerable<TElement>;

/**
 * Returns the element at the specified index in the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param index The index of the element that will be returned.
 * @returns {TElement} The element at the specified index in the sequence.
 * @throws {IndexOutOfBoundsException} If index is less than 0 or greater than or equal to the number of elements in the sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c', 'd']);
 *      const secondLetter = letters.elementAt(1);
 *      // secondLetter = 'b'
 *
 *      try {
 *          letters.elementAt(4); // Throws IndexOutOfBoundsException
 *      } catch (e) {
 *          console.log(e.message); // Output: Index was outside the bounds of the sequence.
 *      }
 *
 *      try {
 *          letters.elementAt(-1); // Throws IndexOutOfBoundsException
 *      } catch (e) {
 *          console.log(e.message); // Output: Index was outside the bounds of the sequence.
 *      }
 */
export declare const elementAt: <TElement>(source: Iterable<TElement>, index: number) => TElement;

/**
 * Returns the element at the specified index in the sequence or a default value if the index is out of range.
 * @template TElement
 * @param source The source iterable.
 * @param index The index of the element that will be returned.
 * @returns {TElement|null} The element at the specified index in the sequence or null if the index is out of range.
 * @example
 *      const letters = new List(['a', 'b', 'c', 'd']);
 *      const secondLetter = letters.elementAtOrDefault(1);
 *      // secondLetter = 'b'
 *
 *      const fifthLetter = letters.elementAtOrDefault(4);
 *      // fifthLetter = null
 *
 *      const negativeIndex = letters.elementAtOrDefault(-1);
 *      // negativeIndex = null
 */
export declare const elementAtOrDefault: <TElement>(source: Iterable<TElement>, index: number) => TElement | null;

/**
 * Creates an empty sequence.
 *
 * @template TElement The type of elements in the sequence.
 * @returns {IEnumerable<TElement>} An empty sequence.
 */
export declare const empty: <TElement>() => IEnumerable<TElement>;

export declare class Enumerable<TElement> implements IEnumerable<TElement> {
    #private;
    private readonly iterable;
    constructor(iterable: Iterable<TElement>);
    /**
     * Creates an empty sequence.
     *
     * @template TElement The type of elements in the sequence.
     * @returns {IEnumerable<TElement>} An empty sequence.
     */
    static empty<TSource>(): IEnumerable<TSource>;
    /**
     * Creates an enumerable sequence from the given source.
     * @template TElement The type of elements in the sequence.
     * @param source The source iterable that will be converted to an enumerable sequence.
     * @returns {IEnumerable<TElement>} An enumerable sequence that contains the elements of the source.
     */
    static from<TSource>(source: Iterable<TSource>): IEnumerable<TSource>;
    /**
     * Creates a range of numbers starting from the specified start value and containing the specified count of elements.
     * @param {number} start The start value of the range.
     * @param {number} count The number of elements in the range.
     * @returns {IEnumerable<number>} An enumerable range of numbers.
     */
    static range(start: number, count: number): IEnumerable<number>;
    /**
     * Repeats the specified element a specified number of times.
     *
     * @template TElement The type of the element to repeat.
     * @param {TElement} element The element to repeat.
     * @param {number} count The number of times to repeat the element.
     * @returns {IEnumerable<TElement>} An Iterable representing the repeated elements.
     */
    static repeat<TSource>(element: TSource, count: number): IEnumerable<TSource>;
    [Symbol.iterator](): Iterator<TElement>;
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): TAccumulate | TResult;
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, TAccumulate>>;
    all(predicate: Predicate<TElement>): boolean;
    any(predicate?: Predicate<TElement>): boolean;
    append(element: TElement): IEnumerable<TElement>;
    average(selector?: Selector<TElement, number>): number;
    cast<TResult>(): IEnumerable<TResult>;
    chunk(size: number): IEnumerable<IEnumerable<TElement>>;
    combinations(size?: number): IEnumerable<IEnumerable<TElement>>;
    concat(iterable: Iterable<TElement>): IEnumerable<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    count(predicate?: Predicate<TElement>): number;
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, number>>;
    cycle(count?: number): IEnumerable<TElement>;
    defaultIfEmpty(value?: TElement | null): IEnumerable<TElement | null>;
    distinct(keyComparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    elementAt(index: number): TElement;
    elementAtOrDefault(index: number): TElement | null;
    except(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    exceptBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    first(predicate?: Predicate<TElement>): TElement;
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    forEach(action: IndexedAction<TElement>): void;
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<IGroup<TKey, TElement>>;
    groupJoin<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TResult>;
    index(): IEnumerable<[number, TElement]>;
    intersect(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    intersectBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    intersperse<TSeparator = TElement>(separator: TSeparator): IEnumerable<TElement | TSeparator>;
    join<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IEnumerable<TResult>;
    last(predicate?: Predicate<TElement>): TElement;
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    max(selector?: Selector<TElement, number>): number;
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    min(selector?: Selector<TElement, number>): number;
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    none(predicate?: Predicate<TElement>): boolean;
    ofType<TResult extends ObjectType>(type: TResult): IEnumerable<InferredType<TResult>>;
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    pairwise(resulSelector?: PairwiseSelector<TElement, TElement>): IEnumerable<[TElement, TElement]>;
    partition(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    permutations(size?: number): IEnumerable<IEnumerable<TElement>>;
    prepend(element: TElement): IEnumerable<TElement>;
    product(selector?: Selector<TElement, number>): number;
    reverse(): IEnumerable<TElement>;
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IEnumerable<TAccumulate>;
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IEnumerable<TResult>;
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IEnumerable<TResult>;
    sequenceEqual(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): boolean;
    shuffle(): IEnumerable<TElement>;
    single(predicate?: Predicate<TElement>): TElement;
    singleOrDefault(predicate?: Predicate<TElement>): TElement | null;
    skip(count: number): IEnumerable<TElement>;
    skipLast(count: number): IEnumerable<TElement>;
    skipWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    span(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    step(step: number): IEnumerable<TElement>;
    sum(selector?: Selector<TElement, number>): number;
    take(count: number): IEnumerable<TElement>;
    takeLast(count: number): IEnumerable<TElement>;
    takeWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    toArray(): TElement[];
    toCircularLinkedList(comparator?: EqualityComparator<TElement, TElement>): CircularLinkedList<TElement>;
    toDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): Dictionary<TKey, TValue>;
    toEnumerableSet(): EnumerableSet<TElement>;
    toImmutableDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    toImmutableList(comparator?: EqualityComparator<TElement>): ImmutableList<TElement>;
    toImmutablePriorityQueue(comparator?: OrderComparator<TElement>): ImmutablePriorityQueue<TElement>;
    toImmutableQueue(comparator?: EqualityComparator<TElement>): ImmutableQueue<TElement>;
    toImmutableSet(): ImmutableSet<TElement>;
    toImmutableSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    toImmutableSortedSet(comparator?: OrderComparator<TElement>): ImmutableSortedSet<TElement>;
    toImmutableStack(comparator?: EqualityComparator<TElement>): ImmutableStack<TElement>;
    toLinkedList(comparator?: EqualityComparator<TElement>): LinkedList<TElement>;
    toList(comparator?: EqualityComparator<TElement>): List<TElement>;
    toLookup<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>): ILookup<TKey, TValue>;
    toMap<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Map<TKey, TValue>;
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Record<TKey, TValue>;
    toPriorityQueue(comparator?: OrderComparator<TElement>): PriorityQueue<TElement>;
    toQueue(comparator?: EqualityComparator<TElement>): Queue<TElement>;
    toSet(): Set<TElement>;
    toSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): SortedDictionary<TKey, TValue>;
    toSortedSet(comparator?: OrderComparator<TElement>): SortedSet<TElement>;
    toStack(comparator?: EqualityComparator<TElement>): Stack<TElement>;
    union(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    unionBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    where(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    windows(size: number): IEnumerable<IEnumerable<TElement>>;
    zip<TSecond, TResult = [TElement, TSecond]>(iterable: Iterable<TSecond>, zipper?: Zipper<TElement, TSecond, TResult>): IEnumerable<[TElement, TSecond]> | IEnumerable<TResult>;
}

export declare class EnumerableSet<TElement> extends AbstractSet<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    add(element: TElement): boolean;
    clear(): void;
    contains(element: TElement): boolean;
    remove(element: TElement): boolean;
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    removeIf(predicate: Predicate<TElement>): boolean;
    retainAll(collection: Iterable<TElement>): boolean;
    size(): number;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

export declare class Enumerator<TElement> implements IOrderedEnumerable<TElement> {
    private readonly iterable;
    constructor(iterable: () => Iterable<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): TAccumulate | TResult;
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, TAccumulate>>;
    all(predicate: Predicate<TElement>): boolean;
    any(predicate?: Predicate<TElement>): boolean;
    append(element: TElement): IEnumerable<TElement>;
    average(selector?: Selector<TElement, number>): number;
    cast<TResult>(): IEnumerable<TResult>;
    chunk(size: number): IEnumerable<IEnumerable<TElement>>;
    combinations(size?: number): IEnumerable<IEnumerable<TElement>>;
    concat(iterable: Iterable<TElement>): IEnumerable<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    count(predicate?: Predicate<TElement>): number;
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, number>>;
    cycle(count?: number): IEnumerable<TElement>;
    defaultIfEmpty(value?: TElement | null): IEnumerable<TElement | null>;
    distinct(keyComparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    elementAt(index: number): TElement;
    elementAtOrDefault(index: number): TElement | null;
    except(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    exceptBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    first(predicate?: Predicate<TElement>): TElement;
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    forEach(action: IndexedAction<TElement>): void;
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<IGroup<TKey, TElement>>;
    groupJoin<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TResult>;
    index(): IEnumerable<[number, TElement]>;
    intersect(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    intersectBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    intersperse<TSeparator = TElement>(separator: TSeparator): IEnumerable<TElement | TSeparator>;
    join<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IEnumerable<TResult>;
    last(predicate?: Predicate<TElement>): TElement;
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    max(selector?: Selector<TElement, number>): number;
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    min(selector?: Selector<TElement, number>): number;
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    none(predicate?: Predicate<TElement>): boolean;
    ofType<TResult extends ObjectType>(type: TResult): IEnumerable<InferredType<TResult>>;
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    pairwise(resultSelector?: PairwiseSelector<TElement, TElement>): IEnumerable<[TElement, TElement]>;
    partition(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    permutations(size?: number): IEnumerable<IEnumerable<TElement>>;
    prepend(element: TElement): IEnumerable<TElement>;
    product(selector?: Selector<TElement, number>): number;
    reverse(): IEnumerable<TElement>;
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IEnumerable<TAccumulate>;
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IEnumerable<TResult>;
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IEnumerable<TResult>;
    sequenceEqual(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): boolean;
    shuffle(): IEnumerable<TElement>;
    single(predicate?: Predicate<TElement>): TElement;
    singleOrDefault(predicate?: Predicate<TElement>): TElement | null;
    skip(count: number): IEnumerable<TElement>;
    skipLast(count: number): IEnumerable<TElement>;
    skipWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    span(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    step(step: number): IEnumerable<TElement>;
    sum(selector?: Selector<TElement, number>): number;
    take(count: number): IEnumerable<TElement>;
    takeLast(count: number): IEnumerable<TElement>;
    takeWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    thenBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    thenByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    toArray(): TElement[];
    toCircularLinkedList(comparator?: EqualityComparator<TElement, TElement>): CircularLinkedList<TElement>;
    toDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): Dictionary<TKey, TValue>;
    toEnumerableSet(): EnumerableSet<TElement>;
    toImmutableDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    toImmutableList(comparator?: EqualityComparator<TElement>): ImmutableList<TElement>;
    toImmutablePriorityQueue(comparator?: OrderComparator<TElement>): ImmutablePriorityQueue<TElement>;
    toImmutableQueue(comparator?: EqualityComparator<TElement>): ImmutableQueue<TElement>;
    toImmutableSet(): ImmutableSet<TElement>;
    toImmutableSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    toImmutableSortedSet(comparator?: OrderComparator<TElement>): ImmutableSortedSet<TElement>;
    toImmutableStack(comparator?: EqualityComparator<TElement>): ImmutableStack<TElement>;
    toLinkedList(comparator?: EqualityComparator<TElement>): LinkedList<TElement>;
    toList(comparator?: EqualityComparator<TElement>): List<TElement>;
    toLookup<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>): ILookup<TKey, TValue>;
    toMap<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Map<TKey, TValue>;
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Record<TKey, TValue>;
    toPriorityQueue(comparator?: OrderComparator<TElement>): PriorityQueue<TElement>;
    toQueue(comparator?: EqualityComparator<TElement>): Queue<TElement>;
    toSet(): Set<TElement>;
    toSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): SortedDictionary<TKey, TValue>;
    toSortedSet(comparator?: OrderComparator<TElement>): SortedSet<TElement>;
    toStack(comparator?: EqualityComparator<TElement>): Stack<TElement>;
    union(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    unionBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    where(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    windows(size: number): IEnumerable<IEnumerable<TElement>>;
    zip<TSecond, TResult = [TElement, TSecond]>(iterable: Iterable<TSecond>, zipper?: Zipper<TElement, TSecond, TResult>): IEnumerable<[TElement, TSecond]> | IEnumerable<TResult>;
    private appendGenerator;
    private castGenerator;
    private chunkGenerator;
    private combinationsGenerator;
    private concatGenerator;
    private cycleGenerator;
    private defaultIfEmptyGenerator;
    private exceptByGenerator;
    private exceptGenerator;
    private groupByGenerator;
    private groupJoinGenerator;
    private indexGenerator;
    private intersectByGenerator;
    private intersectGenerator;
    private intersperseGenerator;
    private joinGenerator;
    private ofTypeGenerator;
    private pairwiseGenerator;
    private permutationsGenerator;
    private prependGenerator;
    private reverseGenerator;
    private scanGenerator;
    private selectGenerator;
    private selectManyGenerator;
    private shuffleGenerator;
    private skipGenerator;
    private skipLastGenerator;
    private skipWhileGenerator;
    private stepGenerator;
    private takeGenerator;
    private takeLastGenerator;
    private takeWhileGenerator;
    private unionByGenerator;
    private unionGenerator;
    private whereGenerator;
    private windowsGenerator;
    private zipGenerator;
}

export declare interface EqualityComparator<TFirst, TSecond = TFirst> {
    (e1: TFirst, e2: TSecond): boolean;
}

/**
 * Produces the set difference of two sequences by using the specified equality comparer or order comparer to compare values.
 * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
 * @template TElement
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements that also appear in the first sequence will be removed.
 * @param comparator The comparator function that will be used for item comparison. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set difference of the two sequences.
 * @example
 *      const numbers1 = new List([1, 2, 3, 4, 5]);
 *      const numbers2 = new List([3, 5, 6, 7]);
 *      const difference = numbers1.except(numbers2).toArray();
 *      // difference = [1, 2, 4]
 *
 *      // Using custom object comparison
 *      interface Item { id: number; }
 *      const items1 = new List<Item>([{ id: 1 }, { id: 2 }, { id: 3 }]);
 *      const items2 = new List<Item>([{ id: 2 }, { id: 4 }]);
 *      const itemDifference = items1.except(items2, (a, b) => a.id === b.id).toArray();
 *      // itemDifference = [{ id: 1 }, { id: 3 }]
 */
export declare const except: <TElement>(source: Iterable<TElement>, other: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>) => IEnumerable<TElement>;

/**
 * Produces the set difference of two sequences by using the specified key selector function to compare elements.
 * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
 * @template TElement, TKey
 * @typeParam TElement The type of the elements in the source sequence.
 * @typeParam TKey The type of the key that will be used for comparison.
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements that also appear in the first sequence will be removed.
 * @param keySelector The key selector function that will be used for selecting a key which will be used for comparison.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set difference of the two sequences.
 * @example
 *      interface Product { code: string; name: string; }
 *      const store1Products = new List<Product>([
 *          { code: 'A1', name: 'Apple' },
 *          { code: 'B2', name: 'Banana' },
 *          { code: 'C3', name: 'Cherry' }
 *      ]);
 *      const store2Products = new List<Product>([
 *          { code: 'B2', name: 'Banana' }, // Same code as store1
 *          { code: 'D4', name: 'Date' }
 *      ]);
 *
 *      // Find products in store1 whose codes are not in store2
 *      const uniqueToStore1 = store1Products.exceptBy(
 *          store2Products,
 *          p => p.code // Compare based on the 'code' property
 *      ).toArray();
 *      // uniqueToStore1 = [ { code: 'A1', name: 'Apple' }, { code: 'C3', name: 'Cherry' } ]
 *
 *      // Example with case-insensitive key comparison
 *      const listA = new List([{ val: 'a' }, { val: 'b' }]);
 *      const listB = new List([{ val: 'B' }, { val: 'c' }]);
 *      const diffCaseInsensitive = listA.exceptBy(
 *          listB,
 *          item => item.val,
 *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
 *      ).toArray();
 *      // diffCaseInsensitive = [ { val: 'a' } ]
 */
export declare const exceptBy: <TElement, TKey>(source: Iterable<TElement>, other: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>) => IEnumerable<TElement>;

/**
 * Gets the first element of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
 * @returns {TElement} The first element of the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @throws {NoMatchingElementException} If no element satisfies the condition.
 * @example
 *      const numbers = new List([10, 20, 30, 40]);
 *      const firstElement = numbers.first();
 *      // firstElement = 10
 *
 *      const firstGreaterThan25 = numbers.first(n => n > 25);
 *      // firstGreaterThan25 = 30
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.first(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 *
 *      try {
 *          numbers.first(n => n > 50); // Throws NoMatchingElementException
 *      } catch (e) {
 *          console.log(e.message); // Output: No element satisfies the condition.
 *      }
 */
export declare const first: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement;

/**
 * Gets the first element of the sequence or a default value if the no element satisfies the condition.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
 * @returns {TElement|null} The first element of the sequence or null if no element satisfies the condition or the sequence is empty.
 * @example
 *      const numbers = new List([10, 20, 30, 40]);
 *      const firstElement = numbers.firstOrDefault();
 *      // firstElement = 10
 *
 *      const firstGreaterThan25 = numbers.firstOrDefault(n => n > 25);
 *      // firstGreaterThan25 = 30
 *
 *      const firstGreaterThan50 = numbers.firstOrDefault(n => n > 50);
 *      // firstGreaterThan50 = null
 *
 *      const emptyList = new List<number>();
 *      const firstFromEmpty = emptyList.firstOrDefault();
 *      // firstFromEmpty = null
 */
export declare const firstOrDefault: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement | null;

/**
 * Iterates over the sequence and performs the specified action on each element.
 * @param source The source iterable.
 * @param action The action function that will be performed on each element. The second parameter of the action is the index.
 * @example
 *      const names = new List(['Alice', 'Bob', 'Charlie']);
 *      let output = '';
 *      names.forEach((name, index) => {
 *          output += `${index}: ${name}\n`;
 *      });
 *      // output:
 *      // 0: Alice
 *      // 1: Bob
 *      // 2: Charlie
 *
 *      // Note: forEach executes immediately and does not return a new sequence.
 */
export declare const forEach: <TElement>(source: Iterable<TElement>, action: IndexedAction<TElement>) => void;

/**
 * Creates an enumerable sequence from the given source.
 * @template TElement The type of elements in the sequence.
 * @param source The source iterable that will be converted to an enumerable sequence.
 * @returns {IEnumerable<TElement>} An enumerable sequence that contains the elements of the source.
 */
export declare const from: <TElement>(source: Iterable<TElement>) => IEnumerable<TElement>;

declare type FunctionType = Function | FunctionConstructor;

export declare class Group<TKey, TElement> extends Enumerable<TElement> implements IGroup<TKey, TElement> {
    readonly key: TKey;
    readonly source: IEnumerable<TElement>;
    constructor(key: TKey, source: IEnumerable<TElement>);
}

/**
 * Groups the elements of the sequence according to a specified key selector function.
 * @template TKey, TElement
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used for grouping.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<IGroup<TKey, TElement>>} A new enumerable sequence whose elements are groups that contain the elements of the source sequence.
 * @example
 *      interface Pet { name: string; species: string; age: number; }
 *      const pets = new List<Pet>([
 *          { name: 'Fluffy', species: 'Cat', age: 3 },
 *          { name: 'Buddy', species: 'Dog', age: 5 },
 *          { name: 'Whiskers', species: 'Cat', age: 2 },
 *          { name: 'Rex', species: 'Dog', age: 7 }
 *      ]);
 *
 *      // Group pets by species
 *      const groupsBySpecies = pets.groupBy(pet => pet.species).toArray();
 *      // groupsBySpecies will contain IGroup objects. Example structure:
 *      // [
 *      //   { key: 'Cat', source: Enumerable containing Fluffy and Whiskers },
 *      //   { key: 'Dog', source: Enumerable containing Buddy and Rex }
 *      // ]
 *
 *      // To get results as arrays:
 *      const speciesArrays = pets
 *          .groupBy(pet => pet.species)
 *          .select(group => ({ species: group.key, pets: group.source.toArray() }))
 *          .toArray();
 *      // speciesArrays = [
 *      //   { species: 'Cat', pets: [{ name: 'Fluffy', ... }, { name: 'Whiskers', ... }] },
 *      //   { species: 'Dog', pets: [{ name: 'Buddy', ... }, { name: 'Rex', ... }] }
 *      // ]
 *
 *      // Using a custom comparator (e.g., grouping ages into ranges)
 *      const ageGroups = pets.groupBy(
 *          pet => pet.age, // Temporary key selector
 *          (age1, age2) => Math.floor(age1 / 3) === Math.floor(age2 / 3) // Comparator: group by age range (0-2, 3-5, 6-8, etc.)
 *      )
 *          .select(group => ({ ageRangeKey: group.key, pets: group.source.toArray() })) // Note: group.key will be the first age encountered in that range
 *          .toArray();
 *      // ageGroups might look like:
 *      // [
 *      //   { ageRangeKey: 3, pets: [Fluffy, Whiskers] }, // Ages 3 and 2 fall in the same range (key is 3 as it was encountered first)
 *      //   { ageRangeKey: 5, pets: [Buddy, Rex] }      // Ages 5 and 7 fall in the same range (key is 5)
 *      // ]
 */
export declare const groupBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>) => IEnumerable<IGroup<TKey, TElement>>;

/**
 * Correlates the elements of two sequences based on equality of keys and groups the results.
 * The result contains elements from the first (outer) sequence and a collection of matching elements from the second (inner) sequence.
 * @template TInner, TKey, TResult, TElement
 * @param source The source iterable.
 * @param innerEnumerable The enumerable sequence to join to the first sequence.
 * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
 * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
 * @param resultSelector The result selector function that will be used to create a result element from an element from the first sequence and a collection of matching elements from the second sequence.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the group join operation.
 * @example
 *      interface Department { id: number; name: string; }
 *      interface Employee { name: string; deptId: number; }
 *
 *      const departments = new List<Department>([
 *          { id: 1, name: 'HR' },
 *          { id: 2, name: 'Engineering' },
 *          { id: 3, name: 'Sales' }
 *      ]);
 *
 *      const employees = new List<Employee>([
 *          { name: 'Alice', deptId: 2 },
 *          { name: 'Bob', deptId: 1 },
 *          { name: 'Charlie', deptId: 2 },
 *          { name: 'David', deptId: 4 } // Belongs to a non-listed department
 *      ]);
 *
 *      // Group employees by department
 *      const departmentEmployees = departments.groupJoin(
 *          employees,
 *          dept => dept.id, // Outer key selector (department ID)
 *          emp => emp.deptId, // Inner key selector (employee department ID)
 *          (dept, emps) => ({ // Result selector
 *              departmentName: dept.name,
 *              employees: emps.select(e => e.name).toArray() // Project employee names
 *          })
 *      ).toArray();
 *
 *      // departmentEmployees = [
 *      //   { departmentName: 'HR', employees: ['Bob'] },
 *      //   { departmentName: 'Engineering', employees: ['Alice', 'Charlie'] },
 *      //   { departmentName: 'Sales', employees: [] } // Sales has no matching employees
 *      // ]
 *      // Note: Employees in non-listed departments (David) are ignored as they don't match an outer key.
 */
export declare const groupJoin: <TElement, TInner, TKey, TResult>(source: Iterable<TElement>, innerEnumerable: Iterable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>) => IEnumerable<TResult>;

export declare class Heap<TElement> extends AbstractRandomAccessCollection<TElement> {
    #private;
    constructor();
    constructor(comparator: OrderComparator<TElement>);
    constructor(comparator: OrderComparator<TElement> | null, iterable: Iterable<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds an element to the heap.
     * @template TElement The type of elements in the heap.
     * @param element The element to add.
     * @returns true
     */
    add(element: TElement): boolean;
    /**
     * Adds all elements from the specified collection to the heap.
     * @template TSource The type of elements in the collection.
     * @param {Iterable<TSource>} collection The collection of elements to add.
     * @returns true if the heap was modified; otherwise, false.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Clears the heap.
     */
    clear(): void;
    contains(element: TElement): boolean;
    containsAll(collection: Iterable<TElement>): boolean;
    isEmpty(): boolean;
    /**
     * Retrieves the element at the root of the heap without removing it.
     * @template TElement The type of elements in the heap.
     * @returns {TElement|null} The element at the root of the heap, or null if the heap is empty.
     */
    peek(): TElement | null;
    /**
     * Retrieves the element at the root of the heap and removes it.
     * @template TElement The type of elements in the heap.
     * @returns {TElement|null} The element at the root of the heap, or null if the heap is empty.
     */
    poll(): TElement | null;
    /**
     * Removes the specified element from the heap.
     * @template TElement The type of elements in the heap.
     * @param {TElement} element The element to remove.
     * @returns true if the element was removed; otherwise, false.
     */
    remove(element: TElement): boolean;
    /**
     * Removes all elements in the specified collection from the heap.
     * @template TSource The type of elements that will be removed from the heap.
     * @param {Iterable<TSource>} collection The collection of elements to remove.
     * @returns true if any elements were removed; otherwise, false.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Removes all elements from the heap that satisfy the specified predicate.
     * @param predicate The predicate used to determine which elements to remove.
     * @returns true if any elements were removed; otherwise, false.
     */
    removeIf(predicate: Predicate<TElement>): boolean;
    /**
     * Returns the number of elements in the heap.
     * @returns The number of elements in the heap.
     */
    size(): number;
    /**
     * Builds a heap from the current elements in O(n) time.
     * This is more efficient than adding elements one by one, which takes O(n log n) time.
     * @private
     */
    private buildHeap;
    get comparator(): OrderComparator<TElement>;
    /**
     * Returns the number of elements in the heap.
     * @returns The number of elements in the heap.
     */
    get length(): number;
    private getLeftChildIndex;
    private getLeftChildValue;
    private getParentIndex;
    private getParentValue;
    private getRightChildIndex;
    private getRightChildValue;
    private hasLeftChild;
    private hasRightChild;
    private heapifyDown;
    private heapifyUp;
}

export declare interface IAsyncEnumerable<TElement> extends AsyncIterable<TElement> {
    /**
     * Applies an accumulator function over the sequence. If seed is specified, it is used as the initial value.
     * If resultSelector function is specified, it will be used to select the result value.
     * @param accumulator The accumulator function that will be applied over the sequence.
     * @param seed The value that will be used as the initial value. If not specified, first element of the sequence will be used as seed value.
     * @param resultSelector The function that will be used to select the result value.
     * @throws {NoElementsException} If the source is empty and seed is not provided.
     */
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): Promise<TAccumulate | TResult>;
    /**
     * Groups the elements of the sequence according to a specified key selector function and applies an accumulator function over each group.
     * @template TKey The type of the key returned by the key selector.
     * @template TAccumulate The type of the accumulated value.
     * @param keySelector The key selector function that will be used for selecting the key for each element.
     * @param seedSelector The seed selector function that will be used to get the initial value for each group, or a constant value that will be used as the initial value for all groups.
     * @param accumulator The accumulator function that will be applied over each group.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     * @returns {IAsyncEnumerable<KeyValuePair<TKey, TAccumulate>>} An enumerable sequence of key-value pairs, where each key is a unique key from the source sequence and each value is the accumulated value for that key.
     */
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<KeyValuePair<TKey, TAccumulate>>;
    /**
     * Determines if all elements of the sequence satisfy the specified predicate.
     * @param predicate The predicate function that will be used to check each element for a condition.
     */
    all(predicate: Predicate<TElement>): Promise<boolean>;
    /**
     * Determines if any element of the sequence satisfies the specified predicate.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, it will return true if sequence has elements, otherwise false.
     */
    any(predicate?: Predicate<TElement>): Promise<boolean>;
    /**
     * Appends the specified element to the end of the sequence.
     * @param element The element that will be appended to the end of the sequence
     */
    append(element: TElement): IAsyncEnumerable<TElement>;
    /**
     * Computes the average of the sequence. The sequence should be either a sequence consisting of numbers, or an appropriate selector function should be provided.
     * @param selector The selector function that will select a numeric value from the sequence elements.
     * @throws {NoElementsException} If the source is empty.
     */
    average(selector?: Selector<TElement, number>): Promise<number>;
    /**
     * Casts the elements of the sequence to the specified type.
     * @template TResult
     * @returns {IAsyncEnumerable<TResult>} The elements of the sequence cast to the specified type.
     */
    cast<TResult>(): IAsyncEnumerable<TResult>;
    /**
     * Splits the elements of the sequence into chunks of size at most the specified size.
     * @param size The maximum size of each chunk.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     */
    chunk(size: number): IAsyncEnumerable<IEnumerable<TElement>>;
    /**
     * Returns all combinations of the elements of the sequence.
     * The outputs will not include duplicate combinations.
     * @template TElement
     * @param size The size of the combinations. If not specified, it will return all possible combinations.
     * @returns {IAsyncEnumerable<IEnumerable<TElement>>} A new enumerable sequence whose elements are combinations of the source sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     */
    combinations(size?: number): IAsyncEnumerable<IEnumerable<TElement>>;
    /**
     * Concatenates two sequences.
     * @param other The enumerable sequence that will be concatenated to the first sequence.
     */
    concat(other: AsyncIterable<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Determines where the sequence contains the specified element.
     * @param element The element whose existence will be checked.
     * @param comparator The comparator function that will be used for equality comparison. If not provided, default equality comparison is used.
     */
    contains(element: TElement, comparator?: EqualityComparator<TElement>): Promise<boolean>;
    /**
     * Returns the number of elements in the sequence.
     *
     * <b>Note:</b> If you want to check whether a sequence contains any elements, do not use <code>sequence.count() > 0</code>. Use <code>sequence.any()</code> instead.
     * @param predicate The predicate function that will be used to check each element for a condition.
     */
    count(predicate?: Predicate<TElement>): Promise<number>;
    /**
     * Returns an enumerable sequence of key value pair objects that contain the key and the number of occurrences of the key in the source sequence.
     * @template TKey
     * @param keySelector The key selector function that will be used for selecting the key for each element.
     * @param comparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     * @returns {IAsyncEnumerable<KeyValuePair<TKey, number>>} An enumerable sequence of key value pair objects that contain the key and the number of occurrences of the key in the source sequence.
     */
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IAsyncEnumerable<KeyValuePair<TKey, number>>;
    /**
     * Returns a new enumerable sequence that repeats the elements of the source sequence a specified number of times.
     * If count is not specified, the sequence will be repeated indefinitely.
     * If the sequence is empty, an error will be thrown.
     * @template TElement
     * @param count The number of times the source sequence will be repeated.
     * @returns {IAsyncEnumerable<TElement>} A new enumerable sequence that repeats the elements of the source sequence.
     * @throws {NoElementsException} If the source is empty.
     */
    cycle(count?: number): IAsyncEnumerable<TElement>;
    /**
     * Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
     * @param defaultValue The value to return if the sequence is empty.
     */
    defaultIfEmpty(defaultValue?: TElement | null): IAsyncEnumerable<TElement | null>;
    /**
     * Returns distinct elements from the sequence.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     */
    distinct(keyComparator?: EqualityComparator<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Returns distinct elements from the sequence based on a key selector function.
     * @template TKey The type of the key returned by the key selector.
     * @param keySelector The key selector function that will be used for selecting the key for each element.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     * @returns {IAsyncEnumerable<TElement>} A new enumerable sequence that contains distinct elements from the source sequence based on the key selector.
     */
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<TElement>;
    /**
     * Returns the element at the specified index in the sequence.
     * @param index The index of the element that will be returned.
     * @throws {IndexOutOfBoundsException} If index is less than 0 or greater than or equal to the number of elements in the sequence.
     * @throws {NoSuchElementException} If the source is empty.
     */
    elementAt(index: number): Promise<TElement>;
    /**
     * Returns the element at the specified index in the sequence or a default value if the index is out of range.
     * @param index The index of the element that will be returned.
     */
    elementAtOrDefault(index: number): Promise<TElement | null>;
    /**
     * Produces the set difference of two sequences by using the specified equality comparer or order comparer to compare values.
     *
     * About the difference between comparator and orderComparator:
     * - If both comparator and orderComparator are specified, the order comparator will be used for internal operations.
     * - If only one of the comparators is specified, the specified comparator will be used for internal operations.
     * - If no comparator is specified, it will use the <b>default equality</b> comparer.
     *
     * If the elements of the enumerable can be sorted, it is advised to use the orderComparator due to its better performance.
     *
     * Example:
     * ```
     *     var numberList1 = new List([1, 2, 2, 3, 3, 3, 4, 5]);
     *     var numberList2 = new List([2, 5, 5, 6, 7, 8, 8]);
     *     var result = numberList1.except(numberList2).toArray(); // [1, 3, 4]
     * ```
     * @param enumerable The enumerable sequence whose distinct elements that also appear in the first sequence will be removed.
     * @param comparator The comparator function that will be used for equality comparison. If not provided, default equality comparison is used.
     * @throws {Error} If the enumerable is null or undefined.
     */
    except(enumerable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Produces the set difference of two sequences by using the specified key selector function and comparator.
     * @template TKey The type of the key returned by the key selector.
     * @param enumerable The enumerable sequence whose distinct elements that also appear in the first sequence will be removed.
     * @param keySelector The key selector function that will be used for selecting the key for each element.
     * @param comparator The comparator function that will be used for equality comparison or order comparison of selected keys. If not provided, default equality comparison is used.
     * @returns {IAsyncEnumerable<TElement>} A sequence that contains the set difference of the elements from the source sequence and the enumerable sequence.
     * @throws {Error} If the enumerable is null or undefined.
     */
    exceptBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IAsyncEnumerable<TElement>;
    /**
     * Gets the first element of the sequence.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
     * @throws {NoElementsException} If the source is empty.
     * @throws {NoMatchingElementException} If no element satisfies the condition.
     */
    first(predicate?: Predicate<TElement>): Promise<TElement>;
    /**
     * Gets the first element of the sequence or a default value if the no element satisfies the condition.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
     */
    firstOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    /**
     * Iterates over the sequence and performs the specified action on each element.
     * @param action The action function that will be performed on each element.
     */
    forEach(action: IndexedAction<TElement>): Promise<void>;
    /**
     * Groups the elements of the sequence according to a specified key selector function.
     * @param keySelector The key selector function that will be used for grouping.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     */
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<IGroup<TKey, TElement>>;
    /**
     * Correlates the elements of two sequences based on equality of keys and groups the results.
     * @param inner The enumerable sequence to join to the first sequence.
     * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
     * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
     * @param resultSelector The result selector function that will be used to create a result element from an element from the first sequence and a collection of matching elements from the second sequence.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     */
    groupJoin<TInner, TKey, TResult>(inner: IAsyncEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IAsyncEnumerable<TResult>;
    /**
     * Returns an enumerable of tuples, each containing the index and the element from the source sequence.
     * @template TElement
     * @returns {IAsyncEnumerable<[number, TElement]>} An enumerable of tuples, each containing the index and the element from the source sequence.
     */
    index(): IAsyncEnumerable<[number, TElement]>;
    /**
     * Produces the set intersection of two sequences by using the specified equality comparer or order comparer to compare values.
     *
     * About the difference between comparator and orderComparator:
     * - If both comparator and orderComparator are specified, the order comparator will be used for internal operations.
     * - If only one of the comparators is specified, the specified comparator will be used for internal operations.
     * - If no comparator is specified, it will use the <b>default equality</b> comparer.
     *
     * If the elements of the enumerable can be sorted, it is advised to use the orderComparator due to its better performance.
     *
     * Example:
     * ```
     *     var numberList1 = new List([1, 2, 2, 3, 3, 3, 4, 5]);
     *     var numberList2 = new List([2, 5, 5, 6, 7, 8, 8]);
     *     var result = numberList1.except(numberList2).toArray(); // [2, 5]
     * ```
     * @param enumerable The enumerable sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param comparator The comparator function that will be used for equality comparison. If not provided, default equality comparison is used.
     * @throws {Error} If the enumerable is null or undefined.
     */
    intersect(enumerable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Produces the set intersection of two sequences by using the specified key selector function and comparator.
     * @template TKey The type of the key returned by the key selector.
     * @param enumerable The enumerable sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param keySelector The key selector function that will be used for selecting the key for each element.
     * @param comparator The comparator function that will be used for equality comparison or order comparison of selected keys. If not provided, default equality comparison is used.
     * @returns {IAsyncEnumerable<TElement>} A sequence that contains the elements that form the set intersection of the source sequence and the enumerable sequence.
     * @throws {Error} If the enumerable is null or undefined.
     */
    intersectBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IAsyncEnumerable<TElement>;
    /**
     * Intersperses a specified element between each element of the sequence.
     * @template TElement, TSeparator
     * @param separator The element that will be interspersed between each element of the sequence.
     * @returns {IAsyncEnumerable<TElement|TSeparator>} A new enumerable sequence whose elements are the elements of the source sequence interspersed with the specified element.
     */
    intersperse<TSeparator = TElement>(separator: TSeparator): IAsyncEnumerable<TElement | TSeparator>;
    /**
     * Correlates the elements of two sequences based on equality of keys
     * @param inner The enumerable sequence to join to the first sequence.
     * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
     * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
     * @param resultSelector The result selector function that will be used to create a result element from two matching elements.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, default equality comparison is used.
     * @param leftJoin If true, the result sequence will have the value of null for unmatched inner elements.
     */
    join<TInner, TKey, TResult>(inner: IAsyncEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IAsyncEnumerable<TResult>;
    /**
     * Returns the last element of the sequence.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
     * @throws {NoElementsException} If the source is empty.
     * @throws {NoMatchingElementException} If no element satisfies the condition.
     */
    last(predicate?: Predicate<TElement>): Promise<TElement>;
    /**
     * Returns the last element of the sequence or a default value if the no element satisfies the condition.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
     * @throws {Error} If the source is null or undefined.
     */
    lastOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    /**
     * Returns the maximum value in the sequence.
     * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
     * @throws {NoElementsException} If the source is empty.
     */
    max(selector?: Selector<TElement, number>): Promise<number>;
    /**
     * Returns the element with the maximum value that is obtained by applying the key selector function to each element in the sequence.
     * @template TElement
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, default order comparison will be used.
     * @returns {Promise<TElement>} The element with the maximum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     */
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): Promise<TElement>;
    /**
     * Returns the minimum value in the sequence.
     * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
     * @throws {NoElementsException} If the source is empty.
     */
    min(selector?: Selector<TElement, number>): Promise<number>;
    /**
     * Returns the element with the minimum value that is obtained by applying the key selector function to each element in the sequence.
     * @template TElement
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, default order comparison will be used.
     * @returns {Promise<TElement>} The element with the minimum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     */
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): Promise<TElement>;
    /**
     * Determines whether no elements of the sequence satisfy the specified predicate.
     * If no predicate is specified, it will return true if the sequence is empty.
     * @param predicate The predicate function that will be used to check each element for a condition.
     * @returns {Promise<boolean>} true if no elements of the sequence satisfy the specified predicate; otherwise, false.
     */
    none(predicate?: Predicate<TElement>): Promise<boolean>;
    /**
     * Returns the elements that are of the specified type.
     * The type can be specified either as a constructor function or as a string.
     * @template TResult
     * @param type The type to filter the elements of the sequence with.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are of the specified type.
     */
    ofType<TResult extends ObjectType>(type: TResult): IAsyncEnumerable<InferredType<TResult>>;
    /**
     * Sorts the elements of a sequence in ascending order by using a specified comparer.
     * @param keySelector The key selector function that will be used for selecting the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, default order comparison will be used.
     */
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
    /**
     * Sorts the elements of a sequence in descending order by using a specified comparer.
     * @param keySelector The key selector function that will be used for selecting the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, default order comparison will be used.
     */
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
    /**
     * Produces a tuple of the element and the following element.
     * @param resultSelector The result selector function that will be used to create a result element from the current and the following element.
     *
     * <br/>
     * Example:
     * ```
     *    const numberList = new List([1, 2, 3, 4, 5]);
     *    const result = numberList.pairwise((current, next) => current + "-" + next).toArray(); // [1-2, 2-3, 3-4, 4-5]
     * ```
     */
    pairwise(resultSelector: PairwiseSelector<TElement, TElement>): IAsyncEnumerable<[TElement, TElement]>;
    /**
     * Produces a tuple of two enumerable sequences, the first one containing the elements that satisfy the condition, and the second one containing the rest of the elements.
     * @param predicate The predicate function that will be used to check each element for a condition.
     */
    partition(predicate: Predicate<TElement>): Promise<[IEnumerable<TElement>, IEnumerable<TElement>]>;
    /**
     * Returns an enumerable sequence of permutations, each containing a permutation of the elements of the source sequence.
     * @template TElement
     * @param size If specified, it will return only the permutations of the specified size.
     * If not specified, it will return permutations of the size of the source sequence.
     * @returns {IAsyncEnumerable<IEnumerable<TElement>>} An enumerable of enumerable sequences, each containing a permutation of the elements of the source sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     */
    permutations(size?: number): IAsyncEnumerable<IEnumerable<TElement>>;
    /**
     * Adds a value to the beginning of the sequence.
     * @param element The element to add to the sequence.
     */
    prepend(element: TElement): IAsyncEnumerable<TElement>;
    /**
     * Computes the product of the sequence.
     * @param selector The selector function that will be used to select a numeric value from the sequence elements.
     * @returns {Promise<number>} The product of the sequence.
     */
    product(selector?: Selector<TElement, number>): Promise<number>;
    /**
     * Inverts the order of the elements in the sequence.
     */
    reverse(): IAsyncEnumerable<TElement>;
    /**
     * Applies an accumulator function over the sequence and yields the result of each intermediate computation.
     * If seed is specified, it is used as the initial value for the accumulator; but it is not included in the result.
     * @param accumulator The accumulator function that will be applied over the sequence.
     * @param seed The value that will be used as the initial value. If not specified, first element of the sequence will be used as seed value.
     * @throws {NoElementsException} If the source is empty and seed is not provided.
     */
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IAsyncEnumerable<TAccumulate>;
    /**
     * Projects each element of a sequence into a new form.
     * @param selector The selector function that will be used to project each element into a new form.
     */
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IAsyncEnumerable<TResult>;
    /**
     * Projects each element of a sequence into a new form and flattens the resulting sequences into one sequence.
     * @param selector The selector function that will be used to project each element into a new form.
     */
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IAsyncEnumerable<TResult>;
    /**
     * Determines whether two sequences are equal by comparing the elements by using an equality comparer for their type.
     * @param enumerable The enumerable sequence to compare to the source sequence.
     * @param comparator The equality comparer that will be used to compare the elements. If not specified, default equality comparer will be used.
     */
    sequenceEqual(enumerable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement>): Promise<boolean>;
    /**
     * Returns a new enumerable sequence whose elements are shuffled.
     */
    shuffle(): IAsyncEnumerable<TElement>;
    /**
     * Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the only element of the sequence will be returned.
     * @throws {NoElementsException} If the source is empty.
     * @throws {MoreThanOneElementException} If the source contains more than one element.
     * @throws {NoMatchingElementException} If no element satisfies the condition.
     * @throws {MoreThanOneMatchingElementException} If more than one element satisfies the condition.
     */
    single(predicate?: Predicate<TElement>): Promise<TElement>;
    /**
     * Returns the only element of a sequence, or a default value if the sequence is empty. This method throws an exception if there is more than one element in the sequence.
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the only element of the sequence will be returned.
     * @throws {MoreThanOneElementException} If the source contains more than one element.
     * @throws {MoreThanOneMatchingElementException} If more than one element satisfies the condition.
     */
    singleOrDefault(predicate?: Predicate<TElement>): Promise<TElement | null>;
    /**
     * Bypasses a specified number of elements in a sequence and then returns the remaining elements.
     * @param count The number of elements to skip before returning the remaining elements.
     */
    skip(count: number): IAsyncEnumerable<TElement>;
    /**
     * Returns a new enumerable sequence that contains the elements from source with the last count elements of the source sequence omitted.
     * @param count The number of elements to omit from the end of the collection.
     */
    skipLast(count: number): IAsyncEnumerable<TElement>;
    /**
     * Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
     * @param predicate The predicate function that will be used to test each element.
     */
    skipWhile(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Splits the sequence into two sequences based on a predicate.
     * The first sequence contains the elements from the start of the input sequence that satisfy the predicate,
     * and it continues until the predicate no longer holds.
     * The second sequence contains the remaining elements.
     * @template TElement
     * @param predicate The predicate function that will be used to test each element.
     * @returns {Promise<[IEnumerable<TElement>, IEnumerable<TElement>]>} A tuple of two enumerable sequences, the first one containing the elements that satisfy the condition,
     * and the second one containing the rest of the elements regardless of the condition.
     */
    span(predicate: Predicate<TElement>): Promise<[IEnumerable<TElement>, IEnumerable<TElement>]>;
    /**
     * Skips elements in a sequence according to a specified step size.
     *
     * Example:
     * ```typescript
     *    const numberList = new List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
     *    const result = numberList.step(2).toArray(); // [1, 3, 5, 7, 9]
     *    const result2 = numberList.step(3).toArray(); // [1, 4, 7, 10]
     * ```
     * @template TElement
     * @param step The number of elements to skip between each element.
     * @returns {IAsyncEnumerable<TElement>} A new enumerable sequence that contains the elements from the input sequence with the elements skipped according to the specified step size.
     */
    step(step: number): IAsyncEnumerable<TElement>;
    /**
     * Returns the sum of the values in the sequence.
     * @param selector The selector function that will be used to select the value to sum. If not specified, the value itself will be used.
     * @throws {NoElementsException} If the source is empty.
     */
    sum(selector?: Selector<TElement, number>): Promise<number>;
    /**
     * Returns a specified number of contiguous elements from the start of a sequence.
     * @param count The number of elements to return.
     */
    take(count: number): IAsyncEnumerable<TElement>;
    /**
     * Returns a specified number of contiguous elements from the end of a sequence.
     * @param count The number of elements to return.
     */
    takeLast(count: number): IAsyncEnumerable<TElement>;
    /**
     * Returns elements from a sequence as long as a specified condition is true and then skips the remaining elements.
     * @param predicate The predicate function that will be used to test each element.
     */
    takeWhile(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Creates a new array from the elements of the sequence.
     */
    toArray(): Promise<TElement[]>;
    /**
     * Converts this enumerable to an object.
     * @template TKey
     * @template TValue
     * @param keySelector The selector that will be used to select the property that will be used as the key of the object. Can only be a string, number or symbol.
     * @param valueSelector The selector that will be used to select the property that will be used as the value of the object.
     * @returns {Promise<Record<TKey, TValue>>} An object that contains the elements of the sequence.
     */
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Promise<Record<TKey, TValue>>;
    /**
     * Produces the set union of two sequences by using an equality comparer.
     * @param enumerable The enumerable sequence whose distinct elements form the second set for the union.
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, default equality comparer will be used.
     */
    union(enumerable: AsyncIterable<TElement>, comparator?: EqualityComparator<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Produces the set union of two sequences by using the specified key selector function.
     * @param enumerable The enumerable sequence whose distinct elements form the second set for the union.
     * @param keySelector The key selector function that will be used to select the key for each element.
     * @param comparator The equality comparator function that will be used to compare two keys. If not specified, default equality comparer will be used.
     */
    unionBy<TKey>(enumerable: AsyncIterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IAsyncEnumerable<TElement>;
    /**
     * Filters a sequence of values based on a predicate.
     * @param predicate The predicate function that will be used to test each element.
     */
    where(predicate: IndexedPredicate<TElement>): IAsyncEnumerable<TElement>;
    /**
     * Returns an enumerable sequence of windows of the specified size.
     * If the size is less than or equal to 0, an error will be thrown.
     * If the size is greater than the number of elements in the sequence, an empty sequence will be returned.
     *
     * The windows will overlap, meaning that each element will be included in multiple windows.
     *
     * Example:
     * ```typescript
     *   const numberList = new List([1, 2, 3, 4, 5]);
     *   const result = numberList.windows(3).toArray(); // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
     *   const result2 = numberList.windows(1).toArray(); // [[1], [2], [3], [4], [5]]
     *   const result3 = numberList.windows(5).toArray(); // [[1, 2, 3, 4, 5]]
     *   const result4 = numberList.windows(6).toArray(); // []
     *   const result5 = numberList.windows(0).toArray(); // Error
     *   const result6 = numberList.windows(-1).toArray(); // Error
     * ```
     * @template TElement
     * @param size The size of the windows.
     * @returns {IAsyncEnumerable<IEnumerable<TElement>>} A new enumerable sequence that contains the specified number of elements from the start of the input sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     */
    windows(size: number): IAsyncEnumerable<IEnumerable<TElement>>;
    /**
     * Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
     * @param iterable The iterable sequence to merge with the first sequence.
     */
    zip<TSecond>(iterable: AsyncIterable<TSecond>): IAsyncEnumerable<[TElement, TSecond]>;
    /**
     * Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
     * @param iterable The iterable sequence to merge with the first sequence.
     * @param zipper The function that specifies how to merge the elements from the two sequences. If this is not specified, the merge result will be a tuple of two elements.
     */
    zip<TSecond, TResult = [TElement, TSecond]>(iterable: AsyncIterable<TSecond>, zipper: Zipper<TElement, TSecond, TResult>): IAsyncEnumerable<TResult>;
}

export declare interface ICollection<TElement> extends IReadonlyCollection<TElement> {
    /**
     * Adds the element to the collection.
     * @param element The element that will be added to the collection
     * @returns {boolean} true if item is added, false otherwise.
     */
    add(element: TElement): boolean;
    /**
     * Add all items from the provided collection to this collection
     * @param collection The collection whose element will be added
     *                   to this collection.
     * @returns {boolean} true if this collection is changed.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Remove all elements from this collection.
     */
    clear(): void;
    /**
     * Returns a readonly collection that is backed by this collection.
     * @returns {IReadonlyCollection} A readonly collection that is backed by this collection.
     */
    toReadonlyCollection(): IReadonlyCollection<TElement>;
}

export declare interface ICollectionChangedEventArgs<TElement> {
    action: CollectionChangedAction;
    newItems: ReadonlyList<TElement>;
    oldItems: ReadonlyList<TElement>;
}

export declare interface IDictionary<TKey, TValue> extends IReadonlyDictionary<TKey, TValue> {
    /**
     * Adds the specified key and value to the dictionary.
     * @param key The key of the element to add.
     * @param value The value of the element to add. It can be <code>null</code>.
     * @returns The added value.
     * @throws {InvalidArgumentException} If the key already exists in the dictionary.
     */
    add(key: TKey, value: TValue): TValue;
    /**
     * Removes all elements from this dictionary.
     */
    clear(): void;
    /**
     * Adds a value to the dictionary.
     * If the key already exists, the value associated with the key will be replaced by the new value.
     * @param key The key of the element to add or update.
     * @param value The value which will be added or updated.
     * @returns The old value of the key, or null if there was no mapping for the key.
     * @throws {Error} If the key is null
     */
    put(key: TKey, value: TValue): void;
    /**
     * Removes the specified key and its associated value from this dictionary.
     * @param key They key whose itself and its associated value will be removed from the dictionary.
     * @returns The removed value or null if the key does not exist in the dictionary.
     */
    remove(key: TKey): TValue | null;
    /**
     * Sets the value of the given key.
     * @param key The key whose value will be set.
     * @param value The new value of the key
     * @throws {KeyNotFoundException} If the key does not exist in the dictionary.
     */
    set(key: TKey, value: TValue): void;
    /**
     * Attempts to add the specified key and value to the dictionary. Unlike `add` method,
     * it will not throw an error if key already exists.
     * @param key The key of the element to add.
     * @param value The value of the element to add. It can be <code>null</code>.
     * @returns {boolean} true if key-value pair is added; false otherwise.
     */
    tryAdd(key: TKey, value: TValue): boolean;
}

export declare interface IEnumerable<TElement> extends Iterable<TElement> {
    /**
     * Applies an accumulator function over the sequence. If seed is specified, it is used as the initial value.
     * If the resultSelector function is specified, it will be used to select the result value.
     * @template TAccumulate
     * @template TResult
     * @param accumulator The accumulator function that will be applied over the sequence.
     * @param seed The value that will be used as the initial value. If not specified, the first element of the sequence will be used as the seed value.
     * @param resultSelector The function that will be used to select the result value.
     * @returns {TAccumulate|TResult} The final accumulator value.
     * @throws {NoElementsException} If the source is empty and seed is not provided.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const sum = numbers.aggregate((acc, current) => acc + current); // sum = 15
     *      const productWithSeed = numbers.aggregate((acc, current) => acc * current, 10); // productWithSeed = 1200 (10 * 1 * 2 * 3 * 4 * 5)
     *      const sumAsString = numbers.aggregate(
     *          (acc, current) => acc + current, // Accumulator
     *          0,                             // Seed
     *          (finalResult) => `Sum: ${finalResult}` // Result selector
     *      ); // sumAsString = "Sum: 15"
     */
    aggregate<TAccumulate = TElement, TResult = TAccumulate>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate, resultSelector?: Selector<TAccumulate, TResult>): TAccumulate | TResult;
    /**
     * Applies an accumulator function over the sequence, grouping the results by a key from the key selector function.
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param seedSelector Initial accumulator value or a function that will be used to select the initial accumulator value.
     * @param accumulator The accumulator function that will be applied over the sequence.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @example
     *      interface Product { category: string; price: number; }
     *      const products = new List<Product>([
     *           { category: 'Electronics', price: 700 },
     *           { category: 'Books', price: 120 },
     *           { category: 'Electronics', price: 200 },
     *           { category: 'Books', price: 90 }
     *      ]);
     *      const totalValuePerCategory = products.aggregateBy(
     *           p => p.category, // keySelector: group by category
     *           0,               // seedSelector: start sum at 0 for each category
     *           (sum, p) => sum + p.price // accumulator: add product price to sum
     *      ).toArray();
     *      // totalValuePerCategory: [{ key: 'Electronics', value: 900 }, { key: 'Books', value: 210 }]
     */
    aggregateBy<TKey, TAccumulate = TElement>(keySelector: Selector<TElement, TKey>, seedSelector: Selector<TKey, TAccumulate> | TAccumulate, accumulator: Accumulator<TElement, TAccumulate>, keyComparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, TAccumulate>>;
    /**
     * Determines if all elements of the sequence satisfy the specified predicate.
     * @param predicate The predicate function that will be used to check each element for a condition.
     * @returns {boolean} true if all elements of the sequence satisfy the specified predicate; otherwise, false.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const allPositive = numbers.all(n => n > 0); // allPositive = true
     *      const allEven = numbers.all(n => n % 2 === 0); // allEven = false
     */
    all(predicate: Predicate<TElement>): boolean;
    /**
     * Determines if any element of the sequence satisfies the specified predicate.
     * @param predicate The predicate function that will be used to check each element for a condition.
     * If not specified, it will return true if the sequence has elements, otherwise false.
     * @returns {boolean} true if any element of the sequence satisfies the specified predicate; otherwise, false.
     * @example
     *      const numbers = new List([1, 2, -3, 4, 5]);
     *      const hasNegative = numbers.any(n => n < 0); // hasNegative = true
     *      const hasGreaterThanTen = numbers.any(n => n > 10); // hasGreaterThanTen = false
     *      const isEmpty = new List<number>().any(); // isEmpty = false
     *      const isNotEmpty = numbers.any(); // isNotEmpty = true
     */
    any(predicate?: Predicate<TElement>): boolean;
    /**
     * Appends the specified element to the end of the sequence.
     * @template TElement
     * @param element The element that will be appended to the end of the sequence
     * @returns {IEnumerable<TElement>} A new enumerable sequence that ends with the specified element.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const appended = numbers.append(4).toArray(); // appended = [1, 2, 3, 4]
     */
    append(element: TElement): IEnumerable<TElement>;
    /**
     * Computes the average of the sequence. The sequence should be either a sequence consisting of numbers, or an appropriate selector function should be provided.
     * @param selector The selector function that will select a numeric value from the sequence elements.
     * @returns {number} The average of the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const avg = numbers.average(); // avg = 3
     *
     *      interface Item { value: number; }
     *      const items = new List<Item>([{ value: 10 }, { value: 20 }, { value: 60 }]);
     *      const avgValue = items.average(item => item.value); // avgValue = 30
     */
    average(selector?: Selector<TElement, number>): number;
    /**
     * Casts the elements of the sequence to the specified type.
     * @template TResult
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are of the specified type.
     * @example
     *      const mixedList = new List([1, 'two', 3, 'four', 5]);
     *      const numbersOnly = mixedList
     *          .where(item => typeof item === 'number')
     *          .cast<number>();
     *      // numbersOnly = [1, 3, 5]
     *
     *      // Note: Cast doesn't perform type conversion, only type assertion.
     *      // If an element cannot be cast, it may lead to runtime errors later.
     *      // Example of a potential issue (if not pre-filtered):
     *      // const potentialError = mixedList.cast<number>();
     *      // Iterating potentialError might throw errors when 'two' or 'four' are accessed as numbers.
     */
    cast<TResult>(): IEnumerable<TResult>;
    /**
     * Splits the elements of the sequence into chunks of size at most the specified size.
     * @template TElement
     * @param size The maximum size of each chunk.
     * @return {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence whose elements are chunks of the original sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8]);
     *      const chunks = numbers.chunk(3).select(chunk => chunk.toArray()).toArray();
     *      // chunks = [[1, 2, 3], [4, 5, 6], [7, 8]]
     */
    chunk(size: number): IEnumerable<IEnumerable<TElement>>;
    /**
     * Returns all combinations of the elements of the sequence.
     * The outputs will not include duplicate combinations.
     * @template TElement
     * @param size The size of the combinations. If not specified, it will return all possible combinations.
     * @returns {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence whose elements are combinations of the source sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const combinationsOfTwo = letters.combinations(2)
     *          .select(c => c.toArray())
     *          .toArray();
     *      // combinationsOfTwo = [['a', 'b'], ['a', 'c'], ['b', 'c']]
     *
     *      const allCombinations = letters.combinations()
     *          .select(c => c.toArray())
     *          .toArray();
     *      // allCombinations = [['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']]
     */
    combinations(size?: number): IEnumerable<IEnumerable<TElement>>;
    /**
     * Concatenates two sequences.
     * @template TElement
     * @param iterable The iterable sequence that will be concatenated to the first sequence.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements of both sequences.
     * @example
     *      const list1 = new List([1, 2]);
     *      const list2 = new List([3, 4]);
     *      const concatenated = list1.concat(list2).toArray();
     *      // concatenated = [1, 2, 3, 4]
     */
    concat(iterable: Iterable<TElement>): IEnumerable<TElement>;
    /**
     * Determines whether the sequence contains the specified element.
     * @param element The element whose existence will be checked.
     * @param comparator The comparator function that will be used for equality comparison. If not provided, the default equality comparison is used.
     * @returns {boolean} true if the sequence contains the specified element; otherwise, false.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const hasThree = numbers.contains(3);
     *      // hasThree = true
     *      const hasTen = numbers.contains(10);
     *      // hasTen = false
     *
     *      // Using a custom comparator for objects
     *      interface Person { id: number; name: string; }
     *      const people = new List<Person>([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
     *      const bob = { id: 2, name: 'Bob' };
     *      const hasBobById = people.contains(bob, (p1, p2) => p1.id === p2.id);
     *      // hasBobById = true
     */
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    /**
     * Returns the number of elements in the sequence.
     *
     * <b>Note:</b> If you want to check whether a sequence contains any elements, do not use <code>sequence.count() > 0</code>. Use <code>sequence.any()</code> instead.
     * @param predicate The predicate function that will be used to check each element for a condition.
     * @returns {number} The number of elements in the sequence.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *      const totalCount = numbers.count();
     *      // totalCount = 6
     *
     *      const evenCount = numbers.count(n => n % 2 === 0);
     *      // evenCount = 3
     */
    count(predicate?: Predicate<TElement>): number;
    /**
     * Returns an enumerable sequence of key value pair objects that contain the key and the number of occurrences of the key in the source sequence.
     * @template TKey
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<KeyValuePair<TKey, number>>} A new enumerable sequence that contains key value pair objects.
     * @example
     *      const fruits = new List(['apple', 'banana', 'apple', 'orange', 'banana', 'apple']);
     *      const fruitCounts = fruits.countBy(fruit => fruit).toArray();
     *      // fruitCounts = [
     *      //   { key: 'apple', value: 3 },
     *      //   { key: 'banana', value: 2 },
     *      //   { key: 'orange', value: 1 }
     *      // ]
     *
     *      // Example with objects and key selector
     *      interface Item { type: string; }
     *      const items = new List<Item>([{ type: 'A' }, { type: 'B' }, { type: 'A' }]);
     *      const typeCounts = items.countBy(item => item.type).toArray();
     *      // typeCounts = [ { key: 'A', value: 2 }, { key: 'B', value: 1 } ]
     */
    countBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<KeyValuePair<TKey, number>>;
    /**
     * Returns a new enumerable sequence that repeats the elements of the source sequence a specified number of times.
     * If the count is not specified, the sequence will be repeated indefinitely.
     * If the sequence is empty, an error will be thrown.
     * @template TElement
     * @param count The number of times the source sequence will be repeated.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that repeats the elements of the source sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const pattern = new List([1, 2]);
     *      const repeatedFinite = pattern.cycle(3).toArray();
     *      // repeatedFinite = [1, 2, 1, 2, 1, 2]
     *
     *      // Infinite cycle (use with caution, typically with take)
     *      // const repeatedInfinite = pattern.cycle();
     *      // const firstTen = repeatedInfinite.take(10).toArray();
     *      // firstTen = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
     *
     *      // Throws error if the source is empty
     *      // new List<number>().cycle(); // Throws NoElementsException
     */
    cycle(count?: number): IEnumerable<TElement>;
    /**
     * Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
     * @template TElement
     * @param value The value to return if the sequence is empty. Defaults to null if not provided.
     * @returns {IEnumerable<TElement | null>} The specified sequence or the specified value in a singleton collection if the sequence is empty.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const resultNotEmpty = numbers.defaultIfEmpty(0).toArray();
     *      // resultNotEmpty = [1, 2, 3]
     *
     *      const emptyList = new List<number>();
     *      const resultEmptyWithDefault = emptyList.defaultIfEmpty(0).toArray();
     *      // resultEmptyWithDefault = [0]
     *
     *      const resultEmptyNull = emptyList.defaultIfEmpty().toArray(); // No value specified, defaults to null
     *      // resultEmptyNull = [null]
     */
    defaultIfEmpty(value?: TElement | null): IEnumerable<TElement | null>;
    /**
     * Returns distinct elements from the sequence.
     * @template TElement
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains distinct elements from the source sequence.
     * @example
     *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
     *      const distinctNumbers = numbers.distinct().toArray();
     *      // distinctNumbers = [1, 2, 3, 4, 5]
     *
     *      // Using a custom comparator for objects (compares based on id)
     *      interface Item { id: number; value: string; }
     *      const items = new List<Item>([
     *          { id: 1, value: 'A' },
     *          { id: 2, value: 'B' },
     *          { id: 1, value: 'C' }
     *      ]);
     *      const distinctItemsById = items.distinct((a, b) => a.id === b.id).toArray();
     *      // distinctItemsById = [{ id: 1, value: 'A' }, { id: 2, value: 'B' }]
     */
    distinct(keyComparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    /**
     * Returns distinct elements from the sequence based on a key selector.
     * @template TElement, TKey
     * @param keySelector The key selector function that will be used for selecting a key which will be used for distinctness comparison.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains distinct elements from the source sequence.
     * @example
     *      interface Product { category: string; name: string; }
     *      const products = new List<Product>([
     *          { category: 'Electronics', name: 'Laptop' },
     *          { category: 'Books', name: 'TypeScript Guide' },
     *          { category: 'Electronics', name: 'Mouse' },
     *          { category: 'Books', name: 'Another Book' }
     *      ]);
     *
     *      // Get one product from each distinct category
     *      const distinctByCategory = products.distinctBy(p => p.category).toArray();
     *      // distinctByCategory might be:
     *      // [
     *      //   { category: 'Electronics', name: 'Laptop' },
     *      //   { category: 'Books', name: 'TypeScript Guide' }
     *      // ]
     *      // (The specific element kept from duplicates is the first one encountered)
     *
     *      // Using a custom key comparator (case-insensitive category)
     *      const productsMixedCase = new List<Product>([
     *          { category: 'Electronics', name: 'Laptop' },
     *          { category: 'electronics', name: 'Keyboard' },
     *          { category: 'Books', name: 'Guide' }
     *      ]);
     *      const distinctCaseInsensitive = productsMixedCase.distinctBy(
     *          p => p.category,
     *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase()
     *      ).toArray();
     *      // distinctCaseInsensitive might be:
     *      // [
     *      //   { category: 'Electronics', name: 'Laptop' },
     *      //   { category: 'Books', name: 'Guide' }
     *      // ]
     */
    distinctBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    /**
     * Returns the element at the specified index in the sequence.
     * @template TElement
     * @param index The index of the element that will be returned.
     * @returns {TElement} The element at the specified index in the sequence.
     * @throws {IndexOutOfBoundsException} If index is less than 0 or greater than or equal to the number of elements in the sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c', 'd']);
     *      const secondLetter = letters.elementAt(1);
     *      // secondLetter = 'b'
     *
     *      try {
     *          letters.elementAt(4); // Throws IndexOutOfBoundsException
     *      } catch (e) {
     *          console.log(e.message); // Output: Index was outside the bounds of the sequence.
     *      }
     *
     *      try {
     *          letters.elementAt(-1); // Throws IndexOutOfBoundsException
     *      } catch (e) {
     *          console.log(e.message); // Output: Index was outside the bounds of the sequence.
     *      }
     */
    elementAt(index: number): TElement;
    /**
     * Returns the element at the specified index in the sequence or a default value if the index is out of range.
     * @template TElement
     * @param index The index of the element that will be returned.
     * @returns {TElement|null} The element at the specified index in the sequence or null if the index is out of range.
     * @example
     *      const letters = new List(['a', 'b', 'c', 'd']);
     *      const secondLetter = letters.elementAtOrDefault(1);
     *      // secondLetter = 'b'
     *
     *      const fifthLetter = letters.elementAtOrDefault(4);
     *      // fifthLetter = null
     *
     *      const negativeIndex = letters.elementAtOrDefault(-1);
     *      // negativeIndex = null
     */
    elementAtOrDefault(index: number): TElement | null;
    /**
     * Produces the set difference of two sequences by using the specified equality comparer or order comparer to compare values.
     * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
     * @template TElement
     * @param iterable The iterable sequence whose distinct elements that also appear in the first sequence will be removed.
     * @param comparator The comparator function that will be used for item comparison. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set difference of the two sequences.
     * @example
     *      const numbers1 = new List([1, 2, 3, 4, 5]);
     *      const numbers2 = new List([3, 5, 6, 7]);
     *      const difference = numbers1.except(numbers2).toArray();
     *      // difference = [1, 2, 4]
     *
     *      // Using custom object comparison
     *      interface Item { id: number; }
     *      const items1 = new List<Item>([{ id: 1 }, { id: 2 }, { id: 3 }]);
     *      const items2 = new List<Item>([{ id: 2 }, { id: 4 }]);
     *      const itemDifference = items1.except(items2, (a, b) => a.id === b.id).toArray();
     *      // itemDifference = [{ id: 1 }, { id: 3 }]
     */
    except(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    /**
     * Produces the set difference of two sequences by using the specified key selector function to compare elements.
     * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
     * @template TElement, TKey
     * @typeParam TElement The type of the elements in the source sequence.
     * @typeParam TKey The type of the key that will be used for comparison.
     * @param iterable The iterable sequence whose distinct elements that also appear in the first sequence will be removed.
     * @param keySelector The key selector function that will be used for selecting a key which will be used for comparison.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set difference of the two sequences.
     * @example
     *      interface Product { code: string; name: string; }
     *      const store1Products = new List<Product>([
     *          { code: 'A1', name: 'Apple' },
     *          { code: 'B2', name: 'Banana' },
     *          { code: 'C3', name: 'Cherry' }
     *      ]);
     *      const store2Products = new List<Product>([
     *          { code: 'B2', name: 'Banana' }, // Same code as store1
     *          { code: 'D4', name: 'Date' }
     *      ]);
     *
     *      // Find products in store1 whose codes are not in store2
     *      const uniqueToStore1 = store1Products.exceptBy(
     *          store2Products,
     *          p => p.code // Compare based on the 'code' property
     *      ).toArray();
     *      // uniqueToStore1 = [ { code: 'A1', name: 'Apple' }, { code: 'C3', name: 'Cherry' } ]
     *
     *      // Example with case-insensitive key comparison
     *      const listA = new List([{ val: 'a' }, { val: 'b' }]);
     *      const listB = new List([{ val: 'B' }, { val: 'c' }]);
     *      const diffCaseInsensitive = listA.exceptBy(
     *          listB,
     *          item => item.val,
     *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
     *      ).toArray();
     *      // diffCaseInsensitive = [ { val: 'a' } ]
     */
    exceptBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    /**
     * Gets the first element of the sequence.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
     * @returns {TElement} The first element of the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @throws {NoMatchingElementException} If no element satisfies the condition.
     * @example
     *      const numbers = new List([10, 20, 30, 40]);
     *      const firstElement = numbers.first();
     *      // firstElement = 10
     *
     *      const firstGreaterThan25 = numbers.first(n => n > 25);
     *      // firstGreaterThan25 = 30
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.first(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     *
     *      try {
     *          numbers.first(n => n > 50); // Throws NoMatchingElementException
     *      } catch (e) {
     *          console.log(e.message); // Output: No element satisfies the condition.
     *      }
     */
    first(predicate?: Predicate<TElement>): TElement;
    /**
     * Gets the first element of the sequence or a default value if the no element satisfies the condition.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the first element of the sequence will be returned.
     * @returns {TElement|null} The first element of the sequence or null if no element satisfies the condition or the sequence is empty.
     * @example
     *      const numbers = new List([10, 20, 30, 40]);
     *      const firstElement = numbers.firstOrDefault();
     *      // firstElement = 10
     *
     *      const firstGreaterThan25 = numbers.firstOrDefault(n => n > 25);
     *      // firstGreaterThan25 = 30
     *
     *      const firstGreaterThan50 = numbers.firstOrDefault(n => n > 50);
     *      // firstGreaterThan50 = null
     *
     *      const emptyList = new List<number>();
     *      const firstFromEmpty = emptyList.firstOrDefault();
     *      // firstFromEmpty = null
     */
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    /**
     * Iterates over the sequence and performs the specified action on each element.
     * @param action The action function that will be performed on each element. The second parameter of the action is the index.
     * @example
     *      const names = new List(['Alice', 'Bob', 'Charlie']);
     *      let output = '';
     *      names.forEach((name, index) => {
     *          output += `${index}: ${name}\n`;
     *      });
     *      // output:
     *      // 0: Alice
     *      // 1: Bob
     *      // 2: Charlie
     *
     *      // Note: forEach executes immediately and does not return a new sequence.
     */
    forEach(action: IndexedAction<TElement>): void;
    /**
     * Groups the elements of the sequence according to a specified key selector function.
     * @template TKey, TElement
     * @param keySelector The key selector function that will be used for grouping.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<IGroup<TKey, TElement>>} A new enumerable sequence whose elements are groups that contain the elements of the source sequence.
     * @example
     *      interface Pet { name: string; species: string; age: number; }
     *      const pets = new List<Pet>([
     *          { name: 'Fluffy', species: 'Cat', age: 3 },
     *          { name: 'Buddy', species: 'Dog', age: 5 },
     *          { name: 'Whiskers', species: 'Cat', age: 2 },
     *          { name: 'Rex', species: 'Dog', age: 7 }
     *      ]);
     *
     *      // Group pets by species
     *      const groupsBySpecies = pets.groupBy(pet => pet.species).toArray();
     *      // groupsBySpecies will contain IGroup objects. Example structure:
     *      // [
     *      //   { key: 'Cat', source: Enumerable containing Fluffy and Whiskers },
     *      //   { key: 'Dog', source: Enumerable containing Buddy and Rex }
     *      // ]
     *
     *      // To get results as arrays:
     *      const speciesArrays = pets
     *          .groupBy(pet => pet.species)
     *          .select(group => ({ species: group.key, pets: group.source.toArray() }))
     *          .toArray();
     *      // speciesArrays = [
     *      //   { species: 'Cat', pets: [{ name: 'Fluffy', ... }, { name: 'Whiskers', ... }] },
     *      //   { species: 'Dog', pets: [{ name: 'Buddy', ... }, { name: 'Rex', ... }] }
     *      // ]
     *
     *      // Using a custom comparator (e.g., grouping ages into ranges)
     *      const ageGroups = pets.groupBy(
     *          pet => pet.age, // Temporary key selector
     *          (age1, age2) => Math.floor(age1 / 3) === Math.floor(age2 / 3) // Comparator: group by age range (0-2, 3-5, 6-8, etc.)
     *      )
     *          .select(group => ({ ageRangeKey: group.key, pets: group.source.toArray() })) // Note: group.key will be the first age encountered in that range
     *          .toArray();
     *      // ageGroups might look like:
     *      // [
     *      //   { ageRangeKey: 3, pets: [Fluffy, Whiskers] }, // Ages 3 and 2 fall in the same range (key is 3 as it was encountered first)
     *      //   { ageRangeKey: 5, pets: [Buddy, Rex] }      // Ages 5 and 7 fall in the same range (key is 5)
     *      // ]
     */
    groupBy<TKey>(keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey>): IEnumerable<IGroup<TKey, TElement>>;
    /**
     * Correlates the elements of two sequences based on equality of keys and groups the results.
     * The result contains elements from the first (outer) sequence and a collection of matching elements from the second (inner) sequence.
     * @template TInner, TKey, TResult, TElement
     * @param innerEnumerable The enumerable sequence to join to the first sequence.
     * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
     * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
     * @param resultSelector The result selector function that will be used to create a result element from an element from the first sequence and a collection of matching elements from the second sequence.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the group join operation.
     * @example
     *      interface Department { id: number; name: string; }
     *      interface Employee { name: string; deptId: number; }
     *
     *      const departments = new List<Department>([
     *          { id: 1, name: 'HR' },
     *          { id: 2, name: 'Engineering' },
     *          { id: 3, name: 'Sales' }
     *      ]);
     *
     *      const employees = new List<Employee>([
     *          { name: 'Alice', deptId: 2 },
     *          { name: 'Bob', deptId: 1 },
     *          { name: 'Charlie', deptId: 2 },
     *          { name: 'David', deptId: 4 } // Belongs to a non-listed department
     *      ]);
     *
     *      // Group employees by department
     *      const departmentEmployees = departments.groupJoin(
     *          employees,
     *          dept => dept.id, // Outer key selector (department ID)
     *          emp => emp.deptId, // Inner key selector (employee department ID)
     *          (dept, emps) => ({ // Result selector
     *              departmentName: dept.name,
     *              employees: emps.select(e => e.name).toArray() // Project employee names
     *          })
     *      ).toArray();
     *
     *      // departmentEmployees = [
     *      //   { departmentName: 'HR', employees: ['Bob'] },
     *      //   { departmentName: 'Engineering', employees: ['Alice', 'Charlie'] },
     *      //   { departmentName: 'Sales', employees: [] } // Sales has no matching employees
     *      // ]
     *      // Note: Employees in non-listed departments (David) are ignored as they don't match an outer key.
     */
    groupJoin<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, IEnumerable<TInner>, TResult>, keyComparator?: EqualityComparator<TKey>): IEnumerable<TResult>;
    /**
     * Returns an enumerable of tuples, each containing the index and the element from the source sequence.
     * @template TElement
     * @returns {IEnumerable<[number, TElement]>} A new enumerable sequence whose elements are tuples of the index and the element.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const indexedLetters = letters.index().toArray();
     *      // indexedLetters = [[0, 'a'], [1, 'b'], [2, 'c']]
     */
    index(): IEnumerable<[number, TElement]>;
    /**
     * Produces the set intersection of two sequences by using the specified equality comparer or order comparer to compare values.
     * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
     * @template TElement
     * @param iterable The iterable sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param comparator The comparator function that will be used for item comparison. If not provided, a default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set intersection of the two sequences.
     * @throws {Error} If the iterable is null or undefined.
     * @example
     *      const numbers1 = new List([1, 2, 3, 4, 5, 5]); // Source has duplicates
     *      const numbers2 = new List([3, 5, 6, 7, 5]); // Other has duplicates
     *      const intersection = numbers1.intersect(numbers2).toArray();
     *      // intersection = [3, 5] (Order matches source, duplicates removed)
     *
     *      // Using custom object comparison
     *      interface Item { id: number; value: string; }
     *      const items1 = new List<Item>([{ id: 1, value: 'A' }, { id: 2, value: 'B' }]);
     *      const items2 = new List<Item>([{ id: 2, value: 'Different B' }, { id: 3, value: 'C' }]);
     *      const itemIntersection = items1.intersect(items2, (a, b) => a.id === b.id).toArray();
     *      // itemIntersection = [{ id: 2, value: 'B' }] (Keeps the element from the first list)
     */
    intersect(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>): IEnumerable<TElement>;
    /**
     * Produces the set intersection of two sequences by using the specified key selector function to compare elements.
     * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
     * @template TElement, TKey
     * @typeParam TElement The type of the elements in the source sequence.
     * @typeParam TKey The type of the key that will be used for comparison.
     * @param iterable The iterable sequence whose distinct elements that also appear in the first sequence will be returned.
     * @param keySelector The key selector function that will be used for selecting a key which will be used for comparison.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set intersection of the two sequences.
     * @example
     *      interface Product { code: string; name: string; }
     *      const store1Products = new List<Product>([
     *          { code: 'A1', name: 'Apple' },
     *          { code: 'B2', name: 'Banana' },
     *          { code: 'C3', name: 'Cherry' }
     *      ]);
     *      const store2Products = new List<Product>([
     *          { code: 'B2', name: 'Banana V2' }, // Same code as store1
     *          { code: 'D4', name: 'Date' }
     *      ]);
     *
     *      // Find products in store1 whose codes also exist in store2
     *      const commonProducts = store1Products.intersectBy(
     *          store2Products,
     *          p => p.code // Compare based on the 'code' property
     *      ).toArray();
     *      // commonProducts = [ { code: 'B2', name: 'Banana' } ] (Takes the element from store1)
     *
     *      // Example with case-insensitive key comparison
     *      const listA = new List([{ val: 'a' }, { val: 'b' }]);
     *      const listB = new List([{ val: 'B' }, { val: 'c' }]);
     *      const intersectCaseInsensitive = listA.intersectBy(
     *          listB,
     *          item => item.val,
     *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
     *      ).toArray();
     *      // intersectCaseInsensitive = [ { val: 'b' } ] (Keeps 'b' from listA as it matches 'B')
     */
    intersectBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>): IEnumerable<TElement>;
    /**
     * Intersperses a specified element between each element of the sequence.
     * @template TElement, TSeparator
     * @param separator The element that will be interspersed between each element of the sequence.
     * @returns {IEnumerable<TElement|TSeparator>} A new enumerable sequence whose elements are the elements of the source sequence interspersed with the specified element.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const interspersedLetters = letters.intersperse('-').toArray();
     *      // interspersedLetters = ['a', '-', 'b', '-', 'c']
     *
     *      const numbers = new List([1, 2, 3]);
     *      const interspersedNumbers = numbers.intersperse(0).toArray();
     *      // interspersedNumbers = [1, 0, 2, 0, 3]
     *
     *      const emptyList = new List<string>();
     *      const interspersedEmpty = emptyList.intersperse('-').toArray();
     *      // interspersedEmpty = []
     *
     *      const singleItemList = new List(['a']);
     *      const interspersedSingle = singleItemList.intersperse('-').toArray();
     *      // interspersedSingle = ['a']
     */
    intersperse<TSeparator = TElement>(separator: TSeparator): IEnumerable<TElement | TSeparator>;
    /**
     * Correlates the elements of two sequences based on equality of keys.
     * @template TInner, TKey, TResult, TElement
     * @param innerEnumerable The enumerable sequence to join to the first sequence.
     * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
     * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
     * @param resultSelector The result selector function that will be used to create a result element from two matching elements.
     * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
     * @param leftJoin If true, the result sequence will include outer elements that have no matching inner element, with null provided as the inner element to the resultSelector. Defaults to false.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the join operation.
     * @example
     *      interface Department { id: number; name: string; location: string }
     *      interface Employee { name: string; deptId: number; role: string }
     *
     *      const departments = new List<Department>([
     *          { id: 1, name: 'HR', location: 'Building A' },
     *          { id: 2, name: 'Engineering', location: 'Building B' },
     *          { id: 3, name: 'Marketing', location: 'Building A' } // No employees here
     *      ]);
     *
     *      const employees = new List<Employee>([
     *          { name: 'Alice', deptId: 2, role: 'Developer' },
     *          { name: 'Bob', deptId: 1, role: 'Manager' },
     *          { name: 'Charlie', deptId: 2, role: 'Tester' },
     *          { name: 'David', deptId: 4, role: 'Intern' } // Department 4 not in departments list
     *      ]);
     *
     *      // Inner Join (default: leftJoin = false)
     *      const innerJoinResult = departments.join(
     *          employees,
     *          dept => dept.id,           // Outer key: department ID
     *          emp => emp.deptId,         // Inner key: employee department ID
     *          (dept, emp) => ({          // Result selector
     *              employeeName: emp.name,
     *              departmentName: dept.name
     *          })
     *      ).toArray();
     *      // innerJoinResult = [
     *      //   { employeeName: 'Bob', departmentName: 'HR' },
     *      //   { employeeName: 'Alice', departmentName: 'Engineering' },
     *      //   { employeeName: 'Charlie', departmentName: 'Engineering' }
     *      // ]
     *      // Note: Marketing dept and David (dept 4) are excluded.
     *
     *      // Left Join (leftJoin = true)
     *      const leftJoinResult = departments.join(
     *          employees,
     *          dept => dept.id,
     *          emp => emp.deptId,
     *          (dept, emp) => ({
     *              departmentName: dept.name,
     *              employeeName: emp?.name ?? 'N/A' // Use nullish coalescing for unmatched employees
     *          }),
     *          Comparators.equalityComparator, // Default comparator can be explicit or omitted
     *          true                       // Set leftJoin to true
     *      ).toArray();
     *      // leftJoinResult = [
     *      //   { departmentName: 'HR', employeeName: 'Bob' },
     *      //   { departmentName: 'Engineering', employeeName: 'Alice' },
     *      //   { departmentName: 'Engineering', employeeName: 'Charlie' },
     *      //   { departmentName: 'Marketing', employeeName: 'N/A' } // Marketing included, no matching employee
     *      // ]
     *      // Note: David (dept 4) is still excluded as the join starts from departments.
     */
    join<TInner, TKey, TResult>(innerEnumerable: IEnumerable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean): IEnumerable<TResult>;
    /**
     * Returns the last element of the sequence.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
     * @returns {TElement} The last element of the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @throws {NoMatchingElementException} If no element satisfies the condition.
     * @example
     *      const numbers = new List([10, 20, 30, 25, 40]);
     *      const lastElement = numbers.last();
     *      // lastElement = 40
     *
     *      const lastLessThan30 = numbers.last(n => n < 30);
     *      // lastLessThan30 = 25
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.last(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     *
     *      try {
     *          numbers.last(n => n > 50); // Throws NoMatchingElementException
     *      } catch (e) {
     *          console.log(e.message); // Output: No element satisfies the condition.
     *      }
     */
    last(predicate?: Predicate<TElement>): TElement;
    /**
     * Returns the last element of the sequence or a default value if the no element satisfies the condition.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
     * @returns {TElement|null} The last element of the sequence or null if the sequence is empty or no element satisfies the condition.
     * @example
     *      const numbers = new List([10, 20, 30, 25, 40]);
     *      const lastElement = numbers.lastOrDefault();
     *      // lastElement = 40
     *
     *      const lastLessThan30 = numbers.lastOrDefault(n => n < 30);
     *      // lastLessThan30 = 25
     *
     *      const lastGreaterThan50 = numbers.lastOrDefault(n => n > 50);
     *      // lastGreaterThan50 = null
     *
     *      const emptyList = new List<number>();
     *      const lastFromEmpty = emptyList.lastOrDefault();
     *      // lastFromEmpty = null
     */
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    /**
     * Returns the maximum value in the sequence.
     * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
     * @returns {number} The maximum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const numbers = new List([10, 50, 20, 45, 30]);
     *      const maxNumber = numbers.max();
     *      // maxNumber = 50
     *
     *      interface Item { value: number; }
     *      const items = new List<Item>([{ value: 100 }, { value: 50 }, { value: 200 }]);
     *      const maxValue = items.max(item => item.value);
     *      // maxValue = 200
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.max(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    max(selector?: Selector<TElement, number>): number;
    /**
     * Returns the element with the maximum value that is obtained by applying the key selector function to each element in the sequence.
     * @template TElement
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
     * @returns {TElement} The element with the maximum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      interface Product { name: string; price: number; }
     *      const products = new List<Product>([
     *          { name: 'Laptop', price: 1200 },
     *          { name: 'Mouse', price: 25 },
     *          { name: 'Keyboard', price: 75 },
     *          { name: 'Monitor', price: 300 }
     *      ]);
     *
     *      // Find the most expensive product
     *      const mostExpensive = products.maxBy(p => p.price);
     *      // mostExpensive = { name: 'Laptop', price: 1200 }
     *
     *      // Using a custom comparator (e.g., longest name)
     *      const productWithLongestName = products.maxBy(
     *          p => p.name.length // Key is the length of the name
     *      );
     *      // productWithLongestName = { name: 'Keyboard', price: 75 }
     *
     *      const emptyList = new List<Product>();
     *      try {
     *          emptyList.maxBy(p => p.price); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    maxBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    /**
     * Returns the minimum value in the sequence.
     * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
     * @returns {number} The minimum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const numbers = new List([10, 50, 20, 45, 30]);
     *      const minNumber = numbers.min();
     *      // minNumber = 10
     *
     *      interface Item { value: number; }
     *      const items = new List<Item>([{ value: 100 }, { value: 50 }, { value: 200 }]);
     *      const minValue = items.min(item => item.value);
     *      // minValue = 50
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.min(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    min(selector?: Selector<TElement, number>): number;
    /**
     * Returns the element with the minimum value that is obtained by applying the key selector function to each element in the sequence.
     * @template TElement
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
     * @returns {TElement} The element with the minimum value in the sequence.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      interface Product { name: string; price: number; }
     *      const products = new List<Product>([
     *          { name: 'Laptop', price: 1200 },
     *          { name: 'Mouse', price: 25 },
     *          { name: 'Keyboard', price: 75 },
     *          { name: 'Monitor', price: 300 }
     *      ]);
     *
     *      // Find the cheapest product
     *      const cheapest = products.minBy(p => p.price);
     *      // cheapest = { name: 'Mouse', price: 25 }
     *
     *      // Using a custom comparator (e.g., the shortest name)
     *      const productWithShortestName = products.minBy(
     *          p => p.name.length // Key is the length of the name
     *      );
     *      // productWithShortestName = { name: 'Mouse', price: 25 }
     *
     *      const emptyList = new List<Product>();
     *      try {
     *          emptyList.minBy(p => p.price); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    minBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): TElement;
    /**
     * Determines whether no elements of the sequence satisfy the specified predicate.
     * If no predicate is specified, it returns true if the sequence is empty, and false otherwise.
     * @param predicate The predicate function that will be used to check each element for a condition.
     * @returns {boolean} true if no elements satisfy the predicate, or if the sequence is empty and no predicate is provided; otherwise, false.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *
     *      // Check if none are negative
     *      const noneNegative = numbers.none(n => n < 0);
     *      // noneNegative = true
     *
     *      // Check if none are greater than 10
     *      const noneGreaterThan10 = numbers.none(n => n > 10);
     *      // noneGreaterThan10 = true
     *
     *      // Check if none are even (this will be false)
     *      const noneEven = numbers.none(n => n % 2 === 0);
     *      // noneEven = false
     *
     *      // Check if an empty list has no elements (no predicate)
     *      const emptyList = new List<number>();
     *      const emptyNone = emptyList.none();
     *      // emptyNone = true
     *
     *      // Check if a non-empty list has no elements (no predicate)
     *      const nonEmptyNone = numbers.none();
     *      // nonEmptyNone = false
     */
    none(predicate?: Predicate<TElement>): boolean;
    /**
     * Returns the elements that are of the specified type.
     * The type can be specified either as a constructor function or as a string representing a primitive type.
     * @template TResult
     * @param type The type to filter the elements of the sequence with (e.g., 'string', 'number', Boolean, Date, MyCustomClass).
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are of the specified type.
     * @example
     *      // --- Basic Usage with Primitives (string type name) ---
     *      const mixedList = new List<any>([1, 'apple', true, 2.5, 'banana', false, null, undefined]);
     *
     *      const stringsOnly = mixedList.ofType('string').toArray();
     *      // stringsOnly = ['apple', 'banana']
     *
     *      const numbersOnly = mixedList.ofType('number').toArray();
     *      // numbersOnly = [1, 2.5]
     *
     *      const booleansOnly = mixedList.ofType('boolean').toArray();
     *      // booleansOnly = [true, false]
     *
     *      // Note: 'object' will match non-null objects, including arrays, dates, custom objects, etc.
     *      const objectsOnly = mixedList.ofType('object').toArray();
     *      // objectsOnly = [] (in this specific list, as null is considered object but often filtered implicitly)
     *
     *      const listWithObject = new List<any>([1, { name: 'obj' }, new Date(), [1,2] ]);
     *      const objectsInList = listWithObject.ofType('object').toArray();
     *      // objectsInList = [ { name: 'obj' }, Date(...), [1, 2] ]
     *
     *      // --- Usage with Constructor Functions ---
     *      class Animal { constructor(public name: string) {} }
     *      class Dog extends Animal { constructor(name: string, public breed: string) { super(name); } }
     *      class Cat extends Animal { constructor(name: string, public lives: number) { super(name); } }
     *
     *      const animals = new List<Animal | string>([
     *          new Dog('Buddy', 'Golden Retriever'),
     *          new Cat('Whiskers', 9),
     *          'Not an animal',
     *          new Dog('Rex', 'German Shepherd'),
     *          null // Will be filtered out
     *      ]);
     *
     *      // Get only Dog instances
     *      const dogs = animals.ofType(Dog).toArray();
     *      // dogs = [ Dog { name: 'Buddy', breed: 'Golden Retriever' }, Dog { name: 'Rex', breed: 'German Shepherd' } ]
     *      // TypeScript knows `dogs` is of type Dog[]
     *
     *      // Get only Cat instances
     *      const cats = animals.ofType(Cat).toArray();
     *      // cats = [ Cat { name: 'Whiskers', lives: 9 } ]
     *      // TypeScript knows `cats` is of type Cat[]
     *
     *      // --- Inheritance Handling ---
     *      // Get all instances of Animal (includes Dogs and Cats)
     *      const allAnimals = animals.ofType(Animal).toArray();
     *      // allAnimals = [
     *      //   Dog { name: 'Buddy', breed: 'Golden Retriever' },
     *      //   Cat { name: 'Whiskers', lives: 9 },
     *      //   Dog { name: 'Rex', breed: 'German Shepherd' }
     *      // ]
     *      // TypeScript knows `allAnimals` is of type Animal[]
     *
     *      // --- Using with built-in constructors ---
     *      const variousData = new List<any>([new Date(), 123, "hello", new Date(0), true]);
     *      const datesOnly = variousData.ofType(Date).toArray();
     *      // datesOnly = [ Date(...), Date(0) ] // Contains the two Date objects
     *
     *      const numbersFromAny = variousData.ofType(Number).toArray();
     *      // numbersFromAny = [ 123 ]
     *
     *      // --- Edge Cases ---
     *      const nullsAndUndefined = new List<any>([null, undefined, 0, '']);
     *      const objects = nullsAndUndefined.ofType('object').toArray(); // 'object' typically matches non-null objects
     *      // objects = []
     *
     *      const undefinedOnly = nullsAndUndefined.ofType('undefined').toArray();
     *      // undefinedOnly = [undefined]
     */
    ofType<TResult extends ObjectType>(type: TResult): IEnumerable<InferredType<TResult>>;
    /**
     * Sorts the elements of a sequence in ascending order by using a specified comparer.
     * @template TElement
     * @param keySelector The key selector function that will be used for selecting the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
     * @returns {IOrderedEnumerable<TElement>} A new enumerable sequence whose elements are sorted in ascending order.
     * @example
     *      const numbers = new List([50, 10, 40, 30, 20]);
     *      const sortedNumbers = numbers.orderBy(n => n).toArray();
     *      // sortedNumbers = [10, 20, 30, 40, 50]
     *
     *      interface Person { name: string; age: number; }
     *      const people = new List<Person>([
     *          { name: 'Charlie', age: 30 },
     *          { name: 'Alice', age: 25 },
     *          { name: 'Bob', age: 35 }
     *      ]);
     *
     *      // Order by age (ascending)
     *      const peopleByAge = people.orderBy(p => p.age).toArray();
     *      // peopleByAge = [
     *      //   { name: 'Alice', age: 25 },
     *      //   { name: 'Charlie', age: 30 },
     *      //   { name: 'Bob', age: 35 }
     *      // ]
     *
     *      // Order by name (string comparison, ascending)
     *      const peopleByName = people.orderBy(p => p.name).toArray();
     *      // peopleByName = [
     *      //   { name: 'Alice', age: 25 },
     *      //   { name: 'Bob', age: 35 },
     *      //   { name: 'Charlie', age: 30 }
     *      // ]
     *
     *      // Using a custom comparator (e.g., sort numbers as strings)
     *      const numbersToSortAsString = new List([1, 10, 2, 20]);
     *      const sortedAsString = numbersToSortAsString.orderBy(
     *          n => n,
     *          (a, b) => String(a).localeCompare(String(b)) // String comparison
     *      ).toArray();
     *      // sortedAsString = [1, 10, 2, 20] (standard numeric sort would be [1, 2, 10, 20])
     */
    orderBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    /**
     * Sorts the elements of a sequence in descending order by using a specified comparer.
     * @template TElement
     * @param keySelector The key selector function that will be used for selecting the key for an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
     * @returns {IOrderedEnumerable<TElement>} A new enumerable sequence whose elements are sorted in descending order.
     * @example
     *      const numbers = new List([50, 10, 40, 30, 20]);
     *      const sortedNumbersDesc = numbers.orderByDescending(n => n).toArray();
     *      // sortedNumbersDesc = [50, 40, 30, 20, 10]
     *
     *      interface Person { name: string; age: number; }
     *      const people = new List<Person>([
     *          { name: 'Charlie', age: 30 },
     *          { name: 'Alice', age: 25 },
     *          { name: 'Bob', age: 35 }
     *      ]);
     *
     *      // Order by age (descending)
     *      const peopleByAgeDesc = people.orderByDescending(p => p.age).toArray();
     *      // peopleByAgeDesc = [
     *      //   { name: 'Bob', age: 35 },
     *      //   { name: 'Charlie', age: 30 },
     *      //   { name: 'Alice', age: 25 }
     *      // ]
     *
     *      // Order by name (string comparison, descending)
     *      const peopleByNameDesc = people.orderByDescending(p => p.name).toArray();
     *      // peopleByNameDesc = [
     *      //   { name: 'Charlie', age: 30 },
     *      //   { name: 'Bob', age: 35 },
     *      //   { name: 'Alice', age: 25 }
     *      // ]
     */
    orderByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    /**
     * Produces a sequence of tuples containing the element and the following element.
     * @template TElement, TResult
     * @param resultSelector The optional function to create a result element from the current and the next element. Defaults to creating a tuple `[current, next]`.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of applying the `resultSelector` to adjacent elements.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *
     *      // Default behavior: creates tuples
     *      const pairs = numbers.pairwise().toArray();
     *      // pairs = [[1, 2], [2, 3], [3, 4], [4, 5]]
     *
     *      // Custom result selector: calculate differences
     *      const differences = numbers.pairwise((current, next) => next - current).toArray();
     *      // differences = [1, 1, 1, 1]
     *
     *      // Custom result selector: create strings
     *      const pairStrings = numbers.pairwise((current, next) => `${current}-${next}`).toArray();
     *      // pairStrings = ["1-2", "2-3", "3-4", "4-5"]
     *
     *      const shortList = new List([10]);
     *      const noPairs = shortList.pairwise().toArray();
     *      // noPairs = []
     *
     *      const emptyList = new List<number>();
     *      const emptyPairs = emptyList.pairwise().toArray();
     *      // emptyPairs = []
     */
    pairwise(resultSelector?: PairwiseSelector<TElement, TElement>): IEnumerable<[TElement, TElement]>;
    /**
     * Produces a tuple of two enumerable sequences, the first one containing the elements that satisfy the condition, and the second one containing the rest of the elements.
     * Note: This method iterates the source sequence immediately and stores the results.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition.
     * @returns {[IEnumerable<TElement>, IEnumerable<TElement>]} A tuple containing two enumerable sequences: the first for elements satisfying the predicate, the second for the rest.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
     *
     *      const [evens, odds] = numbers.partition(n => n % 2 === 0);
     *
     *      const evensArray = evens.toArray();
     *      // evensArray = [2, 4, 6, 8, 10]
     *
     *      const oddsArray = odds.toArray();
     *      // oddsArray = [1, 3, 5, 7, 9]
     *
     *      interface Person { name: string; age: number; }
     *      const people = new List<Person>([
     *          { name: 'Alice', age: 25 },
     *          { name: 'Bob', age: 17 },
     *          { name: 'Charlie', age: 30 },
     *          { name: 'Diana', age: 15 }
     *      ]);
     *
     *      const [adults, minors] = people.partition(p => p.age >= 18);
     *
     *      const adultNames = adults.select(p => p.name).toArray();
     *      // adultNames = ['Alice', 'Charlie']
     *
     *      const minorNames = minors.select(p => p.name).toArray();
     *      // minorNames = ['Bob', 'Diana']
     */
    partition(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    /**
     * Returns an enumerable sequence of permutations, each containing a permutation of the elements of the source sequence.
     * Note: This method first extracts distinct elements from the source before generating permutations.
     * @template TElement
     * @param size If specified, it will return only the permutations of the specified size. If not specified, it will return permutations of the size of the distinct elements in the source sequence.
     * @returns {IEnumerable<IEnumerable<TElement>>} An enumerable of enumerable sequences, each containing a permutation of the distinct elements of the source sequence.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const allPermutations = letters.permutations()
     *          .select(p => p.toArray().join('')) // Convert each permutation sequence to a string
     *          .toArray();
     *      // allPermutations = ["abc", "acb", "bac", "bca", "cab", "cba"]
     *
     *      const permutationsOfTwo = letters.permutations(2)
     *          .select(p => p.toArray().join(''))
     *          .toArray();
     *      // permutationsOfTwo = ["ab", "ac", "ba", "bc", "ca", "cb"]
     *
     *      // With duplicates in source - only distinct elements are permuted
     *      const lettersWithDuplicates = new List(['a', 'a', 'b']);
     *      const permsFromDup = lettersWithDuplicates.permutations() // Equivalent to permutations of ['a', 'b']
     *          .select(p => p.toArray().join(''))
     *          .toArray();
     *      // permsFromDup = ["ab", "ba"]
     *
     *      const permsOfOne = letters.permutations(1)
     *          .select(p => p.toArray().join(''))
     *          .toArray();
     *      // permsOfOne = ["a", "b", "c"]
     *
     *      try {
     *          letters.permutations(0); // Throws InvalidArgumentException
     *      } catch (e) {
     *          console.log(e.message); // Output: Size must be greater than 0.
     *      }
     */
    permutations(size?: number): IEnumerable<IEnumerable<TElement>>;
    /**
     * Adds a value to the beginning of the sequence.
     * @template TElement
     * @param element The element to add to the sequence.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that starts with the specified element.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const prepended = numbers.prepend(0).toArray();
     *      // prepended = [0, 1, 2, 3]
     *
     *      const emptyList = new List<string>();
     *      const prependedToEmpty = emptyList.prepend("first").toArray();
     *      // prependedToEmpty = ["first"]
     */
    prepend(element: TElement): IEnumerable<TElement>;
    /**
     * Computes the product of the sequence. Assumes elements are numbers or uses a selector to get numbers.
     * @param selector The selector function that will be used to select a numeric value from the sequence elements.
     * @returns {number} The product of the sequence. Returns 1 if the sequence is empty.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const totalProduct = numbers.product();
     *      // totalProduct = 120 (1 * 2 * 3 * 4 * 5)
     *
     *      interface Item { value: number; }
     *      const items = new List<Item>([{ value: 2 }, { value: 5 }, { value: 10 }]);
     *      const itemValueProduct = items.product(item => item.value);
     *      // itemValueProduct = 100 (2 * 5 * 10)
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.product(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    product(selector?: Selector<TElement, number>): number;
    /**
     * Inverts the order of the elements in the sequence.
     *
     * Note: This method internally converts the sequence to an array to reverse it.
     * @template TElement
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are in the reverse order of the source sequence.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const reversedNumbers = numbers.reverse().toArray();
     *      // reversedNumbers = [5, 4, 3, 2, 1]
     *
     *      const letters = new List(['a', 'b', 'c']);
     *      const reversedLetters = letters.reverse().toArray();
     *      // reversedLetters = ['c', 'b', 'a']
     *
     *      const emptyList = new List<number>();
     *      const reversedEmpty = emptyList.reverse().toArray();
     *      // reversedEmpty = []
     */
    reverse(): IEnumerable<TElement>;
    /**
     * Applies an accumulator function over the sequence and yields the result of each intermediate computation.
     * If seed is specified, it is used as the initial value for the accumulator, but it is not included in the result sequence.
     * @template TAccumulate
     * @param accumulator The accumulator function that will be applied over the sequence.
     * @param seed The value that will be used as the initial value. If not specified, the first element of the sequence will be used as the seed value and also included as the first element of the result.
     * @returns {IEnumerable<TAccumulate>} A new enumerable sequence whose elements are the result of each intermediate computation.
     * @throws {NoElementsException} If the source is empty and seed is not provided.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *
     *      // Running sum without a seed (the first element is the initial value and first result)
     *      const runningSumNoSeed = numbers.scan((acc, current) => acc + current).toArray();
     *      // runningSumNoSeed = [1, 3, 6, 10, 15]
     *
     *      // Running sum with seed (seed is initial value, but not in output)
     *      const runningSumWithSeed = numbers.scan((acc, current) => acc + current, 100).toArray();
     *      // runningSumWithSeed = [101, 103, 106, 110, 115]
     *
     *      // Building intermediate strings
     *      const letters = new List(['a', 'b', 'c']);
     *      const intermediateStrings = letters.scan((acc, current) => acc + current, '').toArray();
     *      // intermediateStrings = ['a', 'ab', 'abc']
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.scan((a, b) => a + b).toArray(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     *      const scanEmptyWithSeed = emptyList.scan((a, b) => a + b, 0).toArray();
     *      // scanEmptyWithSeed = []
     */
    scan<TAccumulate = TElement>(accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate): IEnumerable<TAccumulate>;
    /**
     * Projects each element of a sequence into a new form.
     * @template TResult
     * @param selector The selector function that will be used to project each element into a new form. The second parameter is the index.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the selector function.
     * @example
     *      const numbers = new List([1, 2, 3, 4]);
     *      const squares = numbers.select(n => n * n).toArray();
     *      // squares = [1, 4, 9, 16]
     *
     *      interface Person { firstName: string; lastName: string; }
     *      const people = new List<Person>([
     *          { firstName: 'John', lastName: 'Doe' },
     *          { firstName: 'Jane', lastName: 'Smith' }
     *      ]);
     *      const fullNames = people.select(p => `${p.firstName} ${p.lastName}`).toArray();
     *      // fullNames = ["John Doe", "Jane Smith"]
     *
     *      // Using the index
     *      const indexedValues = people.select((p, index) => `${index}: ${p.firstName}`).toArray();
     *      // indexedValues = ["0: John", "1: Jane"]
     */
    select<TResult>(selector: IndexedSelector<TElement, TResult>): IEnumerable<TResult>;
    /**
     * Projects each element of a sequence into a new form (which is an iterable) and flattens the resulting sequences into one sequence.
     * @template TResult
     * @param selector The selector function that will be used to project each element into a new iterable form. The second parameter is the index.
     * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the flattened result of the selector function.
     * @example
     *      interface Customer { name: string; orders: string[]; }
     *      const customers = new List<Customer>([
     *          { name: 'Alice', orders: ['Apple', 'Banana'] },
     *          { name: 'Bob', orders: ['Cherry'] },
     *          { name: 'Charlie', orders: [] } // No orders
     *      ]);
     *
     *      // Get a single list of all orders from all customers
     *      const allOrders = customers.selectMany(c => c.orders).toArray();
     *      // allOrders = ['Apple', 'Banana', 'Cherry']
     *
     *      // Example: splitting strings and flattening
     *      const sentences = new List(['Hello world', 'How are you']);
     *      const words = sentences.selectMany(s => s.split(' ')).toArray();
     *      // words = ['Hello', 'world', 'How', 'are', 'you']
     *
     *      // Using index in selector
     *      const indexedFlatten = customers.selectMany((c, index) => c.orders.map(o => `${index}-${o}`)).toArray();
     *      // indexedFlatten = ['0-Apple', '0-Banana', '1-Cherry']
     */
    selectMany<TResult>(selector: IndexedSelector<TElement, Iterable<TResult>>): IEnumerable<TResult>;
    /**
     * Determines whether two sequences are equal by comparing the elements by using an equality comparer for their type.
     * Compares elements pairwise in order. Sequences must have the same length and equal elements at corresponding positions.
     * @param iterable The iterable sequence to compare to the source sequence.
     * @param comparator The equality comparer that will be used to compare the elements. If not specified, the default equality comparer will be used.
     * @returns {boolean} true if the two source sequences are of equal length and their corresponding elements are equal, according to the specified equality comparer; otherwise, false.
     * @example
     *      const list1 = new List([1, 2, 3]);
     *      const list2 = new List([1, 2, 3]);
     *      const list3 = new List([1, 3, 2]); // Different order
     *      const list4 = new List([1, 2]); // Different length
     *      const array1 = [1, 2, 3]; // Can compare with other iterables
     *
     *      const isEqual12 = list1.sequenceEqual(list2);
     *      // isEqual12 = true
     *
     *      const isEqual13 = list1.sequenceEqual(list3);
     *      // isEqual13 = false
     *
     *      const isEqual14 = list1.sequenceEqual(list4);
     *      // isEqual14 = false
     *
     *      const isEqual1Array = list1.sequenceEqual(array1);
     *      // isEqual1Array = true
     *
     *      // Custom comparison for objects
     *      interface Item { id: number; }
     *      const items1 = new List<Item>([{ id: 1 }, { id: 2 }]);
     *      const items2 = new List<Item>([{ id: 1 }, { id: 2 }]);
     *      const items3 = new List<Item>([{ id: 1 }, { id: 3 }]);
     *
     *      const areItemsEqualById = items1.sequenceEqual(items2, (a, b) => a.id === b.id);
     *      // areItemsEqualById = true
     *
     *      const areItems3EqualById = items1.sequenceEqual(items3, (a, b) => a.id === b.id);
     *      // areItems3EqualById = false
     */
    sequenceEqual(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): boolean;
    /**
     * Returns a new enumerable sequence whose elements are shuffled randomly.
     * Note: This method internally converts the sequence to an array to shuffle it.
     * @template TElement
     * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are shuffled.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const shuffledNumbers = numbers.shuffle().toArray();
     *      // shuffledNumbers will be a random permutation of [1, 2, 3, 4, 5], e.g., [3, 1, 5, 2, 4]
     *
     *      // Shuffling is not stable; subsequent calls will likely produce different orders
     *      const shuffledAgain = numbers.shuffle().toArray();
     *      // shuffledAgain will likely be different from shuffledNumbers
     */
    shuffle(): IEnumerable<TElement>;
    /**
     * Returns the only element of a sequence and throws an exception if there is not exactly one element in the sequence.
     * Can optionally apply a predicate to filter the sequence first.
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, checks the entire sequence.
     * @returns {TElement} The single element of the sequence (or the single element satisfying the predicate).
     * @throws {NoElementsException} If the source (or filtered sequence) is empty.
     * @throws {MoreThanOneElementException} If the source (or filtered sequence) contains more than one element.
     * @throws {NoMatchingElementException} If a predicate is specified and no element satisfies the condition.
     * @throws {MoreThanOneMatchingElementException} If a predicate is specified and more than one element satisfies the condition.
     * @example
     *      const singleItemList = new List([42]);
     *      const theOnlyItem = singleItemList.single();
     *      // theOnlyItem = 42
     *
     *      const numbers = new List([10, 20, 30, 40]);
     *      const theOnlyThirty = numbers.single(n => n === 30);
     *      // theOnlyThirty = 30
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.single(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message);
     *      }
     *
     *      const multipleItems = new List([1, 2]);
     *      try {
     *          multipleItems.single(); // Throws MoreThanOneElementException
     *      } catch (e) {
     *          console.log(e.message);
     *      }
     *
     *      try {
     *          numbers.single(n => n > 50); // Throws NoMatchingElementException
     *      } catch (e) {
     *          console.log(e.message);
     *      }
     *
     *      try {
     *          numbers.single(n => n > 15); // Throws MoreThanOneMatchingElementException
     *      } catch (e) {
     *          console.log(e.message);
     *      }
     */
    single(predicate?: Predicate<TElement>): TElement;
    /**
     * Returns the only element of a sequence, or a default value (null) if the sequence is empty.
     * Throws an exception if there is more than one element in the sequence (or more than one matching the predicate).
     * @template TElement
     * @param predicate The predicate function that will be used to check each element for a condition. If not specified, checks the entire sequence.
     * @returns {TElement|null} The single element of the sequence (or the single element satisfying the predicate), or null if the sequence (or filtered sequence) is empty.
     * @throws {MoreThanOneElementException} If the source contains more than one element (and no predicate is used).
     * @throws {MoreThanOneMatchingElementException} If a predicate is specified and more than one element satisfies the condition.
     * @example
     *      const singleItemList = new List([42]);
     *      const theOnlyItem = singleItemList.singleOrDefault();
     *      // theOnlyItem = 42
     *
     *      const numbers = new List([10, 20, 30, 40]);
     *      const theOnlyThirty = numbers.singleOrDefault(n => n === 30);
     *      // theOnlyThirty = 30
     *
     *      const emptyList = new List<number>();
     *      const singleFromEmpty = emptyList.singleOrDefault();
     *      // singleFromEmpty = null
     *
     *      const singleNoMatch = numbers.singleOrDefault(n => n > 50);
     *      // singleNoMatch = null
     *
     *      const multipleItems = new List([1, 2]);
     *      try {
     *          multipleItems.singleOrDefault(); // Throws MoreThanOneElementException
     *      } catch (e) { console.log(e.message); }
     *
     *      try {
     *          numbers.singleOrDefault(n => n > 15); // Throws MoreThanOneMatchingElementException
     *      } catch (e) {
     *          console.log(e.message);
     *      }
     */
    singleOrDefault(predicate?: Predicate<TElement>): TElement | null;
    /**
     * Bypasses a specified number of elements in a sequence and then returns the remaining elements.
     * @template TElement
     * @param count The number of elements to skip before returning the remaining elements. If the count is zero or negative, all elements are returned.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements that occur after the specified number of skipped elements.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *      const skipFirstThree = numbers.skip(3).toArray();
     *      // skipFirstThree = [4, 5, 6]
     *
     *      const skipZero = numbers.skip(0).toArray();
     *      // skipZero = [1, 2, 3, 4, 5, 6]
     *
     *      const skipMoreThanAvailable = numbers.skip(10).toArray();
     *      // skipMoreThanAvailable = []
     *
     *      const skipNegative = numbers.skip(-5).toArray(); // Negative count is treated as 0
     *      // skipNegative = [1, 2, 3, 4, 5, 6]
     */
    skip(count: number): IEnumerable<TElement>;
    /**
     * Returns a new enumerable sequence that contains the elements from the source with the last count elements of the source sequence omitted.
     * @template TElement
     * @param count The number of elements to omit from the end of the collection. If the count is zero or negative, all elements are returned.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from source with the last count elements omitted.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *      const skipLastTwo = numbers.skipLast(2).toArray();
     *      // skipLastTwo = [1, 2, 3, 4]
     *
     *      const skipLastZero = numbers.skipLast(0).toArray();
     *      // skipLastZero = [1, 2, 3, 4, 5, 6]
     *
     *      const skipLastMoreThanAvailable = numbers.skipLast(10).toArray();
     *      // skipLastMoreThanAvailable = []
     *
     *      const skipLastNegative = numbers.skipLast(-3).toArray(); // Negative count is treated as 0
     *      // skipLastNegative = [1, 2, 3, 4, 5, 6]
     */
    skipLast(count: number): IEnumerable<TElement>;
    /**
     * Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
     * The element that first fails the condition is included in the result.
     * @template TElement
     * @param predicate The predicate function (accepting element and index) that will be used to test each element.
     * @returns {IEnumerable<TElement>} A new enumerable sequence containing elements starting from the first element that does not satisfy the predicate.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 1, 2]);
     *
     *      // Skip while less than 4
     *      const skipWhileLessThan4 = numbers.skipWhile(n => n < 4).toArray();
     *      // skipWhileLessThan4 = [4, 5, 1, 2] (Stops skipping at 4)
     *
     *      // Skip based on index
     *      const skipWhileIndexLessThan3 = numbers.skipWhile((n, index) => index < 3).toArray();
     *      // skipWhileIndexLessThan3 = [4, 5, 1, 2] (Skips elements at index 0, 1, 2)
     *
     *      // Condition never met
     *      const skipWhileAlwaysTrue = numbers.skipWhile(n => true).toArray();
     *      // skipWhileAlwaysTrue = []
     *
     *      // Condition immediately false
     *      const skipWhileAlwaysFalse = numbers.skipWhile(n => false).toArray();
     *      // skipWhileAlwaysFalse = [1, 2, 3, 4, 5, 1, 2]
     */
    skipWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    /**
     * Splits the sequence into two sequences based on a predicate.
     * The first sequence contains the elements from the start of the input sequence that satisfy the predicate continuously.
     * The second sequence contains the remaining elements, starting from the first element that failed the predicate.
     * Note: This method iterates the source sequence immediately and stores the results.
     * @template TElement
     * @param predicate The predicate function that will be used to test each element.
     * @returns {[IEnumerable<TElement>, IEnumerable<TElement>]} A tuple of two enumerable sequences.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 1, 5, 6]);
     *
     *      // Span while numbers are less than 4
     *      const [lessThan4, rest1] = numbers.span(n => n < 4);
     *      const lessThan4Array = lessThan4.toArray();
     *      // lessThan4Array = [1, 2, 3]
     *      const rest1Array = rest1.toArray();
     *      // rest1Array = [4, 1, 5, 6] (Starts from the first element failing the condition)
     *
     *      // Span while condition is always true
     *      const [allElements, rest2] = numbers.span(n => true);
     *      const allElementsArray = allElements.toArray();
     *      // allElementsArray = [1, 2, 3, 4, 1, 5, 6]
     *      const rest2Array = rest2.toArray();
     *      // rest2Array = []
     *
     *      // Span while the condition is initially false
     *      const [initialSpan, rest3] = numbers.span(n => n > 10);
     *      const initialSpanArray = initialSpan.toArray();
     *      // initialSpanArray = []
     *      const rest3Array = rest3.toArray();
     *      // rest3Array = [1, 2, 3, 4, 1, 5, 6]
     */
    span(predicate: Predicate<TElement>): [IEnumerable<TElement>, IEnumerable<TElement>];
    /**
     * Selects elements from a sequence at regular intervals (steps).
     * Includes the first element (index 0) and then every 'step'-th element after that.
     * @template TElement
     * @param step The number of elements to skip between included elements. Must be 1 or greater.
     * @returns {IEnumerable<TElement>} A new enumerable sequence containing elements at the specified step intervals.
     * @throws {InvalidArgumentException} If the step is less than 1.
     * @example
     *      const numbers = new List([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
     *
     *      // Take every 2nd element (step = 2)
     *      const step2 = numbers.step(2).toArray();
     *      // step2 = [0, 2, 4, 6, 8, 10]
     *
     *      // Take every 3rd element (step = 3)
     *      const step3 = numbers.step(3).toArray();
     *      // step3 = [0, 3, 6, 9]
     *
     *      // Step = 1 includes all elements
     *      const step1 = numbers.step(1).toArray();
     *      // step1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     *
     *      try {
     *          numbers.step(0); // Throws InvalidArgumentException
     *      } catch (e) {
     *          console.log(e.message); // Output: Step must be greater than 0.
     *      }
     */
    step(step: number): IEnumerable<TElement>;
    /**
     * Returns the sum of the values in the sequence. Assumes elements are numbers or uses a selector to get numbers.
     * @param selector The selector function that will be used to select the numeric value to sum. If not specified, the element itself is used.
     * @returns {number} The sum of the values in the sequence. Returns 0 if the sequence is empty.
     * @throws {NoElementsException} If the source is empty.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5]);
     *      const totalSum = numbers.sum();
     *      // totalSum = 15
     *
     *      interface Item { value: number; quantity: number; }
     *      const items = new List<Item>([
     *          { value: 10, quantity: 2 }, // Total value = 20
     *          { value: 5, quantity: 3 }  // Total value = 15
     *      ]);
     *      const totalItemValue = items.sum(item => item.value * item.quantity);
     *      // totalItemValue = 35
     *
     *      const emptyList = new List<number>();
     *      try {
     *          emptyList.sum(); // Throws NoElementsException
     *      } catch (e) {
     *          console.log(e.message); // Output: The sequence contains no elements.
     *      }
     */
    sum(selector?: Selector<TElement, number>): number;
    /**
     * Returns a specified number of contiguous elements from the start of a sequence.
     * @template TElement
     * @param count The number of elements to return. If the count is zero or negative, an empty sequence is returned. If the count is greater than the number of elements, all elements are returned.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the specified number of elements from the start of the input sequence.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *
     *      const takeFirstThree = numbers.take(3).toArray();
     *      // takeFirstThree = [1, 2, 3]
     *
     *      const takeZero = numbers.take(0).toArray();
     *      // takeZero = []
     *
     *      const takeMoreThanAvailable = numbers.take(10).toArray();
     *      // takeMoreThanAvailable = [1, 2, 3, 4, 5, 6]
     *
     *      const takeNegative = numbers.take(-2).toArray(); // Negative count is treated as 0
     *      // takeNegative = []
     */
    take(count: number): IEnumerable<TElement>;
    /**
     * Returns a specified number of contiguous elements from the end of a sequence.
     * @template TElement
     * @param count The number of elements to return. If the count is zero or negative, an empty sequence is returned. If the count is greater than the number of elements, all elements are returned.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the specified number of elements from the end of the input sequence.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *
     *      const takeLastTwo = numbers.takeLast(2).toArray();
     *      // takeLastTwo = [5, 6]
     *
     *      const takeLastZero = numbers.takeLast(0).toArray();
     *      // takeLastZero = []
     *
     *      const takeLastMoreThanAvailable = numbers.takeLast(10).toArray();
     *      // takeLastMoreThanAvailable = [1, 2, 3, 4, 5, 6] (Order is preserved)
     *
     *      const takeLastNegative = numbers.takeLast(-3).toArray(); // Negative count is treated as 0
     *      // takeLastNegative = []
     */
    takeLast(count: number): IEnumerable<TElement>;
    /**
     * Returns elements from a sequence as long as a specified condition is true and then skips the remaining elements.
     * @template TElement
     * @param predicate The predicate function (accepting element and index) that will be used to test each element.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 1, 5, 6]);
     *
     *      // Take while less than 4
     *      const takeWhileLessThan4 = numbers.takeWhile(n => n < 4).toArray();
     *      // takeWhileLessThan4 = [1, 2, 3] (Stops taking at 4)
     *
     *      // Take based on index
     *      const takeWhileIndexLessThan3 = numbers.takeWhile((n, index) => index < 3).toArray();
     *      // takeWhileIndexLessThan3 = [1, 2, 3] (Takes elements at index 0, 1, 2)
     *
     *      // Condition never met (the first element fails)
     *      const takeWhileAlwaysFalse = numbers.takeWhile(n => n > 10).toArray();
     *      // takeWhileAlwaysFalse = []
     *
     *      // Condition always true
     *      const takeWhileAlwaysTrue = numbers.takeWhile(n => true).toArray();
     *      // takeWhileAlwaysTrue = [1, 2, 3, 4, 1, 5, 6]
     */
    takeWhile(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    /**
     * Creates a new array from the elements of the sequence.
     * This forces evaluation of the entire sequence.
     * @template TElement
     * @returns {TElement[]} An array that contains the elements from the input sequence.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const numberArray = numbers.toArray();
     *      // numberArray = [1, 2, 3] (a standard JavaScript Array)
     *
     *      const squares = numbers.select(n => n * n); // squares is an IEnumerable
     *      const squaresArray = squares.toArray(); // squaresArray forces evaluation
     *      // squaresArray = [1, 4, 9]
     */
    toArray(): TElement[];
    /**
     * Creates a new circular linked list from the elements of the sequence.
     * Forces evaluation of the sequence.
     * @template TElement The type of elements in the sequence.
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {CircularLinkedList<TElement>} A new circular linked list that contains the elements from the input sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const circularList = letters.toCircularLinkedList();
     *      // circularList is a CircularLinkedList instance containing 'a', 'b', 'c'
     *      // circularList.firstNode?.value === 'a'
     *      // circularList.lastNode?.value === 'c'
     *      // circularList.lastNode?.next === circularList.firstNode // Circular nature
     */
    toCircularLinkedList(comparator?: EqualityComparator<TElement>): CircularLinkedList<TElement>;
    /**
     * Creates a new dictionary from the elements of the sequence.
     * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
     * @template TKey, TValue
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param valueSelector The value selector function that will be used to select the value for an element.
     * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
     * @returns {Dictionary<TKey, TValue>} A new dictionary that contains the elements from the input sequence.
     * @example
     *      interface Product { id: number; name: string; price: number; }
     *      const products = new List<Product>([
     *          { id: 1, name: 'Apple', price: 0.5 },
     *          { id: 2, name: 'Banana', price: 0.3 },
     *          { id: 3, name: 'Cherry', price: 1.0 }
     *      ]);
     *
     *      // Create a dictionary mapping ID to Product Name
     *      const productDict = products.toDictionary(p => p.id, p => p.name);
     *      // productDict.get(2) === 'Banana'
     *      // productDict.size === 3
     *
     *      // Example with KeyValuePair source
     *      const pairs = new List([
     *          new KeyValuePair('one', 1),
     *          new KeyValuePair('two', 2)
     *      ]);
     *      const dictFromPairs = pairs.toDictionary(kv => kv.key, kv => kv.value);
     *      // dictFromPairs.get('one') === 1
     *
     *      // Example causing error due to a duplicate key
     *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
     *      try {
     *          duplicateKeys.toDictionary(item => item.key, item => item.val);
     *      } catch (e) {
     *          console.log(e.message); // Output likely: "An item with the same key has already been added."
     *      }
     */
    toDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): Dictionary<TKey, TValue>;
    /**
     * Creates a new enumerable set from the elements of the sequence. Duplicate elements are ignored.
     * Forces evaluation of the sequence.
     * @template TElement
     * @returns {EnumerableSet<TElement>} An enumerable set that contains the distinct elements from the input sequence.
     * @example
     *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
     *      const numberSet = numbers.toEnumerableSet();
     *      // numberSet contains {1, 2, 3, 4, 5}
     *      // numberSet.size === 5
     *      // numberSet.contains(2) === true
     *      // numberSet.toArray() results in [1, 2, 3, 4, 5] (order depends on Set implementation)
     */
    toEnumerableSet(): EnumerableSet<TElement>;
    /**
     * Creates a new immutable dictionary from the elements of the sequence.
     * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
     * @template TKey, TValue
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param valueSelector The value selector function that will be used to select the value for an element.
     * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
     * @returns {ImmutableDictionary<TKey, TValue>} A new immutable dictionary that contains the elements from the input sequence.
     * @example
     *      interface Product { id: number; name: string; price: number; }
     *      const products = new List<Product>([
     *          { id: 1, name: 'Apple', price: 0.5 },
     *          { id: 2, name: 'Banana', price: 0.3 }
     *      ]);
     *
     *      const immutableProductDict = products.toImmutableDictionary(p => p.id, p => p.name);
     *      // immutableProductDict.get(1) === 'Apple'
     *      // immutableProductDict.size === 2
     *      // Attempting immutableProductDict.add(3, 'Cherry') would throw an error or return a new dictionary.
     */
    toImmutableDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    /**
     * Creates a new immutable list from the elements of the sequence.
     * Forces evaluation of the sequence.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {ImmutableList<TElement>} A new immutable list that contains the elements from the input sequence.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const immutableList = numbers.toImmutableList();
     *      // immutableList contains [1, 2, 3]
     *      // immutableList.size === 3
     *      // immutableList.get(0) === 1
     *      // Attempting immutableList.add(4) would throw an error or return a new list.
     */
    toImmutableList(comparator?: EqualityComparator<TElement>): ImmutableList<TElement>;
    /**
     * Creates a new immutable priority queue from the elements of the sequence.
     * Forces evaluation of the sequence. Elements are ordered based on the comparator.
     * @template TElement
     * @param comparator The order comparator function that will be used to compare two elements. If not specified, the default order comparer will be used.
     * @returns {ImmutablePriorityQueue<TElement>} A new immutable priority queue that contains the elements from the input sequence.
     * @example
     *      const numbers = new List([5, 1, 3, 4, 2]);
     *      // Default comparator assumes min-heap (smaller numbers have higher priority)
     *      const immutableMinQueue = numbers.toImmutablePriorityQueue();
     *      // immutableMinQueue.peek() === 1
     *
     *      // Custom comparator for max-heap
     *      const immutableMaxQueue = numbers.toImmutablePriorityQueue((a, b) => b - a); // Larger numbers first
     *      // immutableMaxQueue.peek() === 5
     *
     *      // Attempting immutableMinQueue.enqueue(0) would return a new queue.
     */
    toImmutablePriorityQueue(comparator?: OrderComparator<TElement>): ImmutablePriorityQueue<TElement>;
    /**
     * Creates a new immutable queue from the elements of the sequence (FIFO).
     * Forces evaluation of the sequence.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {ImmutableQueue<TElement>} A new immutable queue that contains the elements from the input sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const immutableQueue = letters.toImmutableQueue();
     *      // immutableQueue.peek() === 'a'
     *      // immutableQueue.size === 3
     *      // Attempting immutableQueue.enqueue('d') would return a new queue.
     */
    toImmutableQueue(comparator?: EqualityComparator<TElement>): ImmutableQueue<TElement>;
    /**
     * Creates a new immutable set from the elements of the sequence. Duplicate elements are ignored.
     * Forces evaluation of the sequence.
     * @template TElement
     * @returns {ImmutableSet<TElement>} A new immutable set that contains the distinct elements from the input sequence.
     * @example
     *      const numbers = new List([1, 2, 2, 3, 1]);
     *      const immutableSet = numbers.toImmutableSet();
     *      // immutableSet contains {1, 2, 3}
     *      // immutableSet.size === 3
     *      // immutableSet.contains(2) === true
     *      // Attempting immutableSet.add(4) would return a new set.
     */
    toImmutableSet(): ImmutableSet<TElement>;
    /**
     * Creates a new immutable sorted dictionary from the elements of the sequence.
     * Forces evaluation of the sequence. Keys are sorted. Throws if duplicate keys are encountered.
     * @template TKey, TValue
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param valueSelector The value selector function that will be used to select the value for an element.
     * @param keyComparator The key comparator function that will be used to compare two keys for sorting. If not specified, the default order comparer will be used.
     * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
     * @returns {ImmutableSortedDictionary<TKey, TValue>} A new immutable sorted dictionary that contains the elements from the input sequence.
     * @example
     *      interface Product { id: number; name: string; }
     *      const products = new List<Product>([
     *          { id: 3, name: 'Cherry' },
     *          { id: 1, name: 'Apple' },
     *          { id: 2, name: 'Banana' }
     *      ]);
     *
     *      const immutableSortedDict = products.toImmutableSortedDictionary(p => p.id, p => p.name);
     *      // Keys will be sorted: 1, 2, 3
     *      // immutableSortedDict.get(2) === 'Banana'
     *      // immutableSortedDict.keys().toArray() === [1, 2, 3]
     *      // Attempting immutableSortedDict.add(4, 'Date') would return a new dictionary.
     */
    toImmutableSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    /**
     * Creates a new immutable sorted set from the elements of the sequence. Duplicate elements are ignored.
     * Forces evaluation of the sequence. Elements are sorted.
     * @template TElement
     * @param comparator The order comparator function that will be used to compare two elements for sorting. If not specified, the default order comparer will be used.
     * @returns {ImmutableSortedSet<TElement>} A new immutable sorted set that contains the distinct, sorted elements from the input sequence.
     * @example
     *      const numbers = new List([5, 1, 3, 1, 4, 2, 5]);
     *      const immutableSortedSet = numbers.toImmutableSortedSet();
     *      // immutableSortedSet contains {1, 2, 3, 4, 5} in sorted order
     *      // immutableSortedSet.toArray() === [1, 2, 3, 4, 5]
     *      // immutableSortedSet.contains(3) === true
     *      // immutableSortedSet.size === 5
     *      // Attempting immutableSortedSet.add(0) would return a new set.
     */
    toImmutableSortedSet(comparator?: OrderComparator<TElement>): ImmutableSortedSet<TElement>;
    /**
     * Creates a new immutable stack from the elements of the sequence (LIFO).
     * Forces evaluation of the sequence. The last element of the source sequence becomes the top of the stack.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {ImmutableStack<TElement>} A new immutable stack that contains the elements from the input sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c']); // 'c' is the last element
     *      const immutableStack = letters.toImmutableStack();
     *      // immutableStack.peek() === 'c'
     *      // immutableStack.size === 3
     *      // Attempting immutableStack.push('d') would return a new stack.
     */
    toImmutableStack(comparator?: EqualityComparator<TElement>): ImmutableStack<TElement>;
    /**
     * Creates a new linked list from the elements of the sequence.
     * Forces evaluation of the sequence.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {LinkedList<TElement>} A new linked list that contains the elements from the input sequence.
     * @example
     *      const numbers = new List([10, 20, 30]);
     *      const linkedList = numbers.toLinkedList();
     *      // linkedList is a LinkedList instance
     *      // linkedList.firstNode?.value === 10
     *      // linkedList.lastNode?.value === 30
     *      // linkedList.size() === 3
     */
    toLinkedList(comparator?: EqualityComparator<TElement>): LinkedList<TElement>;
    /**
     * Creates a new list from the elements of the sequence.
     * Forces evaluation of the sequence.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {List<TElement>} A new list that contains the elements from the input sequence.
     * @example
     *      const numbers = Enumerable.range(1, 3); // Creates IEnumerable [1, 2, 3]
     *      const list = numbers.toList();
     *      // list is a List instance containing [1, 2, 3]
     *      // list.get(0) === 1
     *      // list.size() === 3
     *
     *      // Creates a copy of an existing list
     *      const originalList = new List(['a', 'b']);
     *      const newList = originalList.toList();
     *      // newList !== originalList (it's a new instance)
     *      // newList.toArray() results in ['a', 'b']
     */
    toList(comparator?: EqualityComparator<TElement>): List<TElement>;
    /**
     * Creates a new lookup from the elements of the sequence. A lookup is similar to a dictionary but allows multiple values per key.
     * Forces evaluation of the sequence.
     * @template TKey
     * @template TValue
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param valueSelector The value selector function that will be used to select the value for an element.
     * @param keyComparator The key comparator function that will be used to compare two keys. If not specified, the default equality comparer will be used.
     * @returns {ILookup<TKey, TValue>} A new lookup that contains the elements from the input sequence, grouped by key.
     * @example
     *      interface Pet { name: string; species: string; age: number; }
     *      const pets = new List<Pet>([
     *          { name: 'Fluffy', species: 'Cat', age: 3 },
     *          { name: 'Buddy', species: 'Dog', age: 5 },
     *          { name: 'Whiskers', species: 'Cat', age: 2 },
     *          { name: 'Rex', species: 'Dog', age: 7 }
     *      ]);
     *
     *      // Group pet names by species
     *      const lookup = pets.toLookup(pet => pet.species, pet => pet.name);
     *
     *      // lookup.count() === 2 (number of distinct keys: 'Cat', 'Dog')
     *      // lookup.contains('Cat') === true
     *
     *      const catNames = lookup.get('Cat').toArray();
     *      // catNames = ['Fluffy', 'Whiskers']
     *
     *      const dogNames = lookup.get('Dog').toArray();
     *      // dogNames = ['Buddy', 'Rex']
     *
     *      const fishNames = lookup.get('Fish').toArray(); // Key not present
     *      // fishNames = []
     */
    toLookup<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>): ILookup<TKey, TValue>;
    /**
     * Converts this enumerable to a JavaScript Map.
     * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
     * @template TKey
     * @template TValue
     * @param keySelector The selector that will be used to select the property that will be used as the key of the map.
     * @param valueSelector The selector that will be used to select the property that will be used as the value of the map.
     * @returns {Map<TKey, TValue>} A Map representation of this sequence.
     * @example
     *      interface Product { id: number; name: string; price: number; }
     *      const products = new List<Product>([
     *          { id: 1, name: 'Apple', price: 0.5 },
     *          { id: 2, name: 'Banana', price: 0.3 },
     *          { id: 3, name: 'Cherry', price: 1.0 }
     *      ]);
     *
     *      // Create a Map mapping ID to Product Name
     *      const productMap = products.toMap(p => p.id, p => p.name);
     *      // productMap instanceof Map === true
     *      // productMap.get(2) === 'Banana'
     *      // productMap.size === 3
     *
     *      // Example causing error due to a duplicate key
     *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
     *      try {
     *          duplicateKeys.toMap(item => item.key, item => item.val);
     *      } catch (e) {
     *          console.log(e.message); // Map structure prevents duplicate keys by default. Behavior might depend on the underlying Map implementation if custom logic is used.
     *      }
     */
    toMap<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Map<TKey, TValue>;
    /**
     * Converts this enumerable to a JavaScript object (Record).
     * Forces evaluation of the sequence. If duplicate keys are encountered, the last value for that key overwrites previous ones.
     * @template TKey
     * @template TValue
     * @param keySelector The selector that will be used to select the property that will be used as the key of the object. Must return string, number, or symbol.
     * @param valueSelector The selector that will be used to select the property that will be used as the value of the object.
     * @returns {Record<TKey, TValue>} An object that contains the elements of the sequence.
     * @example
     *      interface Product { id: string; name: string; price: number; }
     *      const products = new List<Product>([
     *          { id: 'A1', name: 'Apple', price: 0.5 },
     *          { id: 'B2', name: 'Banana', price: 0.3 },
     *          { id: 'C3', name: 'Cherry', price: 1.0 }
     *      ]);
     *
     *      // Create an object mapping ID to Product Price
     *      const productObject = products.toObject(p => p.id, p => p.price);
     *      // productObject = { A1: 0.5, B2: 0.3, C3: 1.0 }
     *      // productObject['B2'] === 0.3
     *
     *      // Example with duplicate keys (last one wins)
     *      const duplicateKeys = new List([
     *          { key: 'a', val: 1 },
     *          { key: 'b', val: 2 },
     *          { key: 'a', val: 3 } // This value for 'a' will overwrite the first one
     *      ]);
     *      const objectFromDups = duplicateKeys.toObject(item => item.key, item => item.val);
     *      // objectFromDups = { a: 3, b: 2 }
     */
    toObject<TKey extends string | number | symbol, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>): Record<TKey, TValue>;
    /**
     * Creates a new priority queue from the elements of the sequence.
     * Forces evaluation of the sequence. Elements are ordered based on the comparator.
     * @template TElement
     * @param comparator The order comparator function that will be used to compare two elements. If not specified, the default order comparer will be used (min-heap).
     * @returns {PriorityQueue<TElement>} A new priority queue that contains the elements from the input sequence.
     * @example
     *      const numbers = new List([5, 1, 3, 4, 2]);
     *      // Default comparator assumes min-heap (smaller numbers have higher priority)
     *      const minQueue = numbers.toPriorityQueue();
     *      // minQueue.peek() === 1
     *      // minQueue.dequeue() === 1
     *      // minQueue.peek() === 2
     *
     *      // Custom comparator for max-heap
     *      const maxQueue = numbers.toPriorityQueue((a, b) => b - a); // Larger numbers first
     *      // maxQueue.peek() === 5
     *      // maxQueue.dequeue() === 5
     *      // maxQueue.peek() === 4
     */
    toPriorityQueue(comparator?: OrderComparator<TElement>): PriorityQueue<TElement>;
    /**
     * Creates a new queue from the elements of the sequence (FIFO).
     * Forces evaluation of the sequence.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {Queue<TElement>} A new queue that contains the elements from the input sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c']);
     *      const queue = letters.toQueue();
     *      // queue.peek() === 'a'
     *      // queue.size() === 3
     *      // queue.dequeue() === 'a'
     *      // queue.peek() === 'b'
     */
    toQueue(comparator?: EqualityComparator<TElement>): Queue<TElement>;
    /**
     * Creates a new JavaScript Set from the elements of the sequence. Duplicate elements are ignored.
     * Forces evaluation of the sequence.
     * @template TElement
     * @returns {Set<TElement>} A new Set that contains the distinct elements from the input sequence.
     * @example
     *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
     *      const numberSet = numbers.toSet();
     *      // numberSet instanceof Set === true
     *      // numberSet contains {1, 2, 3, 4, 5}
     *      // numberSet.size === 5
     *      // numberSet.has(2) === true
     */
    toSet(): Set<TElement>;
    /**
     * Creates a new sorted dictionary from the elements of the sequence.
     * Forces evaluation of the sequence. Keys are sorted. Throws if duplicate keys are encountered.
     * @template TKey
     * @template TValue
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param valueSelector The value selector function that will be used to select the value for an element.
     * @param keyComparator The key comparator function that will be used to compare two keys for sorting. If not specified, the default order comparer will be used.
     * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
     * @returns {SortedDictionary<TKey, TValue>} A new sorted dictionary that contains the elements from the input sequence.
     * @example
     *      interface Product { id: number; name: string; }
     *      const products = new List<Product>([
     *          { id: 3, name: 'Cherry' },
     *          { id: 1, name: 'Apple' },
     *          { id: 2, name: 'Banana' }
     *      ]);
     *
     *      const sortedDict = products.toSortedDictionary(p => p.id, p => p.name);
     *      // Keys will be sorted: 1, 2, 3
     *      // sortedDict.get(2) === 'Banana'
     *      // sortedDict.keys().toArray() results in [1, 2, 3]
     *
     *      // Example causing error due to duplicate key
     *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
     *      try {
     *          duplicateKeys.toSortedDictionary(item => item.key, item => item.val);
     *      } catch (e) {
     *          console.log(e.message); // Output likely: "An item with the same key has already been added."
     *      }
     */
    toSortedDictionary<TKey, TValue>(keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): SortedDictionary<TKey, TValue>;
    /**
     * Creates a new sorted set from the elements of the sequence. Duplicate elements are ignored.
     * Forces evaluation of the sequence. Elements are sorted.
     * @template TElement
     * @param comparator The order comparator function that will be used to compare two elements for sorting. If not specified, the default order comparer will be used.
     * @returns {SortedSet<TElement>} A new sorted set that contains the distinct, sorted elements from the input sequence.
     * @example
     *      const numbers = new List([5, 1, 3, 1, 4, 2, 5]);
     *      const sortedSet = numbers.toSortedSet();
     *      // sortedSet contains {1, 2, 3, 4, 5} in sorted order
     *      // sortedSet.toArray() results in [1, 2, 3, 4, 5]
     *      // sortedSet.contains(3) === true
     *      // sortedSet.size() === 5
     */
    toSortedSet(comparator?: OrderComparator<TElement>): SortedSet<TElement>;
    /**
     * Creates a new stack from the elements of the sequence (LIFO).
     * Forces evaluation of the sequence. The last element of the source sequence becomes the top of the stack.
     * @template TElement
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {Stack<TElement>} A new stack that contains the elements from the input sequence.
     * @example
     *      const letters = new List(['a', 'b', 'c']); // 'c' is the last element
     *      const stack = letters.toStack();
     *      // stack.peek() === 'c'
     *      // stack.size() === 3
     *      // stack.pop() === 'c'
     *      // stack.peek() === 'b'
     */
    toStack(comparator?: EqualityComparator<TElement>): Stack<TElement>;
    /**
     * Produces the set union of two sequences by using an equality comparer.
     * The result contains all unique elements from both sequences.
     * @template TElement
     * @param iterable The iterable sequence whose distinct elements form the second set for the union.
     * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from both input sequences, excluding duplicates. Order is preserved from the original sequences, with elements from the first sequence appearing before elements from the second.
     * @example
     *      const numbers1 = new List([1, 2, 3, 3]);
     *      const numbers2 = new List([3, 4, 5, 4]);
     *      const unionResult = numbers1.union(numbers2).toArray();
     *      // unionResult = [1, 2, 3, 4, 5] (Order: elements from numbers1 first, then unique from numbers2)
     *
     *      // Using custom object comparison
     *      interface Item { id: number; value: string; }
     *      const items1 = new List<Item>([{ id: 1, value: 'A' }, { id: 2, value: 'B' }]);
     *      const items2 = new List<Item>([{ id: 2, value: 'B_alt' }, { id: 3, value: 'C' }]);
     *      const itemUnion = items1.union(items2, (a, b) => a.id === b.id).toArray();
     *      // itemUnion = [
     *      //   { id: 1, value: 'A' }, // From items1
     *      //   { id: 2, value: 'B' }, // From items1 (id=2 from items2 is considered duplicate by comparator)
     *      //   { id: 3, value: 'C' }  // From items2
     *      // ]
     */
    union(iterable: Iterable<TElement>, comparator?: EqualityComparator<TElement>): IEnumerable<TElement>;
    /**
     * Produces the set union of two sequences by using a key selector function.
     * The result contains all elements from both sequences whose selected keys are unique.
     * @template TElement, TKey
     * @param iterable The iterable sequence whose distinct elements form the second set for the union.
     * @param keySelector The key selector function that will be used to select the key for an element.
     * @param comparator The equality comparator function that will be used to compare two keys. If not specified, the default equality comparer will be used.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from both input sequences, excluding elements with duplicate keys based on the selector. Order is preserved.
     * @example
     *      interface Product { code: string; name: string; }
     *      const store1Products = new List<Product>([
     *          { code: 'A1', name: 'Apple' },
     *          { code: 'B2', name: 'Banana' }
     *      ]);
     *      const store2Products = new List<Product>([
     *          { code: 'B2', name: 'Banana V2' }, // Duplicate code 'B2'
     *          { code: 'C3', name: 'Cherry' }
     *      ]);
     *
     *      // Union based on product code
     *      const allUniqueProducts = store1Products.unionBy(
     *          store2Products,
     *          p => p.code // Select code as the key for comparison
     *      ).toArray();
     *      // allUniqueProducts = [
     *      //   { code: 'A1', name: 'Apple' },   // From store1
     *      //   { code: 'B2', name: 'Banana' },  // From store1 (item with code 'B2' from store2 is ignored)
     *      //   { code: 'C3', name: 'Cherry' }    // From store2
     *      // ]
     *
     *      // Example with case-insensitive key comparison
     *      const listA = new List([{ val: 'a', id: 1 }, { val: 'b', id: 2 }]);
     *      const listB = new List([{ val: 'B', id: 3 }, { val: 'c', id: 4 }]); // 'B' has same key as 'b' case-insensitively
     *      const unionCaseInsensitive = listA.unionBy(
     *          listB,
     *          item => item.val,
     *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
     *      ).toArray();
     *      // unionCaseInsensitive = [
     *      //  { val: 'a', id: 1 }, // From listA
     *      //  { val: 'b', id: 2 }, // From listA ('B' from listB is ignored)
     *      //  { val: 'c', id: 4 }  // From listB
     *      // ]
     */
    unionBy<TKey>(iterable: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>): IEnumerable<TElement>;
    /**
     * Filters a sequence of values based on a predicate.
     * @template TElement
     * @param predicate The predicate function (accepting element and index) that will be used to test each element. Return true to keep the element, false to filter it out.
     * @returns {IEnumerable<TElement>} A new enumerable sequence that contains elements from the input sequence that satisfy the condition.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8]);
     *
     *      // Get only even numbers
     *      const evens = numbers.where(n => n % 2 === 0).toArray();
     *      // evens = [2, 4, 6, 8]
     *
     *      // Get numbers greater than 3 at an odd index
     *      const complexFilter = numbers.where((n, index) => n > 3 && index % 2 !== 0).toArray();
     *      // Indices: 0, 1, 2, 3, 4, 5, 6, 7
     *      // Elements:1, 2, 3, 4, 5, 6, 7, 8
     *      // Filter checks:
     *      // - index 1 (value 2): 2 > 3 is false
     *      // - index 3 (value 4): 4 > 3 is true, index 3 is odd. Keep 4.
     *      // - index 5 (value 6): 6 > 3 is true, index 5 is odd. Keep 6.
     *      // - index 7 (value 8): 8 > 3 is true, index 7 is odd. Keep 8.
     *      // complexFilter = [4, 6, 8]
     *
     *      interface Product { name: string; price: number; }
     *      const products = new List<Product>([
     *          { name: 'Apple', price: 0.5 },
     *          { name: 'Banana', price: 0.3 },
     *          { name: 'Cherry', price: 1.0 }
     *      ]);
     *      const cheapProducts = products.where(p => p.price < 0.6).toArray();
     *      // cheapProducts = [ { name: 'Apple', price: 0.5 }, { name: 'Banana', price: 0.3 } ]
     */
    where(predicate: IndexedPredicate<TElement>): IEnumerable<TElement>;
    /**
     * Returns an enumerable sequence of sliding windows of the specified size over the source sequence.
     * Each window is an IEnumerable itself.
     * @template TElement
     * @param size The size of the windows. Must be 1 or greater.
     * @returns {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence where each element is a window (as an IEnumerable) of the specified size.
     * @throws {InvalidArgumentException} If size is less than or equal to 0.
     * @example
     *      const numbers = new List([1, 2, 3, 4, 5, 6]);
     *
     *      // Get windows of size 3
     *      const windowsOf3 = numbers.windows(3)
     *          .select(window => window.toArray()) // Convert each window IEnumerable to an array for clarity
     *          .toArray();
     *      // windowsOf3 = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
     *
     *      // Get windows of size 1
     *      const windowsOf1 = numbers.windows(1)
     *          .select(window => window.toArray())
     *          .toArray();
     *      // windowsOf1 = [[1], [2], [3], [4], [5], [6]]
     *
     *      // Size larger than the list returns an empty sequence
     *      const windowsOf10 = numbers.windows(10).toArray();
     *      // windowsOf10 = []
     *
     *      // Size equal to the list returns one window
     *      const windowsOf6 = numbers.windows(6)
     *          .select(window => window.toArray())
     *          .toArray();
     *      // windowsOf6 = [[1, 2, 3, 4, 5, 6]]
     *
     *      try {
     *          numbers.windows(0); // Throws InvalidArgumentException
     *      } catch (e) {
     *          console.log(e.message); // Output: Size must be greater than 0.
     *      }
     */
    windows(size: number): IEnumerable<IEnumerable<TElement>>;
    /**
     * Applies a specified function to the corresponding elements of two sequences, producing a sequence of tuples.
     * If the two sequences are of different lengths, the resulting sequence will have the length of the shorter sequence.
     * @template TElement The type of elements in the first sequence.
     * @template TSecond The type of elements in the second sequence.
     * @param iterable The iterable sequence to merge with the first sequence.
     * @returns {IEnumerable<[TElement, TSecond]>} A new enumerable sequence that contains tuples [TElement, TSecond] of the merged elements.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const letters = new List(['A', 'B', 'C', 'D']); // Longer than numbers
     *      const symbols = new List(['!', '?']); // Shorter than numbers
     *
     *      // Zip with the longer second sequence (result length = shorter sequence length)
     *      const zippedNumbersLetters = numbers.zip(letters).toArray();
     *      // zippedNumbersLetters = [[1, 'A'], [2, 'B'], [3, 'C']]
     *
     *      // Zip with the shorter second sequence (result length = shorter sequence length)
     *      const zippedNumbersSymbols = numbers.zip(symbols).toArray();
     *      // zippedNumbersSymbols = [[1, '!'], [2, '?']]
     */
    zip<TSecond>(iterable: Iterable<TSecond>): IEnumerable<[TElement, TSecond]>;
    /**
     * Applies a specified function (zipper) to the corresponding elements of two sequences, producing a sequence of the results.
     * If the two sequences are of different lengths, the resulting sequence will have the length of the shorter sequence.
     * @template TElement The type of elements in the first sequence.
     * @template TSecond The type of elements in the second sequence.
     * @template TResult The type of elements in the result sequence, as determined by the zipper function.
     * @param iterable The iterable sequence to merge with the first sequence.
     * @param zipper The function that specifies how to merge the elements from the two sequences into a result element.
     * @returns {IEnumerable<TResult>} A new enumerable sequence that contains the result of applying the zipper function to corresponding elements.
     * @example
     *      const numbers = new List([1, 2, 3]);
     *      const letters = new List(['A', 'B', 'C']);
     *
     *      // Combine numbers and letters into strings using the zipper
     *      const combinedStrings = numbers.zip(
     *      letters,
     *          (num, char) => `${num}-${char}` // Zipper function
     *      ).toArray();
     *      // combinedStrings = ["1-A", "2-B", "3-C"]
     *
     *      // Sum corresponding elements using the zipper
     *      const listA = new List([10, 20, 30]);
     *      const listB = new List([5, 15, 25, 35]); // listB is longer
     *      const sums = listA.zip(
     *      listB,
     *          (a, b) => a + b // Zipper function
     *      ).toArray();
     *      // sums = [15, 35, 55] (Length limited by the shorter listA)
     */
    zip<TSecond, TResult>(iterable: Iterable<TSecond>, zipper: Zipper<TElement, TSecond, TResult>): IEnumerable<TResult>;
}

export declare interface IGroup<TKey, TElement> extends IEnumerable<TElement> {
    readonly key: TKey;
    readonly source: IEnumerable<TElement>;
}

export declare interface IImmutableCollection<TElement> extends IReadonlyCollection<TElement> {
    /**
     * Adds the given element to this collection.
     * @param element The element that will be added to this collection.
     * @returns {IImmutableCollection} A new collection with the added element.
     */
    add(element: TElement): IImmutableCollection<TElement>;
    /**
     * Adds all elements from the provided collection to this collection.
     * @param collection The collection whose element will be added to this collection.
     * @returns {IImmutableCollection} A new collection with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): IImmutableCollection<TElement>;
    /**
     * Removes all elements from this collection.
     * @returns {IImmutableCollection} An empty collection.
     */
    clear(): IImmutableCollection<TElement>;
}

export declare interface IImmutableDictionary<TKey, TValue> extends IReadonlyDictionary<TKey, TValue> {
    readonly length: number;
    /**
     * Adds the specified key and value to the dictionary.
     * @param key The key of the element to add.
     * @param value The value of the element to add.
     * @returns {IImmutableDictionary} A new dictionary with the added key-value pair.
     */
    add(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
    /**
     * Removes all elements from this dictionary.
     * @returns {IImmutableDictionary} An empty dictionary.
     */
    clear(): IImmutableDictionary<TKey, TValue>;
    /**
     * Adds a value to the dictionary.
     * If the key already exists, the value associated with the key will be replaced by the new value.
     * @param key The key of the element to add or update.
     * @param value The value which will be added or updated.
     * @returns {IImmutableDictionary} A new dictionary with the added or updated key-value pair.
     */
    put(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
    /**
     * Removes the specified key and its associated value from this dictionary.
     * @param key They key whose itself and its associated value will be removed from the dictionary.
     * @returns {IImmutableDictionary} A new dictionary without the specified key-value pair.
     */
    remove(key: TKey): IImmutableDictionary<TKey, TValue>;
    /**
     * Sets the value of the given key.
     * @param key The key whose value will be set.
     * @param value The new value of the key
     * @returns {IImmutableDictionary} A new dictionary with the updated value.
     */
    set(key: TKey, value: TValue): IImmutableDictionary<TKey, TValue>;
}

export declare interface IList<TElement> extends IReadonlyList<TElement>, IRandomAccessCollection<TElement> {
    /**
     * Adds the given element to the specified index of this list.
     * @param element The element that will be added to this list.
     * @param index The index that the element will be added to.
     * @returns {boolean} true if the element is added to the list.
     * @throws {Error} If the index is out of bounds.
     */
    addAt(element: TElement, index: number): boolean;
    /**
     * Removes the element at the given index from this list.
     * @param index The index from which the element will be removed.
     * @returns The removed element.
     * @throws {Error} If the index is out of bounds.
     */
    removeAt(index: number): TElement;
    /**
     * Replaces the element at the given index with the given element.
     * @param {number} index The index at which the element will be replaced
     * @param element The element which will replace the element at the given index.
     * @returns The old replaced element
     * @throws {Error} If the index is out of bounds.
     */
    set(index: number, element: TElement): TElement;
    /**
     * Sorts the lists according to the specified comparator.
     * If not specified, the list will be sorted by using the natural ordering of the elements.
     * @param comparator The comparator used to compare the list elements.
     */
    sort(comparator?: OrderComparator<TElement>): void;
}

export declare interface ILookup<TKey, TElement> extends IEnumerable<IGroup<TKey, TElement>> {
    /**
     * Returns the collection of elements that has the specified key.
     * @param key The key of the desired group.
     * @returns The collection of elements that has the specified key.
     */
    get(key: TKey): IEnumerable<TElement>;
    /**
     * Determines whether a specified key exists in the ILookup<TKey, TElement>.
     * @param key The key to locate in the ILookup<TKey, TElement>.
     * @returns true if key is in the ILookup<TKey, TElement>; otherwise, false.
     */
    hasKey(key: TKey): boolean;
    /**
     * Gets the number of key/value collection pairs in the ILookup<TKey, TElement>.
     * @returns The number of key/value collection pairs in the ILookup<TKey, TElement>.
     */
    size(): number;
    /**
     * Gets the number of elements in the ILookup<TKey, TElement>.
     * @returns The number of elements in the ILookup<TKey, TElement>.
     */
    get length(): number;
}

export declare class ImmutableDictionary<TKey, TValue> extends AbstractImmutableDictionary<TKey, TValue> {
    #private;
    private constructor();
    static create<TKey, TValue>(): ImmutableDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<KeyValuePair<TKey, TValue>>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<[TKey, TValue]>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<KeyValuePair<TKey, TValue>> | Iterable<[TKey, TValue]>, valueComparator?: EqualityComparator<TValue>): ImmutableDictionary<TKey, TValue>;
    [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    add(key: TKey, value: TValue): ImmutableDictionary<TKey, TValue>;
    clear(): ImmutableDictionary<TKey, TValue>;
    containsKey(key: TKey): boolean;
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    entries(): IterableIterator<[TKey, TValue]>;
    get(key: TKey): TValue | null;
    keys(): ISet<TKey>;
    put(key: TKey, value: TValue): ImmutableDictionary<TKey, TValue>;
    remove(key: TKey): ImmutableDictionary<TKey, TValue>;
    set(key: TKey, value: TValue): ImmutableDictionary<TKey, TValue>;
    size(): number;
    values(): ICollection<TValue>;
    private getCopiedDictionary;
    get length(): number;
}

export declare class ImmutableList<TElement> extends AbstractRandomAccessImmutableCollection<TElement> implements IReadonlyList<TElement> {
    #private;
    private constructor();
    static create<TElement>(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>): ImmutableList<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to the end of this list.
     * @param element The element that will be added to this list.
     * @returns {ImmutableList} A new list with the added element.
     */
    add(element: TElement): ImmutableList<TElement>;
    /**
     * Adds all elements from the provided collection to this list.
     * @param collection The collection whose element will be added to this list.
     * @returns {ImmutableList} A new list with the added element.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableList<TElement>;
    addAt(element: TElement, index: number): ImmutableList<TElement>;
    any(predicate?: Predicate<TElement>): boolean;
    /**
     * Removes all elements from this list.
     * @returns {ImmutableList} An empty list.
     */
    clear(): ImmutableList<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    containsAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    count(predicate?: Predicate<TElement>): number;
    elementAt(index: number): TElement;
    elementAtOrDefault(index: number): TElement | null;
    entries(): IterableIterator<[number, TElement]>;
    first(predicate?: Predicate<TElement>): TElement;
    firstOrDefault(predicate?: Predicate<TElement>): TElement | null;
    get(index: number): TElement;
    /**
     * Returns a new list that contains the elements in the specified range.
     * @param index The index at which the range starts.
     * @param count The number of elements in the range.
     * @returns {ImmutableList} A new list that contains the elements in the specified range.
     */
    getRange(index: number, count: number): ImmutableList<TElement>;
    indexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    last(predicate?: Predicate<TElement>): TElement;
    lastIndexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    lastOrDefault(predicate?: Predicate<TElement>): TElement | null;
    /**
     * Removes the element at the given index from this list.
     * @param element The element that will be removed from the list.
     * @returns {ImmutableList} A new list without the specified element.
     */
    remove(element: TElement): ImmutableList<TElement>;
    /**
     * Removes all elements from this list that are contained in the specified collection.
     * @param collection The collection whose elements will be removed from this list.
     * @returns {ImmutableList} A new list without the elements in the specified collection.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableList<TElement>;
    /**
     * Removes the element at the given index from this list.
     * @param index The index from which the element will be removed.
     * @returns {ImmutableList} A new list without the element at the specified index.
     */
    removeAt(index: number): ImmutableList<TElement>;
    /**
     * Removes all elements from this list that satisfies the specified predicate.
     * @param predicate The predicate used to remove elements from this list.
     * @returns {ImmutableList} A new list without the elements that satisfies the specified predicate.
     */
    removeIf(predicate: Predicate<TElement>): ImmutableList<TElement>;
    /**
     * Removes all elements from this list except the ones that are contained in the specified collection.
     * @param collection The collection whose elements will be retained in this list.
     * @returns {ImmutableList} A new list with only the elements in the specified collection.
     */
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableList<TElement>;
    /**
     * Replaces the element at the given index with the given element.
     * @param index The index at which the element will be replaced
     * @param element The element which will replace the element at the given index.
     * @returns {ImmutableList} A new list with the replaced element.
     */
    set(index: number, element: TElement): ImmutableList<TElement>;
    size(): number;
    /**
     * Sorts the list according to the specified comparator. The original list will not be modified.
     * @param comparator The comparator used to compare the list elements.
     * @returns {ImmutableList} A new list that is sorted according to the specified comparator.
     */
    sort(comparator?: OrderComparator<TElement>): ImmutableList<TElement>;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

export declare class ImmutablePriorityQueue<TElement> extends AbstractImmutableCollection<TElement> {
    #private;
    private constructor();
    /**
     * Creates a new immutable priority queue.
     * @template TElement The type of elements in the queue.
     * @param iterable An optional iterable to populate the queue with.
     * @param comparator An optional order comparator for the elements.
     * @returns {ImmutablePriorityQueue<TElement>} A new immutable priority queue.
     */
    static create<TElement>(iterable?: Iterable<TElement>, comparator?: OrderComparator<TElement>): ImmutablePriorityQueue<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to this priority queue.
     * @template TElement The type of elements in the queue.
     * @param element The element to add.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue with the added element.
     */
    add(element: TElement): ImmutablePriorityQueue<TElement>;
    /**
     * Adds all elements from the provided collection to this priority queue.
     * @template TElement The type of elements in the queue.
     * @template TSource The type of elements in the collection.
     * @param collection The collection whose elements will be added.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutablePriorityQueue<TElement>;
    /**
     * Removes all elements from this priority queue.
     * @returns {ImmutablePriorityQueue<TElement>} An empty priority queue with the same comparator.
     */
    clear(): ImmutablePriorityQueue<TElement>;
    /**
     * Checks if the priority queue contains the specified element.
     * Note: This operation can be inefficient on a heap (O(N)).
     * @param element The element to locate.
     * @returns {boolean} True if the element is found; otherwise, false.
     */
    contains(element: TElement): boolean;
    /**
     * Checks if this collection contains all the elements of the given collection.
     * Note: This operation can be inefficient on a heap (O(N*M)).
     * @param collection The collection whose elements will be tested for existence.
     * @returns {boolean} True if this collection contains all the elements, false otherwise.
     */
    containsAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Removes the element with the highest priority from the queue.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue with the highest priority element removed.
     * @throws {NoElementsException} If the queue is empty.
     */
    dequeue(): ImmutablePriorityQueue<TElement>;
    /**
     * Adds an element to this priority queue. This is an alias for `add`.
     * @template TElement The type of elements in the queue.
     * @param element The element to add.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue with the added element.
     */
    enqueue(element: TElement): ImmutablePriorityQueue<TElement>;
    /**
     * Retrieves, but does not remove, the element with the highest priority.
     * @returns {TElement} The element with the highest priority.
     * @throws {NoElementsException} If the queue is empty.
     */
    front(): TElement;
    /**
     * Checks if the priority queue is empty.
     * @returns {boolean} True if the queue is empty, false otherwise.
     */
    isEmpty(): boolean;
    /**
     * Retrieves, but does not remove, the element with the highest priority, or returns null if the queue is empty.
     * @returns {TElement | null} The element with the highest priority, or null if the queue is empty.
     */
    peek(): TElement | null;
    /**
     * Removes the element with the highest priority from the queue.
     * If the queue is empty, returns an empty queue.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue with the highest priority element removed, or an empty queue if the original was empty.
     */
    poll(): ImmutablePriorityQueue<TElement>;
    /**
     * Removes the specified element from the priority queue.
     * Note: This operation can be inefficient on a heap (O(N)).
     * @param element The element to remove.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue without the specified element, or the original queue if the element was not found.
     */
    remove(element: TElement): ImmutablePriorityQueue<TElement>;
    /**
     * Removes all elements from this queue that are contained in the specified collection.
     * Note: This operation can be inefficient on a heap (O(N*M)).
     * @param collection The collection whose elements will be removed.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue without the specified elements.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutablePriorityQueue<TElement>;
    /**
     * Removes all elements from this queue that satisfy the specified predicate.
     * Note: This operation can be inefficient on a heap (O(N)).
     * @param predicate The predicate used to test elements.
     * @returns {ImmutablePriorityQueue<TElement>} A new priority queue without the elements satisfying the predicate.
     */
    removeIf(predicate: Predicate<TElement>): ImmutablePriorityQueue<TElement>;
    size(): number;
    /**
     * Gets the order comparator used by the priority queue.
     * @returns {OrderComparator<TElement>} The comparator.
     */
    get comparator(): OrderComparator<TElement>;
    get length(): number;
}

export declare class ImmutableQueue<TElement> extends AbstractImmutableCollection<TElement> {
    #private;
    private constructor();
    static create<TElement>(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>): ImmutableQueue<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to this queue.
     * @template TElement The type of elements in the queue.
     * @param element The element that will be added to this queue.
     * @returns {ImmutableQueue} A new queue with the added element.
     */
    add(element: TElement): ImmutableQueue<TElement>;
    /**
     * Adds all elements from the provided collection to this queue.
     * @template TElement The type of elements in the queue.
     * @param collection The collection whose element will be added to this queue.
     * @returns {ImmutableQueue} A new queue with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableQueue<TElement>;
    /**
     * Removes all elements from this queue.
     * @returns {ImmutableQueue} An empty queue.
     */
    clear(): ImmutableQueue<TElement>;
    /**
     * Removes the element at the front of this queue.
     * @returns {ImmutableQueue} A new queue with the element at the front removed.
     * @throws {Error} Thrown when the queue is empty.
     */
    dequeue(): ImmutableQueue<TElement>;
    /**
     * Adds an element to the end of this queue. This method is equivalent to {@link add}.
     * @template TElement The type of elements in the queue.
     * @param element The element that will be added to this queue.
     * @returns {ImmutableQueue} A new queue with the added element.
     */
    enqueue(element: TElement): ImmutableQueue<TElement>;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link peek}, this method throws an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     */
    front(): TElement;
    isEmpty(): boolean;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link front}, this method returns null if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue or null if the queue is empty.
     */
    peek(): TElement | null;
    /**
     * Removes the element at the beginning of the queue and returns the new queue.
     * Unlike {@link dequeue}, this method does not throw an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {ImmutableQueue} A new queue with the head removed, or an empty queue if the queue is empty.
     */
    poll(): ImmutableQueue<TElement>;
    size(): number;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

export declare class ImmutableSet<TElement> extends AbstractRandomAccessImmutableCollection<TElement> {
    #private;
    private constructor();
    static create<TElement>(iterable?: Iterable<TElement>): ImmutableSet<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to this set.
     * @param element The element that will be added to this set.
     * @returns {ImmutableSet} A new set with the added element.
     */
    add(element: TElement): ImmutableSet<TElement>;
    /**
     * Adds all elements from the provided collection to this set.
     * @param collection The collection whose element will be added to this set.
     * @returns {ImmutableSet} A new set with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSet<TElement>;
    /**
     * Removes all elements from this set.
     * @returns {ImmutableSet} An empty set.
     */
    clear(): ImmutableSet<TElement>;
    contains(element: TElement): boolean;
    count(predicate?: Predicate<TElement>): number;
    /**
     * Returns a new set that contains elements from this set that are not in the provided collection.
     * @param collection The collection whose elements will be excluded from this set.
     * @returns {ImmutableSet} A new set that contains elements from this set that are not in the provided collection.
     */
    exceptWith<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSet<TElement>;
    /**
     * Returns a new set that contains elements that are in both this set and the provided collection.
     * @param collection The collection whose elements will be intersected with this set.
     * @returns {ImmutableSet} A new set that contains elements that are in both this set and the provided collection.
     */
    intersectWith<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSet<TElement>;
    isProperSubsetOf(collection: Iterable<TElement>): boolean;
    isProperSupersetOf(collection: Iterable<TElement>): boolean;
    isSubsetOf(collection: Iterable<TElement>): boolean;
    isSupersetOf(collection: Iterable<TElement>): boolean;
    overlaps(collection: Iterable<TElement>): boolean;
    /**
     * Removes the specified element from this set.
     * @param element The element that will be removed from this set.
     * @returns {ImmutableSet} A new set without the specified element.
     */
    remove(element: TElement): ImmutableSet<TElement>;
    /**
     * Removes all elements from this set that are contained in the specified collection.
     * @param collection The collection whose elements will be removed from this set.
     * @returns {ImmutableSet} A new set without the elements in the specified collection.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSet<TElement>;
    /**
     * Removes all elements from this set that satisfy the specified predicate.
     * @param predicate The predicate used to remove elements from this set.
     * @returns {ImmutableSet} A new set without the elements that satisfy the specified predicate.
     */
    removeIf(predicate: Predicate<TElement>): ImmutableSet<TElement>;
    /**
     * Removes all elements from this set except the ones that are contained in the specified collection.
     * @param collection The collection whose elements will be retained in this set.
     * @returns {ImmutableSet} A new set with only the elements in the specified collection.
     */
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSet<TElement>;
    size(): number;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

export declare class ImmutableSortedDictionary<TKey, TValue> extends AbstractImmutableDictionary<TKey, TValue> {
    #private;
    private constructor();
    static create<TKey, TValue>(): ImmutableSortedDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<KeyValuePair<TKey, TValue>>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<[TKey, TValue]>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    static create<TKey, TValue>(iterable: Iterable<KeyValuePair<TKey, TValue>> | Iterable<[TKey, TValue]>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>): ImmutableSortedDictionary<TKey, TValue>;
    [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    add(key: TKey, value: TValue): ImmutableSortedDictionary<TKey, TValue>;
    clear(): ImmutableSortedDictionary<TKey, TValue>;
    containsKey(key: TKey): boolean;
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    entries(): IterableIterator<[TKey, TValue]>;
    get(key: TKey): TValue | null;
    keys(): ISet<TKey>;
    put(key: TKey, value: TValue): ImmutableSortedDictionary<TKey, TValue>;
    remove(key: TKey): ImmutableSortedDictionary<TKey, TValue>;
    set(key: TKey, value: TValue): ImmutableSortedDictionary<TKey, TValue>;
    size(): number;
    values(): ICollection<TValue>;
    private getCopiedDictionary;
    get length(): number;
}

export declare class ImmutableSortedSet<TElement> extends AbstractRandomAccessImmutableCollection<TElement> {
    #private;
    private constructor();
    static create<TElement>(iterable?: Iterable<TElement>, comparator?: OrderComparator<TElement>): ImmutableSortedSet<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to this set.
     * @param element The element that will be added to this set.
     * @returns {ImmutableSortedSet} A new set with the added element.
     */
    add(element: TElement): ImmutableSortedSet<TElement>;
    /**
     * Adds all elements from the provided collection to this set.
     * @param collection The collection whose element will be added to this set.
     * @returns {ImmutableSortedSet} A new set with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSortedSet<TElement>;
    /**
     * Removes all elements from this set.
     * @returns {ImmutableSortedSet} An empty set.
     */
    clear(): ImmutableSortedSet<TElement>;
    contains(element: TElement): boolean;
    count(predicate?: Predicate<TElement>): number;
    exceptWith(other: Iterable<TElement>): ImmutableSortedSet<TElement>;
    /**
     * Returns the first n elements of this set.
     * @param toElement The element up to which the elements will be included.
     * @param inclusive If true, the element toElement will be included; otherwise, it will be excluded.
     * @returns {ImmutableSortedSet} A new set that contains the first n elements of this set.
     */
    headSet(toElement: TElement, inclusive?: boolean): ImmutableSortedSet<TElement>;
    /**
     * Returns a new set that contains elements that are in both this set and the provided collection.
     * @param other The collection whose elements will be intersected with this set.
     * @returns {ImmutableSortedSet} A new set that contains elements that are in both this set and the provided collection.
     */
    intersectWith(other: Iterable<TElement>): ImmutableSortedSet<TElement>;
    isProperSubsetOf(other: Iterable<TElement>): boolean;
    isProperSupersetOf(other: Iterable<TElement>): boolean;
    isSubsetOf(other: Iterable<TElement>): boolean;
    isSupersetOf(other: Iterable<TElement>): boolean;
    overlaps(other: Iterable<TElement>): boolean;
    /**
     * Removes the given element from this set.
     * @param element The element that will be removed from this set.
     * @returns {ImmutableSortedSet} A new set with the removed element.
     */
    remove(element: TElement): ImmutableSortedSet<TElement>;
    /**
     * Removes all elements from this set that are contained in the specified collection.
     * @param collection The collection whose elements will be removed from this set.
     * @returns {ImmutableSortedSet} A new set without the elements in the specified collection.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSortedSet<TElement>;
    /**
     * Removes all elements from this set that satisfy the specified predicate.
     * @param predicate The predicate that will be used to remove elements from this set.
     * @returns {ImmutableSortedSet} A new set without the elements that satisfy the predicate.
     */
    removeIf(predicate: Predicate<TElement>): ImmutableSortedSet<TElement>;
    /**
     * Removes all elements from this set except the ones that are contained in the specified collection.
     * @param collection The collection whose elements will be retained in this set.
     * @returns {ImmutableSortedSet} A new set with only the elements in the specified collection.
     */
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableSortedSet<TElement>;
    size(): number;
    /**
     * Returns a new set that contains elements that are in the specified range.
     * @param fromElement The element from which the range will start.
     * @param toElement The element up to which the range will be included.
     * @param fromInclusive If true, the element fromElement will be included; otherwise, it will be excluded.
     * @param toInclusive If true, the element toElement will be included; otherwise, it will be excluded.
     * @returns {ImmutableSortedSet} A new set that contains elements that are in the specified range.
     */
    subSet(fromElement: TElement, toElement: TElement, fromInclusive?: boolean, toInclusive?: boolean): ImmutableSortedSet<TElement>;
    /**
     * Returns a new set that contains elements that are greater than or equal to the specified element.
     * @param fromElement The element from which the range will start.
     * @param inclusive If true, the element fromElement will be included; otherwise, it will be excluded.
     * @returns {ImmutableSortedSet} A new set that contains elements that are greater than or equal to the specified element.
     */
    tailSet(fromElement: TElement, inclusive?: boolean): ImmutableSortedSet<TElement>;
    get comparator(): OrderComparator<TElement>;
    get length(): number;
}

export declare class ImmutableStack<TElement> extends AbstractImmutableCollection<TElement> {
    #private;
    private constructor();
    static create<TElement>(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>): ImmutableStack<TElement>;
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds the given element to this stack.
     * @template TElement The type of elements in the stack.
     * @param element The element that will be added to this stack.
     * @returns {ImmutableStack} A new stack with the added element.
     */
    add(element: TElement): ImmutableStack<TElement>;
    /**
     * Adds all elements from the provided collection to this stack.
     * The elements of the collection are added in the reverse order of the collection.
     * For example, if the collection is [1, 2, 3], the stack will contain [3, 2, 1], with 3 at the top.
     * @template TElement The type of elements in the stack.
     * @param collection The collection whose element will be added to this stack.
     * @returns {ImmutableStack} A new stack with the added elements.
     */
    addAll<TSource extends TElement>(collection: Iterable<TSource>): ImmutableStack<TElement>;
    /**
     * Removes all elements from this stack.
     * @returns {ImmutableStack} An empty stack.
     */
    clear(): IImmutableCollection<TElement>;
    /**
     * Retrieves but does not remove the element at the top of the stack.
     * Unlike {@link top}, this method returns null if the stack is empty.
     * @template TElement The type of elements in the stack.
     * @returns {TElement | null} The top of the stack or null if the stack is empty.
     */
    peek(): TElement | null;
    /**
     * Removes the element at the top of the stack and returns the new stack.
     * @template TElement The type of elements in the stack.
     * @returns {ImmutableStack} A new stack with the top element removed.
     */
    pop(): ImmutableStack<TElement>;
    /**
     * Adds the given element to this stack.
     * @template TElement The type of elements in the stack.
     * @param element The element that will be added to this stack.
     * @returns {ImmutableStack} A new stack with the added element.
     */
    push(element: TElement): ImmutableStack<TElement>;
    size(): number;
    /**
     * Retrieves but does not remove the element at the top of the stack.
     * Unlike {@link peek}, this method throws an error if the stack is empty.
     * @template TElement The type of elements in the stack.
     * @returns {TElement} The top of the stack.
     * @throws {Error} If the stack is empty.
     */
    top(): TElement;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * Returns an enumerable of tuples, each containing the index and the element from the source sequence.
 * @template TElement
 * @param source The source iterable.
 * @returns {IEnumerable<[number, TElement]>} A new enumerable sequence whose elements are tuples of the index and the element.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const indexedLetters = letters.index().toArray();
 *      // indexedLetters = [[0, 'a'], [1, 'b'], [2, 'c']]
 */
export declare const index: <TElement>(source: Iterable<TElement>) => IEnumerable<[number, TElement]>;

export declare interface IndexedAction<TElement, TReturn = void> {
    (item: TElement, index: number): TReturn;
}

export declare interface IndexedPredicate<T> {
    (item: T, index: number): boolean;
}

export declare interface IndexedSelector<TElement, TResult> {
    (item: TElement, index: number): TResult;
}

export declare interface IndexedTupleSelector<TElement> {
    (index: number, item: TElement): [number, TElement];
}

declare type InferredType<T> = T extends SymbolType ? symbol : T extends BooleanType ? boolean : T extends NumberType ? number : T extends BigIntType ? bigint : T extends "object" ? object : T extends "function" ? Function : T extends StringType ? string : T extends Class<infer R> ? R : T extends FunctionType ? Function : T extends ObjectType_2 ? object : T;

declare interface INode<TElement> {
    getData(): TElement;
    getLeft(): INode<TElement> | null;
    getRight(): INode<TElement> | null;
    setData(data: TElement): void;
    setLeft(node: INode<TElement>): void;
    setRight(node: INode<TElement>): void;
}

/**
 * Produces the set intersection of two sequences by using the specified equality comparer or order comparer to compare values.
 * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
 * @template TElement
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements that also appear in the first sequence will be returned.
 * @param comparator The comparator function that will be used for item comparison. If not provided, a default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set intersection of the two sequences.
 * @throws {Error} If the iterable is null or undefined.
 * @example
 *      const numbers1 = new List([1, 2, 3, 4, 5, 5]); // Source has duplicates
 *      const numbers2 = new List([3, 5, 6, 7, 5]); // Other has duplicates
 *      const intersection = numbers1.intersect(numbers2).toArray();
 *      // intersection = [3, 5] (Order matches source, duplicates removed)
 *
 *      // Using custom object comparison
 *      interface Item { id: number; value: string; }
 *      const items1 = new List<Item>([{ id: 1, value: 'A' }, { id: 2, value: 'B' }]);
 *      const items2 = new List<Item>([{ id: 2, value: 'Different B' }, { id: 3, value: 'C' }]);
 *      const itemIntersection = items1.intersect(items2, (a, b) => a.id === b.id).toArray();
 *      // itemIntersection = [{ id: 2, value: 'B' }] (Keeps the element from the first list)
 */
export declare const intersect: <TElement>(source: Iterable<TElement>, other: Iterable<TElement>, comparator?: EqualityComparator<TElement> | OrderComparator<TElement>) => IEnumerable<TElement>;

/**
 * Produces the set intersection of two sequences by using the specified key selector function to compare elements.
 * If the elements of the iterable can be sorted, it is advised to use an order comparator for better performance.
 * @template TElement, TKey
 * @typeParam TElement The type of the elements in the source sequence.
 * @typeParam TKey The type of the key that will be used for comparison.
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements that also appear in the first sequence will be returned.
 * @param keySelector The key selector function that will be used for selecting a key which will be used for comparison.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are the set intersection of the two sequences.
 * @example
 *      interface Product { code: string; name: string; }
 *      const store1Products = new List<Product>([
 *          { code: 'A1', name: 'Apple' },
 *          { code: 'B2', name: 'Banana' },
 *          { code: 'C3', name: 'Cherry' }
 *      ]);
 *      const store2Products = new List<Product>([
 *          { code: 'B2', name: 'Banana V2' }, // Same code as store1
 *          { code: 'D4', name: 'Date' }
 *      ]);
 *
 *      // Find products in store1 whose codes also exist in store2
 *      const commonProducts = store1Products.intersectBy(
 *          store2Products,
 *          p => p.code // Compare based on the 'code' property
 *      ).toArray();
 *      // commonProducts = [ { code: 'B2', name: 'Banana' } ] (Takes the element from store1)
 *
 *      // Example with case-insensitive key comparison
 *      const listA = new List([{ val: 'a' }, { val: 'b' }]);
 *      const listB = new List([{ val: 'B' }, { val: 'c' }]);
 *      const intersectCaseInsensitive = listA.intersectBy(
 *          listB,
 *          item => item.val,
 *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
 *      ).toArray();
 *      // intersectCaseInsensitive = [ { val: 'b' } ] (Keeps 'b' from listA as it matches 'B')
 */
export declare const intersectBy: <TElement, TKey>(source: Iterable<TElement>, other: Iterable<TElement>, keySelector: Selector<TElement, TKey>, keyComparator?: EqualityComparator<TKey> | OrderComparator<TKey>) => IEnumerable<TElement>;

/**
 * Intersperses a specified element between each element of the sequence.
 * @template TElement, TSeparator
 * @param source The source iterable.
 * @param separator The element that will be interspersed between each element of the sequence.
 * @returns {IEnumerable<TElement|TSeparator>} A new enumerable sequence whose elements are the elements of the source sequence interspersed with the specified element.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const interspersedLetters = letters.intersperse('-').toArray();
 *      // interspersedLetters = ['a', '-', 'b', '-', 'c']
 *
 *      const numbers = new List([1, 2, 3]);
 *      const interspersedNumbers = numbers.intersperse(0).toArray();
 *      // interspersedNumbers = [1, 0, 2, 0, 3]
 *
 *      const emptyList = new List<string>();
 *      const interspersedEmpty = emptyList.intersperse('-').toArray();
 *      // interspersedEmpty = []
 *
 *      const singleItemList = new List(['a']);
 *      const interspersedSingle = singleItemList.intersperse('-').toArray();
 *      // interspersedSingle = ['a']
 */
export declare const intersperse: <TElement, TSeparator>(source: Iterable<TElement>, separator: TSeparator) => IEnumerable<TElement | TSeparator>;

export declare interface IOrderedAsyncEnumerable<TElement> extends IAsyncEnumerable<TElement> {
    /**
     * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
     * @param keySelector A function to extract a key from an element.
     * @param comparator A function to compare keys.
     */
    thenBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
    /**
     * Performs a subsequent ordering of the elements in a sequence in descending order according to a key.
     * @param keySelector A function to extract a key from an element.
     * @param comparator A function to compare keys.
     */
    thenByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedAsyncEnumerable<TElement>;
}

export declare interface IOrderedEnumerable<TElement> extends IEnumerable<TElement> {
    /**
     * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key.
     * This method must be called on an IOrderedEnumerable (the result of orderBy, orderByDescending, thenBy, or thenByDescending).
     * Calling orderBy or orderByDescending after this method will override the entire sorting sequence.
     * @template TKey The type of the key returned by keySelector.
     * @param keySelector The function to extract the key for secondary sorting from an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default ascending order comparison will be used.
     * @returns {IOrderedEnumerable<TElement>} An IOrderedEnumerable whose elements are sorted by the primary condition and then by this secondary condition (ascending).
     * @example
     *      interface Person { name: string; city: string; age: number; }
     *      const people = new List<Person>([
     *          { name: 'Alice', city: 'London', age: 30 },
     *          { name: 'Bob', city: 'Paris', age: 25 },
     *          { name: 'Charlie', city: 'London', age: 35 },
     *          { name: 'Diana', city: 'Paris', age: 30 }
     *      ]);
     *
     *      // --- Standard Usage ---
     *      // Order by city ascending (primary), then by age ascending (secondary)
     *      const sortedPeople = people
     *          .orderBy(p => p.city) // Primary sort: city ascending
     *          .thenBy(p => p.age)      // Secondary sort: age ascending
     *          .toArray();
     *      // sortedPeople = [
     *      //   { name: 'Alice', city: 'London', age: 30 },
     *      //   { name: 'Charlie', city: 'London', age: 35 },
     *      //   { name: 'Bob', city: 'Paris', age: 25 },
     *      //   { name: 'Diana', city: 'Paris', age: 30 }
     *      // ]
     *
     *      // --- Overriding Behavior ---
     *      // Order by city, then by age, but then override with a new primary order by name
     *      const overriddenSort = people
     *          .orderBy(p => p.city)
     *          .thenBy(p => p.age)
     *          .orderBy(p => p.name) // This orderBy overrides the previous sorts
     *          .toArray();
     *      // The final sort is based only on name ascending:
     *      // overriddenSort = [
     *      //   { name: 'Alice', city: 'London', age: 30 },
     *      //   { name: 'Bob', city: 'Paris', age: 25 },
     *      //   { name: 'Charlie', city: 'London', age: 35 },
     *      //   { name: 'Diana', city: 'Paris', age: 30 }
     *      // ]
     */
    thenBy<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
    /**
     * Performs a subsequent ordering of the elements in a sequence in descending order according to a key.
     * This method must be called on an IOrderedEnumerable (the result of orderBy, orderByDescending, thenBy, or thenByDescending).
     * Calling orderBy or orderByDescending after this method will override the entire sorting sequence.
     * @template TKey The type of the key returned by keySelector.
     * @param keySelector The function to extract the key for secondary sorting from an element.
     * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default descending order comparison will be used.
     * @returns {IOrderedEnumerable<TElement>} An IOrderedEnumerable whose elements are sorted by the primary condition and then by this secondary condition (descending).
     * @example
     *      interface Person { name: string; city: string; age: number; }
     *      const people = new List<Person>([
     *          { name: 'Alice', city: 'London', age: 30 },
     *          { name: 'Bob', city: 'Paris', age: 25 },
     *          { name: 'Charlie', city: 'London', age: 35 },
     *          { name: 'Diana', city: 'Paris', age: 30 }
     *      ]);
     *
     *      // --- Standard Usage ---
     *      // Order by city ascending (primary), then by age descending (secondary)
     *      const sortedPeople = people
     *          .orderBy(p => p.city)          // Primary sort: city ascending
     *          .thenByDescending(p => p.age) // Secondary sort: age descending
     *          .toArray();
     *      // sortedPeople = [
     *      //   { name: 'Charlie', city: 'London', age: 35 },
     *      //   { name: 'Alice', city: 'London', age: 30 },
     *      //   { name: 'Diana', city: 'Paris', age: 30 },
     *      //   { name: 'Bob', city: 'Paris', age: 25 }
     *      // ]
     *
     *      // --- Overriding Behavior ---
     *      // Order by city, then by age descending, but then override with order by name descending
     *      const overriddenSort = people
     *          .orderBy(p => p.city)
     *          .thenByDescending(p => p.age)
     *          .orderByDescending(p => p.name) // This overrides the previous sorts
     *          .toArray();
     *      // The final sort is based only on name descending:
     *      // overriddenSort = [
     *      //   { name: 'Diana', city: 'Paris', age: 30 },
     *      //   { name: 'Charlie', city: 'London', age: 35 },
     *      //   { name: 'Bob', city: 'Paris', age: 25 },
     *      //   { name: 'Alice', city: 'London', age: 30 }
     *      // ]
     */
    thenByDescending<TKey>(keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>): IOrderedEnumerable<TElement>;
}

/**
 * Represents a collection of objects whose elements can be accessed without restriction.
 */
export declare interface IRandomAccessCollection<TElement> extends ICollection<TElement> {
    /**
     * Removes the given element from this collection. Comparison is made by the collection's current comparator.
     * @param element The element that will be removed from the collection.
     * @returns true if the element is removed from the collection, false otherwise.
     */
    remove(element: TElement): boolean;
    /**
     * Removes all the elements of the given collection or array from this collection.
     * @param collection The collection or array whose elements will be removed from this collection.
     * @returns true if this collection is modified as a result.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Removes all the elements that satisfy the given predicate from this collection.
     * @param predicate The predicate method against which the elements of this collection will be tested.
     * @returns {boolean} true if this collection is modified as a result.
     */
    removeIf(predicate: Predicate<TElement>): boolean;
    /**
     * Removes all the elements that do not exist in the given collection or array.
     * @param collection The collection or array whose elements will remain in this collection.
     * @returns {boolean} true if this collection is modified as a result.
     */
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
}

export declare interface IRandomAccessImmutableCollection<TElement> extends IImmutableCollection<TElement> {
    /**
     * Removes the given element from this collection.
     * @param element The element that will be removed from this collection.
     * @returns {IImmutableCollection} A new collection without the removed element.
     */
    remove(element: TElement): IRandomAccessImmutableCollection<TElement>;
    /**
     * Removes all elements from this collection that are in the provided collection.
     * @param collection The collection whose elements will be removed from this collection.
     * @returns {IImmutableCollection} A new collection without the removed elements.
     */
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): IRandomAccessImmutableCollection<TElement>;
    /**
     * Removes all elements from this collection that satisfy the provided predicate.
     * @param predicate A function that will test each element for a condition.
     * @returns {IImmutableCollection} A new collection without the removed elements.
     */
    removeIf(predicate: Predicate<TElement>): IRandomAccessImmutableCollection<TElement>;
    /**
     * Retains only the elements in this collection that are contained in the specified collection.
     * @param collection The collection whose elements will be retained in this collection.
     * @returns {IImmutableCollection} A new collection with only the elements in the specified collection.
     */
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): IRandomAccessImmutableCollection<TElement>;
}

export declare interface IReadonlyCollection<TElement> extends IEnumerable<TElement> {
    /**
     * Returns the number of element in this collection.
     * @returns {number} The number of elements in this collection.
     */
    get length(): number;
    /**
     * Returns the current comparator used by this collection.
     * @template TElement The type of the elements in the collection.
     * @returns {EqualityComparator<TElement> | OrderComparator<TElement>} The current comparator used by this collection.
     */
    get comparator(): EqualityComparator<TElement> | OrderComparator<TElement>;
    /**
     * Check if this collection contains all the elements of the given collection.
     * @param collection The collection whose element will be tested for existence against this collection.
     * @returns {boolean} true if this collection contains all the elements from the given collection, false otherwise.
     */
    containsAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    /**
     * Iterates over the collection and runs the given action against every element.
     * @param action The action that will be run against the elements of this collection
     */
    forEach(action: IndexedAction<TElement>): void;
    /**
     * Checks whether this collection is empty or not.
     * @returns {boolean} true if collection is empty
     */
    isEmpty(): boolean;
    /**
     * Returns the number of elements in this collection.
     * @returns {number} The number of elements in this collection.
     */
    size(): number;
    /**
     * Returns a string representation of this collection.
     * @returns {string} A string representation of this collection.
     */
    toString(): string;
    /**
     * Returns a string representation of this collection.
     * @param separator The separator that will be used to separate the elements of this collection.
     * @returns {string} A string representation of this collection.
     */
    toString(separator?: string): string;
    /**
     * Returns a string representation of this collection.
     * @param separator The separator that will be used to separate the elements of this collection.
     * @param selector The selector that will be used to select the property that will be used to generate the string representation of this collection.
     * @returns {string} A string representation of this collection.
     */
    toString(separator?: string, selector?: Selector<TElement, string>): string;
}

declare interface IReadonlyDictionary<TKey, TValue> extends IEnumerable<KeyValuePair<TKey, TValue>> {
    /**
     * Returns the number of elements in this dictionary.
     */
    get length(): number;
    /**
     * Returns an object representation of this dictionary.
     *
     * {@link toObject} is a more versatile version of this method, as it allows you to select the key and value properties
     * that will be used to generate the object representation.
     * @template TValue The type of the values of the dictionary.
     * @returns {Record<string|number|symbol, TValue>} An object representation of this dictionary.
     */
    asObject<TObjectKey extends string | number | symbol>(): Record<TObjectKey, TValue>;
    /**
     * Checks whether this dictionary contains the specified key.
     * @param key The key to locate in this dictionary.
     * @returns {boolean} true if this dictionary contains an element with the specified key; false otherwise.
     */
    containsKey(key: TKey): boolean;
    /**
     * Checks whether this dictionary contains the specified value.
     * @param value The value to locate in this dictionary.
     * @param comparator The comparator function which will be used to compare the equality of the values.
     * @returns {boolean} true if this dictionary contains an element with the specified value; false otherwise.
     */
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    /**
     * Returns an IterableIterator that yields a tuple of [key, value].
     */
    entries(): IterableIterator<[TKey, TValue]>;
    /**
     * Returns the value associated with the given key.
     * @param key The key whose associated value will be returned
     * @returns The associated value of the key. If key does not exist, it will return null.
     */
    get(key: TKey): TValue | null;
    /**
     * Checks if the dictionary is empty or not.
     * @returns {boolean} true if the dictionary is empty; false otherwise.
     */
    isEmpty(): boolean;
    /**
     * Returns a set of keys of this dictionary.
     * @returns {ISet} A set of keys of this dictionary.
     */
    keys(): ISet<TKey>;
    /**
     * Returns the number of elements in this dictionary.
     */
    size(): number;
    /**
     * Returns a string representation of this dictionary.
     */
    toString(): string;
    /**
     * Returns a string representation of this dictionary.
     * @param selector The selector that will be used to select the property that will be used to generate the string representation of this dictionary.
     */
    toString(selector?: Selector<KeyValuePair<TKey, TValue>, string>): string;
    /**
     * Returns a list of values of the dictionary.
     * @returns {ICollection} A collection of values of this dictionary.
     */
    values(): ICollection<TValue>;
    get keyValueComparator(): EqualityComparator<KeyValuePair<TKey, TValue>>;
    get valueComparator(): EqualityComparator<TValue>;
}

export declare interface IReadonlyList<TElement> extends IReadonlyCollection<TElement> {
    /**
     * Returns an IterableIterator that yields a tuple of [index, element].
     * <pre>
     *      for (const [index, element] of list.entries())
     * </pre>
     */
    entries(): IterableIterator<[number, TElement]>;
    /**
     * Returns the element at the given index.
     * @param index The index from which the element will be returned.
     * @returns The element at the given index
     */
    get(index: number): TElement;
    /**
     * Returns a shallow copy of a range of elements in the source list.
     * @param index The index at which the range will start.
     * @param count The number of elements in the range.
     * @returns A shallow copy of a range of elements in the source list.
     * @throws {Error} If the index is out of bounds.
     */
    getRange(index: number, count: number): IReadonlyList<TElement>;
    /**
     * Finds and returns the index of the first occurrence of the given element.
     * @param element The element whose index will be found.
     * @param comparator The comparator that will be used to compare for equality.
     * @returns the index of the given element. -1 if item is not found.
     */
    indexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    /**
     * Finds and returns the index of the last occurrence of the given element.
     * @param element The element whose index will be found.
     * @param comparator The comparator that will be used to compare for equality.
     * @returns The index of the given element. -1 if item is not found.
     */
    lastIndexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
}

export declare interface ISet<TElement> extends IRandomAccessCollection<TElement> {
    /**
     * Removes all elements in the specified collection from the current set.
     * @param other The collection of items to remove from the set.
     */
    exceptWith(other: Iterable<TElement>): void;
    /**
     * Modifies the current set so that it contains only the elements that are also in the specified collection.
     * @param other
     */
    intersectWith(other: Iterable<TElement>): void;
    /**
     * Determines whether this set is a proper subset of the specified collection.
     * @param other The iterable to compare to this set.
     * @returns true if this set is a proper subset of other; otherwise, false.
     */
    isProperSubsetOf(other: Iterable<TElement>): boolean;
    /**
     * Determines whether this set is a proper superset of the specified collection.
     * @param other The iterable to compare to this set.
     * @returns true if this set is a proper superset of other; otherwise, false.
     */
    isProperSupersetOf(other: Iterable<TElement>): boolean;
    /**
     * Determines whether this set is a subset of the specified collection.
     * @param other The iterable to compare to this set.
     * @returns true if this set is a subset of other; otherwise, false.
     */
    isSubsetOf(other: Iterable<TElement>): boolean;
    /**
     * Determines whether this set is a superset of the specified collection.
     * @param other The iterable to compare to this set.
     * @returns true if this set is a superset of other; otherwise, false.
     */
    isSupersetOf(other: Iterable<TElement>): boolean;
    /**
     * Determines whether this set and the specified collection share common elements.
     * @param other The iterable to compare to this set.
     * @returns true if the set and other share at least one common element; otherwise, false.
     */
    overlaps(other: Iterable<TElement>): boolean;
}

export declare interface ITree<TElement> extends IRandomAccessCollection<TElement> {
    /**
     * Deletes the given item from the tree.
     * @param element Item to be removed from the tree.
     */
    delete(element: TElement): void;
    /**
     * Returns the first occurrence of an item that satisfy the condition by the predicate.
     * @param  predicate The function that will be used to find the items.
     * @return First occurrence of the item that satisfies the predicate. If no item satisfies, returns null.
     */
    find(predicate: Predicate<TElement>): TElement | null;
    /**
     * Returns the data at the root of this tree.
     * @return The data at the root of the tree.
     */
    getRootData(): TElement | null;
    /**
     * Inserts an item to this tree.
     * @param element Item that is to be inserted.
     */
    insert(element: TElement): void;
    /**
     * Searches for an item in this tree.
     * @param  element Item to be searched.
     * @return true if the item is found, false otherwise.
     */
    search(element: TElement): boolean;
}

/**
 * Correlates the elements of two sequences based on equality of keys.
 * @template TInner, TKey, TResult, TElement
 * @param source The source iterable.
 * @param innerEnumerable The enumerable sequence to join to the first sequence.
 * @param outerKeySelector The key selector function that will be used for selecting the key for an element from the first sequence.
 * @param innerKeySelector The key selector function that will be used for selecting the key for an element from the second sequence.
 * @param resultSelector The result selector function that will be used to create a result element from two matching elements.
 * @param keyComparator The comparator function that will be used for equality comparison of selected keys. If not provided, the default equality comparison is used.
 * @param leftJoin If true, the result sequence will include outer elements that have no matching inner element, with null provided as the inner element to the resultSelector. Defaults to false.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the join operation.
 * @example
 *      interface Department { id: number; name: string; location: string }
 *      interface Employee { name: string; deptId: number; role: string }
 *
 *      const departments = new List<Department>([
 *          { id: 1, name: 'HR', location: 'Building A' },
 *          { id: 2, name: 'Engineering', location: 'Building B' },
 *          { id: 3, name: 'Marketing', location: 'Building A' } // No employees here
 *      ]);
 *
 *      const employees = new List<Employee>([
 *          { name: 'Alice', deptId: 2, role: 'Developer' },
 *          { name: 'Bob', deptId: 1, role: 'Manager' },
 *          { name: 'Charlie', deptId: 2, role: 'Tester' },
 *          { name: 'David', deptId: 4, role: 'Intern' } // Department 4 not in departments list
 *      ]);
 *
 *      // Inner Join (default: leftJoin = false)
 *      const innerJoinResult = departments.join(
 *          employees,
 *          dept => dept.id,           // Outer key: department ID
 *          emp => emp.deptId,         // Inner key: employee department ID
 *          (dept, emp) => ({          // Result selector
 *              employeeName: emp.name,
 *              departmentName: dept.name
 *          })
 *      ).toArray();
 *      // innerJoinResult = [
 *      //   { employeeName: 'Bob', departmentName: 'HR' },
 *      //   { employeeName: 'Alice', departmentName: 'Engineering' },
 *      //   { employeeName: 'Charlie', departmentName: 'Engineering' }
 *      // ]
 *      // Note: Marketing dept and David (dept 4) are excluded.
 *
 *      // Left Join (leftJoin = true)
 *      const leftJoinResult = departments.join(
 *          employees,
 *          dept => dept.id,
 *          emp => emp.deptId,
 *          (dept, emp) => ({
 *              departmentName: dept.name,
 *              employeeName: emp?.name ?? 'N/A' // Use nullish coalescing for unmatched employees
 *          }),
 *          Comparators.equalityComparator, // Default comparator can be explicit or omitted
 *          true                       // Set leftJoin to true
 *      ).toArray();
 *      // leftJoinResult = [
 *      //   { departmentName: 'HR', employeeName: 'Bob' },
 *      //   { departmentName: 'Engineering', employeeName: 'Alice' },
 *      //   { departmentName: 'Engineering', employeeName: 'Charlie' },
 *      //   { departmentName: 'Marketing', employeeName: 'N/A' } // Marketing included, no matching employee
 *      // ]
 *      // Note: David (dept 4) is still excluded as the join starts from departments.
 */
export declare const join: <TElement, TInner, TKey, TResult>(source: Iterable<TElement>, innerEnumerable: Iterable<TInner>, outerKeySelector: Selector<TElement, TKey>, innerKeySelector: Selector<TInner, TKey>, resultSelector: JoinSelector<TElement, TInner, TResult>, keyComparator?: EqualityComparator<TKey>, leftJoin?: boolean) => IEnumerable<TResult>;

export declare interface JoinSelector<TFirst, TSecond, TResult> {
    (firstItem: TFirst, secondItem: TSecond | null): TResult;
}

export declare class KeyValuePair<TKey, TValue> {
    readonly key: TKey;
    value: TValue;
    constructor(key: TKey, value: TValue);
    equals(pair: KeyValuePair<TKey, TValue>, keyComparator?: EqualityComparator<TKey>, valueComparator?: EqualityComparator<TValue>): boolean;
}

/**
 * Returns the last element of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
 * @returns {TElement} The last element of the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @throws {NoMatchingElementException} If no element satisfies the condition.
 * @example
 *      const numbers = new List([10, 20, 30, 25, 40]);
 *      const lastElement = numbers.last();
 *      // lastElement = 40
 *
 *      const lastLessThan30 = numbers.last(n => n < 30);
 *      // lastLessThan30 = 25
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.last(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 *
 *      try {
 *          numbers.last(n => n > 50); // Throws NoMatchingElementException
 *      } catch (e) {
 *          console.log(e.message); // Output: No element satisfies the condition.
 *      }
 */
export declare const last: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement;

/**
 * Returns the last element of the sequence or a default value if the no element satisfies the condition.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, the last element of the sequence will be returned.
 * @returns {TElement|null} The last element of the sequence or null if the sequence is empty or no element satisfies the condition.
 * @example
 *      const numbers = new List([10, 20, 30, 25, 40]);
 *      const lastElement = numbers.lastOrDefault();
 *      // lastElement = 40
 *
 *      const lastLessThan30 = numbers.lastOrDefault(n => n < 30);
 *      // lastLessThan30 = 25
 *
 *      const lastGreaterThan50 = numbers.lastOrDefault(n => n > 50);
 *      // lastGreaterThan50 = null
 *
 *      const emptyList = new List<number>();
 *      const lastFromEmpty = emptyList.lastOrDefault();
 *      // lastFromEmpty = null
 */
export declare const lastOrDefault: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement | null;

export declare class LinkedList<TElement> extends AbstractList<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    add(element: TElement): boolean;
    addAt(element: TElement, index: number): boolean;
    addFirst(element: TElement): void;
    addLast(element: TElement): void;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    get(index: number): TElement;
    getRange(index: number, count: number): LinkedList<TElement>;
    peek(): TElement | null;
    peekLast(): TElement | null;
    poll(): TElement | null;
    pollLast(): TElement | null;
    remove(element: TElement): boolean;
    removeAt(index: number): TElement;
    removeFirst(): TElement;
    removeLast(): TElement;
    set(index: number, element: TElement): TElement;
    size(): number;
    sort(comparator?: OrderComparator<TElement>): void;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
    private checkElementIndex;
    private checkPositionIndex;
    private isElementIndex;
    private isPositionIndex;
    private linkBefore;
    private linkFirst;
    private linkLast;
    private node;
    private unlink;
    private unlinkFirst;
    private unlinkLast;
    private set ListSize(value);
}

export declare class List<TElement> extends AbstractList<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    addAt(element: TElement, index: number): boolean;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    get(index: number): TElement;
    getRange(index: number, count: number): List<TElement>;
    remove(element: TElement): boolean;
    removeAt(index: number): TElement;
    set(index: number, element: TElement): TElement;
    size(): number;
    sort(comparator?: OrderComparator<TElement>): void;
    toArray(): TElement[];
    toString(): string;
    toString(separator?: string): string;
    toString(separator?: string, selector?: Selector<TElement, string>): string;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * Returns the maximum value in the sequence.
 * @param source The source iterable.
 * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
 * @returns {number} The maximum value in the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const numbers = new List([10, 50, 20, 45, 30]);
 *      const maxNumber = numbers.max();
 *      // maxNumber = 50
 *
 *      interface Item { value: number; }
 *      const items = new List<Item>([{ value: 100 }, { value: 50 }, { value: 200 }]);
 *      const maxValue = items.max(item => item.value);
 *      // maxValue = 200
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.max(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const max: <TElement>(source: Iterable<TElement>, selector?: Selector<TElement, number>) => number;

/**
 * Returns the element with the maximum value that is obtained by applying the key selector function to each element in the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
 * @returns {TElement} The element with the maximum value in the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      interface Product { name: string; price: number; }
 *      const products = new List<Product>([
 *          { name: 'Laptop', price: 1200 },
 *          { name: 'Mouse', price: 25 },
 *          { name: 'Keyboard', price: 75 },
 *          { name: 'Monitor', price: 300 }
 *      ]);
 *
 *      // Find the most expensive product
 *      const mostExpensive = products.maxBy(p => p.price);
 *      // mostExpensive = { name: 'Laptop', price: 1200 }
 *
 *      // Using a custom comparator (e.g., longest name)
 *      const productWithLongestName = products.maxBy(
 *          p => p.name.length // Key is the length of the name
 *      );
 *      // productWithLongestName = { name: 'Keyboard', price: 75 }
 *
 *      const emptyList = new List<Product>();
 *      try {
 *          emptyList.maxBy(p => p.price); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const maxBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>) => TElement;

/**
 * Returns the minimum value in the sequence.
 * @param source The source iterable.
 * @param selector The selector function that will be used to select the value to compare. If not specified, the value itself will be used.
 * @returns {number} The minimum value in the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const numbers = new List([10, 50, 20, 45, 30]);
 *      const minNumber = numbers.min();
 *      // minNumber = 10
 *
 *      interface Item { value: number; }
 *      const items = new List<Item>([{ value: 100 }, { value: 50 }, { value: 200 }]);
 *      const minValue = items.min(item => item.value);
 *      // minValue = 50
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.min(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const min: <TElement>(source: Iterable<TElement>, selector?: Selector<TElement, number>) => number;

/**
 * Returns the element with the minimum value that is obtained by applying the key selector function to each element in the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
 * @returns {TElement} The element with the minimum value in the sequence.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      interface Product { name: string; price: number; }
 *      const products = new List<Product>([
 *          { name: 'Laptop', price: 1200 },
 *          { name: 'Mouse', price: 25 },
 *          { name: 'Keyboard', price: 75 },
 *          { name: 'Monitor', price: 300 }
 *      ]);
 *
 *      // Find the cheapest product
 *      const cheapest = products.minBy(p => p.price);
 *      // cheapest = { name: 'Mouse', price: 25 }
 *
 *      // Using a custom comparator (e.g., the shortest name)
 *      const productWithShortestName = products.minBy(
 *          p => p.name.length // Key is the length of the name
 *      );
 *      // productWithShortestName = { name: 'Mouse', price: 25 }
 *
 *      const emptyList = new List<Product>();
 *      try {
 *          emptyList.minBy(p => p.price); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const minBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>) => TElement;

/**
 * Determines whether no elements of the sequence satisfy the specified predicate.
 * If no predicate is specified, it returns true if the sequence is empty, and false otherwise.
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition.
 * @returns {boolean} true if no elements satisfy the predicate, or if the sequence is empty and no predicate is provided; otherwise, false.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *
 *      // Check if none are negative
 *      const noneNegative = numbers.none(n => n < 0);
 *      // noneNegative = true
 *
 *      // Check if none are greater than 10
 *      const noneGreaterThan10 = numbers.none(n => n > 10);
 *      // noneGreaterThan10 = true
 *
 *      // Check if none are even (this will be false)
 *      const noneEven = numbers.none(n => n % 2 === 0);
 *      // noneEven = false
 *
 *      // Check if an empty list has no elements (no predicate)
 *      const emptyList = new List<number>();
 *      const emptyNone = emptyList.none();
 *      // emptyNone = true
 *
 *      // Check if a non-empty list has no elements (no predicate)
 *      const nonEmptyNone = numbers.none();
 *      // nonEmptyNone = false
 */
export declare const none: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => boolean;

declare type NumberType = number | Number | NumberConstructor | "number";

declare type ObjectType<T = unknown> = PrimitiveSymbol | "symbol" | PrimitiveBoolean | "boolean" | PrimitiveNumber | "number" | PrimitiveBigInt | "bigint" | Class<T> | Function | "function" | PrimitiveObject | "object" | PrimitiveString | "string";

declare type ObjectType_2 = object | Object | ObjectConstructor;

export declare class ObservableCollection<TElement> extends AbstractEnumerable<TElement> {
    #private;
    collectionChanged?: (sender: this, args: ICollectionChangedEventArgs<TElement>) => void;
    constructor();
    constructor(iterable?: Iterable<TElement>);
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    add(element: TElement): boolean;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    containsAll<TSource extends TElement>(iterable: Iterable<TSource>): boolean;
    get(index: number): TElement;
    insert(index: number, element: TElement): void;
    isEmpty(): boolean;
    move(oldIndex: number, newIndex: number): void;
    remove(element: TElement): boolean;
    removeAt(index: number): TElement;
    set(index: number, element: TElement): void;
    size(): number;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * Returns the elements that are of the specified type.
 * The type can be specified either as a constructor function or as a string representing a primitive type.
 * @template TResult
 * @param source The source iterable.
 * @param type The type to filter the elements of the sequence with (e.g., 'string', 'number', Boolean, Date, MyCustomClass).
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are of the specified type.
 * @example
 *      // --- Basic Usage with Primitives (string type name) ---
 *      const mixedList = new List<any>([1, 'apple', true, 2.5, 'banana', false, null, undefined]);
 *
 *      const stringsOnly = mixedList.ofType('string').toArray();
 *      // stringsOnly = ['apple', 'banana']
 *
 *      const numbersOnly = mixedList.ofType('number').toArray();
 *      // numbersOnly = [1, 2.5]
 *
 *      const booleansOnly = mixedList.ofType('boolean').toArray();
 *      // booleansOnly = [true, false]
 *
 *      // Note: 'object' will match non-null objects, including arrays, dates, custom objects, etc.
 *      const objectsOnly = mixedList.ofType('object').toArray();
 *      // objectsOnly = [] (in this specific list, as null is considered object but often filtered implicitly)
 *
 *      const listWithObject = new List<any>([1, { name: 'obj' }, new Date(), [1,2] ]);
 *      const objectsInList = listWithObject.ofType('object').toArray();
 *      // objectsInList = [ { name: 'obj' }, Date(...), [1, 2] ]
 *
 *      // --- Usage with Constructor Functions ---
 *      class Animal { constructor(public name: string) {} }
 *      class Dog extends Animal { constructor(name: string, public breed: string) { super(name); } }
 *      class Cat extends Animal { constructor(name: string, public lives: number) { super(name); } }
 *
 *      const animals = new List<Animal | string>([
 *          new Dog('Buddy', 'Golden Retriever'),
 *          new Cat('Whiskers', 9),
 *          'Not an animal',
 *          new Dog('Rex', 'German Shepherd'),
 *          null // Will be filtered out
 *      ]);
 *
 *      // Get only Dog instances
 *      const dogs = animals.ofType(Dog).toArray();
 *      // dogs = [ Dog { name: 'Buddy', breed: 'Golden Retriever' }, Dog { name: 'Rex', breed: 'German Shepherd' } ]
 *      // TypeScript knows `dogs` is of type Dog[]
 *
 *      // Get only Cat instances
 *      const cats = animals.ofType(Cat).toArray();
 *      // cats = [ Cat { name: 'Whiskers', lives: 9 } ]
 *      // TypeScript knows `cats` is of type Cat[]
 *
 *      // --- Inheritance Handling ---
 *      // Get all instances of Animal (includes Dogs and Cats)
 *      const allAnimals = animals.ofType(Animal).toArray();
 *      // allAnimals = [
 *      //   Dog { name: 'Buddy', breed: 'Golden Retriever' },
 *      //   Cat { name: 'Whiskers', lives: 9 },
 *      //   Dog { name: 'Rex', breed: 'German Shepherd' }
 *      // ]
 *      // TypeScript knows `allAnimals` is of type Animal[]
 *
 *      // --- Using with built-in constructors ---
 *      const variousData = new List<any>([new Date(), 123, "hello", new Date(0), true]);
 *      const datesOnly = variousData.ofType(Date).toArray();
 *      // datesOnly = [ Date(...), Date(0) ] // Contains the two Date objects
 *
 *      const numbersFromAny = variousData.ofType(Number).toArray();
 *      // numbersFromAny = [ 123 ]
 *
 *      // --- Edge Cases ---
 *      const nullsAndUndefined = new List<any>([null, undefined, 0, '']);
 *      const objects = nullsAndUndefined.ofType('object').toArray(); // 'object' typically matches non-null objects
 *      // objects = []
 *
 *      const undefinedOnly = nullsAndUndefined.ofType('undefined').toArray();
 *      // undefinedOnly = [undefined]
 */
export declare const ofType: <TElement, TResult extends ObjectType>(source: Iterable<TElement>, type: TResult) => IEnumerable<InferredType<TResult>>;

/**
 * Sorts the elements of a sequence in ascending order by using a specified comparer.
 * @template TElement
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used for selecting the key for an element.
 * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
 * @returns {IOrderedEnumerable<TElement>} A new enumerable sequence whose elements are sorted in ascending order.
 * @example
 *      const numbers = new List([50, 10, 40, 30, 20]);
 *      const sortedNumbers = numbers.orderBy(n => n).toArray();
 *      // sortedNumbers = [10, 20, 30, 40, 50]
 *
 *      interface Person { name: string; age: number; }
 *      const people = new List<Person>([
 *          { name: 'Charlie', age: 30 },
 *          { name: 'Alice', age: 25 },
 *          { name: 'Bob', age: 35 }
 *      ]);
 *
 *      // Order by age (ascending)
 *      const peopleByAge = people.orderBy(p => p.age).toArray();
 *      // peopleByAge = [
 *      //   { name: 'Alice', age: 25 },
 *      //   { name: 'Charlie', age: 30 },
 *      //   { name: 'Bob', age: 35 }
 *      // ]
 *
 *      // Order by name (string comparison, ascending)
 *      const peopleByName = people.orderBy(p => p.name).toArray();
 *      // peopleByName = [
 *      //   { name: 'Alice', age: 25 },
 *      //   { name: 'Bob', age: 35 },
 *      //   { name: 'Charlie', age: 30 }
 *      // ]
 *
 *      // Using a custom comparator (e.g., sort numbers as strings)
 *      const numbersToSortAsString = new List([1, 10, 2, 20]);
 *      const sortedAsString = numbersToSortAsString.orderBy(
 *          n => n,
 *          (a, b) => String(a).localeCompare(String(b)) // String comparison
 *      ).toArray();
 *      // sortedAsString = [1, 10, 2, 20] (standard numeric sort would be [1, 2, 10, 20])
 */
export declare const orderBy: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>) => IOrderedEnumerable<TElement>;

/**
 * Sorts the elements of a sequence in descending order by using a specified comparer.
 * @template TElement
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used for selecting the key for an element.
 * @param comparator The comparator function that will be used for comparing two keys. If not specified, the default order comparison will be used.
 * @returns {IOrderedEnumerable<TElement>} A new enumerable sequence whose elements are sorted in descending order.
 * @example
 *      const numbers = new List([50, 10, 40, 30, 20]);
 *      const sortedNumbersDesc = numbers.orderByDescending(n => n).toArray();
 *      // sortedNumbersDesc = [50, 40, 30, 20, 10]
 *
 *      interface Person { name: string; age: number; }
 *      const people = new List<Person>([
 *          { name: 'Charlie', age: 30 },
 *          { name: 'Alice', age: 25 },
 *          { name: 'Bob', age: 35 }
 *      ]);
 *
 *      // Order by age (descending)
 *      const peopleByAgeDesc = people.orderByDescending(p => p.age).toArray();
 *      // peopleByAgeDesc = [
 *      //   { name: 'Bob', age: 35 },
 *      //   { name: 'Charlie', age: 30 },
 *      //   { name: 'Alice', age: 25 }
 *      // ]
 *
 *      // Order by name (string comparison, descending)
 *      const peopleByNameDesc = people.orderByDescending(p => p.name).toArray();
 *      // peopleByNameDesc = [
 *      //   { name: 'Charlie', age: 30 },
 *      //   { name: 'Bob', age: 35 },
 *      //   { name: 'Alice', age: 25 }
 *      // ]
 */
export declare const orderByDescending: <TElement, TKey>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: OrderComparator<TKey>) => IOrderedEnumerable<TElement>;

export declare interface OrderComparator<TFirst, TSecond = TFirst> {
    (e1: TFirst, e2: TSecond): number;
}

/**
 * Produces a sequence of tuples containing the element and the following element.
 * @template TElement, TResult
 * @param source The source iterable.
 * @param resultSelector The optional function to create a result element from the current and the next element. Defaults to creating a tuple `[current, next]`.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of applying the `resultSelector` to adjacent elements.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *
 *      // Default behavior: creates tuples
 *      const pairs = numbers.pairwise().toArray();
 *      // pairs = [[1, 2], [2, 3], [3, 4], [4, 5]]
 *
 *      // Custom result selector: calculate differences
 *      const differences = numbers.pairwise((current, next) => next - current).toArray();
 *      // differences = [1, 1, 1, 1]
 *
 *      // Custom result selector: create strings
 *      const pairStrings = numbers.pairwise((current, next) => `${current}-${next}`).toArray();
 *      // pairStrings = ["1-2", "2-3", "3-4", "4-5"]
 *
 *      const shortList = new List([10]);
 *      const noPairs = shortList.pairwise().toArray();
 *      // noPairs = []
 *
 *      const emptyList = new List<number>();
 *      const emptyPairs = emptyList.pairwise().toArray();
 *      // emptyPairs = []
 */
export declare const pairwise: <TElement>(source: Iterable<TElement>, resultSelector?: PairwiseSelector<TElement, TElement>) => IEnumerable<[TElement, TElement]>;

export declare interface PairwiseSelector<TFirst, TSecond = TFirst, TResult = [TFirst, TSecond]> {
    (first: TFirst, second: TSecond): TResult;
}

/**
 * Produces a tuple of two enumerable sequences, the first one containing the elements that satisfy the condition, and the second one containing the rest of the elements.
 * Note: This method iterates the source sequence immediately and stores the results.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition.
 * @returns {[IEnumerable<TElement>, IEnumerable<TElement>]} A tuple containing two enumerable sequences: the first for elements satisfying the predicate, the second for the rest.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
 *
 *      const [evens, odds] = numbers.partition(n => n % 2 === 0);
 *
 *      const evensArray = evens.toArray();
 *      // evensArray = [2, 4, 6, 8, 10]
 *
 *      const oddsArray = odds.toArray();
 *      // oddsArray = [1, 3, 5, 7, 9]
 *
 *      interface Person { name: string; age: number; }
 *      const people = new List<Person>([
 *          { name: 'Alice', age: 25 },
 *          { name: 'Bob', age: 17 },
 *          { name: 'Charlie', age: 30 },
 *          { name: 'Diana', age: 15 }
 *      ]);
 *
 *      const [adults, minors] = people.partition(p => p.age >= 18);
 *
 *      const adultNames = adults.select(p => p.name).toArray();
 *      // adultNames = ['Alice', 'Charlie']
 *
 *      const minorNames = minors.select(p => p.name).toArray();
 *      // minorNames = ['Bob', 'Diana']
 */
export declare const partition: <TElement>(source: Iterable<TElement>, predicate: Predicate<TElement>) => [IEnumerable<TElement>, IEnumerable<TElement>];

/**
 * Returns an enumerable sequence of permutations, each containing a permutation of the elements of the source sequence.
 * Note: This method first extracts distinct elements from the source before generating permutations.
 * @template TElement
 * @param source The source iterable.
 * @param size If specified, it will return only the permutations of the specified size. If not specified, it will return permutations of the size of the distinct elements in the source sequence.
 * @returns {IEnumerable<IEnumerable<TElement>>} An enumerable of enumerable sequences, each containing a permutation of the distinct elements of the source sequence.
 * @throws {InvalidArgumentException} If size is less than or equal to 0.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const allPermutations = letters.permutations()
 *          .select(p => p.toArray().join('')) // Convert each permutation sequence to a string
 *          .toArray();
 *      // allPermutations = ["abc", "acb", "bac", "bca", "cab", "cba"]
 *
 *      const permutationsOfTwo = letters.permutations(2)
 *          .select(p => p.toArray().join(''))
 *          .toArray();
 *      // permutationsOfTwo = ["ab", "ac", "ba", "bc", "ca", "cb"]
 *
 *      // With duplicates in source - only distinct elements are permuted
 *      const lettersWithDuplicates = new List(['a', 'a', 'b']);
 *      const permsFromDup = lettersWithDuplicates.permutations() // Equivalent to permutations of ['a', 'b']
 *          .select(p => p.toArray().join(''))
 *          .toArray();
 *      // permsFromDup = ["ab", "ba"]
 *
 *      const permsOfOne = letters.permutations(1)
 *          .select(p => p.toArray().join(''))
 *          .toArray();
 *      // permsOfOne = ["a", "b", "c"]
 *
 *      try {
 *          letters.permutations(0); // Throws InvalidArgumentException
 *      } catch (e) {
 *          console.log(e.message); // Output: Size must be greater than 0.
 *      }
 */
export declare const permutations: <TElement>(source: Iterable<TElement>, size?: number) => IEnumerable<IEnumerable<TElement>>;

export declare interface Predicate<TElement> {
    (element: TElement): boolean;
}

/**
 * Adds a value to the beginning of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param element The element to add to the sequence.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that starts with the specified element.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const prepended = numbers.prepend(0).toArray();
 *      // prepended = [0, 1, 2, 3]
 *
 *      const emptyList = new List<string>();
 *      const prependedToEmpty = emptyList.prepend("first").toArray();
 *      // prependedToEmpty = ["first"]
 */
export declare const prepend: <TElement>(source: Iterable<TElement>, element: TElement) => IEnumerable<TElement>;

declare class PrimitiveBigInt {
    static [Symbol.hasInstance]: (x: unknown) => x is bigint;
}

declare class PrimitiveBoolean extends Boolean {
    static readonly [Symbol.hasInstance]: (x: unknown) => x is boolean;
}

declare class PrimitiveNumber extends Number {
    static readonly [Symbol.hasInstance]: (x: unknown) => x is number;
}

declare class PrimitiveObject extends Object {
    static readonly [Symbol.hasInstance]: (x: unknown) => x is object | null;
}

declare class PrimitiveString extends String {
    static readonly [Symbol.hasInstance]: (x: unknown) => x is string;
}

declare class PrimitiveSymbol {
    static [Symbol.hasInstance]: (x: unknown) => x is symbol;
}

export declare class PriorityQueue<TElement> extends AbstractCollection<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: OrderComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds an element to the queue.
     * @template TElement The type of elements in the queue.
     * @param element The element to add.
     * @returns true
     */
    add(element: TElement): boolean;
    /**
     * Clears the queue.
     */
    clear(): void;
    contains(element: TElement): boolean;
    /**
     * Retrieves and removes the element at the beginning of the queue.
     * Unlike {@link poll}, this method throws an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     * @throws {Error} If the queue is empty.
     */
    dequeue(): TElement;
    /**
     * Adds an element to the queue.
     * @template TElement The type of elements in the queue.
     * @param element The element to add.
     */
    enqueue(element: TElement): void;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link peek}, this method throws an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     * @throws {Error} If the queue is empty.
     */
    front(): TElement;
    isEmpty(): boolean;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link front}, this method returns null if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue or null if the queue is empty.
     */
    peek(): TElement | null;
    /**
     * Retrieves and removes the element at the beginning of the queue.
     * Unlike {@link dequeue}, this method does not throw an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue, or null if the queue is empty.
     */
    poll(): TElement | null;
    size(): number;
    get comparator(): OrderComparator<TElement>;
    get length(): number;
}

/**
 * Computes the product of the sequence. Assumes elements are numbers or uses a selector to get numbers.
 * @param source The source iterable.
 * @param selector The selector function that will be used to select a numeric value from the sequence elements.
 * @returns {number} The product of the sequence. Returns 1 if the sequence is empty.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const totalProduct = numbers.product();
 *      // totalProduct = 120 (1 * 2 * 3 * 4 * 5)
 *
 *      interface Item { value: number; }
 *      const items = new List<Item>([{ value: 2 }, { value: 5 }, { value: 10 }]);
 *      const itemValueProduct = items.product(item => item.value);
 *      // itemValueProduct = 100 (2 * 5 * 10)
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.product(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const product: <TElement>(source: Iterable<TElement>, selector?: Selector<TElement, number>) => number;

export declare class Queue<TElement> extends AbstractCollection<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds an element to the end of the queue.
     * @param element The element that will be added to the queue.
     * @return true if the element was added to the queue, false otherwise.
     */
    add(element: TElement): boolean;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    /**
     * Retrieves and returns the element at the beginning of the queue.
     * Unlike {@link poll}, this method does not return null if the queue is empty.
     * Instead, it throws an exception.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     * @throws {Error} If the queue is empty.
     */
    dequeue(): TElement;
    /**
     * Adds an element to the end of the queue.
     * @param element The element that will be added to the queue.
     */
    enqueue(element: TElement): void;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link peek}, this method throws an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     */
    front(): TElement;
    isEmpty(): boolean;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link front}, this method returns null if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue or null if the queue is empty.
     */
    peek(): TElement | null;
    /**
     * Retrieves and removes the element at the beginning of the queue.
     * Unlike {@link dequeue}, this method does not throw an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue, or null if the queue is empty.
     */
    poll(): TElement | null;
    size(): number;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * Creates a range of numbers starting from the specified start value and containing the specified count of elements.
 * @param {number} start The start value of the range.
 * @param {number} count The number of elements in the range.
 * @returns {IEnumerable<number>} An enumerable range of numbers.
 */
export declare const range: (start: number, count: number) => IEnumerable<number>;

export declare class ReadonlyCollection<TElement> extends AbstractReadonlyCollection<TElement> {
    #private;
    constructor(collection: ICollection<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    size(): number;
    get length(): number;
}

export declare class ReadonlyDictionary<TKey, TValue> extends AbstractReadonlyDictionary<TKey, TValue> {
    #private;
    constructor(dictionary: IDictionary<TKey, TValue>);
    [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    containsKey(key: TKey): boolean;
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    entries(): IterableIterator<[TKey, TValue]>;
    get(key: TKey): TValue | null;
    keys(): ISet<TKey>;
    size(): number;
    values(): ICollection<TValue>;
    get length(): number;
}

export declare class ReadonlyList<TElement> extends AbstractReadonlyCollection<TElement> implements IReadonlyList<TElement> {
    #private;
    constructor(list: IList<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    containsAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    entries(): IterableIterator<[number, TElement]>;
    get(index: number): TElement;
    getRange(index: number, count: number): IReadonlyList<TElement>;
    indexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    lastIndexOf(element: TElement, comparator?: EqualityComparator<TElement>): number;
    get length(): number;
    size(): number;
}

export declare class RedBlackTree<TElement> extends AbstractTree<TElement> {
    constructor(iterable?: Iterable<TElement>, comparator?: OrderComparator<TElement>);
    add(element: TElement): boolean;
    delete(element: TElement): void;
    insert(element: TElement): void;
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    removeIf(predicate: Predicate<TElement>): boolean;
    search(element: TElement): boolean;
    private canMoveLeft;
    private canMoveRight;
    private deleteNode;
    private detachNodeFromParent;
    private findReplaceItem;
    private fixDoubleBlack;
    private fixDoubleRed;
    private getSuccessor;
    private handleBlackOrNullUncle;
    private handleBlackSibling;
    private handleBlackSiblingWithBlackChildren;
    private handleBlackSiblingWithRedChild;
    private handleLeftParent;
    private handleNullReplacement;
    private handleRedSibling;
    private handleRedUncle;
    private handleRightParent;
    private handleSiblingWithRedLeftChild;
    private handleSiblingWithRedRightChild;
    private handleSingleChildReplacement;
    private isExactMatch;
    private leftRotate;
    private moveLeft;
    private moveRight;
    private rightRotate;
    private searchNode;
    private swapColors;
    private swapValues;
}

/**
 * Repeats the specified element a specified number of times.
 *
 * @template TElement The type of the element to repeat.
 * @param {TElement} element The element to repeat.
 * @param {number} count The number of times to repeat the element.
 * @returns {IEnumerable<TElement>} An Iterable representing the repeated elements.
 */
export declare const repeat: <TElement>(element: TElement, count: number) => IEnumerable<TElement>;

/**
 * Inverts the order of the elements in the sequence.
 *
 * Note: This method internally converts the sequence to an array to reverse it.
 * @template TElement
 * @param source The source iterable.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are in the reverse order of the source sequence.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const reversedNumbers = numbers.reverse().toArray();
 *      // reversedNumbers = [5, 4, 3, 2, 1]
 *
 *      const letters = new List(['a', 'b', 'c']);
 *      const reversedLetters = letters.reverse().toArray();
 *      // reversedLetters = ['c', 'b', 'a']
 *
 *      const emptyList = new List<number>();
 *      const reversedEmpty = emptyList.reverse().toArray();
 *      // reversedEmpty = []
 */
export declare const reverse: <TElement>(source: Iterable<TElement>) => IEnumerable<TElement>;

/**
 * Applies an accumulator function over the sequence and yields the result of each intermediate computation.
 * If seed is specified, it is used as the initial value for the accumulator, but it is not included in the result sequence.
 * @template TAccumulate
 * @param source The source iterable.
 * @param accumulator The accumulator function that will be applied over the sequence.
 * @param seed The value that will be used as the initial value. If not specified, the first element of the sequence will be used as the seed value and also included as the first element of the result.
 * @returns {IEnumerable<TAccumulate>} A new enumerable sequence whose elements are the result of each intermediate computation.
 * @throws {NoElementsException} If the source is empty and seed is not provided.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *
 *      // Running sum without a seed (the first element is the initial value and first result)
 *      const runningSumNoSeed = numbers.scan((acc, current) => acc + current).toArray();
 *      // runningSumNoSeed = [1, 3, 6, 10, 15]
 *
 *      // Running sum with seed (seed is initial value, but not in output)
 *      const runningSumWithSeed = numbers.scan((acc, current) => acc + current, 100).toArray();
 *      // runningSumWithSeed = [101, 103, 106, 110, 115]
 *
 *      // Building intermediate strings
 *      const letters = new List(['a', 'b', 'c']);
 *      const intermediateStrings = letters.scan((acc, current) => acc + current, '').toArray();
 *      // intermediateStrings = ['a', 'ab', 'abc']
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.scan((a, b) => a + b).toArray(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 *      const scanEmptyWithSeed = emptyList.scan((a, b) => a + b, 0).toArray();
 *      // scanEmptyWithSeed = []
 */
export declare const scan: <TElement, TAccumulate = TElement>(source: Iterable<TElement>, accumulator: Accumulator<TElement, TAccumulate>, seed?: TAccumulate) => IEnumerable<TAccumulate>;

/**
 * Projects each element of a sequence into a new form.
 * @template TResult
 * @param source The source iterable.
 * @param selector The selector function that will be used to project each element into a new form. The second parameter is the index.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the result of the selector function.
 * @example
 *      const numbers = new List([1, 2, 3, 4]);
 *      const squares = numbers.select(n => n * n).toArray();
 *      // squares = [1, 4, 9, 16]
 *
 *      interface Person { firstName: string; lastName: string; }
 *      const people = new List<Person>([
 *          { firstName: 'John', lastName: 'Doe' },
 *          { firstName: 'Jane', lastName: 'Smith' }
 *      ]);
 *      const fullNames = people.select(p => `${p.firstName} ${p.lastName}`).toArray();
 *      // fullNames = ["John Doe", "Jane Smith"]
 *
 *      // Using the index
 *      const indexedValues = people.select((p, index) => `${index}: ${p.firstName}`).toArray();
 *      // indexedValues = ["0: John", "1: Jane"]
 */
export declare const select: <TElement, TResult>(source: Iterable<TElement>, selector: IndexedSelector<TElement, TResult>) => IEnumerable<TResult>;

/**
 * Projects each element of a sequence into a new form (which is an iterable) and flattens the resulting sequences into one sequence.
 * @template TResult
 * @param source The source iterable.
 * @param selector The selector function that will be used to project each element into a new iterable form. The second parameter is the index.
 * @returns {IEnumerable<TResult>} A new enumerable sequence whose elements are the flattened result of the selector function.
 * @example
 *      interface Customer { name: string; orders: string[]; }
 *      const customers = new List<Customer>([
 *          { name: 'Alice', orders: ['Apple', 'Banana'] },
 *          { name: 'Bob', orders: ['Cherry'] },
 *          { name: 'Charlie', orders: [] } // No orders
 *      ]);
 *
 *      // Get a single list of all orders from all customers
 *      const allOrders = customers.selectMany(c => c.orders).toArray();
 *      // allOrders = ['Apple', 'Banana', 'Cherry']
 *
 *      // Example: splitting strings and flattening
 *      const sentences = new List(['Hello world', 'How are you']);
 *      const words = sentences.selectMany(s => s.split(' ')).toArray();
 *      // words = ['Hello', 'world', 'How', 'are', 'you']
 *
 *      // Using index in selector
 *      const indexedFlatten = customers.selectMany((c, index) => c.orders.map(o => `${index}-${o}`)).toArray();
 *      // indexedFlatten = ['0-Apple', '0-Banana', '1-Cherry']
 */
export declare const selectMany: <TElement, TResult>(source: Iterable<TElement>, selector: IndexedSelector<TElement, Iterable<TResult>>) => IEnumerable<TResult>;

export declare interface Selector<TElement, TResult> {
    (item: TElement): TResult;
}

/**
 * Determines whether two sequences are equal by comparing the elements by using an equality comparer for their type.
 * Compares elements pairwise in order. Sequences must have the same length and equal elements at corresponding positions.
 * @param source The source iterable.
 * @param other The iterable sequence to compare to the source sequence.
 * @param comparator The equality comparer that will be used to compare the elements. If not specified, the default equality comparer will be used.
 * @returns {boolean} true if the two source sequences are of equal length and their corresponding elements are equal, according to the specified equality comparer; otherwise, false.
 * @example
 *      const list1 = new List([1, 2, 3]);
 *      const list2 = new List([1, 2, 3]);
 *      const list3 = new List([1, 3, 2]); // Different order
 *      const list4 = new List([1, 2]); // Different length
 *      const array1 = [1, 2, 3]; // Can compare with other iterables
 *
 *      const isEqual12 = list1.sequenceEqual(list2);
 *      // isEqual12 = true
 *
 *      const isEqual13 = list1.sequenceEqual(list3);
 *      // isEqual13 = false
 *
 *      const isEqual14 = list1.sequenceEqual(list4);
 *      // isEqual14 = false
 *
 *      const isEqual1Array = list1.sequenceEqual(array1);
 *      // isEqual1Array = true
 *
 *      // Custom comparison for objects
 *      interface Item { id: number; }
 *      const items1 = new List<Item>([{ id: 1 }, { id: 2 }]);
 *      const items2 = new List<Item>([{ id: 1 }, { id: 2 }]);
 *      const items3 = new List<Item>([{ id: 1 }, { id: 3 }]);
 *
 *      const areItemsEqualById = items1.sequenceEqual(items2, (a, b) => a.id === b.id);
 *      // areItemsEqualById = true
 *
 *      const areItems3EqualById = items1.sequenceEqual(items3, (a, b) => a.id === b.id);
 *      // areItems3EqualById = false
 */
export declare const sequenceEqual: <TElement>(source: Iterable<TElement>, other: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => boolean;

/**
 * Returns a new enumerable sequence whose elements are shuffled randomly.
 * Note: This method internally converts the sequence to an array to shuffle it.
 * @template TElement
 * @param source The source iterable.
 * @returns {IEnumerable<TElement>} A new enumerable sequence whose elements are shuffled.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const shuffledNumbers = numbers.shuffle().toArray();
 *      // shuffledNumbers will be a random permutation of [1, 2, 3, 4, 5], e.g., [3, 1, 5, 2, 4]
 *
 *      // Shuffling is not stable; subsequent calls will likely produce different orders
 *      const shuffledAgain = numbers.shuffle().toArray();
 *      // shuffledAgain will likely be different from shuffledNumbers
 */
export declare const shuffle: <TElement>(source: Iterable<TElement>) => IEnumerable<TElement>;

/**
 * Returns the only element of a sequence and throws an exception if there is not exactly one element in the sequence.
 * Can optionally apply a predicate to filter the sequence first.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, checks the entire sequence.
 * @returns {TElement} The single element of the sequence (or the single element satisfying the predicate).
 * @throws {NoElementsException} If the source (or filtered sequence) is empty.
 * @throws {MoreThanOneElementException} If the source (or filtered sequence) contains more than one element.
 * @throws {NoMatchingElementException} If a predicate is specified and no element satisfies the condition.
 * @throws {MoreThanOneMatchingElementException} If a predicate is specified and more than one element satisfies the condition.
 * @example
 *      const singleItemList = new List([42]);
 *      const theOnlyItem = singleItemList.single();
 *      // theOnlyItem = 42
 *
 *      const numbers = new List([10, 20, 30, 40]);
 *      const theOnlyThirty = numbers.single(n => n === 30);
 *      // theOnlyThirty = 30
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.single(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message);
 *      }
 *
 *      const multipleItems = new List([1, 2]);
 *      try {
 *          multipleItems.single(); // Throws MoreThanOneElementException
 *      } catch (e) {
 *          console.log(e.message);
 *      }
 *
 *      try {
 *          numbers.single(n => n > 50); // Throws NoMatchingElementException
 *      } catch (e) {
 *          console.log(e.message);
 *      }
 *
 *      try {
 *          numbers.single(n => n > 15); // Throws MoreThanOneMatchingElementException
 *      } catch (e) {
 *          console.log(e.message);
 *      }
 */
export declare const single: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement;

/**
 * Returns the only element of a sequence, or a default value (null) if the sequence is empty.
 * Throws an exception if there is more than one element in the sequence (or more than one matching the predicate).
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to check each element for a condition. If not specified, checks the entire sequence.
 * @returns {TElement|null} The single element of the sequence (or the single element satisfying the predicate), or null if the sequence (or filtered sequence) is empty.
 * @throws {MoreThanOneElementException} If the source contains more than one element (and no predicate is used).
 * @throws {MoreThanOneMatchingElementException} If a predicate is specified and more than one element satisfies the condition.
 * @example
 *      const singleItemList = new List([42]);
 *      const theOnlyItem = singleItemList.singleOrDefault();
 *      // theOnlyItem = 42
 *
 *      const numbers = new List([10, 20, 30, 40]);
 *      const theOnlyThirty = numbers.singleOrDefault(n => n === 30);
 *      // theOnlyThirty = 30
 *
 *      const emptyList = new List<number>();
 *      const singleFromEmpty = emptyList.singleOrDefault();
 *      // singleFromEmpty = null
 *
 *      const singleNoMatch = numbers.singleOrDefault(n => n > 50);
 *      // singleNoMatch = null
 *
 *      const multipleItems = new List([1, 2]);
 *      try {
 *          multipleItems.singleOrDefault(); // Throws MoreThanOneElementException
 *      } catch (e) { console.log(e.message); }
 *
 *      try {
 *          numbers.singleOrDefault(n => n > 15); // Throws MoreThanOneMatchingElementException
 *      } catch (e) {
 *          console.log(e.message);
 *      }
 */
export declare const singleOrDefault: <TElement>(source: Iterable<TElement>, predicate?: Predicate<TElement>) => TElement | null;

/**
 * Bypasses a specified number of elements in a sequence and then returns the remaining elements.
 * @template TElement
 * @param source The source iterable.
 * @param count The number of elements to skip before returning the remaining elements. If the count is zero or negative, all elements are returned.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements that occur after the specified number of skipped elements.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *      const skipFirstThree = numbers.skip(3).toArray();
 *      // skipFirstThree = [4, 5, 6]
 *
 *      const skipZero = numbers.skip(0).toArray();
 *      // skipZero = [1, 2, 3, 4, 5, 6]
 *
 *      const skipMoreThanAvailable = numbers.skip(10).toArray();
 *      // skipMoreThanAvailable = []
 *
 *      const skipNegative = numbers.skip(-5).toArray(); // Negative count is treated as 0
 *      // skipNegative = [1, 2, 3, 4, 5, 6]
 */
export declare const skip: <TElement>(source: Iterable<TElement>, count: number) => IEnumerable<TElement>;

/**
 * Returns a new enumerable sequence that contains the elements from the source with the last count elements of the source sequence omitted.
 * @template TElement
 * @param source The source iterable.
 * @param count The number of elements to omit from the end of the collection. If the count is zero or negative, all elements are returned.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from source with the last count elements omitted.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *      const skipLastTwo = numbers.skipLast(2).toArray();
 *      // skipLastTwo = [1, 2, 3, 4]
 *
 *      const skipLastZero = numbers.skipLast(0).toArray();
 *      // skipLastZero = [1, 2, 3, 4, 5, 6]
 *
 *      const skipLastMoreThanAvailable = numbers.skipLast(10).toArray();
 *      // skipLastMoreThanAvailable = []
 *
 *      const skipLastNegative = numbers.skipLast(-3).toArray(); // Negative count is treated as 0
 *      // skipLastNegative = [1, 2, 3, 4, 5, 6]
 */
export declare const skipLast: <TElement>(source: Iterable<TElement>, count: number) => IEnumerable<TElement>;

/**
 * Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
 * The element that first fails the condition is included in the result.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function (accepting element and index) that will be used to test each element.
 * @returns {IEnumerable<TElement>} A new enumerable sequence containing elements starting from the first element that does not satisfy the predicate.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 1, 2]);
 *
 *      // Skip while less than 4
 *      const skipWhileLessThan4 = numbers.skipWhile(n => n < 4).toArray();
 *      // skipWhileLessThan4 = [4, 5, 1, 2] (Stops skipping at 4)
 *
 *      // Skip based on index
 *      const skipWhileIndexLessThan3 = numbers.skipWhile((n, index) => index < 3).toArray();
 *      // skipWhileIndexLessThan3 = [4, 5, 1, 2] (Skips elements at index 0, 1, 2)
 *
 *      // Condition never met
 *      const skipWhileAlwaysTrue = numbers.skipWhile(n => true).toArray();
 *      // skipWhileAlwaysTrue = []
 *
 *      // Condition immediately false
 *      const skipWhileAlwaysFalse = numbers.skipWhile(n => false).toArray();
 *      // skipWhileAlwaysFalse = [1, 2, 3, 4, 5, 1, 2]
 */
export declare const skipWhile: <TElement>(source: Iterable<TElement>, predicate: IndexedPredicate<TElement>) => IEnumerable<TElement>;

export declare class SortedDictionary<TKey, TValue> extends AbstractDictionary<TKey, TValue> {
    #private;
    constructor();
    constructor(iterable: Iterable<KeyValuePair<TKey, TValue>>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>);
    constructor(iterable: Iterable<[TKey, TValue]>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>);
    constructor(iterable: Iterable<KeyValuePair<TKey, TValue>> | Iterable<[TKey, TValue]>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>);
    [Symbol.iterator](): Iterator<KeyValuePair<TKey, TValue>>;
    add(key: TKey, value: TValue): TValue;
    clear(): void;
    containsKey(key: TKey): boolean;
    containsValue(value: TValue, comparator?: EqualityComparator<TValue>): boolean;
    entries(): IterableIterator<[TKey, TValue]>;
    get(key: TKey): TValue | null;
    keys(): ISet<TKey>;
    remove(key: TKey): TValue | null;
    set(key: TKey, value: TValue): void;
    size(): number;
    values(): ICollection<TValue>;
    get keyComparator(): OrderComparator<TKey>;
    get length(): number;
}

export declare class SortedSet<TElement> extends AbstractSet<TElement> implements ISet<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: OrderComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    add(element: TElement): boolean;
    clear(): void;
    contains(element: TElement, comparator?: EqualityComparator<TElement>): boolean;
    headSet(toElement: TElement, inclusive?: boolean): ISet<TElement>;
    remove(element: TElement): boolean;
    removeAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    removeIf(predicate: Predicate<TElement>): boolean;
    retainAll<TSource extends TElement>(collection: Iterable<TSource>): boolean;
    size(): number;
    subSet(fromElement: TElement, toElement: TElement, fromInclusive?: boolean, toInclusive?: boolean): ISet<TElement>;
    tailSet(fromElement: TElement, inclusive?: boolean): ISet<TElement>;
    get comparator(): OrderComparator<TElement>;
    get length(): number;
}

/**
 * Splits the sequence into two sequences based on a predicate.
 * The first sequence contains the elements from the start of the input sequence that satisfy the predicate continuously.
 * The second sequence contains the remaining elements, starting from the first element that failed the predicate.
 * Note: This method iterates the source sequence immediately and stores the results.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function that will be used to test each element.
 * @returns {[IEnumerable<TElement>, IEnumerable<TElement>]} A tuple of two enumerable sequences.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 1, 5, 6]);
 *
 *      // Span while numbers are less than 4
 *      const [lessThan4, rest1] = numbers.span(n => n < 4);
 *      const lessThan4Array = lessThan4.toArray();
 *      // lessThan4Array = [1, 2, 3]
 *      const rest1Array = rest1.toArray();
 *      // rest1Array = [4, 1, 5, 6] (Starts from the first element failing the condition)
 *
 *      // Span while condition is always true
 *      const [allElements, rest2] = numbers.span(n => true);
 *      const allElementsArray = allElements.toArray();
 *      // allElementsArray = [1, 2, 3, 4, 1, 5, 6]
 *      const rest2Array = rest2.toArray();
 *      // rest2Array = []
 *
 *      // Span while the condition is initially false
 *      const [initialSpan, rest3] = numbers.span(n => n > 10);
 *      const initialSpanArray = initialSpan.toArray();
 *      // initialSpanArray = []
 *      const rest3Array = rest3.toArray();
 *      // rest3Array = [1, 2, 3, 4, 1, 5, 6]
 */
export declare const span: <TElement>(source: Iterable<TElement>, predicate: Predicate<TElement>) => [IEnumerable<TElement>, IEnumerable<TElement>];

export declare class Stack<TElement> extends AbstractCollection<TElement> {
    #private;
    constructor(iterable?: Iterable<TElement>, comparator?: EqualityComparator<TElement>);
    [Symbol.iterator](): Iterator<TElement>;
    /**
     * Adds an element to the top of the stack.
     * @param element The element to add.
     */
    add(element: TElement): boolean;
    clear(): void;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link top}, this method returns null if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue or null if the queue is empty.
     */
    peek(): TElement | null;
    /**
     * Retrieves and removes the element at the beginning of the queue.
     * @template TElement The type of elements in the queue.
     * @returns {TElement | null} The head of the queue, or null if the queue is empty.
     * @throws {Error} If the queue is empty.
     */
    pop(): TElement | null;
    /**
     * Adds an element to the top of the stack.
     * @param element The element to add.
     */
    push(element: TElement): void;
    size(): number;
    /**
     * Retrieves but does not remove the element at the beginning of the queue.
     * Unlike {@link peek}, this method throws an error if the queue is empty.
     * @template TElement The type of elements in the queue.
     * @returns {TElement} The head of the queue.
     */
    top(): TElement;
    get comparator(): EqualityComparator<TElement>;
    get length(): number;
}

/**
 * Selects elements from a sequence at regular intervals (steps).
 * Includes the first element (index 0) and then every 'step'-th element after that.
 * @template TElement
 * @param source The source iterable.
 * @param step The number of elements to skip between included elements. Must be 1 or greater.
 * @returns {IEnumerable<TElement>} A new enumerable sequence containing elements at the specified step intervals.
 * @throws {InvalidArgumentException} If the step is less than 1.
 * @example
 *      const numbers = new List([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
 *
 *      // Take every 2nd element (step = 2)
 *      const step2 = numbers.step(2).toArray();
 *      // step2 = [0, 2, 4, 6, 8, 10]
 *
 *      // Take every 3rd element (step = 3)
 *      const step3 = numbers.step(3).toArray();
 *      // step3 = [0, 3, 6, 9]
 *
 *      // Step = 1 includes all elements
 *      const step1 = numbers.step(1).toArray();
 *      // step1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 *
 *      try {
 *          numbers.step(0); // Throws InvalidArgumentException
 *      } catch (e) {
 *          console.log(e.message); // Output: Step must be greater than 0.
 *      }
 */
export declare const step: <TElement>(source: Iterable<TElement>, step: number) => IEnumerable<TElement>;

declare type StringType = string | String | StringConstructor;

/**
 * Returns the sum of the values in the sequence. Assumes elements are numbers or uses a selector to get numbers.
 * @param source The source iterable.
 * @param selector The selector function that will be used to select the numeric value to sum. If not specified, the element itself is used.
 * @returns {number} The sum of the values in the sequence. Returns 0 if the sequence is empty.
 * @throws {NoElementsException} If the source is empty.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5]);
 *      const totalSum = numbers.sum();
 *      // totalSum = 15
 *
 *      interface Item { value: number; quantity: number; }
 *      const items = new List<Item>([
 *          { value: 10, quantity: 2 }, // Total value = 20
 *          { value: 5, quantity: 3 }  // Total value = 15
 *      ]);
 *      const totalItemValue = items.sum(item => item.value * item.quantity);
 *      // totalItemValue = 35
 *
 *      const emptyList = new List<number>();
 *      try {
 *          emptyList.sum(); // Throws NoElementsException
 *      } catch (e) {
 *          console.log(e.message); // Output: The sequence contains no elements.
 *      }
 */
export declare const sum: <TElement>(source: Iterable<TElement>, selector?: Selector<TElement, number>) => number;

declare type SymbolType = symbol | Symbol | SymbolConstructor | "symbol";

/**
 * Returns a specified number of contiguous elements from the start of a sequence.
 * @template TElement
 * @param source The source iterable.
 * @param count The number of elements to return. If the count is zero or negative, an empty sequence is returned. If the count is greater than the number of elements, all elements are returned.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the specified number of elements from the start of the input sequence.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *
 *      const takeFirstThree = numbers.take(3).toArray();
 *      // takeFirstThree = [1, 2, 3]
 *
 *      const takeZero = numbers.take(0).toArray();
 *      // takeZero = []
 *
 *      const takeMoreThanAvailable = numbers.take(10).toArray();
 *      // takeMoreThanAvailable = [1, 2, 3, 4, 5, 6]
 *
 *      const takeNegative = numbers.take(-2).toArray(); // Negative count is treated as 0
 *      // takeNegative = []
 */
export declare const take: <TElement>(source: Iterable<TElement>, count: number) => IEnumerable<TElement>;

/**
 * Returns a specified number of contiguous elements from the end of a sequence.
 * @template TElement
 * @param source The source iterable.
 * @param count The number of elements to return. If the count is zero or negative, an empty sequence is returned. If the count is greater than the number of elements, all elements are returned.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the specified number of elements from the end of the input sequence.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *
 *      const takeLastTwo = numbers.takeLast(2).toArray();
 *      // takeLastTwo = [5, 6]
 *
 *      const takeLastZero = numbers.takeLast(0).toArray();
 *      // takeLastZero = []
 *
 *      const takeLastMoreThanAvailable = numbers.takeLast(10).toArray();
 *      // takeLastMoreThanAvailable = [1, 2, 3, 4, 5, 6] (Order is preserved)
 *
 *      const takeLastNegative = numbers.takeLast(-3).toArray(); // Negative count is treated as 0
 *      // takeLastNegative = []
 */
export declare const takeLast: <TElement>(source: Iterable<TElement>, count: number) => IEnumerable<TElement>;

/**
 * Returns elements from a sequence as long as a specified condition is true and then skips the remaining elements.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function (accepting element and index) that will be used to test each element.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from the input sequence that occur before the element at which the test no longer passes.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 1, 5, 6]);
 *
 *      // Take while less than 4
 *      const takeWhileLessThan4 = numbers.takeWhile(n => n < 4).toArray();
 *      // takeWhileLessThan4 = [1, 2, 3] (Stops taking at 4)
 *
 *      // Take based on index
 *      const takeWhileIndexLessThan3 = numbers.takeWhile((n, index) => index < 3).toArray();
 *      // takeWhileIndexLessThan3 = [1, 2, 3] (Takes elements at index 0, 1, 2)
 *
 *      // Condition never met (the first element fails)
 *      const takeWhileAlwaysFalse = numbers.takeWhile(n => n > 10).toArray();
 *      // takeWhileAlwaysFalse = []
 *
 *      // Condition always true
 *      const takeWhileAlwaysTrue = numbers.takeWhile(n => true).toArray();
 *      // takeWhileAlwaysTrue = [1, 2, 3, 4, 1, 5, 6]
 */
export declare const takeWhile: <TElement>(source: Iterable<TElement>, predicate: IndexedPredicate<TElement>) => IEnumerable<TElement>;

/**
 * Creates a new array from the elements of the sequence.
 * This forces evaluation of the entire sequence.
 * @template TElement
 * @param source The source iterable.
 * @returns {TElement[]} An array that contains the elements from the input sequence.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const numberArray = numbers.toArray();
 *      // numberArray = [1, 2, 3] (a standard JavaScript Array)
 *
 *      const squares = numbers.select(n => n * n); // squares is an IEnumerable
 *      const squaresArray = squares.toArray(); // squaresArray forces evaluation
 *      // squaresArray = [1, 4, 9]
 */
export declare const toArray: <TElement>(source: Iterable<TElement>) => TElement[];

/**
 * Creates a new circular linked list from the elements of the sequence.
 * Forces evaluation of the sequence.
 * @template TElement The type of elements in the sequence.
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {CircularLinkedList<TElement>} A new circular linked list that contains the elements from the input sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const circularList = letters.toCircularLinkedList();
 *      // circularList is a CircularLinkedList instance containing 'a', 'b', 'c'
 *      // circularList.firstNode?.value === 'a'
 *      // circularList.lastNode?.value === 'c'
 *      // circularList.lastNode?.next === circularList.firstNode // Circular nature
 */
export declare const toCircularLinkedList: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => CircularLinkedList<TElement>;

/**
 * Creates a new dictionary from the elements of the sequence.
 * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
 * @template TKey, TValue
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param valueSelector The value selector function that will be used to select the value for an element.
 * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
 * @returns {Dictionary<TKey, TValue>} A new dictionary that contains the elements from the input sequence.
 * @example
 *      interface Product { id: number; name: string; price: number; }
 *      const products = new List<Product>([
 *          { id: 1, name: 'Apple', price: 0.5 },
 *          { id: 2, name: 'Banana', price: 0.3 },
 *          { id: 3, name: 'Cherry', price: 1.0 }
 *      ]);
 *
 *      // Create a dictionary mapping ID to Product Name
 *      const productDict = products.toDictionary(p => p.id, p => p.name);
 *      // productDict.get(2) === 'Banana'
 *      // productDict.size === 3
 *
 *      // Example with KeyValuePair source
 *      const pairs = new List([
 *          new KeyValuePair('one', 1),
 *          new KeyValuePair('two', 2)
 *      ]);
 *      const dictFromPairs = pairs.toDictionary(kv => kv.key, kv => kv.value);
 *      // dictFromPairs.get('one') === 1
 *
 *      // Example causing error due to a duplicate key
 *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
 *      try {
 *          duplicateKeys.toDictionary(item => item.key, item => item.val);
 *      } catch (e) {
 *          console.log(e.message); // Output likely: "An item with the same key has already been added."
 *      }
 */
export declare const toDictionary: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>) => Dictionary<TKey, TValue>;

/**
 * Creates a new enumerable set from the elements of the sequence. Duplicate elements are ignored.
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @returns {EnumerableSet<TElement>} An enumerable set that contains the distinct elements from the input sequence.
 * @example
 *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
 *      const numberSet = numbers.toEnumerableSet();
 *      // numberSet contains {1, 2, 3, 4, 5}
 *      // numberSet.size === 5
 *      // numberSet.contains(2) === true
 *      // numberSet.toArray() results in [1, 2, 3, 4, 5] (order depends on Set implementation)
 */
export declare const toEnumerableSet: <TElement>(source: Iterable<TElement>) => EnumerableSet<TElement>;

/**
 * Creates a new immutable dictionary from the elements of the sequence.
 * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
 * @template TKey, TValue
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param valueSelector The value selector function that will be used to select the value for an element.
 * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
 * @returns {ImmutableDictionary<TKey, TValue>} A new immutable dictionary that contains the elements from the input sequence.
 * @example
 *      interface Product { id: number; name: string; price: number; }
 *      const products = new List<Product>([
 *          { id: 1, name: 'Apple', price: 0.5 },
 *          { id: 2, name: 'Banana', price: 0.3 }
 *      ]);
 *
 *      const immutableProductDict = products.toImmutableDictionary(p => p.id, p => p.name);
 *      // immutableProductDict.get(1) === 'Apple'
 *      // immutableProductDict.size === 2
 *      // Attempting immutableProductDict.add(3, 'Cherry') would throw an error or return a new dictionary.
 */
export declare const toImmutableDictionary: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, valueComparator?: EqualityComparator<TValue>) => ImmutableDictionary<TKey, TValue>;

/**
 * Creates a new immutable list from the elements of the sequence.
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {ImmutableList<TElement>} A new immutable list that contains the elements from the input sequence.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const immutableList = numbers.toImmutableList();
 *      // immutableList contains [1, 2, 3]
 *      // immutableList.size === 3
 *      // immutableList.get(0) === 1
 *      // Attempting immutableList.add(4) would throw an error or return a new list.
 */
export declare const toImmutableList: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => ImmutableList<TElement>;

/**
 * Creates a new immutable priority queue from the elements of the sequence.
 * Forces evaluation of the sequence. Elements are ordered based on the comparator.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The order comparator function that will be used to compare two elements. If not specified, the default order comparer will be used.
 * @returns {ImmutablePriorityQueue<TElement>} A new immutable priority queue that contains the elements from the input sequence.
 * @example
 *      const numbers = new List([5, 1, 3, 4, 2]);
 *      // Default comparator assumes min-heap (smaller numbers have higher priority)
 *      const immutableMinQueue = numbers.toImmutablePriorityQueue();
 *      // immutableMinQueue.peek() === 1
 *
 *      // Custom comparator for max-heap
 *      const immutableMaxQueue = numbers.toImmutablePriorityQueue((a, b) => b - a); // Larger numbers first
 *      // immutableMaxQueue.peek() === 5
 *
 *      // Attempting immutableMinQueue.enqueue(0) would return a new queue.
 */
export declare const toImmutablePriorityQueue: <TElement>(source: Iterable<TElement>, comparator?: OrderComparator<TElement>) => ImmutablePriorityQueue<TElement>;

/**
 * Creates a new immutable queue from the elements of the sequence (FIFO).
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {ImmutableQueue<TElement>} A new immutable queue that contains the elements from the input sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const immutableQueue = letters.toImmutableQueue();
 *      // immutableQueue.peek() === 'a'
 *      // immutableQueue.size === 3
 *      // Attempting immutableQueue.enqueue('d') would return a new queue.
 */
export declare const toImmutableQueue: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => ImmutableQueue<TElement>;

/**
 * Creates a new immutable set from the elements of the sequence. Duplicate elements are ignored.
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @returns {ImmutableSet<TElement>} A new immutable set that contains the distinct elements from the input sequence.
 * @example
 *      const numbers = new List([1, 2, 2, 3, 1]);
 *      const immutableSet = numbers.toImmutableSet();
 *      // immutableSet contains {1, 2, 3}
 *      // immutableSet.size === 3
 *      // immutableSet.contains(2) === true
 *      // Attempting immutableSet.add(4) would return a new set.
 */
export declare const toImmutableSet: <TElement>(source: Iterable<TElement>) => ImmutableSet<TElement>;

/**
 * Creates a new immutable sorted dictionary from the elements of the sequence.
 * Forces evaluation of the sequence. Keys are sorted. Throws if duplicate keys are encountered.
 * @template TKey, TValue
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param valueSelector The value selector function that will be used to select the value for an element.
 * @param keyComparator The key comparator function that will be used to compare two keys for sorting. If not specified, the default order comparer will be used.
 * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
 * @returns {ImmutableSortedDictionary<TKey, TValue>} A new immutable sorted dictionary that contains the elements from the input sequence.
 * @example
 *      interface Product { id: number; name: string; }
 *      const products = new List<Product>([
 *          { id: 3, name: 'Cherry' },
 *          { id: 1, name: 'Apple' },
 *          { id: 2, name: 'Banana' }
 *      ]);
 *
 *      const immutableSortedDict = products.toImmutableSortedDictionary(p => p.id, p => p.name);
 *      // Keys will be sorted: 1, 2, 3
 *      // immutableSortedDict.get(2) === 'Banana'
 *      // immutableSortedDict.keys().toArray() === [1, 2, 3]
 *      // Attempting immutableSortedDict.add(4, 'Date') would return a new dictionary.
 */
export declare const toImmutableSortedDictionary: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>) => ImmutableSortedDictionary<TKey, TValue>;

/**
 * Creates a new immutable sorted set from the elements of the sequence. Duplicate elements are ignored.
 * Forces evaluation of the sequence. Elements are sorted.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The order comparator function that will be used to compare two elements for sorting. If not specified, the default order comparer will be used.
 * @returns {ImmutableSortedSet<TElement>} A new immutable sorted set that contains the distinct, sorted elements from the input sequence.
 * @example
 *      const numbers = new List([5, 1, 3, 1, 4, 2, 5]);
 *      const immutableSortedSet = numbers.toImmutableSortedSet();
 *      // immutableSortedSet contains {1, 2, 3, 4, 5} in sorted order
 *      // immutableSortedSet.toArray() === [1, 2, 3, 4, 5]
 *      // immutableSortedSet.contains(3) === true
 *      // immutableSortedSet.size === 5
 *      // Attempting immutableSortedSet.add(0) would return a new set.
 */
export declare const toImmutableSortedSet: <TElement>(source: Iterable<TElement>, comparator?: OrderComparator<TElement>) => ImmutableSortedSet<TElement>;

/**
 * Creates a new immutable stack from the elements of the sequence (LIFO).
 * Forces evaluation of the sequence. The last element of the source sequence becomes the top of the stack.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {ImmutableStack<TElement>} A new immutable stack that contains the elements from the input sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c']); // 'c' is the last element
 *      const immutableStack = letters.toImmutableStack();
 *      // immutableStack.peek() === 'c'
 *      // immutableStack.size === 3
 *      // Attempting immutableStack.push('d') would return a new stack.
 */
export declare const toImmutableStack: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => ImmutableStack<TElement>;

/**
 * Creates a new linked list from the elements of the sequence.
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {LinkedList<TElement>} A new linked list that contains the elements from the input sequence.
 * @example
 *      const numbers = new List([10, 20, 30]);
 *      const linkedList = numbers.toLinkedList();
 *      // linkedList is a LinkedList instance
 *      // linkedList.firstNode?.value === 10
 *      // linkedList.lastNode?.value === 30
 *      // linkedList.size() === 3
 */
export declare const toLinkedList: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => LinkedList<TElement>;

/**
 * Creates a new list from the elements of the sequence.
 * Forces evaluation of the sequence.
 * @param source The source iterable.
 * @template TElement
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {List<TElement>} A new list that contains the elements from the input sequence.
 * @example
 *      const numbers = Enumerable.range(1, 3); // Creates IEnumerable [1, 2, 3]
 *      const list = numbers.toList();
 *      // list is a List instance containing [1, 2, 3]
 *      // list.get(0) === 1
 *      // list.size() === 3
 *
 *      // Creates a copy of an existing list
 *      const originalList = new List(['a', 'b']);
 *      const newList = originalList.toList();
 *      // newList !== originalList (it's a new instance)
 *      // newList.toArray() results in ['a', 'b']
 */
export declare const toList: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => List<TElement>;

/**
 * Creates a new lookup from the elements of the sequence. A lookup is similar to a dictionary but allows multiple values per key.
 * Forces evaluation of the sequence.
 * @template TKey
 * @template TValue
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param valueSelector The value selector function that will be used to select the value for an element.
 * @param keyComparator The key comparator function that will be used to compare two keys. If not specified, the default equality comparer will be used.
 * @returns {ILookup<TKey, TValue>} A new lookup that contains the elements from the input sequence, grouped by key.
 * @example
 *      interface Pet { name: string; species: string; age: number; }
 *      const pets = new List<Pet>([
 *          { name: 'Fluffy', species: 'Cat', age: 3 },
 *          { name: 'Buddy', species: 'Dog', age: 5 },
 *          { name: 'Whiskers', species: 'Cat', age: 2 },
 *          { name: 'Rex', species: 'Dog', age: 7 }
 *      ]);
 *
 *      // Group pet names by species
 *      const lookup = pets.toLookup(pet => pet.species, pet => pet.name);
 *
 *      // lookup.count() === 2 (number of distinct keys: 'Cat', 'Dog')
 *      // lookup.contains('Cat') === true
 *
 *      const catNames = lookup.get('Cat').toArray();
 *      // catNames = ['Fluffy', 'Whiskers']
 *
 *      const dogNames = lookup.get('Dog').toArray();
 *      // dogNames = ['Buddy', 'Rex']
 *
 *      const fishNames = lookup.get('Fish').toArray(); // Key not present
 *      // fishNames = []
 */
export declare const toLookup: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>) => ILookup<TKey, TValue>;

/**
 * Converts this enumerable to a JavaScript Map.
 * Forces evaluation of the sequence. Throws if duplicate keys are encountered.
 * @template TKey
 * @template TValue
 * @param source The source iterable.
 * @param keySelector The selector that will be used to select the property that will be used as the key of the map.
 * @param valueSelector The selector that will be used to select the property that will be used as the value of the map.
 * @returns {Map<TKey, TValue>} A Map representation of this sequence.
 * @example
 *      interface Product { id: number; name: string; price: number; }
 *      const products = new List<Product>([
 *          { id: 1, name: 'Apple', price: 0.5 },
 *          { id: 2, name: 'Banana', price: 0.3 },
 *          { id: 3, name: 'Cherry', price: 1.0 }
 *      ]);
 *
 *      // Create a Map mapping ID to Product Name
 *      const productMap = products.toMap(p => p.id, p => p.name);
 *      // productMap instanceof Map === true
 *      // productMap.get(2) === 'Banana'
 *      // productMap.size === 3
 *
 *      // Example causing error due to a duplicate key
 *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
 *      try {
 *          duplicateKeys.toMap(item => item.key, item => item.val);
 *      } catch (e) {
 *          console.log(e.message); // Map structure prevents duplicate keys by default. Behavior might depend on the underlying Map implementation if custom logic is used.
 *      }
 */
export declare const toMap: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>) => Map<TKey, TValue>;

/**
 * Converts this enumerable to a JavaScript object (Record).
 * Forces evaluation of the sequence. If duplicate keys are encountered, the last value for that key overwrites previous ones.
 * @template TKey
 * @template TValue
 * @param source The source iterable.
 * @param keySelector The selector that will be used to select the property that will be used as the key of the object. Must return string, number, or symbol.
 * @param valueSelector The selector that will be used to select the property that will be used as the value of the object.
 * @returns {Record<TKey, TValue>} An object that contains the elements of the sequence.
 * @example
 *      interface Product { id: string; name: string; price: number; }
 *      const products = new List<Product>([
 *          { id: 'A1', name: 'Apple', price: 0.5 },
 *          { id: 'B2', name: 'Banana', price: 0.3 },
 *          { id: 'C3', name: 'Cherry', price: 1.0 }
 *      ]);
 *
 *      // Create an object mapping ID to Product Price
 *      const productObject = products.toObject(p => p.id, p => p.price);
 *      // productObject = { A1: 0.5, B2: 0.3, C3: 1.0 }
 *      // productObject['B2'] === 0.3
 *
 *      // Example with duplicate keys (last one wins)
 *      const duplicateKeys = new List([
 *          { key: 'a', val: 1 },
 *          { key: 'b', val: 2 },
 *          { key: 'a', val: 3 } // This value for 'a' will overwrite the first one
 *      ]);
 *      const objectFromDups = duplicateKeys.toObject(item => item.key, item => item.val);
 *      // objectFromDups = { a: 3, b: 2 }
 */
export declare const toObject: <TElement, TKey extends string | number | symbol, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>) => Record<TKey, TValue>;

/**
 * Creates a new priority queue from the elements of the sequence.
 * Forces evaluation of the sequence. Elements are ordered based on the comparator.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The order comparator function that will be used to compare two elements. If not specified, the default order comparer will be used (min-heap).
 * @returns {PriorityQueue<TElement>} A new priority queue that contains the elements from the input sequence.
 * @example
 *      const numbers = new List([5, 1, 3, 4, 2]);
 *      // Default comparator assumes min-heap (smaller numbers have higher priority)
 *      const minQueue = numbers.toPriorityQueue();
 *      // minQueue.peek() === 1
 *      // minQueue.dequeue() === 1
 *      // minQueue.peek() === 2
 *
 *      // Custom comparator for max-heap
 *      const maxQueue = numbers.toPriorityQueue((a, b) => b - a); // Larger numbers first
 *      // maxQueue.peek() === 5
 *      // maxQueue.dequeue() === 5
 *      // maxQueue.peek() === 4
 */
export declare const toPriorityQueue: <TElement>(source: Iterable<TElement>, comparator?: OrderComparator<TElement>) => PriorityQueue<TElement>;

/**
 * Creates a new queue from the elements of the sequence (FIFO).
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {Queue<TElement>} A new queue that contains the elements from the input sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c']);
 *      const queue = letters.toQueue();
 *      // queue.peek() === 'a'
 *      // queue.size() === 3
 *      // queue.dequeue() === 'a'
 *      // queue.peek() === 'b'
 */
export declare const toQueue: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => Queue<TElement>;

/**
 * Creates a new JavaScript Set from the elements of the sequence. Duplicate elements are ignored.
 * Forces evaluation of the sequence.
 * @template TElement
 * @param source The source iterable.
 * @returns {Set<TElement>} A new Set that contains the distinct elements from the input sequence.
 * @example
 *      const numbers = new List([1, 2, 2, 3, 1, 4, 5, 5]);
 *      const numberSet = numbers.toSet();
 *      // numberSet instanceof Set === true
 *      // numberSet contains {1, 2, 3, 4, 5}
 *      // numberSet.size === 5
 *      // numberSet.has(2) === true
 */
export declare const toSet: <TElement>(source: Iterable<TElement>) => Set<TElement>;

/**
 * Creates a new sorted dictionary from the elements of the sequence.
 * Forces evaluation of the sequence. Keys are sorted. Throws if duplicate keys are encountered.
 * @template TKey
 * @template TValue
 * @param source The source iterable.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param valueSelector The value selector function that will be used to select the value for an element.
 * @param keyComparator The key comparator function that will be used to compare two keys for sorting. If not specified, the default order comparer will be used.
 * @param valueComparator The value comparator function that will be used to compare two values. If not specified, the default equality comparer will be used.
 * @returns {SortedDictionary<TKey, TValue>} A new sorted dictionary that contains the elements from the input sequence.
 * @example
 *      interface Product { id: number; name: string; }
 *      const products = new List<Product>([
 *          { id: 3, name: 'Cherry' },
 *          { id: 1, name: 'Apple' },
 *          { id: 2, name: 'Banana' }
 *      ]);
 *
 *      const sortedDict = products.toSortedDictionary(p => p.id, p => p.name);
 *      // Keys will be sorted: 1, 2, 3
 *      // sortedDict.get(2) === 'Banana'
 *      // sortedDict.keys().toArray() results in [1, 2, 3]
 *
 *      // Example causing error due to duplicate key
 *      const duplicateKeys = new List([{ key: 'a', val: 1 }, { key: 'a', val: 2 }]);
 *      try {
 *          duplicateKeys.toSortedDictionary(item => item.key, item => item.val);
 *      } catch (e) {
 *          console.log(e.message); // Output likely: "An item with the same key has already been added."
 *      }
 */
export declare const toSortedDictionary: <TElement, TKey, TValue>(source: Iterable<TElement>, keySelector: Selector<TElement, TKey>, valueSelector: Selector<TElement, TValue>, keyComparator?: OrderComparator<TKey>, valueComparator?: EqualityComparator<TValue>) => SortedDictionary<TKey, TValue>;

/**
 * Creates a new sorted set from the elements of the sequence. Duplicate elements are ignored.
 * Forces evaluation of the sequence. Elements are sorted.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The order comparator function that will be used to compare two elements for sorting. If not specified, the default order comparer will be used.
 * @returns {SortedSet<TElement>} A new sorted set that contains the distinct, sorted elements from the input sequence.
 * @example
 *      const numbers = new List([5, 1, 3, 1, 4, 2, 5]);
 *      const sortedSet = numbers.toSortedSet();
 *      // sortedSet contains {1, 2, 3, 4, 5} in sorted order
 *      // sortedSet.toArray() results in [1, 2, 3, 4, 5]
 *      // sortedSet.contains(3) === true
 *      // sortedSet.size() === 5
 */
export declare const toSortedSet: <TElement>(source: Iterable<TElement>, comparator?: OrderComparator<TElement>) => SortedSet<TElement>;

/**
 * Creates a new stack from the elements of the sequence (LIFO).
 * Forces evaluation of the sequence. The last element of the source sequence becomes the top of the stack.
 * @template TElement
 * @param source The source iterable.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {Stack<TElement>} A new stack that contains the elements from the input sequence.
 * @example
 *      const letters = new List(['a', 'b', 'c']); // 'c' is the last element
 *      const stack = letters.toStack();
 *      // stack.peek() === 'c'
 *      // stack.size() === 3
 *      // stack.pop() === 'c'
 *      // stack.peek() === 'b'
 */
export declare const toStack: <TElement>(source: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => Stack<TElement>;

export declare type TraverseType = "INORDER" | "PREORDER" | "POSTORDER";

/**
 * Produces the set union of two sequences by using an equality comparer.
 * The result contains all unique elements from both sequences.
 * @template TElement
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements form the second set for the union.
 * @param comparator The equality comparator function that will be used to compare two elements. If not specified, the default equality comparer will be used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from both input sequences, excluding duplicates. Order is preserved from the original sequences, with elements from the first sequence appearing before elements from the second.
 * @example
 *      const numbers1 = new List([1, 2, 3, 3]);
 *      const numbers2 = new List([3, 4, 5, 4]);
 *      const unionResult = numbers1.union(numbers2).toArray();
 *      // unionResult = [1, 2, 3, 4, 5] (Order: elements from numbers1 first, then unique from numbers2)
 *
 *      // Using custom object comparison
 *      interface Item { id: number; value: string; }
 *      const items1 = new List<Item>([{ id: 1, value: 'A' }, { id: 2, value: 'B' }]);
 *      const items2 = new List<Item>([{ id: 2, value: 'B_alt' }, { id: 3, value: 'C' }]);
 *      const itemUnion = items1.union(items2, (a, b) => a.id === b.id).toArray();
 *      // itemUnion = [
 *      //   { id: 1, value: 'A' }, // From items1
 *      //   { id: 2, value: 'B' }, // From items1 (id=2 from items2 is considered duplicate by comparator)
 *      //   { id: 3, value: 'C' }  // From items2
 *      // ]
 */
export declare const union: <TElement>(source: Iterable<TElement>, other: Iterable<TElement>, comparator?: EqualityComparator<TElement>) => IEnumerable<TElement>;

/**
 * Produces the set union of two sequences by using a key selector function.
 * The result contains all elements from both sequences whose selected keys are unique.
 * @template TElement, TKey
 * @param source The source iterable.
 * @param other The iterable sequence whose distinct elements form the second set for the union.
 * @param keySelector The key selector function that will be used to select the key for an element.
 * @param comparator The equality comparator function that will be used to compare two keys. If not specified, the default equality comparer will be used.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains the elements from both input sequences, excluding elements with duplicate keys based on the selector. Order is preserved.
 * @example
 *      interface Product { code: string; name: string; }
 *      const store1Products = new List<Product>([
 *          { code: 'A1', name: 'Apple' },
 *          { code: 'B2', name: 'Banana' }
 *      ]);
 *      const store2Products = new List<Product>([
 *          { code: 'B2', name: 'Banana V2' }, // Duplicate code 'B2'
 *          { code: 'C3', name: 'Cherry' }
 *      ]);
 *
 *      // Union based on product code
 *      const allUniqueProducts = store1Products.unionBy(
 *          store2Products,
 *          p => p.code // Select code as the key for comparison
 *      ).toArray();
 *      // allUniqueProducts = [
 *      //   { code: 'A1', name: 'Apple' },   // From store1
 *      //   { code: 'B2', name: 'Banana' },  // From store1 (item with code 'B2' from store2 is ignored)
 *      //   { code: 'C3', name: 'Cherry' }    // From store2
 *      // ]
 *
 *      // Example with case-insensitive key comparison
 *      const listA = new List([{ val: 'a', id: 1 }, { val: 'b', id: 2 }]);
 *      const listB = new List([{ val: 'B', id: 3 }, { val: 'c', id: 4 }]); // 'B' has same key as 'b' case-insensitively
 *      const unionCaseInsensitive = listA.unionBy(
 *          listB,
 *          item => item.val,
 *          (keyA, keyB) => keyA.toLowerCase() === keyB.toLowerCase() // Case-insensitive comparator
 *      ).toArray();
 *      // unionCaseInsensitive = [
 *      //  { val: 'a', id: 1 }, // From listA
 *      //  { val: 'b', id: 2 }, // From listA ('B' from listB is ignored)
 *      //  { val: 'c', id: 4 }  // From listB
 *      // ]
 */
export declare const unionBy: <TElement, TKey>(source: Iterable<TElement>, other: Iterable<TElement>, keySelector: Selector<TElement, TKey>, comparator?: EqualityComparator<TKey>) => IEnumerable<TElement>;

/**
 * Filters a sequence of values based on a predicate.
 * @template TElement
 * @param source The source iterable.
 * @param predicate The predicate function (accepting element and index) that will be used to test each element. Return true to keep the element, false to filter it out.
 * @returns {IEnumerable<TElement>} A new enumerable sequence that contains elements from the input sequence that satisfy the condition.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6, 7, 8]);
 *
 *      // Get only even numbers
 *      const evens = numbers.where(n => n % 2 === 0).toArray();
 *      // evens = [2, 4, 6, 8]
 *
 *      // Get numbers greater than 3 at an odd index
 *      const complexFilter = numbers.where((n, index) => n > 3 && index % 2 !== 0).toArray();
 *      // Indices: 0, 1, 2, 3, 4, 5, 6, 7
 *      // Elements:1, 2, 3, 4, 5, 6, 7, 8
 *      // Filter checks:
 *      // - index 1 (value 2): 2 > 3 is false
 *      // - index 3 (value 4): 4 > 3 is true, index 3 is odd. Keep 4.
 *      // - index 5 (value 6): 6 > 3 is true, index 5 is odd. Keep 6.
 *      // - index 7 (value 8): 8 > 3 is true, index 7 is odd. Keep 8.
 *      // complexFilter = [4, 6, 8]
 *
 *      interface Product { name: string; price: number; }
 *      const products = new List<Product>([
 *          { name: 'Apple', price: 0.5 },
 *          { name: 'Banana', price: 0.3 },
 *          { name: 'Cherry', price: 1.0 }
 *      ]);
 *      const cheapProducts = products.where(p => p.price < 0.6).toArray();
 *      // cheapProducts = [ { name: 'Apple', price: 0.5 }, { name: 'Banana', price: 0.3 } ]
 */
export declare const where: <TElement>(source: Iterable<TElement>, predicate: IndexedPredicate<TElement>) => IEnumerable<TElement>;

/**
 * Returns an enumerable sequence of sliding windows of the specified size over the source sequence.
 * Each window is an IEnumerable itself.
 * @template TElement
 * @param source The source iterable.
 * @param size The size of the windows. Must be 1 or greater.
 * @returns {IEnumerable<IEnumerable<TElement>>} A new enumerable sequence where each element is a window (as an IEnumerable) of the specified size.
 * @throws {InvalidArgumentException} If size is less than or equal to 0.
 * @example
 *      const numbers = new List([1, 2, 3, 4, 5, 6]);
 *
 *      // Get windows of size 3
 *      const windowsOf3 = numbers.windows(3)
 *          .select(window => window.toArray()) // Convert each window IEnumerable to an array for clarity
 *          .toArray();
 *      // windowsOf3 = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6]]
 *
 *      // Get windows of size 1
 *      const windowsOf1 = numbers.windows(1)
 *          .select(window => window.toArray())
 *          .toArray();
 *      // windowsOf1 = [[1], [2], [3], [4], [5], [6]]
 *
 *      // Size larger than the list returns an empty sequence
 *      const windowsOf10 = numbers.windows(10).toArray();
 *      // windowsOf10 = []
 *
 *      // Size equal to the list returns one window
 *      const windowsOf6 = numbers.windows(6)
 *          .select(window => window.toArray())
 *          .toArray();
 *      // windowsOf6 = [[1, 2, 3, 4, 5, 6]]
 *
 *      try {
 *          numbers.windows(0); // Throws InvalidArgumentException
 *      } catch (e) {
 *          console.log(e.message); // Output: Size must be greater than 0.
 *      }
 */
export declare const windows: <TElement>(source: Iterable<TElement>, size: number) => IEnumerable<IEnumerable<TElement>>;

/**
 * Applies a specified function (zipper) to the corresponding elements of two sequences, producing a sequence of the results.
 * If the two sequences are of different lengths, the resulting sequence will have the length of the shorter sequence.
 * @template TElement The type of elements in the first sequence.
 * @template TSecond The type of elements in the second sequence.
 * @template TResult The type of elements in the result sequence, as determined by the zipper function.
 * @param source The source iterable.
 * @param other The iterable sequence to merge with the first sequence.
 * @param zipper The function that specifies how to merge the elements from the two sequences into a result element.
 * @returns {IEnumerable<TResult>} A new enumerable sequence that contains the result of applying the zipper function to corresponding elements.
 * @example
 *      const numbers = new List([1, 2, 3]);
 *      const letters = new List(['A', 'B', 'C']);
 *
 *      // Combine numbers and letters into strings using the zipper
 *      const combinedStrings = numbers.zip(
 *      letters,
 *          (num, char) => `${num}-${char}` // Zipper function
 *      ).toArray();
 *      // combinedStrings = ["1-A", "2-B", "3-C"]
 *
 *      // Sum corresponding elements using the zipper
 *      const listA = new List([10, 20, 30]);
 *      const listB = new List([5, 15, 25, 35]); // listB is longer
 *      const sums = listA.zip(
 *      listB,
 *          (a, b) => a + b // Zipper function
 *      ).toArray();
 *      // sums = [15, 35, 55] (Length limited by the shorter listA)
 */
export declare const zip: <TElement, TSecond, TResult = [TElement, TSecond]>(source: Iterable<TElement>, other: Iterable<TSecond>, zipper?: Zipper<TElement, TSecond, TResult>) => IEnumerable<[TElement, TSecond]> | IEnumerable<TResult>;

export declare interface Zipper<TFirst, TSecond, TResult> {
    (sequence1: TFirst, sequence2: TSecond): TResult;
}

export { }
