"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.scanRight = exports.scan = exports.rotate = exports.reverse = exports.replicate = exports.replaceOption = exports.replace = exports.remove = exports.reduceRight = exports.reduce = exports.range = exports.prependAll = exports.prepend = exports.partitionMap = exports.partition = exports.of = exports.modifyOption = exports.modifyNonEmptyLast = exports.modifyNonEmptyHead = exports.modify = exports.min = exports.max = exports.matchRight = exports.matchLeft = exports.match = exports.mapAccum = exports.map = exports.makeBy = exports.make = exports.liftPredicate = exports.liftOption = exports.liftNullable = exports.liftEither = exports.let = exports.length = exports.lastNonEmpty = exports.last = exports.join = exports.isNonEmptyReadonlyArray = exports.isNonEmptyArray = exports.isEmptyReadonlyArray = exports.isEmptyArray = exports.isArray = exports.intersperse = exports.intersectionWith = exports.intersection = exports.insertAt = exports.initNonEmpty = exports.init = exports.headNonEmpty = exports.head = exports.groupWith = exports.groupBy = exports.group = exports.getSomes = exports.getRights = exports.getOrder = exports.getLefts = exports.getEquivalence = exports.get = exports.fromRecord = exports.fromOption = exports.fromNullable = exports.fromIterable = exports.forEach = exports.flatten = exports.flatMapNullable = exports.flatMap = exports.findLastIndex = exports.findLast = exports.findFirstIndex = exports.findFirst = exports.filterMapWhile = exports.filterMap = exports.filter = exports.extend = exports.every = exports.empty = exports.dropWhile = exports.dropRight = exports.drop = exports.differenceWith = exports.difference = exports.dedupeWith = exports.dedupeAdjacentWith = exports.dedupeAdjacent = exports.dedupe = exports.copy = exports.containsWith = exports.contains = exports.chunksOf = exports.chop = exports.cartesianWith = exports.cartesian = exports.bindTo = exports.bind = exports.appendAll = exports.append = exports.allocate = exports.Do = void 0;
exports.zipWith = exports.zip = exports.unzip = exports.unsafeGet = exports.unprepend = exports.unionWith = exports.union = exports.unfold = exports.unappend = exports.takeWhile = exports.takeRight = exports.take = exports.tailNonEmpty = exports.tail = exports.splitWhere = exports.splitNonEmptyAt = exports.splitAt = exports.split = exports.span = exports.sortWith = exports.sortBy = exports.sort = exports.some = exports.setNonEmptyLast = exports.setNonEmptyHead = exports.separate = void 0;
var E = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Either.js"));
var Equal = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Equal.js"));
var Equivalence = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Equivalence.js"));
var _Function = /*#__PURE__*/require("./Function.js");
var readonlyArray = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./internal/array.js"));
var doNotation = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./internal/doNotation.js"));
var EffectIterable = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Iterable.js"));
var O = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Option.js"));
var Order = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Order.js"));
var _Predicate = /*#__PURE__*/require("./Predicate.js");
var Record = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Record.js"));
var Tuple = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./Tuple.js"));
function _getRequireWildcardCache(e) {
  if ("function" != typeof WeakMap) return null;
  var r = new WeakMap(),
    t = new WeakMap();
  return (_getRequireWildcardCache = function (e) {
    return e ? t : r;
  })(e);
}
function _interopRequireWildcard(e, r) {
  if (!r && e && e.__esModule) return e;
  if (null === e || "object" != typeof e && "function" != typeof e) return {
    default: e
  };
  var t = _getRequireWildcardCache(r);
  if (t && t.has(e)) return t.get(e);
  var n = {
      __proto__: null
    },
    a = Object.defineProperty && Object.getOwnPropertyDescriptor;
  for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
    var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;
    i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];
  }
  return n.default = e, t && t.set(e, n), n;
}
/**
 * This module provides utility functions for working with arrays in TypeScript.
 *
 * @since 2.0.0
 */

/**
 * Builds a `NonEmptyArray` from an non-empty collection of elements.
 *
 * @category constructors
 * @since 2.0.0
 */
const make = (...elements) => elements;
/**
 * Creates a new `Array` of the specified length.
 *
 * @category constructors
 * @since 2.0.0
 */
exports.make = make;
const allocate = n => new Array(n);
/**
 * Return a `NonEmptyArray` of length `n` with element `i` initialized with `f(i)`.
 *
 * **Note**. `n` is normalized to an integer >= 1.
 *
 * @example
 * import { makeBy } from "effect/Array"
 *
 * assert.deepStrictEqual(makeBy(5, n => n * 2), [0, 2, 4, 6, 8])
 *
 * @category constructors
 * @since 2.0.0
 */
exports.allocate = allocate;
const makeBy = (n, f) => {
  const max = Math.max(1, Math.floor(n));
  const out = new Array(max);
  for (let i = 0; i < max; i++) {
    out[i] = f(i);
  }
  return out;
};
/**
 * Return a `NonEmptyArray` containing a range of integers, including both endpoints.
 *
 * @example
 * import { range } from "effect/Array"
 *
 * assert.deepStrictEqual(range(1, 3), [1, 2, 3])
 *
 * @category constructors
 * @since 2.0.0
 */
exports.makeBy = makeBy;
const range = (start, end) => start <= end ? makeBy(end - start + 1, i => start + i) : [start];
/**
 * Return a `NonEmptyArray` containing a value repeated the specified number of times.
 *
 * **Note**. `n` is normalized to an integer >= 1.
 *
 * @example
 * import { replicate } from "effect/Array"
 *
 * assert.deepStrictEqual(replicate("a", 3), ["a", "a", "a"])
 *
 * @category constructors
 * @since 2.0.0
 */
exports.range = range;
const replicate = exports.replicate = /*#__PURE__*/(0, _Function.dual)(2, (a, n) => makeBy(n, () => a));
/**
 * Creates a new `Array` from an iterable collection of values.
 *
 * @category constructors
 * @since 2.0.0
 */
const fromIterable = collection => Array.isArray(collection) ? collection : Array.from(collection);
/**
 * Takes a record and returns an array of tuples containing its keys and values.
 *
 * @param self - The record to transform.
 *
 * @example
 * import { fromRecord } from "effect/Array"
 *
 * const x = { a: 1, b: 2, c: 3 }
 * assert.deepStrictEqual(fromRecord(x), [["a", 1], ["b", 2], ["c", 3]])
 *
 * @category conversions
 * @since 2.0.0
 */
