"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
function tail(array, n = 0) {
  return array[array.length - (1 + n)];
}
function tail2(arr) {
  if (arr.length === 0) {
    throw new Error("Invalid tail call");
  }
  return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
}
function equals(one, other, itemEquals = (a, b) => a === b) {
  if (one === other) {
    return true;
  }
  if (!one || !other) {
    return false;
  }
  if (one.length !== other.length) {
    return false;
  }
  for (let i = 0, len = one.length; i < len; i++) {
    if (!itemEquals(one[i], other[i])) {
      return false;
    }
  }
  return true;
}
function binarySearch(array, key, comparator) {
  let low = 0, high = array.length - 1;
  while (low <= high) {
    const mid = (low + high) / 2 | 0;
    const comp = comparator(array[mid], key);
    if (comp < 0) {
      low = mid + 1;
    } else if (comp > 0) {
      high = mid - 1;
    } else {
      return mid;
    }
  }
  return -(low + 1);
}
function findFirstInSorted(array, p) {
  let low = 0, high = array.length;
  if (high === 0) {
    return 0;
  }
  while (low < high) {
    const mid = Math.floor((low + high) / 2);
    if (p(array[mid])) {
      high = mid;
    } else {
      low = mid + 1;
    }
  }
  return low;
}
function quickSelect(nth, data, compare) {
  nth = nth | 0;
  if (nth >= data.length) {
    throw new TypeError("invalid index");
  }
  let pivotValue = data[Math.floor(data.length * Math.random())];
  let lower = [];
  let higher = [];
  let pivots = [];
  for (let value of data) {
    const val = compare(value, pivotValue);
    if (val < 0) {
      lower.push(value);
    } else if (val > 0) {
      higher.push(value);
    } else {
      pivots.push(value);
    }
  }
  if (nth < lower.length) {
    return quickSelect(nth, lower, compare);
  } else if (nth < lower.length + pivots.length) {
    return pivots[0];
  } else {
    return quickSelect(nth - (lower.length + pivots.length), higher, compare);
  }
}
function groupBy(data, compare) {
  const result = [];
  let currentGroup = void 0;
  for (const element of data.slice(0).sort(compare)) {
    if (!currentGroup || compare(currentGroup[0], element) !== 0) {
      currentGroup = [element];
      result.push(currentGroup);
    } else {
      currentGroup.push(element);
    }
  }
  return result;
}
function coalesce(array) {
  return array.filter((e) => !!e);
}
function isFalsyOrEmpty(obj) {
  return !Array.isArray(obj) || obj.length === 0;
}
function isNonEmptyArray(obj) {
  return Array.isArray(obj) && obj.length > 0;
}
function distinct(array, keyFn = (value) => value) {
  const seen = /* @__PURE__ */ new Set();
  return array.filter((element) => {
    const key = keyFn(element);
    if (seen.has(key)) {
      return false;
    }
    seen.add(key);
    return true;
  });
}
function findLast(arr, predicate) {
  const idx = lastIndex(arr, predicate);
  if (idx === -1) {
    return void 0;
  }
  return arr[idx];
}
function lastIndex(array, fn) {
  for (let i = array.length - 1; i >= 0; i--) {
    const element = array[i];
    if (fn(element)) {
      return i;
    }
  }
  return -1;
}
function firstOrDefault(array, notFoundValue) {
  return array.length > 0 ? array[0] : notFoundValue;
}
function flatten(arr) {
  return [].concat(...arr);
}
function range(arg, to) {
  let from = typeof to === "number" ? arg : 0;
  if (typeof to === "number") {
    from = arg;
  } else {
    from = 0;
    to = arg;
  }
  const result = [];
  if (from <= to) {
    for (let i = from; i < to; i++) {
      result.push(i);
    }
  } else {
    for (let i = from; i > to; i--) {
      result.push(i);
    }
  }
  return result;
}
function arrayInsert(target, insertIndex, insertArr) {
  const before = target.slice(0, insertIndex);
  const after = target.slice(insertIndex);
  return before.concat(insertArr, after);
}
function pushToStart(arr, value) {
  const index = arr.indexOf(value);
  if (index > -1) {
    arr.splice(index, 1);
    arr.unshift(value);
  }
}
function pushToEnd(arr, value) {
  const index = arr.indexOf(value);
  if (index > -1) {
    arr.splice(index, 1);
    arr.push(value);
  }
}
function asArray(x) {
  return Array.isArray(x) ? x : [x];
}
function insertInto(array, start, newItems) {
  const startIdx = getActualStartIndex(array, start);
  const originalLength = array.length;
  const newItemsLength = newItems.length;
  array.length = originalLength + newItemsLength;
  for (let i = originalLength - 1; i >= startIdx; i--) {
    array[i + newItemsLength] = array[i];
  }
  for (let i = 0; i < newItemsLength; i++) {
    array[i + startIdx] = newItems[i];
  }
}
function splice(array, start, deleteCount, newItems) {
  const index = getActualStartIndex(array, start);
  const result = array.splice(index, deleteCount);
  insertInto(array, index, newItems);
  return result;
}
function getActualStartIndex(array, start) {
  return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length);
}
function compareBy(selector, comparator) {
  return (a, b) => comparator(selector(a), selector(b));
}
const numberComparator = (a, b) => a - b;
function findMaxBy(items, comparator) {
  if (items.length === 0) {
    return void 0;
  }
  let max = items[0];
  for (let i = 1; i < items.length; i++) {
    const item = items[i];
    if (comparator(item, max) > 0) {
      max = item;
    }
  }
  return max;
}
function findLastMaxBy(items, comparator) {
  if (items.length === 0) {
    return void 0;
  }
  let max = items[0];
  for (let i = 1; i < items.length; i++) {
    const item = items[i];
    if (comparator(item, max) >= 0) {
      max = item;
    }
  }
  return max;
}
function findMinBy(items, comparator) {
  return findMaxBy(items, (a, b) => -comparator(a, b));
}
class ArrayQueue {
  /**
   * Constructs a queue that is backed by the given array. Runtime is O(1).
  */
  constructor(items) {
    this.items = items;
    this.firstIdx = 0;
    this.lastIdx = this.items.length - 1;
  }
  /**
   * Consumes elements from the beginning of the queue as long as the predicate returns true.
   * If no elements were consumed, `null` is returned. Has a runtime of O(result.length).
  */
  takeWhile(predicate) {
    let startIdx = this.firstIdx;
    while (startIdx < this.items.length && predicate(this.items[startIdx])) {
      startIdx++;
    }
    const result = startIdx === this.firstIdx ? null : this.items.slice(this.firstIdx, startIdx);
    this.firstIdx = startIdx;
    return result;
  }
  /**
   * Consumes elements from the end of the queue as long as the predicate returns true.
   * If no elements were consumed, `null` is returned.
   * The result has the same order as the underlying array!
  */
  takeFromEndWhile(predicate) {
    let endIdx = this.lastIdx;
    while (endIdx >= 0 && predicate(this.items[endIdx])) {
      endIdx--;
    }
    const result = endIdx === this.lastIdx ? null : this.items.slice(endIdx + 1, this.lastIdx + 1);
    this.lastIdx = endIdx;
    return result;
  }
  peek() {
    return this.items[this.firstIdx];
  }
  dequeue() {
    const result = this.items[this.firstIdx];
    this.firstIdx++;
    return result;
  }
  takeCount(count) {
    const result = this.items.slice(this.firstIdx, this.firstIdx + count);
    this.firstIdx += count;
    return result;
  }
}
exports.ArrayQueue = ArrayQueue;
exports.arrayInsert = arrayInsert;
exports.asArray = asArray;
exports.binarySearch = binarySearch;
exports.coalesce = coalesce;
exports.compareBy = compareBy;
exports.distinct = distinct;
exports.equals = equals;
exports.findFirstInSorted = findFirstInSorted;
exports.findLast = findLast;
exports.findLastMaxBy = findLastMaxBy;
exports.findMaxBy = findMaxBy;
exports.findMinBy = findMinBy;
exports.firstOrDefault = firstOrDefault;
exports.flatten = flatten;
exports.groupBy = groupBy;
exports.insertInto = insertInto;
exports.isFalsyOrEmpty = isFalsyOrEmpty;
exports.isNonEmptyArray = isNonEmptyArray;
exports.lastIndex = lastIndex;
exports.numberComparator = numberComparator;
exports.pushToEnd = pushToEnd;
exports.pushToStart = pushToStart;
exports.quickSelect = quickSelect;
exports.range = range;
exports.splice = splice;
exports.tail = tail;
exports.tail2 = tail2;
