// 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_