exports.fromIterable = fromIterable;
const fromRecord = exports.fromRecord = Record.toEntries;
/**
 * @category conversions
 * @since 2.0.0
 */
const fromOption = exports.fromOption = O.toArray;
/**
 * @category pattern matching
 * @since 2.0.0
 */
const match = exports.match = /*#__PURE__*/(0, _Function.dual)(2, (self, {
  onEmpty,
  onNonEmpty
}) => isNonEmptyReadonlyArray(self) ? onNonEmpty(self) : onEmpty());
/**
 * @category pattern matching
 * @since 2.0.0
 */
const matchLeft = exports.matchLeft = /*#__PURE__*/(0, _Function.dual)(2, (self, {
  onEmpty,
  onNonEmpty
}) => isNonEmptyReadonlyArray(self) ? onNonEmpty(headNonEmpty(self), tailNonEmpty(self)) : onEmpty());
/**
 * @category pattern matching
 * @since 2.0.0
 */
const matchRight = exports.matchRight = /*#__PURE__*/(0, _Function.dual)(2, (self, {
  onEmpty,
  onNonEmpty
}) => isNonEmptyReadonlyArray(self) ? onNonEmpty(initNonEmpty(self), lastNonEmpty(self)) : onEmpty());
/**
 * Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`.
 *
 * @category concatenating
 * @since 2.0.0
 */
const prepend = exports.prepend = /*#__PURE__*/(0, _Function.dual)(2, (self, head) => [head, ...self]);
/**
 * Prepends the specified prefix array (or iterable) to the beginning of the specified array (or iterable).
 * If either array is non-empty, the result is also a non-empty array.
 *
 * @example
 * import { Array } from "effect"
 *
 * assert.deepStrictEqual(
 *   Array.prependAll([1, 2], ["a", "b"]),
 *   ["a", "b", 1, 2]
 * )
 *
 * @category concatenating
 * @since 2.0.0
 */
const prependAll = exports.prependAll = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => fromIterable(that).concat(fromIterable(self)));
/**
 * Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`.
 *
 * @category concatenating
 * @since 2.0.0
 */
const append = exports.append = /*#__PURE__*/(0, _Function.dual)(2, (self, last) => [...self, last]);
/**
 * Concatenates two arrays (or iterables), combining their elements.
 * If either array is non-empty, the result is also a non-empty array.
 *
 * @category concatenating
 * @since 2.0.0
 */
const appendAll = exports.appendAll = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => fromIterable(self).concat(fromIterable(that)));
/**
 * Reduce an `Iterable` from the left, keeping all intermediate results instead of only the final result.
 *
 * @category folding
 * @since 2.0.0
 */
const scan = exports.scan = /*#__PURE__*/(0, _Function.dual)(3, (self, b, f) => {
  const out = [b];
  let i = 0;
  for (const a of self) {
    out[i + 1] = f(out[i], a);
    i++;
  }
  return out;
});
/**
 * Reduce an `Iterable` from the right, keeping all intermediate results instead of only the final result.
 *
 * @category folding
 * @since 2.0.0
 */
const scanRight = exports.scanRight = /*#__PURE__*/(0, _Function.dual)(3, (self, b, f) => {
  const input = fromIterable(self);
  const out = new Array(input.length + 1);
  out[input.length] = b;
  for (let i = input.length - 1; i >= 0; i--) {
    out[i] = f(out[i + 1], input[i]);
  }
  return out;
});
/**
 * Determine if `unknown` is an Array.
 *
 * @param self - The value to check.
 *
 * @example
 * import { isArray } from "effect/Array"
 *
 * assert.deepStrictEqual(isArray(null), false);
 * assert.deepStrictEqual(isArray([1, 2, 3]), true);
 *
 * @category guards
 * @since 2.0.0
 */
const isArray = exports.isArray = Array.isArray;
/**
 * Determine if an `Array` is empty narrowing down the type to `[]`.
 *
 * @param self - The `Array` to check.
 *
 * @example
 * import { isEmptyArray } from "effect/Array"
 *
 * assert.deepStrictEqual(isEmptyArray([]), true);
 * assert.deepStrictEqual(isEmptyArray([1, 2, 3]), false);
 *
 * @category guards
 * @since 2.0.0
 */
const isEmptyArray = self => self.length === 0;
/**
 * Determine if a `ReadonlyArray` is empty narrowing down the type to `readonly []`.
 *
 * @param self - The `ReadonlyArray` to check.
 *
 * @example
 * import { isEmptyReadonlyArray } from "effect/Array"
 *
 * assert.deepStrictEqual(isEmptyReadonlyArray([]), true);
 * assert.deepStrictEqual(isEmptyReadonlyArray([1, 2, 3]), false);
 *
 * @category guards
 * @since 2.0.0
 */
exports.isEmptyArray = isEmptyArray;
const isEmptyReadonlyArray = exports.isEmptyReadonlyArray = isEmptyArray;
/**
 * Determine if an `Array` is non empty narrowing down the type to `NonEmptyArray`.
 *
 * An `Array` is considered to be a `NonEmptyArray` if it contains at least one element.
 *
 * @param self - The `Array` to check.
 *
 * @example
 * import { isNonEmptyArray } from "effect/Array"
 *
 * assert.deepStrictEqual(isNonEmptyArray([]), false);
 * assert.deepStrictEqual(isNonEmptyArray([1, 2, 3]), true);
 *
 * @category guards
 * @since 2.0.0
 */
const isNonEmptyArray = exports.isNonEmptyArray = readonlyArray.isNonEmptyArray;
/**
 * Determine if a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`.
 *
 * A `ReadonlyArray` is considered to be a `NonEmptyReadonlyArray` if it contains at least one element.
 *
 * @param self - The `ReadonlyArray` to check.
 *
 * @example
 * import { isNonEmptyReadonlyArray } from "effect/Array"
 *
 * assert.deepStrictEqual(isNonEmptyReadonlyArray([]), false);
 * assert.deepStrictEqual(isNonEmptyReadonlyArray([1, 2, 3]), true);
 *
 * @category guards
 * @since 2.0.0
 */
