// ets_tracing: off import * as Array from "effect/Array" import type * as Equivalence from "effect/Equivalence" import * as Option from "effect/Option" import type * as Order from "effect/Order" import { not } from "effect/Predicate" import type * as Result from "effect/Result" import { identity, pipe, type Predicate, type Refinement, tuple } from "./Function.js" export function find_( as: ReadonlySet, refinement: Refinement ): B | undefined export function find_(set: ReadonlySet, predicate: Predicate): A | undefined export function find_(set: ReadonlySet, predicate: Predicate) { return [...set].find(predicate) } export function findFirst_( set: ReadonlySet, refinement: Refinement ): Option.Option export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option export function findFirst_(set: ReadonlySet, predicate: Predicate): Option.Option { return Option.fromNullishOr([...set].find(predicate)) } export function findFirstMap_( set: ReadonlySet, f: (a: A) => Option.Option ): Option.Option { return Array.findFirst([...set], f) } export type MutableSet = globalThis.Set export type Set = ReadonlySet export const empty: Set = new Set() /** * The set of elements which are in both the first and second set */ export function intersection_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { const elemE = elem_(E) return (x, y) => { if (x === empty || y === empty) { return empty } const r = new Set() x.forEach((e) => { if (elemE(y, e)) { r.add(e) } }) return r } } /** * The set of elements which are in both the first and second set */ export function intersection(E: Equivalence.Equivalence): (r: Set) => (l: Set) => Set { const i = intersection_(E) return (x) => (y) => i(x, y) } /** * Convert a mutable set to a readonly one */ export function fromMutable(s: MutableSet): Set { return new Set(s) } /** * Convert a set to a mutable one */ export function toMutable(s: Set): MutableSet { return new Set(s) } // /** // * get Show for set given Show for values // */ // export function getShow(S: Show): Show> { // return { // show: (s) => { // let elements = "" // s.forEach((a) => { // elements += S.show(a) + ", " // }) // if (elements !== "") { // elements = elements.substring(0, elements.length - 2) // } // return `new Set([${elements}])` // } // } // } /** * Convert a set to an Array */ export function toArray(O: Order.Order): (set: Set) => ReadonlyArray { return (x) => { const r: Array = [] x.forEach((e) => r.push(e)) return r.sort(O) } } /** * Convert a set to an Array */ export function toArray_(x: Set, O: Order.Order): ReadonlyArray { return toArray(O)(x) } // /** // * Get Equivalence for Setgiven Equivalence for element // */ // export function getEquivalence.Equivalence(E: Equivalence.Equivalence): Equivalence.Equivalence> { // const subsetE = isSubset_(E) // return makeEquivalence((x, y) => subsetE(x, y) && subsetE(y, x)) // } interface Next { readonly done?: boolean readonly value: A } /** * true if one or more elements match predicate */ export function some(predicate: Predicate): (set: Set) => boolean { return (set) => { const values = set.values() let e: Next let found = false while (!found && !(e = values.next() as any).done) { found = predicate(e.value) } return found } } /** * true if one or more elements match predicate */ export function some_(set: Set, predicate: Predicate): boolean { return some(predicate)(set) } /** * Projects a Set through a function */ export function map(E: Equivalence.Equivalence): (f: (x: A) => B) => (set: Set) => Set { const m = map_(E) return (f) => (set) => m(set, f) } /** * Projects a Set through a function */ export function map_(E: Equivalence.Equivalence): (set: Set, f: (x: A) => B) => Set { const elemE = elem_(E) return (set, f) => { const r = new Set() set.forEach((e) => { const v = f(e) if (!elemE(r, v)) { r.add(v) } }) return r } } /** * true if all elements match predicate */ export function every(predicate: Predicate): (set: Set) => boolean { return (set) => every_(set, predicate) } /** * true if all elements match predicate */ export function every_(set: Set, predicate: Predicate): boolean { return not(some(not(predicate)))(set) } /** * Map + Flatten */ export function chain( E: Equivalence.Equivalence ): (f: (x: A) => Set) => (set: Set) => Set { const c = chain_(E) return (f) => (set) => c(set, f) } /** * Map + Flatten */ export function chain_( E: Equivalence.Equivalence ): (set: Set, f: (x: A) => Set) => Set { const elemE = elem_(E) return (set, f) => { const r = new Set() set.forEach((e) => { f(e).forEach((e) => { if (!elemE(r, e)) { r.add(e) } }) }) return r } } /** * `true` if and only if every element in the first set is an element of the second set */ export function isSubset(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { const i = isSubset_(E) return (x, y) => i(y, x) } /** * `true` if and only if every element in the first set is an element of the second set */ export function isSubset_(E: Equivalence.Equivalence): (x: Set, y: Set) => boolean { const elemE = elem_(E) return (x, y) => every((a: A) => elemE(y, a))(x) } /** * Filter set values using predicate */ export function filter( refinement: Refinement ): (set: Set) => Set export function filter(predicate: Predicate): (set: Set) => Set export function filter(predicate: Predicate): (set: Set) => Set { return (set) => filter_(set, predicate) } /** * Filter set values using predicate */ export function filter_( set: Set, refinement: Refinement ): Set export function filter_(set: Set, predicate: Predicate): Set export function filter_(set: Set, predicate: Predicate): Set { const values = set.values() let e: Next const r = new Set() while (!(e = values.next() as any).done) { const value = e.value if (predicate(value)) { r.add(value) } } return r } /** * Partition set values using predicate */ export function partition( refinement: Refinement ): (set: Set) => readonly [Set, Set] export function partition( predicate: Predicate ): (set: Set) => readonly [Set, Set] export function partition( predicate: Predicate ): (set: Set) => readonly [Set, Set] { return (set) => partition_(set, predicate) } /** * Partition set values using predicate */ export function partition_( set: Set, refinement: Refinement ): readonly [Set, Set] export function partition_( set: Set, predicate: Predicate ): readonly [Set, Set] export function partition_( set: Set, predicate: Predicate ): readonly [Set, Set] { const values = set.values() let e: Next const right = new Set() const left = new Set() while (!(e = values.next() as any).done) { const value = e.value if (predicate(value)) { right.add(value) } else { left.add(value) } } return tuple(left, right) } /** * Test if a value is a member of a set */ export function elem_(E: Equivalence.Equivalence): (set: Set, a: A) => boolean { return (set, a) => { const values = set.values() let e: Next let found = false while (!found && !(e = values.next() as any).done) { found = E(a, e.value) } return found } } /** * Test if a value is a member of a set */ export function elem(E: Equivalence.Equivalence): (a: A) => (set: Set) => boolean { const e = elem_(E) return (a) => (set) => e(set, a) } /** * Partition elements according to f */ export function partitionMap( EB: Equivalence.Equivalence, EC: Equivalence.Equivalence ): (f: (a: A) => Result.Result) => (set: Set) => readonly [Set, Set] { const pm = partitionMap_(EB, EC) return (f: (a: A) => Result.Result) => (set: Set) => pm(set, f) } /** * Partition elements according to f */ export function partitionMap_( EB: Equivalence.Equivalence, EC: Equivalence.Equivalence ): (set: Set, f: (a: A) => Result.Result) => readonly [Set, Set] { return (set: Set, f: (a: A) => Result.Result) => { const values = set.values() let e: Next const left = new Set() const right = new Set() const hasB = elem_(EB) const hasC = elem_(EC) while (!(e = values.next() as any).done) { const v = f(e.value) switch (v._tag) { case "Failure": if (!hasB(left, v.failure)) { left.add(v.failure) } break case "Success": if (!hasC(right, v.success)) { right.add(v.success) } break } } return tuple(left, right) } } /** * Form the set difference (`x` - `y`) */ export function difference_(E: Equivalence.Equivalence): (l: Set, r: Set) => Set { const elemE = elem_(E) return (x, y) => filter((a: A) => !elemE(y, a))(x) } /** * Form the set difference (`x` - `y`) */ export function difference(E: Equivalence.Equivalence): (x: Set, y: Set) => Set { const diff = difference_(E) return (x, y) => diff(x, y) } /** * Reduce over the set values */ export function reduce( O: Order.Order ): (b: B, f: (b: B, a: A) => B) => (fa: Set) => B { const red = reduce_(O) return (b, f) => (fa) => red(fa, b, f) } /** * Reduce over the set values */ export function reduce_( O: Order.Order ): (fa: Set, b: B, f: (b: B, a: A) => B) => B { const toArrayO = toArray(O) return (fa, b, f) => toArrayO(fa).reduce(f, b) } // /** // * Fold + Map // */ // export function foldMap( // O: Order.Order, // M: Identity // ): (f: (a: A) => M) => (fa: Set) => M { // const fm = foldMap_(O, M) // return (f) => (fa) => fm(fa, f) // } // /** // * Fold + Map // */ // export function foldMap_( // O: Order.Order, // M: Identity // ): (fa: Set, f: (a: A) => M) => M { // const toArrayO = toArray(O) // return (fa, f) => toArrayO(fa).reduce((b, a) => M.combine(b, f(a)), M.identity) // } /** * Create a set with one element */ export function singleton(a: A): Set { return new Set([a]) } /** * Insert a value into a set */ export function insert(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { const i = insert_(E) return (a) => (set) => i(set, a) } /** * Insert a value into a set */ export function insert_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { const elemE = elem_(E) return (set, a) => { if (!elemE(set, a)) { const r = new Set(set) r.add(a) return r } else { return set } } } /** * Delete a value from a set */ export function remove(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { const rem = remove_(E) return (a) => (set) => rem(set, a) } /** * Delete a value from a set */ export function remove_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { return (set, a) => filter((ax: A) => !E(a, ax))(set) } /** * If element is present remove it, if not add it */ export function toggle(E: Equivalence.Equivalence): (a: A) => (set: Set) => Set { const t = toggle_(E) return (a) => (set) => t(set, a) } /** * If element is present remove it, if not add it */ export function toggle_(E: Equivalence.Equivalence): (set: Set, a: A) => Set { const elemE = elem_(E) const removeE = remove(E) const insertE = insert(E) return (set, a) => (elemE(set, a) ? removeE : insertE)(a)(set) } /** * Create a set from an array */ export function fromArray(E: Equivalence.Equivalence): (as: ReadonlyArray) => Set { return (as) => { const len = as.length const r = new Set() const has = elem_(E) for (let i = 0; i < len; i++) { const a = as[i]! if (!has(r, a)) { r.add(a) } } return r } } /** * Set compaction, remove none */ export function compact(E: Equivalence.Equivalence): (fa: Set>) => Set { return filterMap(E)(identity) } /** * Separate elements */ export function separate( EE: Equivalence.Equivalence, EA: Equivalence.Equivalence ): (fa: Set>) => readonly [Set, Set] { return (fa) => { const elemEE = elem_(EE) const elemEA = elem_(EA) const left: MutableSet = new Set() const right: MutableSet = new Set() fa.forEach((e) => { switch (e._tag) { case "Failure": if (!elemEE(left, e.failure)) { left.add(e.failure) } break case "Success": if (!elemEA(right, e.success)) { right.add(e.success) } break } }) return tuple(left, right) } } /** * Filter + Map */ export function filterMap( E: Equivalence.Equivalence ): (f: (a: A) => Option.Option) => (fa: Set) => Set { const fm = filterMap_(E) return (f) => (fa) => fm(fa, f) } /** * Filter + Map */ export function filterMap_( E: Equivalence.Equivalence ): (fa: Set, f: (a: A) => Option.Option) => Set { const elemE = elem_(E) return (fa, f) => { const r: MutableSet = new Set() fa.forEach((a) => { const ob = f(a) if (ob._tag === "Some" && !elemE(r, ob.value)) { r.add(ob.value) } }) return r } } /** * Form the union of two sets */ export function union_(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { const elemE = elem_(E) return (x, y) => { if (x === empty) { return y } if (y === empty) { return x } const r = new Set(x) y.forEach((e) => { if (!elemE(r, e)) { r.add(e) } }) return r } } /** * Form the union of two sets */ export function union(E: Equivalence.Equivalence): (set: Set, y: Set) => Set { const u = union_(E) return (x, y) => u(x, y) } function make_(ord: Order.Order, eq_?: Equivalence.Equivalence) { const eq = eq_ ?? ((x, y) => ord(x, y) === 0) const fromArray_ = fromArray(eq) const concat_ = (set: Set, it: Iterable) => fromArray_([...set, ...it]) const insert__ = insert(eq) function replace_(set: Set, a: A) { return pipe(filter_(set, (x) => !eq(x, a)), insert__(a)) } return { insert: insert__, insert_: insert_(eq), remove: remove(eq), remove_: remove_(eq), reduce: reduce(ord), reduce_: reduce_(ord), replace: (a: A) => (set: Set) => replace_(set, a), replace_, toArray: toArray(ord), fromArray, from: (it: Iterable) => fromArray_([...it]), empty: () => new Set(), concat_, concat: (it: Iterable) => (set: Set) => concat_(set, it), // A and B the same, useful when editing elements. map: map(eq), map_: map_(eq), filterMap: filterMap(eq), filterMap_: filterMap_(eq) } // TODO: extend } class Wrapper { wrapped(ord: Order.Order, eq: Equivalence.Equivalence) { return make_(ord, eq) } } export interface SetSchemaExtensions extends ReturnType["wrapped"]> {} export const make: ( ord: Order.Order, eq?: Equivalence.Equivalence ) => SetSchemaExtensions = make_