// ets_tracing: off
import "../Operator/index.js"
import * as A from "../Collections/Immutable/Array/index.js"
import * as Tp from "../Collections/Immutable/Tuple/index.js"
import type { Either } from "../Either/index.js"
import { identity } from "../Function/index.js"
function* genOf(a: A) {
yield a
}
function* genMap(iterator: Iterator, mapping: (a: A, i: number) => B) {
let n = -1
while (true) {
const result = iterator.next()
if (result.done) {
break
}
n += 1
yield mapping(result.value, n)
}
}
function* genChain(iterator: Iterator, mapping: (a: A) => Iterable) {
while (true) {
const result = iterator.next()
if (result.done) {
break
}
const ib = mapping(result.value)[Symbol.iterator]()
while (true) {
const result = ib.next()
if (result.done) {
break
}
yield result.value
}
}
}
// inspired from "Closing Iterables is a Leaky Abstraction" by Reginald Braithwaite
// https://raganwald.com/2017/07/22/closing-iterables-is-a-leaky-abstraction.html
export function zipWith(
iterableA: Iterable,
iterableB: Iterable,
zipper: (a: A, b: B) => C
): Iterable {
return {
[Symbol.iterator]() {
let done = false
const ia = iterableA[Symbol.iterator]()
const ib = iterableB[Symbol.iterator]()
return {
next() {
if (done) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.return!()
}
const va = ia.next()
const vb = ib.next()
return va.done || vb.done
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.return!()
: { done: false, value: zipper(va.value, vb.value) }
},
return(value?: unknown) {
if (!done) {
done = true
if (typeof ia.return === "function") {
ia.return()
}
if (typeof ib.return === "function") {
ib.return()
}
}
return { done: true, value }
}
}
}
}
}
export function map(f: (a: A, k: number) => B) {
return (i: Iterable): Iterable => ({
[Symbol.iterator]: () => genMap(i[Symbol.iterator](), f)
})
}
export function map_(i: Iterable, f: (a: A, k: number) => B): Iterable {
return {
[Symbol.iterator]: () => genMap(i[Symbol.iterator](), f)
}
}
export function zip(fb: Iterable) {
return (fa: Iterable): Iterable> => zipWith(fa, fb, Tp.tuple)
}
export function zip_(
fa: Iterable,
fb: Iterable
): Iterable> {
return zipWith(fa, fb, Tp.tuple)
}
export function chain(f: (a: A) => Iterable) {
return (i: Iterable): Iterable => ({
[Symbol.iterator]: () => genChain(i[Symbol.iterator](), f)
})
}
export function chain_(i: Iterable, f: (a: A) => Iterable): Iterable {
return {
[Symbol.iterator]: () => genChain(i[Symbol.iterator](), f)
}
}
export function ap(fa: Iterable) {
return (fab: Iterable<(a: A) => B>): Iterable => chain_(fab, (f) => map_(fa, f))
}
export function of(a: A): Iterable {
return {
[Symbol.iterator]: () => genOf(a)
}
}
export function take_(a: Iterable, n: number): Iterable {
return {
*[Symbol.iterator]() {
let i = 0
for (const x of a) {
if (i++ >= n) {
return
}
yield x
}
}
}
}
export function skip_(a: Iterable, n: number): Iterable {
return {
*[Symbol.iterator]() {
let i = 0
for (const x of a) {
if (i++ >= n) {
yield x
}
}
}
}
}
export const never: Iterable = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
*[Symbol.iterator]() {}
}
export function foldMap(M: { empty: M; concat: (x: M, y: M) => M }) {
return (f: (a: A, k: number) => M) =>
(fa: Iterable): M => {
let res = M.empty
let n = -1
const iterator = fa[Symbol.iterator]()
// eslint-disable-next-line no-constant-condition
while (true) {
const result = iterator.next()
if (result.done) {
break
}
n += 1
res = M.concat(res, f(result.value, n))
}
return res
}
}
export function reduce(b: B, f: (b: B, a: A, i: number) => B) {
return (fa: Iterable): B => reduce_(fa, b, f)
}
export function reduce_(
fa: Iterable,
b: B,
f: (b: B, a: A, i: number) => B
): B {
let res = b
let n = -1
const iterator = fa[Symbol.iterator]()
// eslint-disable-next-line no-constant-condition
while (true) {
const result = iterator.next()
if (result.done) {
break
}
n += 1
res = f(res, result.value, n)
}
return res
}
export function reduceRight(b: B, f: (a: A, b: B, i: number) => B) {
return (fa: Iterable): B => {
return A.reduceRightWithIndex_(Array.from(fa), b, (i, a, b) => f(a, b, i))
}
}
export function reduceRight_(
fa: Iterable,
b: B,
f: (a: A, b: B, i: number) => B
): B {
return A.reduceRightWithIndex_(Array.from(fa), b, (i, a, b) => f(a, b, i))
}
export function concat(a: Iterable, b: Iterable): Iterable {
return {
*[Symbol.iterator]() {
for (const x of a) {
yield x
}
for (const x of b) {
yield x
}
}
}
}
export function flatten(a: Iterable>) {
return chain_(a, identity)
}
export function partitionMap(f: (a: A) => Either) {
return (as: Iterable): Tp.Tuple<[Iterable, Iterable]> =>
A.separate(Array.from(map_(as, f)))
}
/**
* Infinite sequence produced by repeated application of f to a
*/
export function unfold(a: A, f: (a: A) => A): Iterable {
return {
*[Symbol.iterator]() {
yield a
let current = a
while (true) {
current = f(a)
yield current
}
}
}
}
export function corresponds(
left: Iterable,
right: Iterable,
f: (a: A, b: B) => boolean
) {
const leftIt = left[Symbol.iterator]()
const rightIt = right[Symbol.iterator]()
// eslint-disable-next-line no-constant-condition
while (1) {
const lnext = leftIt.next()
const rnext = rightIt.next()
if (lnext.done !== rnext.done) {
return false
}
if (lnext.done) {
return true
}
if (!f(lnext.value, rnext.value)) {
return false
}
}
throw new Error("Bug")
}