const isNonEmptyReadonlyArray = exports.isNonEmptyReadonlyArray = readonlyArray.isNonEmptyArray;
/**
 * Return the number of elements in a `ReadonlyArray`.
 *
 * @category getters
 * @since 2.0.0
 */
const length = self => self.length;
exports.length = length;
const isOutOfBound = (i, as) => i < 0 || i >= as.length;
const clamp = (i, as) => Math.floor(Math.min(Math.max(0, i), as.length));
/**
 * This function provides a safe way to read a value at a particular index from a `ReadonlyArray`.
 *
 * @category getters
 * @since 2.0.0
 */
const get = exports.get = /*#__PURE__*/(0, _Function.dual)(2, (self, index) => {
  const i = Math.floor(index);
  return isOutOfBound(i, self) ? O.none() : O.some(self[i]);
});
/**
 * Gets an element unsafely, will throw on out of bounds.
 *
 * @since 2.0.0
 * @category unsafe
 */
const unsafeGet = exports.unsafeGet = /*#__PURE__*/(0, _Function.dual)(2, (self, index) => {
  const i = Math.floor(index);
  if (isOutOfBound(i, self)) {
    throw new Error(`Index ${i} out of bounds`);
  }
  return self[i];
});
/**
 * Return a tuple containing the first element, and a new `Array` of the remaining elements, if any.
 *
 * @category splitting
 * @since 2.0.0
 */
const unprepend = self => [headNonEmpty(self), tailNonEmpty(self)];
/**
 * Return a tuple containing a copy of the `NonEmptyReadonlyArray` without its last element, and that last element.
 *
 * @category splitting
 * @since 2.0.0
 */
exports.unprepend = unprepend;
const unappend = self => [initNonEmpty(self), lastNonEmpty(self)];
/**
 * Get the first element of a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty.
 *
 * @category getters
 * @since 2.0.0
 */
exports.unappend = unappend;
const head = exports.head = /*#__PURE__*/get(0);
/**
 * @category getters
 * @since 2.0.0
 */
const headNonEmpty = exports.headNonEmpty = /*#__PURE__*/unsafeGet(0);
/**
 * Get the last element in a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty.
 *
 * @category getters
 * @since 2.0.0
 */
const last = self => isNonEmptyReadonlyArray(self) ? O.some(lastNonEmpty(self)) : O.none();
/**
 * @category getters
 * @since 2.0.0
 */
exports.last = last;
const lastNonEmpty = self => self[self.length - 1];
/**
 * Get all but the first element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty.
 *
 * @category getters
 * @since 2.0.0
 */
exports.lastNonEmpty = lastNonEmpty;
const tail = self => {
  const input = fromIterable(self);
  return isNonEmptyReadonlyArray(input) ? O.some(tailNonEmpty(input)) : O.none();
};
/**
 * @category getters
 * @since 2.0.0
 */
exports.tail = tail;
const tailNonEmpty = self => self.slice(1);
/**
 * Get all but the last element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty.
 *
 * @category getters
 * @since 2.0.0
 */
exports.tailNonEmpty = tailNonEmpty;
const init = self => {
  const input = fromIterable(self);
  return isNonEmptyReadonlyArray(input) ? O.some(initNonEmpty(input)) : O.none();
};
/**
 * Get all but the last element of a non empty array, creating a new array.
 *
 * @category getters
 * @since 2.0.0
 */
exports.init = init;
const initNonEmpty = self => self.slice(0, -1);
/**
 * Keep only a max number of elements from the start of an `Iterable`, creating a new `Array`.
 *
 * **Note**. `n` is normalized to a non negative integer.
 *
 * @category getters
 * @since 2.0.0
 */
exports.initNonEmpty = initNonEmpty;
const take = exports.take = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  return input.slice(0, clamp(n, input));
});
/**
 * Keep only a max number of elements from the end of an `Iterable`, creating a new `Array`.
 *
 * **Note**. `n` is normalized to a non negative integer.
 *
 * @category getters
 * @since 2.0.0
 */
const takeRight = exports.takeRight = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  const i = clamp(n, input);
  return i === 0 ? [] : input.slice(-i);
});
/**
 * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`.
 *
 * @category getters
 * @since 2.0.0
 */
const takeWhile = exports.takeWhile = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => {
  let i = 0;
  const out = [];
  for (const a of self) {
    if (!predicate(a, i)) {
      break;
    }
    out.push(a);
    i++;
  }
  return out;
});
const spanIndex = (self, predicate) => {
  let i = 0;
  for (const a of self) {
    if (!predicate(a, i)) {
      break;
    }
    i++;
  }
  return i;
};
/**
 * Split an `Iterable` into two parts:
 *
 * 1. the longest initial subarray for which all elements satisfy the specified predicate
 * 2. the remaining elements
 *
 * @category splitting
 * @since 2.0.0
 */
const span = exports.span = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => splitAt(self, spanIndex(self, predicate)));
/**
 * Drop a max number of elements from the start of an `Iterable`, creating a new `Array`.
 *
 * **Note**. `n` is normalized to a non negative integer.
 *
 * @category getters
 * @since 2.0.0
 */
const drop = exports.drop = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  return input.slice(clamp(n, input), input.length);
});
/**
 * Drop a max number of elements from the end of an `Iterable`, creating a new `Array`.
 *
 * **Note**. `n` is normalized to a non negative integer.
 *
 * @category getters
 * @since 2.0.0
 */
const dropRight = exports.dropRight = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  return input.slice(0, input.length - clamp(n, input));
});
/**
 * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`.
 *
 * @category getters
 * @since 2.0.0
 */
const dropWhile = exports.dropWhile = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => fromIterable(self).slice(spanIndex(self, predicate)));
/**
 * Return the first index for which a predicate holds.
 *
 * @category elements
 * @since 2.0.0
 */
const findFirstIndex = exports.findFirstIndex = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => {
  let i = 0;
  for (const a of self) {
    if (predicate(a, i)) {
      return O.some(i);
    }
    i++;
  }
  return O.none();
});
/**
 * Return the last index for which a predicate holds.
 *
 * @category elements
 * @since 2.0.0
 */
const findLastIndex = exports.findLastIndex = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => {
  const input = fromIterable(self);
  for (let i = input.length - 1; i >= 0; i--) {
    if (predicate(input[i], i)) {
      return O.some(i);
    }
  }
  return O.none();
});
/**
 * Returns the first element that satisfies the specified
 * predicate, or `None` if no such element exists.
 *
 * @category elements
 * @since 2.0.0
 */
