"use strict";
// Utility functions for iterables that avoid array conversion
Object.defineProperty(exports, "__esModule", { value: true });
exports.asyncToArray = exports.sortedAsyncMerge = exports.reduce = exports.find = exports.concat = exports.filter = exports.map = void 0;
/**
 * Creates a new iterable with the results of calling a provided function on every element in the calling array.
 * Similar to the {@link Array.prototype.map} function.
 * See the documentation of the above function for more details.
 *
 * @param iterable - Iterable on which to call the map function.
 * @param callbackFn - Function that is called for every element.
 * @param thisArg - Value to use as `this` when executing `callbackFn`.
 */
function* map(iterable, callbackFn, thisArg) {
    const boundMapFn = callbackFn.bind(thisArg);
    let count = 0;
    for (const value of iterable) {
        yield boundMapFn(value, count);
        count += 1;
    }
}
exports.map = map;
/**
 * Creates a new iterable with all elements that pass the test implemented by the provided function.
 * Similar to the {@link Array.prototype.filter} function.
 * See the documentation of the above function for more details.
 *
 * @param iterable - Iterable on which to call the map function.
 * @param callbackFn - Function that is called to test every element.
 * @param thisArg - Value to use as `this` when executing `callbackFn`.
 */
function* filter(iterable, callbackFn, thisArg) {
    const boundFilterFn = callbackFn.bind(thisArg);
    let count = 0;
    for (const value of iterable) {
        if (boundFilterFn(value, count)) {
            yield value;
        }
        count += 1;
    }
}
exports.filter = filter;
/**
 * Creates a new iterable that is a concatenation of all the iterables in the input.
 * @param iterables - An iterable of which the contents will be concatenated into a new iterable.
 */
function* concat(iterables) {
    for (const iterable of iterables) {
        yield* iterable;
    }
}
exports.concat = concat;
/**
 * Returns the first element in the provided iterable that satisfies the provided testing function.
 * If no values satisfy the testing function, `undefined` is returned.
 * Similar to the {@link Array.prototype.find} function.
 * See the documentation of the above function for more details.
 *
 * @param iterable - Iterable on which to call the map function.
 * @param callbackFn - Function that is called to test every element.
 * @param thisArg - Value to use as `this` when executing `callbackFn`.
 */
function find(iterable, callbackFn, thisArg) {
    const boundMapFn = callbackFn.bind(thisArg);
    const count = 0;
    for (const value of iterable) {
        if (boundMapFn(value, count)) {
            return value;
        }
    }
}
exports.find = find;
function reduce(iterable, callbackFn, initialValue) {
    const iterator = iterable[Symbol.iterator]();
    let count = 0;
    if (!initialValue) {
        const next = iterator.next();
        if (next.done) {
            throw new TypeError('Iterable is empty and no initial value was provided.');
        }
        // `initialValue` being undefined means the first signature was used where TIn === TOut
        initialValue = next.value;
        count += 1;
    }
    let previousValue = initialValue;
    let next = iterator.next();
    while (!next.done) {
        previousValue = callbackFn(previousValue, next.value, count);
        next = iterator.next();
        count += 1;
    }
    return previousValue;
}
exports.reduce = reduce;
/**
 * Helper function for {@link sortedAsyncMerge}.
 *
 * Returns the next result of an AsyncIterator, or undefined if the iterator is finished.
 */
async function nextAsyncEntry(iterator) {
    const result = await iterator.next();
    if (result.done) {
        return;
    }
    return result.value;
}
/**
 * Helper function for {@link sortedAsyncMerge}.
 *
 * Compares the next results of all `iterators` and returns the first one,
 * determined by the provided `comparator`.
 *
 * `results` should contain the first result of all these iterators.
 * This array will also be updated, replacing the result of the iterator whose result was chosen by the next one.
 */
async function findNextSorted(iterators, results, comparator) {
    let best;
    // For every iterator: see if their next result is the best one so far
    for (let i = 0; i < iterators.length; ++i) {
        const value = results[i];
        if (typeof value !== 'undefined') {
            let compare = 1;
            if (best) {
                compare = comparator(best.value, value);
            }
            if (compare > 0) {
                best = { idx: i, value };
            }
        }
    }
    if (best) {
        // Advance the iterator that returned the new result
        results[best.idx] = await nextAsyncEntry(iterators[best.idx]);
    }
    // Will return undefined if `best` was never initialized above
    return best?.value;
}
/**
 * Merges the results of several sorted iterators.
 * In case the results of the individual iterators are not sorted the outcome results will also not be sorted.
 *
 * @param iterators - The iterators whose results need to be merged.
 * @param comparator - The comparator to use to compare the results.
 */
async function* sortedAsyncMerge(iterators, comparator) {
    if (!comparator) {
        comparator = (left, right) => left < right ? -1 : (left > right ? 1 : 0);
    }
    // Initialize the array to the first result of every iterator
    const results = [];
    for (const iterator of iterators) {
        results.push(await nextAsyncEntry(iterator));
    }
    // Keep returning results as long as we find them
    while (true) {
        const next = await findNextSorted(iterators, results, comparator);
        if (typeof next === 'undefined') {
            return;
        }
        yield next;
    }
}
exports.sortedAsyncMerge = sortedAsyncMerge;
/**
 * Converts an `AsyncIterator` to an array.
 */
async function asyncToArray(iterable) {
    const arr = [];
    for await (const result of iterable) {
        arr.push(result);
    }
    return arr;
}
exports.asyncToArray = asyncToArray;
//# sourceMappingURL=IterableUtil.js.map