/** * @since 1.0.0 */ import * as Equal from "@effect/data/Equal" import * as Dual from "@effect/data/Function" import { pipe } from "@effect/data/Function" import * as Hash from "@effect/data/Hash" import type { Inspectable } from "@effect/data/Inspectable" import { NodeInspectSymbol, toJSON, toString } from "@effect/data/Inspectable" import type { Order } from "@effect/data/Order" import type { Pipeable } from "@effect/data/Pipeable" import { pipeArguments } from "@effect/data/Pipeable" import type { Predicate, Refinement } from "@effect/data/Predicate" import { isObject } from "@effect/data/Predicate" import * as RBT from "@effect/data/RedBlackTree" const TypeId: unique symbol = Symbol.for("@effect/data/SortedSet") /** * @since 1.0.0 * @category symbol */ export type TypeId = typeof TypeId /** * @since 1.0.0 * @category models */ export interface SortedSet extends Iterable, Equal.Equal, Pipeable, Inspectable { readonly [TypeId]: { readonly _A: (_: never) => A } /** @internal */ readonly keyTree: RBT.RedBlackTree } const SortedSetProto: Omit, "keyTree"> = { [TypeId]: { _A: (_: never) => _ }, [Hash.symbol](this: SortedSet): number { return pipe(Hash.hash(this.keyTree), Hash.combine(Hash.hash(TypeId))) }, [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 toString(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 1.0.0 * @category refinements */ export const isSortedSet: { (u: Iterable): u is SortedSet (u: unknown): u is SortedSet } = (u: unknown): u is SortedSet => isObject(u) && TypeId in u /** * @since 1.0.0 * @category constructors */ export const empty = (O: Order): SortedSet => fromTree(RBT.empty(O)) /** * @since 1.0.0 * @category constructors */ export const fromIterable = (ord: Order) => (iterable: Iterable): SortedSet => fromTree(RBT.fromIterable(ord)(Array.from(iterable).map((k) => [k, true]))) /** * @since 1.0.0 * @category constructors */ export const make = (ord: Order) => >(...entries: Entries): SortedSet => fromIterable(ord)(entries) /** * @since 1.0.0 * @category elements */ export const add: { (value: A): (self: SortedSet) => SortedSet (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 1.0.0 */ export const difference: { (that: Iterable): (self: SortedSet) => SortedSet (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 1.0.0 * @category elements */ export const every: { (refinement: Refinement): (self: SortedSet) => self is SortedSet (predicate: Predicate): (self: SortedSet) => boolean (self: SortedSet, refinement: Refinement): self is SortedSet (self: SortedSet, predicate: Predicate): boolean } = Dual.dual(2, (self: SortedSet, refinement: Refinement): self is SortedSet => { for (const value of self) { if (!refinement(value)) { return false } } return true }) /** * @since 1.0.0 * @category filtering */ export const filter: { (refinement: Refinement): (self: SortedSet) => SortedSet (predicate: Predicate): (self: SortedSet) => SortedSet (self: SortedSet, refinement: Refinement): SortedSet (self: SortedSet, predicate: Predicate): SortedSet } = Dual.dual< { (refinement: Refinement): (self: SortedSet) => SortedSet (predicate: Predicate): (self: SortedSet) => SortedSet }, { (self: SortedSet, refinement: Refinement): SortedSet (self: SortedSet, predicate: Predicate): SortedSet } >(2, (self: SortedSet, predicate: Predicate) => { 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 1.0.0 * @category sequencing */ export const flatMap: { (O: Order, f: (a: A) => Iterable): (self: SortedSet) => SortedSet (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 1.0.0 * @category traversing */ export const forEach: { (f: (a: A) => void): (self: SortedSet) => void (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 1.0.0 * @category elements */ export const has: { (value: A): (self: SortedSet) => boolean (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 1.0.0 */ export const intersection: { (that: Iterable): (self: SortedSet) => SortedSet (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 1.0.0 * @category elements */ export const isSubset: { (that: SortedSet): (self: SortedSet) => boolean (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 1.0.0 * @category mapping */ export const map: { (O: Order, f: (a: A) => B): (self: SortedSet) => SortedSet (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 1.0.0 * @category filtering */ export const partition: { ( refinement: Refinement ): (self: SortedSet) => [SortedSet>, SortedSet] (predicate: (a: A) => boolean): (self: SortedSet) => [SortedSet, SortedSet] ( self: SortedSet, refinement: Refinement ): [SortedSet>, SortedSet] (self: SortedSet, predicate: (a: A) => boolean): [SortedSet, SortedSet] } = Dual.dual(2, (self: SortedSet, predicate: Predicate) => { 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] as const }) /** * @since 1.0.0 * @category elements */ export const remove: { (value: A): (self: SortedSet) => SortedSet (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 1.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 1.0.0 * @category elements */ export const some: { (predicate: Predicate): (self: SortedSet) => boolean (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 1.0.0 * @category elements */ export const toggle: { (value: A): (self: SortedSet) => SortedSet (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 1.0.0 */ export const union: { (that: Iterable): (self: SortedSet) => SortedSet (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 1.0.0 * @category getters */ export const values = (self: SortedSet): IterableIterator => RBT.keys(self.keyTree)