const findFirst = exports.findFirst = EffectIterable.findFirst;
/**
 * Find the last element for which a predicate holds.
 *
 * @category elements
 * @since 2.0.0
 */
const findLast = exports.findLast = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  const input = fromIterable(self);
  for (let i = input.length - 1; i >= 0; i--) {
    const a = input[i];
    const o = f(a, i);
    if ((0, _Predicate.isBoolean)(o)) {
      if (o) {
        return O.some(a);
      }
    } else {
      if (O.isSome(o)) {
        return o;
      }
    }
  }
  return O.none();
});
/**
 * Insert an element at the specified index, creating a new `NonEmptyArray`,
 * or return `None` if the index is out of bounds.
 *
 * @since 2.0.0
 */
const insertAt = exports.insertAt = /*#__PURE__*/(0, _Function.dual)(3, (self, i, b) => {
  const out = Array.from(self);
  //             v--- `= self.length` is ok, it means inserting in last position
  if (i < 0 || i > out.length) {
    return O.none();
  }
  out.splice(i, 0, b);
  return O.some(out);
});
/**
 * Change the element at the specified index, creating a new `Array`,
 * or return a copy of the input if the index is out of bounds.
 *
 * @since 2.0.0
 */
const replace = exports.replace = /*#__PURE__*/(0, _Function.dual)(3, (self, i, b) => modify(self, i, () => b));
/**
 * @since 2.0.0
 */
const replaceOption = exports.replaceOption = /*#__PURE__*/(0, _Function.dual)(3, (self, i, b) => modifyOption(self, i, () => b));
/**
 * Apply a function to the element at the specified index, creating a new `Array`,
 * or return a copy of the input if the index is out of bounds.
 *
 * @since 2.0.0
 */
const modify = exports.modify = /*#__PURE__*/(0, _Function.dual)(3, (self, i, f) => O.getOrElse(modifyOption(self, i, f), () => Array.from(self)));
/**
 * Apply a function to the element at the specified index, creating a new `Array`,
 * or return `None` if the index is out of bounds.
 *
 * @since 2.0.0
 */
const modifyOption = exports.modifyOption = /*#__PURE__*/(0, _Function.dual)(3, (self, i, f) => {
  const out = Array.from(self);
  if (isOutOfBound(i, out)) {
    return O.none();
  }
  const next = f(out[i]);
  // @ts-expect-error
  out[i] = next;
  return O.some(out);
});
/**
 * Delete the element at the specified index, creating a new `Array`,
 * or return a copy of the input if the index is out of bounds.
 *
 * @since 2.0.0
 */
const remove = exports.remove = /*#__PURE__*/(0, _Function.dual)(2, (self, i) => {
  const out = Array.from(self);
  if (isOutOfBound(i, out)) {
    return out;
  }
  out.splice(i, 1);
  return out;
});
/**
 * Reverse an `Iterable`, creating a new `Array`.
 *
 * @category elements
 * @since 2.0.0
 */
const reverse = self => Array.from(self).reverse();
/**
 * Create a new array with elements sorted in increasing order based on the specified comparator.
 * If the input is a `NonEmptyReadonlyArray`, the output will also be a `NonEmptyReadonlyArray`.
 *
 * @category sorting
 * @since 2.0.0
 */
exports.reverse = reverse;
const sort = exports.sort = /*#__PURE__*/(0, _Function.dual)(2, (self, O) => {
  const out = Array.from(self);
  out.sort(O);
  return out;
});
/**
 * @since 2.0.0
 * @category elements
 */
const sortWith = exports.sortWith = /*#__PURE__*/(0, _Function.dual)(3, (self, f, order) => sort(self, Order.mapInput(order, f)));
/**
 * Sort the elements of an `Iterable` in increasing order, where elements are compared
 * using first `orders[0]`, then `orders[1]`, etc...
 *
 * @category sorting
 * @since 2.0.0
 */
const sortBy = (...orders) => {
  const sortByAll = sort(Order.combineAll(orders));
  return self => {
    const input = fromIterable(self);
    if (isNonEmptyReadonlyArray(input)) {
      return sortByAll(input);
    }
    return [];
  };
};
/**
 * Takes two `Iterable`s and returns an `Array` of corresponding pairs.
 * If one input `Iterable` is short, excess elements of the
 * longer `Iterable` are discarded.
 *
 * @category zipping
 * @since 2.0.0
 */
exports.sortBy = sortBy;
const zip = exports.zip = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => zipWith(self, that, Tuple.make));
/**
 * Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one
 * input `Iterable` is short, excess elements of the longer `Iterable` are discarded.
 *
 * @category zipping
 * @since 2.0.0
 */
const zipWith = exports.zipWith = /*#__PURE__*/(0, _Function.dual)(3, (self, that, f) => {
  const as = fromIterable(self);
  const bs = fromIterable(that);
  if (isNonEmptyReadonlyArray(as) && isNonEmptyReadonlyArray(bs)) {
    const out = [f(headNonEmpty(as), headNonEmpty(bs))];
    const len = Math.min(as.length, bs.length);
    for (let i = 1; i < len; i++) {
      out[i] = f(as[i], bs[i]);
    }
    return out;
  }
  return [];
});
/**
 * This function is the inverse of `zip`. Takes an `Iterable` of pairs and return two corresponding `Array`s.
 *
 * @since 2.0.0
 */
const unzip = self => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    const fa = [input[0][0]];
    const fb = [input[0][1]];
    for (let i = 1; i < input.length; i++) {
      fa[i] = input[i][0];
      fb[i] = input[i][1];
    }
    return [fa, fb];
  }
  return [[], []];
};
/**
 * Places an element in between members of an `Iterable`.
 * If the input is a non-empty array, the result is also a non-empty array.
 *
 * @since 2.0.0
 */
exports.unzip = unzip;
const intersperse = exports.intersperse = /*#__PURE__*/(0, _Function.dual)(2, (self, middle) => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    const out = [headNonEmpty(input)];
    const tail = tailNonEmpty(input);
    for (let i = 0; i < tail.length; i++) {
      if (i < tail.length) {
        out.push(middle);
      }
      out.push(tail[i]);
    }
    return out;
  }
  return [];
});
/**
 * Apply a function to the head, creating a new `NonEmptyReadonlyArray`.
 *
 * @since 2.0.0
 */
