/** * @since 2.0.0 */ import * as Equal from "./Equal.js" import type * as Equivalence from "./Equivalence.js" import * as Dual from "./Function.js" import { pipe } from "./Function.js" import * as Hash from "./Hash.js" import type { Inspectable } from "./Inspectable.js" import { format, NodeInspectSymbol, toJSON } from "./Inspectable.js" import type { Order } from "./Order.js" import type { Pipeable } from "./Pipeable.js" import { pipeArguments } from "./Pipeable.js" import type { Predicate } from "./Predicate.js" import { hasProperty } from "./Predicate.js" import * as RBT from "./RedBlackTree.js" import type { Invariant, NoInfer } from "./Types.js" const TypeId: unique symbol = Symbol.for("effect/SortedSet") /** * @since 2.0.0 * @category symbol */ export type TypeId = typeof TypeId /** * @since 2.0.0 * @category models */ export interface SortedSet extends Iterable, Equal.Equal, Pipeable, Inspectable { readonly [TypeId]: { readonly _A: Invariant } /** @internal */ readonly keyTree: RBT.RedBlackTree } const SortedSetProto: Omit, "keyTree"> = { [TypeId]: { _A: (_: any) => _ }, [Hash.symbol](this: SortedSet): number { return pipe( Hash.hash(this.keyTree), Hash.combine(Hash.hash(TypeId)), Hash.cached(this) ) }, [Equal.symbol](this: SortedSet, that: unknown): boolean { return isSortedSet(that) && Equal.equals(this.keyTree, that.keyTree) }, [Symbol.iterator](this: SortedSet): Iterator { return RBT.keys(this.keyTree) }, toString(this: SortedSet) { return format(this.toJSON()) }, toJSON() { return { _id: "SortedSet", values: Array.from(this).map(toJSON) } }, [NodeInspectSymbol]() { return this.toJSON() }, pipe() { return pipeArguments(this, arguments) } } const fromTree = (keyTree: RBT.RedBlackTree): SortedSet => { const a = Object.create(SortedSetProto) a.keyTree = keyTree return a } /** * @since 2.0.0 * @category refinements */ export const isSortedSet: { /** * @since 2.0.0 * @category refinements */ (u: Iterable): u is SortedSet /** * @since 2.0.0 * @category refinements */ (u: unknown): u is SortedSet } = (u: unknown): u is SortedSet => hasProperty(u, TypeId) /** * @since 2.0.0 * @category constructors */ export const empty = (O: Order): SortedSet => fromTree(RBT.empty(O)) /** * Creates a new `SortedSet` from an iterable collection of values. * * @since 2.0.0 * @category constructors */ export const fromIterable: { /** * Creates a new `SortedSet` from an iterable collection of values. * * @since 2.0.0 * @category constructors */ (ord: Order): (iterable: Iterable) => SortedSet /** * Creates a new `SortedSet` from an iterable collection of values. * * @since 2.0.0 * @category constructors */ (iterable: Iterable, ord: Order): SortedSet } = Dual.dual( 2, (iterable: Iterable, ord: Order): SortedSet => fromTree(RBT.fromIterable(Array.from(iterable).map((k) => [k, true]), ord)) ) /** * @since 2.0.0 * @category constructors */ export const make = (ord: Order) => >(...entries: Entries): SortedSet => fromIterable(entries, ord) /** * @since 2.0.0 * @category elements */ export const add: { /** * @since 2.0.0 * @category elements */ (value: A): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category elements */ (self: SortedSet, value: A): SortedSet } = Dual.dual< (value: A) => (self: SortedSet) => SortedSet, (self: SortedSet, value: A) => SortedSet >(2, (self, value) => RBT.has(self.keyTree, value) ? self : fromTree(RBT.insert(self.keyTree, value, true))) /** * @since 2.0.0 */ export const difference: { /** * @since 2.0.0 */ (that: Iterable): (self: SortedSet) => SortedSet /** * @since 2.0.0 */ (self: SortedSet, that: Iterable): SortedSet } = Dual.dual< (that: Iterable) => (self: SortedSet) => SortedSet, (self: SortedSet, that: Iterable) => SortedSet >(2, (self: SortedSet, that: Iterable) => { let out = self for (const value of that) { out = remove(out, value) } return out }) /** * Check if a predicate holds true for every `SortedSet` element. * * @since 2.0.0 * @category elements */ export const every: { /** * Check if a predicate holds true for every `SortedSet` element. * * @since 2.0.0 * @category elements */ (predicate: Predicate): (self: SortedSet) => boolean /** * Check if a predicate holds true for every `SortedSet` element. * * @since 2.0.0 * @category elements */ (self: SortedSet, predicate: Predicate): boolean } = Dual.dual(2, (self: SortedSet, predicate: Predicate): boolean => { for (const value of self) { if (!predicate(value)) { return false } } return true }) /** * @since 2.0.0 * @category filtering */ export const filter: { /** * @since 2.0.0 * @category filtering */ (predicate: Predicate): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category filtering */ (self: SortedSet, predicate: Predicate): SortedSet } = Dual.dual(2, (self: SortedSet, predicate: Predicate): SortedSet => { const ord = RBT.getOrder(self.keyTree) let out = empty(ord) for (const value of self) { if (predicate(value)) { out = add(out, value) } } return out }) /** * @since 2.0.0 * @category sequencing */ export const flatMap: { /** * @since 2.0.0 * @category sequencing */ (O: Order, f: (a: A) => Iterable): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category sequencing */ (self: SortedSet, O: Order, f: (a: A) => Iterable): SortedSet } = Dual.dual< (O: Order, f: (a: A) => Iterable) => (self: SortedSet) => SortedSet, (self: SortedSet, O: Order, f: (a: A) => Iterable) => SortedSet >(3, (self, O, f) => { let out = empty(O) forEach(self, (a) => { for (const b of f(a)) { out = add(out, b) } }) return out }) /** * @since 2.0.0 * @category traversing */ export const forEach: { /** * @since 2.0.0 * @category traversing */ (f: (a: A) => void): (self: SortedSet) => void /** * @since 2.0.0 * @category traversing */ (self: SortedSet, f: (a: A) => void): void } = Dual.dual< (f: (a: A) => void) => (self: SortedSet) => void, (self: SortedSet, f: (a: A) => void) => void >(2, (self, f) => RBT.forEach(self.keyTree, f)) /** * @since 2.0.0 * @category elements */ export const has: { /** * @since 2.0.0 * @category elements */ (value: A): (self: SortedSet) => boolean /** * @since 2.0.0 * @category elements */ (self: SortedSet, value: A): boolean } = Dual.dual< (value: A) => (self: SortedSet) => boolean, (self: SortedSet, value: A) => boolean >(2, (self, value) => RBT.has(self.keyTree, value)) /** * @since 2.0.0 */ export const intersection: { /** * @since 2.0.0 */ (that: Iterable): (self: SortedSet) => SortedSet /** * @since 2.0.0 */ (self: SortedSet, that: Iterable): SortedSet } = Dual.dual< (that: Iterable) => (self: SortedSet) => SortedSet, (self: SortedSet, that: Iterable) => SortedSet >(2, (self, that) => { const ord = RBT.getOrder(self.keyTree) let out = empty(ord) for (const value of that) { if (has(self, value)) { out = add(out, value) } } return out }) /** * @since 2.0.0 * @category elements */ export const isSubset: { /** * @since 2.0.0 * @category elements */ (that: SortedSet): (self: SortedSet) => boolean /** * @since 2.0.0 * @category elements */ (self: SortedSet, that: SortedSet): boolean } = Dual.dual< (that: SortedSet) => (self: SortedSet) => boolean, (self: SortedSet, that: SortedSet) => boolean >(2, (self, that) => every(self, (a) => has(that, a))) /** * @since 2.0.0 * @category mapping */ export const map: { /** * @since 2.0.0 * @category mapping */ (O: Order, f: (a: A) => B): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category mapping */ (self: SortedSet, O: Order, f: (a: A) => B): SortedSet } = Dual.dual< (O: Order, f: (a: A) => B) => (self: SortedSet) => SortedSet, (self: SortedSet, O: Order, f: (a: A) => B) => SortedSet >(3, (self, O, f) => { let out = empty(O) forEach(self, (a) => { const b = f(a) if (!has(out, b)) { out = add(out, b) } }) return out }) /** * @since 2.0.0 * @category filtering */ export const partition: { /** * @since 2.0.0 * @category filtering */ (predicate: (a: NoInfer) => boolean): (self: SortedSet) => [excluded: SortedSet, satisfying: SortedSet] /** * @since 2.0.0 * @category filtering */ (self: SortedSet, predicate: (a: A) => boolean): [excluded: SortedSet, satisfying: SortedSet] } = Dual.dual( 2, (self: SortedSet, predicate: (a: A) => boolean): [excluded: SortedSet, satisfying: SortedSet] => { const ord = RBT.getOrder(self.keyTree) let right = empty(ord) let left = empty(ord) for (const value of self) { if (predicate(value)) { right = add(right, value) } else { left = add(left, value) } } return [left, right] } ) /** * @since 2.0.0 * @category elements */ export const remove: { /** * @since 2.0.0 * @category elements */ (value: A): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category elements */ (self: SortedSet, value: A): SortedSet } = Dual.dual< (value: A) => (self: SortedSet) => SortedSet, (self: SortedSet, value: A) => SortedSet >(2, (self, value) => fromTree(RBT.removeFirst(self.keyTree, value))) /** * @since 2.0.0 * @category getters */ export const size = (self: SortedSet): number => RBT.size(self.keyTree) /** * Check if a predicate holds true for some `SortedSet` element. * * @since 2.0.0 * @category elements */ export const some: { /** * Check if a predicate holds true for some `SortedSet` element. * * @since 2.0.0 * @category elements */ (predicate: Predicate): (self: SortedSet) => boolean /** * Check if a predicate holds true for some `SortedSet` element. * * @since 2.0.0 * @category elements */ (self: SortedSet, predicate: Predicate): boolean } = Dual.dual< (predicate: Predicate) => (self: SortedSet) => boolean, (self: SortedSet, predicate: Predicate) => boolean >(2, (self, predicate) => { for (const value of self) { if (predicate(value)) { return true } } return false }) /** * @since 2.0.0 * @category elements */ export const toggle: { /** * @since 2.0.0 * @category elements */ (value: A): (self: SortedSet) => SortedSet /** * @since 2.0.0 * @category elements */ (self: SortedSet, value: A): SortedSet } = Dual.dual< (value: A) => (self: SortedSet) => SortedSet, (self: SortedSet, value: A) => SortedSet >(2, (self, value) => has(self, value) ? remove(self, value) : add(self, value)) /** * @since 2.0.0 */ export const union: { /** * @since 2.0.0 */ (that: Iterable): (self: SortedSet) => SortedSet /** * @since 2.0.0 */ (self: SortedSet, that: Iterable): SortedSet } = Dual.dual< (that: Iterable) => (self: SortedSet) => SortedSet, (self: SortedSet, that: Iterable) => SortedSet >(2, (self: SortedSet, that: Iterable) => { const ord = RBT.getOrder(self.keyTree) let out = empty(ord) for (const value of self) { out = add(value)(out) } for (const value of that) { out = add(value)(out) } return out }) /** * @since 2.0.0 * @category getters */ export const values = (self: SortedSet): IterableIterator => RBT.keys(self.keyTree) /** * @since 2.0.0 * @category equivalence */ export const getEquivalence = (): Equivalence.Equivalence> => (a, b) => isSubset(a, b) && isSubset(b, a)