/**
* @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)