const modifyNonEmptyHead = exports.modifyNonEmptyHead = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => [f(headNonEmpty(self)), ...tailNonEmpty(self)]);
/**
 * Change the head, creating a new `NonEmptyReadonlyArray`.
 *
 * @since 2.0.0
 */
const setNonEmptyHead = exports.setNonEmptyHead = /*#__PURE__*/(0, _Function.dual)(2, (self, b) => modifyNonEmptyHead(self, () => b));
/**
 * Apply a function to the last element, creating a new `NonEmptyReadonlyArray`.
 *
 * @since 2.0.0
 */
const modifyNonEmptyLast = exports.modifyNonEmptyLast = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => append(initNonEmpty(self), f(lastNonEmpty(self))));
/**
 * Change the last element, creating a new `NonEmptyReadonlyArray`.
 *
 * @since 2.0.0
 */
const setNonEmptyLast = exports.setNonEmptyLast = /*#__PURE__*/(0, _Function.dual)(2, (self, b) => modifyNonEmptyLast(self, () => b));
/**
 * Rotate an `Iterable` by `n` steps.
 * If the input is a non-empty array, the result is also a non-empty array.
 *
 * @since 2.0.0
 */
const rotate = exports.rotate = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    const len = input.length;
    const m = Math.round(n) % len;
    if (isOutOfBound(Math.abs(m), input) || m === 0) {
      return copy(input);
    }
    if (m < 0) {
      const [f, s] = splitNonEmptyAt(input, -m);
      return appendAll(s, f);
    } else {
      return rotate(self, m - len);
    }
  }
  return [];
});
/**
 * Returns a function that checks if a `ReadonlyArray` contains a given value using a provided `isEquivalent` function.
 *
 * @category elements
 * @since 2.0.0
 */
const containsWith = isEquivalent => (0, _Function.dual)(2, (self, a) => {
  for (const i of self) {
    if (isEquivalent(a, i)) {
      return true;
    }
  }
  return false;
});
exports.containsWith = containsWith;
const _equivalence = /*#__PURE__*/Equal.equivalence();
/**
 * Returns a function that checks if a `ReadonlyArray` contains a given value using the default `Equivalence`.
 *
 * @category elements
 * @since 2.0.0
 */
const contains = exports.contains = /*#__PURE__*/containsWith(_equivalence);
/**
 * A useful recursion pattern for processing an `Iterable` to produce a new `Array`, often used for "chopping" up the input
 * `Iterable`. Typically chop is called with some function that will consume an initial prefix of the `Iterable` and produce a
 * value and the rest of the `Array`.
 *
 * @since 2.0.0
 */
const chop = exports.chop = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    const [b, rest] = f(input);
    const out = [b];
    let next = rest;
    while (readonlyArray.isNonEmptyArray(next)) {
      const [b, rest] = f(next);
      out.push(b);
      next = rest;
    }
    return out;
  }
  return [];
});
/**
 * Splits an `Iterable` into two segments, with the first segment containing a maximum of `n` elements.
 * The value of `n` can be `0`.
 *
 * @category splitting
 * @since 2.0.0
 */
const splitAt = exports.splitAt = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = Array.from(self);
  const _n = Math.floor(n);
  if (isNonEmptyReadonlyArray(input)) {
    if (_n >= 1) {
      return splitNonEmptyAt(input, _n);
    }
    return [[], input];
  }
  return [input, []];
});
/**
 * Splits a `NonEmptyReadonlyArray` into two segments, with the first segment containing a maximum of `n` elements.
 * The value of `n` must be `>= 1`.
 *
 * @category splitting
 * @since 2.0.0
 */
const splitNonEmptyAt = exports.splitNonEmptyAt = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const _n = Math.max(1, Math.floor(n));
  return _n >= self.length ? [copy(self), []] : [prepend(self.slice(1, _n), headNonEmpty(self)), self.slice(_n)];
});
/**
 * Splits this iterable into `n` equally sized arrays.
 *
 * @since 2.0.0
 * @category splitting
 */
const split = exports.split = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  return chunksOf(input, Math.ceil(input.length / Math.floor(n)));
});
/**
 * Splits this iterable on the first element that matches this predicate.
 * Returns a tuple containing two arrays: the first one is before the match, and the second one is from the match onward.
 *
 * @category splitting
 * @since 2.0.0
 */
const splitWhere = exports.splitWhere = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => span(self, (a, i) => !predicate(a, i)));
/**
 * @since 2.0.0
 */
const copy = self => self.slice();
/**
 * Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of
 * the `Iterable`. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive
 * definition of `chunksOf`; it satisfies the property that
 *
 * ```ts
 * chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys)))
 * ```
 *
 * whenever `n` evenly divides the length of `self`.
 *
 * @category splitting
 * @since 2.0.0
 */
exports.copy = copy;
const chunksOf = exports.chunksOf = /*#__PURE__*/(0, _Function.dual)(2, (self, n) => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    return chop(input, splitNonEmptyAt(n));
  }
  return [];
});
/**
 * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s using the provided `isEquivalent` function.
 *
 * @category grouping
 * @since 2.0.0
 */
const groupWith = exports.groupWith = /*#__PURE__*/(0, _Function.dual)(2, (self, isEquivalent) => chop(self, as => {
  const h = headNonEmpty(as);
  const out = [h];
  let i = 1;
  for (; i < as.length; i++) {
    const a = as[i];
    if (isEquivalent(a, h)) {
      out.push(a);
    } else {
      break;
    }
  }
  return [out, as.slice(i)];
}));
/**
 * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s.
 *
 * @category grouping
 * @since 2.0.0
 */
const group = exports.group = /*#__PURE__*/groupWith( /*#__PURE__*/Equal.equivalence());
/**
 * Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning
 * function on each element, and grouping the results according to values returned
 *
 * @category grouping
 * @since 2.0.0
 */
const groupBy = exports.groupBy = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  const out = {};
  for (const a of self) {
    const k = f(a);
    if (Object.prototype.hasOwnProperty.call(out, k)) {
      out[k].push(a);
    } else {
      out[k] = [a];
    }
  }
  return out;
});
/**
 * @since 2.0.0
 */
