"use strict";
/*!
  Copyright 2018 Ron Buckton (rbuckton@chronicles.org)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */
/** @module "iterable-query" */
Object.defineProperty(exports, "__esModule", { value: true });
const fn = require("./fn");
const internal_1 = require("./internal");
const types_1 = require("./types");
/**
 * Creates a [[Query]] from a [[Queryable]] source.
 *
 * @param source A [[Queryable]] object.
 * @param hierarchy An optional [[HierarchyProvider]].
 */
function from(source, hierarchy) {
    if (hierarchy)
        source = internal_1.MakeHierarchyIterable(source, hierarchy);
    return internal_1.IsOrderedHierarchyIterable(source) ? new OrderedHierarchyQuery(source) :
        internal_1.IsHierarchyIterable(source) ? new HierarchyQuery(source) :
            internal_1.IsOrderedIterable(source) ? new OrderedQuery(source) :
                new Query(source);
}
exports.from = from;
/**
 * A [[Query]] represents a series of operations that act upon an [[Iterable]] or ArrayLike. Evaluation of
 * these operations is deferred until the either a scalar value is requested from the [[Query]] or the
 * [[Query]] is iterated.
 *
 * @typeparam T The type for each element.
 */
class Query {
    /**
     * Creates a [[Query]] from a [[Queryable]] source.
     *
     * @param source A [[Queryable]] object.
     */
    constructor(source) {
        internal_1.assert.mustBeQueryable(source, "source");
        this._source = source instanceof Query
            ? source._source
            : source;
    }
    static from(source, hierarchy) {
        return from(source, hierarchy);
    }
    static of() {
        return from(arguments);
    }
    /**
     * Creates a [[Query]] with no elements.
     *
     * @category Query
     */
    static empty() {
        return from(fn.empty());
    }
    /**
     * Creates a [[Query]] over a single element.
     *
     * @param value The only element for the [[Query]].
     * @category Query
     */
    static once(value) {
        return from(fn.once(value));
    }
    /**
     * Creates a [[Query]] for a value repeated a provided number of times.
     *
     * @param value The value for each element of the [[Query]].
     * @param count The number of times to repeat the value.
     * @category Query
     */
    static repeat(value, count) {
        return from(fn.repeat(value, count));
    }
    /**
     * Creates a [[Query]] over a range of numbers.
     *
     * @param start The starting number of the range.
     * @param end The ending number of the range.
     * @param increment The amount by which to change between each itereated value.
     * @category Query
     */
    static range(start, end, increment) {
        return from(fn.range(start, end, increment));
    }
    /**
     * Creates a [[Query]] that repeats the provided value forever.
     *
     * @param value The value for each element of the [[Query]].
     * @category Query
     */
    static continuous(value) {
        return from(fn.continuous(value));
    }
    /**
     * Creates a [[Query]] whose values are provided by a callback executed a provided number of
     * times.
     *
     * @param count The number of times to execute the callback.
     * @param generator The callback to execute.
     * @category Query
     */
    static generate(count, generator) {
        return from(fn.generate(count, generator));
    }
    /**
     * Creates a HierarchyQuery from a root node and a HierarchyProvider.
     *
     * @param root The root node of the hierarchy.
     * @param hierarchy A [[HierarchyProvider]] object.
     * @category Query
     */
    static hierarchy(root, hierarchy) {
        return from(fn.hierarchy(root, hierarchy));
    }
    /**
     * Creates a [[Query]] that, when iterated, consumes the provided `Iterator`.
     *
     * @param iterator An `Iterator` object.
     * @category Query
     */
    static consume(iterator, options) {
        return from(fn.consume(iterator, options));
    }
    /**
     * Creates a [[Query]] that iterates the elements from one of two sources based on the result of a
     * lazily evaluated condition.
     *
     * @param condition A callback used to choose a source.
     * @param thenQueryable The source to use when the callback evaluates to `true`.
     * @param elseQueryable The source to use when the callback evaluates to `false`.
     * @category Query
     */
    static if(condition, thenQueryable, elseQueryable) {
        return from(fn.if(condition, thenQueryable, elseQueryable));
    }
    /**
     * Creates a [[Query]] that iterates the elements from sources picked from a list based on the
     * result of a lazily evaluated choice.
     *
     * @param chooser A callback used to choose a source.
     * @param choices A list of sources
     * @param otherwise A default source to use when another choice could not be made.
     * @category Query
     */
    static choose(chooser, choices, otherwise) {
        return from(fn.choose(chooser, choices, otherwise));
    }
    /**
     * Creates a [[Query]] for the own property keys of an object.
     *
     * @param source An object.
     * @category Query
     */
    static objectKeys(source) {
        return from(fn.objectKeys(source));
    }
    /**
     * Creates a [[Query]] for the own property values of an object.
     *
     * @param source An object.
     * @category Query
     */
    static objectValues(source) {
        return from(fn.objectValues(source));
    }
    /**
     * Creates a [[Query]] for the own property key-value pairs of an object.
     *
     * @param source An object.
     * @category Query
     */
    static objectEntries(source) {
        return from(fn.objectEntries(source));
    }
    filter(predicate) {
        return from(fn.filter(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery where the selected key for each element matches the supplied predicate.
     *
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element The element from which to select a key.
     * @param predicate A callback used to match each key.
     * @param predicate.key The key to test.
     * @param predicate.offset The offset from the start of the source iterable.
     * @category Subquery
     */
    filterBy(keySelector, predicate) {
        return from(fn.filterBy(internal_1.GetSource(this), keySelector, predicate));
    }
    filterDefined(keySelector) {
        return from(fn.filterDefined(internal_1.GetSource(this), keySelector));
    }
    where(predicate) {
        return from(fn.where(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery where the selected key for each element matches the supplied predicate.
     *
     * NOTE: This is an alias for [[filterBy]].
     *
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element The element from which to select a key.
     * @param predicate A callback used to match each key.
     * @param predicate.key The key to test.
     * @param predicate.offset The offset from the start of the source iterable.
     * @category Subquery
     */
    whereBy(keySelector, predicate) {
        return from(fn.whereBy(internal_1.GetSource(this), keySelector, predicate));
    }
    whereDefined(keySelector) {
        return from(fn.whereDefined(internal_1.GetSource(this), keySelector));
    }
    /**
     * Creates a subquery by applying a callback to each element.
     *
     * @param selector A callback used to map each element.
     * @param selector.element The element to map.
     * @param selector.offset The offset from the start of the source iterable.
     * @category Subquery
     */
    map(selector) {
        return from(fn.map(internal_1.GetSource(this), selector));
    }
    /**
     * Creates a subquery by applying a callback to each element.
     *
     * NOTE: This is an alias for [[map]].
     *
     * @param selector A callback used to map each element.
     * @param selector.element The element to map.
     * @param selector.offset The offset from the start of the source [[Queryable]].
     * @category Subquery
     */
    select(selector) {
        return from(fn.select(internal_1.GetSource(this), selector));
    }
    flatMap(projection, resultSelector) {
        return from(fn.flatMap(internal_1.GetSource(this), projection, resultSelector));
    }
    selectMany(projection, resultSelector) {
        return from(fn.selectMany(internal_1.GetSource(this), projection, resultSelector));
    }
    /**
     * Creates a subquery that iterates the results of recursively expanding the
     * elements of the source.
     *
     * @param projection A callback used to recusively expand each element.
     * @param projection.element The element to expand.
     * @category Subquery
     */
    expand(projection) {
        return from(fn.expand(internal_1.GetSource(this), projection));
    }
    /**
     * Lazily invokes a callback as each element of the [[Query]] is iterated.
     *
     * @param callback The callback to invoke.
     * @param callback.element An element of the source.
     * @param callback.offset The offset from the start of the source iterable.
     * @category Subquery
     */
    do(callback) {
        return from(fn.do(internal_1.GetSource(this), callback));
    }
    /**
     * Lazily invokes a callback as each element of the [[Query]] is iterated.
     *
     * NOTE: This is an alias for [[do]].
     *
     * @param callback The callback to invoke.
     * @param callback.element An element of the source.
     * @param callback.offset The offset from the start of the source iterable.
     * @category Subquery
     */
    tap(callback) {
        return from(fn.tap(internal_1.GetSource(this), callback));
    }
    /**
     * Creates a subquery whose elements are in the reverse order.
     *
     * @category Subquery
     */
    reverse() {
        return from(fn.reverse(internal_1.GetSource(this)));
    }
    /**
     * Creates a subquery with every instance of the specified value removed.
     *
     * @param values The values to exclude.
     * @category Subquery
     */
    exclude(...values) {
        return from(fn.exclude(internal_1.GetSource(this), ...values));
    }
    /**
     * Creates a subquery containing all elements except the first elements up to the supplied
     * count.
     *
     * @param count The number of elements to skip.
     * @category Subquery
     */
    skip(count) {
        return from(fn.skip(internal_1.GetSource(this), count));
    }
    /**
     * Creates a subquery containing all elements except the last elements up to the supplied
     * count.
     *
     * @param count The number of elements to skip.
     * @category Subquery
     */
    skipRight(count) {
        return from(fn.skipRight(internal_1.GetSource(this), count));
    }
    /**
     * Creates a subquery containing all elements except the first elements that match
     * the supplied predicate.
     *
     * @param predicate A callback used to match each element.
     * @param predicate.element The element to match.
     * @category Subquery
     */
    skipWhile(predicate) {
        return from(fn.skipWhile(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery containing all elements except the first elements that don't match
     * the supplied predicate.
     *
     * @param predicate A callback used to match each element.
     * @param predicate.element The element to match.
     * @category Subquery
     */
    skipUntil(predicate) {
        return from(fn.skipUntil(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery containing the first elements up to the supplied
     * count.
     *
     * @param count The number of elements to take.
     * @category Subquery
     */
    take(count) {
        return from(fn.take(internal_1.GetSource(this), count));
    }
    /**
     * Creates a subquery containing the last elements up to the supplied
     * count.
     *
     * @param count The number of elements to take.
     * @category Subquery
     */
    takeRight(count) {
        return from(fn.takeRight(internal_1.GetSource(this), count));
    }
    takeWhile(predicate) {
        return from(fn.takeWhile(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery containing the first elements that do not match the supplied predicate.
     *
     * @param predicate A callback used to match each element.
     * @param predicate.element The element to match.
     * @category Subquery
     */
    takeUntil(predicate) {
        return from(fn.takeUntil(internal_1.GetSource(this), predicate));
    }
    intersect(right, equaler) {
        return from(fn.intersect(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    intersectBy(right, keySelector, keyEqualer) {
        return from(fn.intersectBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the set union of this [[Query]] and another [[Queryable]].
     *
     * @param right A [[Queryable]] object.
     * @param equaler An [[Equaler]] object used to compare equality.
     * @category Subquery
     */
    union(right, equaler) {
        return from(fn.union(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    /**
     * Creates a subquery for the set union of this [[Query]] and another [[Queryable]], where set identity is determined by the selected key.
     *
     * @param right A [[Queryable]] object.
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    unionBy(right, keySelector, keyEqualer) {
        return from(fn.unionBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the set difference between this and another [[Queryable]].
     *
     * @param right A [[Queryable]] object.
     * @param equaler An [[Equaler]] object used to compare equality.
     * @category Subquery
     */
    except(right, equaler) {
        return from(fn.except(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    /**
     * Creates a subquery for the set difference between this and another [[Queryable]], where set identity is determined by the selected key.
     *
     * @param right A [[Queryable]] object.
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    exceptBy(right, keySelector, keyEqualer) {
        return from(fn.exceptBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the set difference between this and another [[Queryable]].
     *
     * @param right A [[Queryable]] object.
     * @param equaler An [[Equaler]] object used to compare equality.
     * @category Subquery
     */
    relativeComplement(right, equaler) {
        return from(fn.relativeComplement(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    /**
     * Creates a subquery for the set difference between this and another [[Queryable]], where set identity is determined by the selected key.
     *
     * @param right A [[Queryable]] object.
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    relativeComplementBy(right, keySelector, keyEqualer) {
        return from(fn.relativeComplementBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the symmetric difference between this and another [[Queryable]].
     *
     * @param right A [[Queryable]] object.
     * @param equaler An [[Equaler]] object used to compare equality.
     * @category Subquery
     */
    symmetricDifference(right, equaler) {
        return from(fn.symmetricDifference(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    /**
     * Creates a subquery for the symmetric difference between this and another [[Queryable]], where set identity is determined by the selected key.
     *
     * @param right A [[Queryable]] object.
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    symmetricDifferenceBy(right, keySelector, keyEqualer) {
        return from(fn.symmetricDifferenceBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the symmetric difference between this and another [[Queryable]].
     *
     * NOTE: This is an alias for [[symmetricDifference]].
     *
     * @param right A [[Queryable]] object.
     * @param equaler An [[Equaler]] object used to compare equality.
     * @category Subquery
     */
    xor(right, equaler) {
        return from(fn.xor(internal_1.GetSource(this), internal_1.GetSource(right), equaler));
    }
    /**
     * Creates a subquery for the symmetric difference between this and another [[Queryable]], where set identity is determined by the selected key.
     *
     * NOTE: This is an alias for [[symmetricDifferenceBy]].
     *
     * @param right A [[Queryable]] object.
     * @param keySelector A callback used to select the key for each element.
     * @param keySelector.element An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    xorBy(right, keySelector, keyEqualer) {
        return from(fn.xorBy(internal_1.GetSource(this), internal_1.GetSource(right), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery that concatenates this [[Query]] with another [[Queryable]].
     *
     * @param right A [[Queryable]] object.
     * @category Subquery
     */
    concat(right) {
        return from(fn.concat(internal_1.GetSource(this), internal_1.GetSource(right)));
    }
    /**
     * Creates a subquery for the distinct elements of this [[Query]].
     * @param equaler An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    distinct(equaler) {
        return from(fn.distinct(internal_1.GetSource(this), equaler));
    }
    /**
     * Creates a subquery for the distinct elements of this [[Query]].
     *
     * @param keySelector A callback used to select the key to determine uniqueness.
     * @param keySelector.value An element from which to select a key.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Subquery
     */
    distinctBy(keySelector, keyEqualer) {
        return from(fn.distinctBy(internal_1.GetSource(this), keySelector, keyEqualer));
    }
    /**
     * Creates a subquery for the elements of this [[Query]] with the provided value appended to the end.
     *
     * @param value The value to append.
     * @category Subquery
     */
    append(value) {
        return from(fn.append(internal_1.GetSource(this), value));
    }
    /**
     * Creates a subquery for the elements of this [[Query]] with the provided value prepended to the beginning.
     *
     * @param value The value to prepend.
     * @category Subquery
     */
    prepend(value) {
        return from(fn.prepend(internal_1.GetSource(this), value));
    }
    /**
     * Creates a subquery for the elements of this [[Query]] with the provided range
     * patched into the results.
     *
     * @param start The offset at which to patch the range.
     * @param skipCount The number of elements to skip from start.
     * @param range The range to patch into the result.
     * @category Subquery
     */
    patch(start, skipCount, range) {
        return from(fn.patch(internal_1.GetSource(this), start, skipCount, range && internal_1.GetSource(range)));
    }
    /**
     * Creates a subquery that contains the provided default value if this [[Query]]
     * contains no elements.
     *
     * @param defaultValue The default value.
     * @category Subquery
     */
    defaultIfEmpty(defaultValue) {
        return from(fn.defaultIfEmpty(internal_1.GetSource(this), defaultValue));
    }
    /**
     * Creates a subquery that splits this [[Query]] into one or more pages.
     * While advancing from page to page is evaluated lazily, the elements of the page are
     * evaluated eagerly.
     *
     * @param pageSize The number of elements per page.
     * @category Subquery
     */
    pageBy(pageSize) {
        return from(fn.pageBy(internal_1.GetSource(this), pageSize));
    }
    spanMap(keySelector, elementSelector, spanSelector) {
        return from(fn.spanMap(internal_1.GetSource(this), keySelector, elementSelector, wrapResultSelector(spanSelector)));
    }
    groupBy(keySelector, elementSelector, resultSelector, keyEqualer) {
        if (internal_1.IsEqualer(elementSelector)) {
            resultSelector = elementSelector;
            elementSelector = undefined;
        }
        if (internal_1.IsEqualer(resultSelector)) {
            keyEqualer = resultSelector;
            resultSelector = undefined;
        }
        return from(fn.groupBy(internal_1.GetSource(this), keySelector, elementSelector, wrapResultSelector(resultSelector), keyEqualer));
    }
    scan(accumulator, seed) {
        return from(arguments.length > 1
            ? fn.scan(internal_1.GetSource(this), accumulator, seed)
            : fn.scan(internal_1.GetSource(this), accumulator));
    }
    scanRight(accumulator, seed) {
        return from(arguments.length > 1
            ? fn.scanRight(internal_1.GetSource(this), accumulator, seed)
            : fn.scanRight(internal_1.GetSource(this), accumulator));
    }
    /**
     * Pass the entire [[Query]] to the provided callback, creating a new [[Query]] from the result.
     *
     * @param callback A callback function.
     * @param callback.source The outer [[Query]].
     * @category Subquery
     */
    through(callback) {
        return from(fn.through(this, callback));
    }
    // #endregion Subquery
    // #region Join
    /**
     * Creates a grouped subquery for the correlated elements of this [[Query]] and another [[Queryable]] object.
     *
     * @param inner A [[Queryable]] object.
     * @param outerKeySelector A callback used to select the key for an element in this [[Query]].
     * @param innerKeySelector A callback used to select the key for an element in the other [[Queryable]] object.
     * @param resultSelector A callback used to select the result for the correlated elements.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Join
     */
    groupJoin(inner, outerKeySelector, innerKeySelector, resultSelector, keyEqualer) {
        return from(fn.groupJoin(internal_1.GetSource(this), internal_1.GetSource(inner), outerKeySelector, innerKeySelector, wrapResultSelector(resultSelector), keyEqualer));
    }
    /**
     * Creates a subquery for the correlated elements of this [[Query]] and another [[Queryable]].
     *
     * @param inner A [[Queryable]] object.
     * @param outerKeySelector A callback used to select the key for an element in this [[Query]].
     * @param innerKeySelector A callback used to select the key for an element in the other Queryable.
     * @param resultSelector A callback used to select the result for the correlated elements.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Join
     */
    join(inner, outerKeySelector, innerKeySelector, resultSelector, keyEqualer) {
        return from(fn.join(internal_1.GetSource(this), internal_1.GetSource(inner), outerKeySelector, innerKeySelector, resultSelector, keyEqualer));
    }
    /**
     * Creates a subquery for the correlated elements of this [[Query]] and another [[Queryable]].
     *
     * @param inner A [[Queryable]] object.
     * @param outerKeySelector A callback used to select the key for an element in this [[Query]].
     * @param innerKeySelector A callback used to select the key for an element in the other Queryable.
     * @param resultSelector A callback used to select the result for the correlated elements.
     * @param keyEqualer An [[Equaler]] object used to compare key equality.
     * @category Join
     */
    fullJoin(inner, outerKeySelector, innerKeySelector, resultSelector, keyEqualer) {
        return from(fn.fullJoin(internal_1.GetSource(this), internal_1.GetSource(inner), outerKeySelector, innerKeySelector, resultSelector, keyEqualer));
    }
    zip(right, selector) {
        return from(fn.zip(internal_1.GetSource(this), internal_1.GetSource(right), selector));
    }
    // #endregion Join
    // #region Order
    /**
     * Creates an ordered subquery whose elements are sorted in ascending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparer An optional callback used to compare two keys.
     * @category Order
     */
    orderBy(keySelector, comparer) {
        return from(fn.orderBy(internal_1.GetSource(this), keySelector, comparer));
    }
    /**
     * Creates an ordered subquery whose elements are sorted in descending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparer An optional callback used to compare two keys.
     * @category Order
     */
    orderByDescending(keySelector, comparer) {
        return from(fn.orderByDescending(internal_1.GetSource(this), keySelector, comparer));
    }
    // #endregion Order
    // #region Hierarchy
    /**
     * Creates a [[HierarchyQuery]] using the provided [[HierarchyProvider]].
     *
     * @param hierarchy A [[HierarchyProvider]].
     * @category Hierarchy
     */
    toHierarchy(hierarchy) {
        return from(fn.toHierarchy(internal_1.GetSource(this), hierarchy));
    }
    reduce(accumulator, seed, resultSelector) {
        return arguments.length > 1
            ? fn.reduce(internal_1.GetSource(this), accumulator, seed, resultSelector)
            : fn.reduce(internal_1.GetSource(this), accumulator);
    }
    reduceRight(accumulator, seed, resultSelector) {
        return arguments.length > 1
            ? fn.reduceRight(internal_1.GetSource(this), accumulator, seed, resultSelector)
            : fn.reduceRight(internal_1.GetSource(this), accumulator);
    }
    /**
     * Counts the number of elements in the [[Query]], optionally filtering elements using the supplied
     * callback.
     *
     * @param predicate An optional callback used to match each element.
     * @category Scalar
     */
    count(predicate) {
        return fn.count(internal_1.GetSource(this), predicate);
    }
    first(predicate) {
        return fn.first(internal_1.GetSource(this), predicate);
    }
    last(predicate) {
        return fn.last(internal_1.GetSource(this), predicate);
    }
    single(predicate) {
        return fn.single(internal_1.GetSource(this), predicate);
    }
    /**
     * Gets the minimum element in the [[Query]], optionally comparing elements using the supplied
     * callback.
     *
     * @param comparer An optional callback used to compare two elements.
     * @category Scalar
     */
    min(comparer) {
        return fn.min(internal_1.GetSource(this), comparer);
    }
    /**
     * Gets the minimum element by its key in the [[Query]], optionally comparing the keys of each element using the supplied callback.
     *
     * @param keySelector A callback used to choose the key to compare.
     * @param keyComparer An optional callback used to compare the keys.
     * @category Scalar
     */
    minBy(keySelector, keyComparer) {
        return fn.minBy(internal_1.GetSource(this), keySelector, keyComparer);
    }
    /**
     * Gets the maximum element in the [[Query]], optionally comparing elements using the supplied
     * callback.
     *
     * @param comparer An optional callback used to compare two elements.
     * @category Scalar
     */
    max(comparer) {
        return fn.max(internal_1.GetSource(this), comparer);
    }
    /**
     * Gets the maximum element by its key in the [[Query]], optionally comparing the keys of each element using the supplied callback.
     *
     * @param keySelector A callback used to choose the key to compare.
     * @param keyComparer An optional callback used to compare the keys.
     * @category Scalar
     */
    maxBy(keySelector, keyComparer) {
        return fn.maxBy(internal_1.GetSource(this), keySelector, keyComparer);
    }
    sum(elementSelector) {
        return fn.sum(internal_1.GetSource(this), elementSelector);
    }
    average(elementSelector) {
        return fn.average(internal_1.GetSource(this), elementSelector);
    }
    /**
     * Computes a scalar value indicating whether the [[Query]] contains any elements,
     * optionally filtering the elements using the supplied callback.
     *
     * @param predicate An optional callback used to match each element.
     * @category Scalar
     */
    some(predicate) {
        return fn.some(internal_1.GetSource(this), predicate);
    }
    every(predicate) {
        return fn.every(internal_1.GetSource(this), predicate);
    }
    corresponds(right, equaler) {
        return fn.corresponds(internal_1.GetSource(this), internal_1.GetSource(right), equaler);
    }
    correspondsBy(right, leftKeySelector, rightKeySelector, keyEqualer) {
        return fn.correspondsBy(internal_1.GetSource(this), internal_1.GetSource(right), leftKeySelector, rightKeySelector, keyEqualer);
    }
    includes(value, equaler) {
        return fn.includes(internal_1.GetSource(this), value, equaler);
    }
    includesSequence(right, equaler) {
        return fn.includesSequence(internal_1.GetSource(this), internal_1.GetSource(right), equaler);
    }
    startsWith(right, equaler) {
        return fn.startsWith(internal_1.GetSource(this), internal_1.GetSource(right), equaler);
    }
    endsWith(right, equaler) {
        return fn.endsWith(internal_1.GetSource(this), internal_1.GetSource(right), equaler);
    }
    /**
     * Finds the value in the [[Query]] at the provided offset. A negative offset starts from the
     * last element.
     *
     * @param offset An offset.
     * @category Scalar
     */
    elementAt(offset) {
        return fn.elementAt(internal_1.GetSource(this), offset);
    }
    /**
     * Finds the value in the [[Query]] at the provided offset. A negative offset starts from the
     * last element.
     *
     * This is an alias for `elementAt`.
     *
     * @param offset An offset.
     * @category Scalar
     */
    nth(offset) {
        return fn.nth(internal_1.GetSource(this), offset);
    }
    span(predicate) {
        const [left, right] = fn.span(internal_1.GetSource(this), predicate);
        return [from(left), from(right)];
    }
    /**
     * Creates a tuple whose first element is a subquery containing the first span of
     * elements that do not match the supplied predicate, and whose second element is a subquery
     * containing the remaining elements.
     *
     * The first subquery is eagerly evaluated, while the second subquery is lazily
     * evaluated.
     *
     * @param predicate The predicate used to match elements.
     * @category Scalar
     */
    break(predicate) {
        const [left, right] = fn.break(internal_1.GetSource(this), predicate);
        return [from(left), from(right)];
    }
    /**
     * Invokes a callback for each element of the [[Query]].
     *
     * @param callback The callback to invoke.
     * @category Scalar
     */
    forEach(callback) {
        fn.forEach(internal_1.GetSource(this), callback);
    }
    /**
     * Iterates over all of the elements in the [[Query]], ignoring the results.
     * @category Scalar
     */
    drain() {
        fn.drain(internal_1.GetSource(this));
    }
    /**
     * Eagerly evaluate the [[Query]], returning a new [[Query]].
     * @category Scalar
     */
    eval() {
        return from(fn.eval(internal_1.GetSource(this)));
    }
    unzip(partSelector) {
        return fn.unzip(internal_1.GetSource(this), partSelector);
    }
    toArray(elementSelector) {
        return fn.toArray(internal_1.GetSource(this), elementSelector);
    }
    toSet(elementSelector, equaler) {
        return fn.toSet(internal_1.GetSource(this), elementSelector, equaler);
    }
    toMap(keySelector, elementSelector, keyEqualer) {
        return fn.toMap(internal_1.GetSource(this), keySelector, elementSelector, keyEqualer);
    }
    toLookup(keySelector, elementSelector, keyEqualer) {
        return fn.toLookup(internal_1.GetSource(this), keySelector, elementSelector, keyEqualer);
    }
    toObject(prototype, keySelector, elementSelector, descriptorSelector) {
        return fn.toObject(internal_1.GetSource(this), prototype, keySelector, elementSelector, descriptorSelector);
    }
    /**
     * Writes each element to a destination array.
     *
     * @param dest The destination array.
     * @param start The offset into the array at which to start writing.
     * @param count The number of elements to write to the array.
     * @category Scalar
     */
    copyTo(dest, start, count) {
        return fn.copyTo(internal_1.GetSource(this), dest, start, count);
    }
    toJSON() {
        return this.toArray();
    }
    // #endregion Scalar
    [Symbol.iterator]() {
        return internal_1.GetIterator(internal_1.ToIterable(internal_1.GetSource(this)));
    }
    /** @internal */ [internal_1.QuerySource.source]() {
        return this._source;
    }
}
exports.Query = Query;
/**
 * Represents a sequence of hierarchically organized values.
 */
class HierarchyQuery extends Query {
    constructor(source, hierarchy) {
        if (hierarchy !== undefined) {
            source = internal_1.MakeHierarchyIterable(source, hierarchy);
        }
        internal_1.assert.mustBeHierarchyIterable(source, "source");
        super(source);
    }
    root(predicate) {
        return from(fn.root(internal_1.GetSource(this), predicate));
    }
    ancestors(predicate) {
        return from(fn.ancestors(internal_1.GetSource(this), predicate));
    }
    ancestorsAndSelf(predicate) {
        return from(fn.ancestorsAndSelf(internal_1.GetSource(this), predicate));
    }
    descendants(predicate) {
        return from(fn.descendants(internal_1.GetSource(this), predicate));
    }
    descendantsAndSelf(predicate) {
        return from(fn.descendantsAndSelf(internal_1.GetSource(this), predicate));
    }
    self(predicate) {
        return from(fn.self(internal_1.GetSource(this), predicate));
    }
    parents(predicate) {
        return from(fn.parents(internal_1.GetSource(this), predicate));
    }
    children(predicate) {
        return from(fn.children(internal_1.GetSource(this), predicate));
    }
    siblings(predicate) {
        return from(fn.siblings(internal_1.GetSource(this), predicate));
    }
    siblingsAndSelf(predicate) {
        return from(fn.siblingsAndSelf(internal_1.GetSource(this), predicate));
    }
    siblingsBeforeSelf(predicate) {
        return from(fn.siblingsBeforeSelf(internal_1.GetSource(this), predicate));
    }
    siblingsAfterSelf(predicate) {
        return from(fn.siblingsAfterSelf(internal_1.GetSource(this), predicate));
    }
    /**
     * Creates a subquery for the child of each element at the specified offset. A negative offset
     * starts from the last child.
     *
     * @param offset The offset for the child.
     * @category Hierarchy
     */
    nthChild(offset) {
        return from(fn.nthChild(internal_1.GetSource(this), offset));
    }
    topMost(predicate) {
        return from(fn.topMost(internal_1.GetSource(this), predicate));
    }
    bottomMost(predicate) {
        return from(fn.bottomMost(internal_1.GetSource(this), predicate));
    }
    // #endregion Hierarchy
    [types_1.Hierarchical.hierarchy]() {
        return internal_1.GetHierarchy(internal_1.GetSource(this));
    }
}
exports.HierarchyQuery = HierarchyQuery;
/**
 * Represents an ordered sequence of elements.
 */
class OrderedQuery extends Query {
    constructor(source) {
        internal_1.assert.mustBeOrderedIterable(source, "source");
        super(source);
    }
    // #region Order
    /**
     * Creates a subsequent ordered subquery whose elements are sorted in ascending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparer An optional callback used to compare two keys.
     * @category Order
     */
    thenBy(keySelector, comparer) {
        return from(fn.thenBy(internal_1.GetSource(this), keySelector, comparer));
    }
    /**
     * Creates a subsequent ordered subquery whose elements are sorted in descending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparer An optional callback used to compare two keys.
     * @category Order
     */
    thenByDescending(keySelector, comparer) {
        return from(fn.thenByDescending(internal_1.GetSource(this), keySelector, comparer));
    }
    // #endregion Order
    [types_1.OrderedIterable.thenBy](keySelector, comparer, descending) {
        return internal_1.ThenBy(internal_1.GetSource(this), keySelector, comparer, descending);
    }
}
exports.OrderedQuery = OrderedQuery;
/**
 * Represents an ordered sequence of hierarchically organized values.
 */
class OrderedHierarchyQuery extends HierarchyQuery {
    constructor(source, hierarchy) {
        internal_1.assert.mustBeOrderedIterable(source, "source");
        if (hierarchy === undefined && internal_1.IsHierarchyIterable(source)) {
            super(source);
        }
        else {
            super(source, hierarchy);
        }
    }
    // #region Order
    /**
     * Creates a subsequent ordered subquery whose elements are sorted in ascending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparison An optional callback used to compare two keys.
     * @category Order
     */
    thenBy(keySelector, comparison) {
        return from(fn.thenBy(internal_1.GetSource(this), keySelector, comparison));
    }
    /**
     * Creates a subsequent ordered subquery whose elements are sorted in descending order by the provided key.
     *
     * @param keySelector A callback used to select the key for an element.
     * @param comparison An optional callback used to compare two keys.
     * @category Order
     */
    thenByDescending(keySelector, comparison) {
        return from(fn.thenByDescending(internal_1.GetSource(this), keySelector, comparison));
    }
    // #endregion Order
    [types_1.OrderedIterable.thenBy](keySelector, comparison, descending) {
        return internal_1.ThenBy(internal_1.GetSource(this), keySelector, comparison, descending);
    }
}
exports.OrderedHierarchyQuery = OrderedHierarchyQuery;
function wrapResultSelector(selector) {
    if (typeof selector === "function") {
        return (inner, outer) => selector(inner, from(outer));
    }
    return selector;
}
//# sourceMappingURL=query.js.map