const unionWith = exports.unionWith = /*#__PURE__*/(0, _Function.dual)(3, (self, that, isEquivalent) => {
  const a = fromIterable(self);
  const b = fromIterable(that);
  if (isNonEmptyReadonlyArray(a)) {
    if (isNonEmptyReadonlyArray(b)) {
      const dedupe = dedupeWith(isEquivalent);
      return dedupe(appendAll(a, b));
    }
    return a;
  }
  return b;
});
/**
 * @since 2.0.0
 */
const union = exports.union = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => unionWith(self, that, _equivalence));
/**
 * Creates an `Array` of unique values that are included in all given `Iterable`s using the provided `isEquivalent` function.
 * The order and references of result values are determined by the first `Iterable`.
 *
 * @since 2.0.0
 */
const intersectionWith = isEquivalent => {
  const has = containsWith(isEquivalent);
  return (0, _Function.dual)(2, (self, that) => fromIterable(self).filter(a => has(that, a)));
};
/**
 * Creates an `Array` of unique values that are included in all given `Iterable`s.
 * The order and references of result values are determined by the first `Iterable`.
 *
 * @since 2.0.0
 */
exports.intersectionWith = intersectionWith;
const intersection = exports.intersection = /*#__PURE__*/intersectionWith(_equivalence);
/**
 * Creates a `Array` of values not included in the other given `Iterable` using the provided `isEquivalent` function.
 * The order and references of result values are determined by the first `Iterable`.
 *
 * @since 2.0.0
 */
const differenceWith = isEquivalent => {
  const has = containsWith(isEquivalent);
  return (0, _Function.dual)(2, (self, that) => fromIterable(self).filter(a => !has(that, a)));
};
/**
 * Creates a `Array` of values not included in the other given `Iterable`.
 * The order and references of result values are determined by the first `Iterable`.
 *
 * @since 2.0.0
 */
exports.differenceWith = differenceWith;
const difference = exports.difference = /*#__PURE__*/differenceWith(_equivalence);
/**
 * @category constructors
 * @since 2.0.0
 */
const empty = () => [];
/**
 * Constructs a new `NonEmptyArray<A>` from the specified value.
 *
 * @category constructors
 * @since 2.0.0
 */
exports.empty = empty;
const of = a => [a];
/**
 * @category mapping
 * @since 2.0.0
 */
exports.of = of;
const map = exports.map = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => self.map(f));
/**
 * Applies a function to each element in an array and returns a new array containing the concatenated mapped elements.
 *
 * @category sequencing
 * @since 2.0.0
 */
const flatMap = exports.flatMap = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  if (isEmptyReadonlyArray(self)) {
    return [];
  }
  const out = [];
  for (let i = 0; i < self.length; i++) {
    const inner = f(self[i], i);
    for (let j = 0; j < inner.length; j++) {
      out.push(inner[j]);
    }
  }
  return out;
});
/**
 * Flattens an array of arrays into a single array by concatenating all arrays.
 *
 * @category sequencing
 * @since 2.0.0
 */
const flatten = exports.flatten = /*#__PURE__*/flatMap(_Function.identity);
/**
 * @category filtering
 * @since 2.0.0
 */
const filterMap = exports.filterMap = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  const as = fromIterable(self);
  const out = [];
  for (let i = 0; i < as.length; i++) {
    const o = f(as[i], i);
    if (O.isSome(o)) {
      out.push(o.value);
    }
  }
  return out;
});
/**
 * Transforms all elements of the `readonlyArray` for as long as the specified function returns some value
 *
 * @category filtering
 * @since 2.0.0
 */
const filterMapWhile = exports.filterMapWhile = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  let i = 0;
  const out = [];
  for (const a of self) {
    const b = f(a, i);
    if (O.isSome(b)) {
      out.push(b.value);
    } else {
      break;
    }
    i++;
  }
  return out;
});
/**
 * @category filtering
 * @since 2.0.0
 */
const partitionMap = exports.partitionMap = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => {
  const left = [];
  const right = [];
  const as = fromIterable(self);
  for (let i = 0; i < as.length; i++) {
    const e = f(as[i], i);
    if (E.isLeft(e)) {
      left.push(e.left);
    } else {
      right.push(e.right);
    }
  }
  return [left, right];
});
/**
 * Retrieves the `Some` values from an `Iterable` of `Option`s, collecting them into an array.
 *
 * @example
 * import { Array, Option } from "effect"
 *
 * assert.deepStrictEqual(
 *   Array.getSomes([Option.some(1), Option.none(), Option.some(2)]),
 *   [1, 2]
 * )
 *
 * @category filtering
 * @since 2.0.0
 */
const getSomes = exports.getSomes = /*#__PURE__*/filterMap(_Function.identity);
/**
 * Retrieves the `Left` values from an `Iterable` of `Either`s, collecting them into an array.
 *
 * @example
 * import { Array, Either } from "effect"
 *
 * assert.deepStrictEqual(
 *   Array.getLefts([Either.right(1), Either.left("err"), Either.right(2)]),
 *   ["err"]
 * )
 *
 * @category filtering
 * @since 2.0.0
 */
const getLefts = self => {
  const out = [];
  for (const a of self) {
    if (E.isLeft(a)) {
      out.push(a.left);
    }
  }
  return out;
};
/**
 * Retrieves the `Right` values from an `Iterable` of `Either`s, collecting them into an array.
 *
 * @example
 * import { Array, Either } from "effect"
 *
 * assert.deepStrictEqual(
 *   Array.getRights([Either.right(1), Either.left("err"), Either.right(2)]),
 *   [1, 2]
 * )
 *
 * @category filtering
 * @since 2.0.0
 */
exports.getLefts = getLefts;
const getRights = self => {
  const out = [];
  for (const a of self) {
    if (E.isRight(a)) {
      out.push(a.right);
    }
  }
  return out;
};
/**
 * @category filtering
 * @since 2.0.0
 */
exports.getRights = getRights;
const filter = exports.filter = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => {
  const as = fromIterable(self);
  const out = [];
  for (let i = 0; i < as.length; i++) {
    if (predicate(as[i], i)) {
      out.push(as[i]);
    }
  }
  return out;
});
/**
 * Separate elements based on a predicate that also exposes the index of the element.
 *
 * @category filtering
 * @since 2.0.0
 */
const partition = exports.partition = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => {
  const left = [];
  const right = [];
  const as = fromIterable(self);
  for (let i = 0; i < as.length; i++) {
    if (predicate(as[i], i)) {
      right.push(as[i]);
    } else {
      left.push(as[i]);
    }
  }
  return [left, right];
});
/**
 * @category filtering
 * @since 2.0.0
 */
const separate = exports.separate = /*#__PURE__*/partitionMap(_Function.identity);
/**
 * @category folding
 * @since 2.0.0
 */
const reduce = exports.reduce = /*#__PURE__*/(0, _Function.dual)(3, (self, b, f) => fromIterable(self).reduce((b, a, i) => f(b, a, i), b));
/**
 * @category folding
 * @since 2.0.0
 */
const reduceRight = exports.reduceRight = /*#__PURE__*/(0, _Function.dual)(3, (self, b, f) => fromIterable(self).reduceRight((b, a, i) => f(b, a, i), b));
/**
 * @category lifting
 * @since 2.0.0
 */
const liftPredicate = predicate => b => predicate(b) ? [b] : [];
/**
 * @category lifting
 * @since 2.0.0
 */
exports.liftPredicate = liftPredicate;
const liftOption = f => (...a) => fromOption(f(...a));
/**
 * @category conversions
 * @since 2.0.0
 */
exports.liftOption = liftOption;
const fromNullable = a => a == null ? empty() : [a];
/**
 * @category lifting
 * @since 2.0.0
 */
exports.fromNullable = fromNullable;
const liftNullable = f => (...a) => fromNullable(f(...a));
/**
 * @category sequencing
 * @since 2.0.0
 */
exports.liftNullable = liftNullable;
const flatMapNullable = exports.flatMapNullable = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => isNonEmptyReadonlyArray(self) ? fromNullable(f(headNonEmpty(self))) : empty());
/**
 * @category lifting
 * @since 2.0.0
 */
const liftEither = f => (...a) => {
  const e = f(...a);
  return E.isLeft(e) ? [] : [e.right];
};
/**
 * Check if a predicate holds true for every `ReadonlyArray` element.
 *
 * @category elements
 * @since 2.0.0
 */
exports.liftEither = liftEither;
const every = exports.every = /*#__PURE__*/(0, _Function.dual)(2, (self, refinement) => self.every(refinement));
/**
 * Check if a predicate holds true for some `ReadonlyArray` element.
 *
 * @category elements
 * @since 2.0.0
 */
const some = exports.some = /*#__PURE__*/(0, _Function.dual)(2, (self, predicate) => self.some(predicate));
/**
 * @since 2.0.0
 */
const extend = exports.extend = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => self.map((_, i, as) => f(as.slice(i))));
/**
 * @since 2.0.0
 */
const min = exports.min = /*#__PURE__*/(0, _Function.dual)(2, (self, O) => self.reduce(Order.min(O)));
/**
 * @since 2.0.0
 */
const max = exports.max = /*#__PURE__*/(0, _Function.dual)(2, (self, O) => self.reduce(Order.max(O)));
/**
 * @category constructors
 * @since 2.0.0
 */
const unfold = (b, f) => {
  const out = [];
  let next = b;
  let o;
  while (O.isSome(o = f(next))) {
    const [a, b] = o.value;
    out.push(a);
    next = b;
  }
  return out;
};
/**
 * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array.
 * The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays.
 * If all elements are equal, the arrays are then compared based on their length.
 * It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array.
 *
 * @category instances
 * @since 2.0.0
 */
exports.unfold = unfold;
const getOrder = exports.getOrder = Order.array;
/**
 * @category instances
 * @since 2.0.0
 */
const getEquivalence = exports.getEquivalence = Equivalence.array;
/**
 * Iterate over the `Iterable` applying `f`.
 *
 * @since 2.0.0
 */
const forEach = exports.forEach = /*#__PURE__*/(0, _Function.dual)(2, (self, f) => fromIterable(self).forEach((a, i) => f(a, i)));
/**
 * Remove duplicates from an `Iterable` using the provided `isEquivalent` function,
 * preserving the order of the first occurrence of each element.
 *
 * @since 2.0.0
 */
const dedupeWith = exports.dedupeWith = /*#__PURE__*/(0, _Function.dual)(2, (self, isEquivalent) => {
  const input = fromIterable(self);
  if (isNonEmptyReadonlyArray(input)) {
    const out = [headNonEmpty(input)];
    const rest = tailNonEmpty(input);
    for (const r of rest) {
      if (out.every(a => !isEquivalent(r, a))) {
        out.push(r);
      }
    }
    return out;
  }
  return [];
});
/**
 * Remove duplicates from an `Iterable`, preserving the order of the first occurrence of each element.
 * The equivalence used to compare elements is provided by `Equal.equivalence()` from the `Equal` module.
 *
 * @since 2.0.0
 */
const dedupe = self => dedupeWith(self, Equal.equivalence());
/**
 * Deduplicates adjacent elements that are identical using the provided `isEquivalent` function.
 *
 * @since 2.0.0
 */
exports.dedupe = dedupe;
const dedupeAdjacentWith = exports.dedupeAdjacentWith = /*#__PURE__*/(0, _Function.dual)(2, (self, isEquivalent) => {
  const out = [];
  let lastA = O.none();
  for (const a of self) {
    if (O.isNone(lastA) || !isEquivalent(a, lastA.value)) {
      out.push(a);
      lastA = O.some(a);
    }
  }
  return out;
});
/**
 * Deduplicates adjacent elements that are identical.
 *
 * @since 2.0.0
 */
const dedupeAdjacent = exports.dedupeAdjacent = /*#__PURE__*/dedupeAdjacentWith( /*#__PURE__*/Equal.equivalence());
/**
 * Joins the elements together with "sep" in the middle.
 *
 * @since 2.0.0
 * @category folding
 */
const join = exports.join = /*#__PURE__*/(0, _Function.dual)(2, (self, sep) => fromIterable(self).join(sep));
/**
 * Statefully maps over the chunk, producing new elements of type `B`.
 *
 * @since 2.0.0
 * @category folding
 */
const mapAccum = exports.mapAccum = /*#__PURE__*/(0, _Function.dual)(3, (self, s, f) => {
  let i = 0;
  let s1 = s;
  const out = [];
  for (const a of self) {
    const r = f(s1, a, i);
    s1 = r[0];
    out.push(r[1]);
    i++;
  }
  return [s1, out];
});
/**
 * Zips this chunk crosswise with the specified chunk using the specified combiner.
 *
 * @since 2.0.0
 * @category elements
 */
const cartesianWith = exports.cartesianWith = /*#__PURE__*/(0, _Function.dual)(3, (self, that, f) => flatMap(self, a => map(that, b => f(a, b))));
/**
 * Zips this chunk crosswise with the specified chunk.
 *
 * @since 2.0.0
 * @category elements
 */
const cartesian = exports.cartesian = /*#__PURE__*/(0, _Function.dual)(2, (self, that) => cartesianWith(self, that, (a, b) => [a, b]));
// -------------------------------------------------------------------------------------
// do notation
// -------------------------------------------------------------------------------------
/**
 * The "do simulation" for array allows you to sequentially apply operations to the elements of arrays, just as nested loops allow you to go through all combinations of elements in an arrays.
 *
 * It can be used to simulate "array comprehension".
 * It's a technique that allows you to create new arrays by iterating over existing ones and applying specific **conditions** or **transformations** to the elements. It's like assembling a new collection from pieces of other collections based on certain rules.
 *
 * Here's how the do simulation works:
 *
 * 1. Start the do simulation using the `Do` value
 * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Array` values
 * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope
 * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
 * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope
 *
 * @see {@link bindTo}
 * @see {@link bind}
 * @see {@link let_ let}
 *
 * @example
 * import { Array as Arr, pipe } from "effect"
 * const doResult = pipe(
 *   Arr.Do,
 *   Arr.bind("x", () => [1, 3, 5]),
 *   Arr.bind("y", () => [2, 4, 6]),
 *   Arr.filter(({ x, y }) => x < y), // condition
 *   Arr.map(({ x, y }) => [x, y] as const) // transformation
 * )
 * assert.deepStrictEqual(doResult, [[1, 2], [1, 4], [1, 6], [3, 4], [3, 6], [5, 6]])
 *
 * // equivalent
 * const x = [1, 3, 5],
 *       y = [2, 4, 6],
 *       result = [];
 * for(let i = 0; i < x.length; i++) {
 *   for(let j = 0; j < y.length; j++) {
 *     const _x = x[i], _y = y[j];
 *     if(_x < _y) result.push([_x, _y] as const)
 *   }
 * }
 *
 * @category do notation
 * @since 3.2.0
 */
const Do = exports.Do = /*#__PURE__*/of({});
/**
 * The "do simulation" for array allows you to sequentially apply operations to the elements of arrays, just as nested loops allow you to go through all combinations of elements in an arrays.
 *
 * It can be used to simulate "array comprehension".
 * It's a technique that allows you to create new arrays by iterating over existing ones and applying specific **conditions** or **transformations** to the elements. It's like assembling a new collection from pieces of other collections based on certain rules.
 *
 * Here's how the do simulation works:
 *
 * 1. Start the do simulation using the `Do` value
 * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Array` values
 * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope
 * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
 * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope
 *
 * @see {@link bindTo}
 * @see {@link Do}
 * @see {@link let_ let}
 *
 * @example
 * import { Array as Arr, pipe } from "effect"
 * const doResult = pipe(
 *   Arr.Do,
 *   Arr.bind("x", () => [1, 3, 5]),
 *   Arr.bind("y", () => [2, 4, 6]),
 *   Arr.filter(({ x, y }) => x < y), // condition
 *   Arr.map(({ x, y }) => [x, y] as const) // transformation
 * )
 * assert.deepStrictEqual(doResult, [[1, 2], [1, 4], [1, 6], [3, 4], [3, 6], [5, 6]])
 *
 * // equivalent
 * const x = [1, 3, 5],
 *       y = [2, 4, 6],
 *       result = [];
 * for(let i = 0; i < x.length; i++) {
 *   for(let j = 0; j < y.length; j++) {
 *     const _x = x[i], _y = y[j];
 *     if(_x < _y) result.push([_x, _y] as const)
 *   }
 * }
 *
 * @category do notation
 * @since 3.2.0
 */
const bind = exports.bind = /*#__PURE__*/doNotation.bind(map, flatMap);
/**
 * The "do simulation" for array allows you to sequentially apply operations to the elements of arrays, just as nested loops allow you to go through all combinations of elements in an arrays.
 *
 * It can be used to simulate "array comprehension".
 * It's a technique that allows you to create new arrays by iterating over existing ones and applying specific **conditions** or **transformations** to the elements. It's like assembling a new collection from pieces of other collections based on certain rules.
 *
 * Here's how the do simulation works:
 *
 * 1. Start the do simulation using the `Do` value
 * 2. Within the do simulation scope, you can use the `bind` function to define variables and bind them to `Array` values
 * 3. You can accumulate multiple `bind` statements to define multiple variables within the scope
 * 4. Inside the do simulation scope, you can also use the `let` function to define variables and bind them to simple values
 * 5. Regular `Option` functions like `map` and `filter` can still be used within the do simulation. These functions will receive the accumulated variables as arguments within the scope
 *
 * @see {@link bindTo}
 * @see {@link Do}
 * @see {@link let_ let}
 *
 * @example
 * import { Array as Arr, pipe } from "effect"
 * const doResult = pipe(
 *   Arr.Do,
 *   Arr.bind("x", () => [1, 3, 5]),
 *   Arr.bind("y", () => [2, 4, 6]),
 *   Arr.filter(({ x, y }) => x < y), // condition
 *   Arr.map(({ x, y }) => [x, y] as const) // transformation
 * )
 * assert.deepStrictEqual(doResult, [[1, 2], [1, 4], [1, 6], [3, 4], [3, 6], [5, 6]])
 *
 * // equivalent
 * const x = [1, 3, 5],
 *       y = [2, 4, 6],
 *       result = [];
 * for(let i = 0; i < x.length; i++) {
 *   for(let j = 0; j < y.length; j++) {
 *     const _x = x[i], _y = y[j];
 *     if(_x < _y) result.push([_x, _y] as const)
 *   }
 * }
 *
 * @category do notation
 * @since 3.2.0
 */
const bindTo = exports.bindTo = /*#__PURE__*/doNotation.bindTo(map);
const let_ = exports.let = /*#__PURE__*/doNotation.let_(map);
//# sourceMappingURL=Array.js.map