import * as Equal from "@effect/data/Equal" import * as Dual from "@effect/data/Function" import * as Hash from "@effect/data/Hash" import type { HashMap } from "@effect/data/HashMap" import type * as HS from "@effect/data/HashSet" import { NodeInspectSymbol, toJSON, toString } from "@effect/data/Inspectable" import * as HM from "@effect/data/internal/HashMap" import { pipeArguments } from "@effect/data/Pipeable" import type { Predicate, Refinement } from "@effect/data/Predicate" import { isObject } from "@effect/data/Predicate" /** @internal */ export const HashSetTypeId: HS.TypeId = Symbol.for("@effect/data/HashSet") as HS.TypeId /** @internal */ export interface HashSetImpl extends HS.HashSet { readonly _keyMap: HashMap } const HashSetProto: Omit, "_keyMap"> = { [HashSetTypeId]: HashSetTypeId, [Symbol.iterator](this: HashSetImpl): Iterator { return HM.keys(this._keyMap) }, [Hash.symbol](this: HashSetImpl): number { return Hash.combine(Hash.hash(this._keyMap))(Hash.hash("HashSet")) }, [Equal.symbol](this: HashSetImpl, that: unknown): boolean { if (isHashSet(that)) { return ( HM.size(this._keyMap) === HM.size((that as HashSetImpl)._keyMap) && Equal.equals(this._keyMap, (that as HashSetImpl)._keyMap) ) } return false }, toString() { return toString(this.toJSON()) }, toJSON() { return { _id: "HashSet", values: Array.from(this).map(toJSON) } }, [NodeInspectSymbol]() { return this.toJSON() }, pipe() { return pipeArguments(this, arguments) } } /** @internal */ export const makeImpl = (keyMap: HashMap): HashSetImpl => { const set = Object.create(HashSetProto) set._keyMap = keyMap return set } /** @internal */ export const isHashSet: { (u: Iterable): u is HS.HashSet (u: unknown): u is HS.HashSet } = (u: unknown): u is HS.HashSet => isObject(u) && HashSetTypeId in u const _empty = makeImpl(HM.empty()) /** @internal */ export const empty = (): HS.HashSet => _empty /** @internal */ export const fromIterable = (elements: Iterable): HS.HashSet => { const set = beginMutation(empty()) for (const value of elements) { add(set, value) } return endMutation(set) } /** @internal */ export const make = >(...elements: As): HS.HashSet => { const set = beginMutation(empty()) for (const value of elements) { add(set, value) } return endMutation(set) } /** @internal */ export const has = Dual.dual< (value: A) => (self: HS.HashSet) => boolean, (self: HS.HashSet, value: A) => boolean >(2, (self: HS.HashSet, value: A) => HM.has((self as HashSetImpl)._keyMap, value)) /** @internal */ export const some = Dual.dual< (f: Predicate) => (self: HS.HashSet) => boolean, (self: HS.HashSet, f: Predicate) => boolean >(2, (self, f) => { let found = false for (const value of self) { found = f(value) if (found) { break } } return found }) /** @internal */ export const every: { (refinement: Refinement): (self: HS.HashSet) => self is HS.HashSet (predicate: Predicate): (self: HS.HashSet) => boolean (self: HS.HashSet, refinement: Refinement): self is HS.HashSet (self: HS.HashSet, predicate: Predicate): boolean } = Dual.dual( 2, (self: HS.HashSet, refinement: Refinement): self is HS.HashSet => !some(self, (a) => !refinement(a)) ) /** @internal */ export const isSubset = Dual.dual< (that: HS.HashSet) => (self: HS.HashSet) => boolean, (self: HS.HashSet, that: HS.HashSet) => boolean >(2, (self, that) => every(self, (value) => has(that, value))) /** @internal */ export const values = (self: HS.HashSet): IterableIterator => HM.keys((self as HashSetImpl)._keyMap) /** @internal */ export const size = (self: HS.HashSet): number => HM.size((self as HashSetImpl)._keyMap) /** @internal */ export const beginMutation = (self: HS.HashSet): HS.HashSet => makeImpl(HM.beginMutation((self as HashSetImpl)._keyMap)) /** @internal */ export const endMutation = (self: HS.HashSet): HS.HashSet => { ;((self as HashSetImpl)._keyMap as HM.HashMapImpl)._editable = false return self } /** @internal */ export const mutate = Dual.dual< (f: (set: HS.HashSet) => void) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, f: (set: HS.HashSet) => void) => HS.HashSet >(2, (self, f) => { const transient = beginMutation(self) f(transient) return endMutation(transient) }) /** @internal */ export const add = Dual.dual< (value: A) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, value: A) => HS.HashSet >( 2, (self: HS.HashSet, value: A) => ((self as HashSetImpl)._keyMap as HM.HashMapImpl)._editable ? (HM.set(value as A, true as unknown)((self as HashSetImpl)._keyMap), self) : makeImpl(HM.set(value as A, true as unknown)((self as HashSetImpl)._keyMap)) ) /** @internal */ export const remove = Dual.dual< (value: A) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, value: A) => HS.HashSet >( 2, (self: HS.HashSet, value: A) => (((self as HashSetImpl)._keyMap) as HM.HashMapImpl)._editable ? (HM.remove(value)((self as HashSetImpl)._keyMap), self) : makeImpl(HM.remove(value)((self as HashSetImpl)._keyMap)) ) /** @internal */ export const difference = Dual.dual< (that: Iterable) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, that: Iterable) => HS.HashSet >(2, (self, that) => mutate(self, (set) => { for (const value of that) { remove(set, value) } })) /** @internal */ export const intersection = Dual.dual< (that: Iterable) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, that: Iterable) => HS.HashSet >(2, (self, that) => mutate(empty(), (set) => { for (const value of that) { if (has(value)(self)) { add(value)(set) } } })) /** @internal */ export const union = Dual.dual< (that: Iterable) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, that: Iterable) => HS.HashSet >(2, (self, that) => mutate(empty(), (set) => { forEach(self, (value) => add(set, value)) for (const value of that) { add(set, value) } })) /** @internal */ export const toggle = Dual.dual< (value: A) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, value: A) => HS.HashSet >(2, (self, value) => has(self, value) ? remove(self, value) : add(self, value)) /** @internal */ export const map = Dual.dual< (f: (a: A) => B) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, f: (a: A) => B) => HS.HashSet >(2, (self, f) => mutate(empty(), (set) => { forEach(self, (a) => { const b = f(a) if (!has(set, b)) { add(set, b) } }) })) /** @internal */ export const flatMap = Dual.dual< (f: (a: A) => Iterable) => (self: HS.HashSet) => HS.HashSet, (self: HS.HashSet, f: (a: A) => Iterable) => HS.HashSet >(2, (self, f) => mutate(empty(), (set) => { forEach(self, (a) => { for (const b of f(a)) { if (!has(set, b)) { add(set, b) } } }) })) /** @internal */ export const forEach = Dual.dual< (f: (value: A) => void) => (self: HS.HashSet) => void, (self: HS.HashSet, f: (value: A) => void) => void >(2, (self: HS.HashSet, f: (value: A) => void) => HM.forEach( (self as HashSetImpl)._keyMap, (_, k) => f(k) )) /** @internal */ export const reduce = Dual.dual< (zero: Z, f: (accumulator: Z, value: A) => Z) => (self: HS.HashSet) => Z, (self: HS.HashSet, zero: Z, f: (accumulator: Z, value: A) => Z) => Z >(3, (self: HS.HashSet, zero: Z, f: (accumulator: Z, value: A) => Z) => HM.reduce( (self as HashSetImpl)._keyMap, zero, (z, _, a) => f(z, a) )) /** @internal */ export const filter = Dual.dual< { (f: Refinement): (self: HS.HashSet) => HS.HashSet (f: Predicate): (self: HS.HashSet) => HS.HashSet }, { (self: HS.HashSet, f: Refinement): HS.HashSet (self: HS.HashSet, f: Predicate): HS.HashSet } >(2, (self: HS.HashSet, f: Predicate) => { return mutate(empty(), (set) => { const iterator = values(self) let next: IteratorResult while (!(next = iterator.next()).done) { const value = next.value if (f(value)) { add(set, value) } } }) }) /** @internal */ export const partition: { ( refinement: Refinement ): (self: HS.HashSet) => [HS.HashSet>, HS.HashSet] (predicate: (a: A) => boolean): (self: HS.HashSet) => [HS.HashSet, HS.HashSet] ( self: HS.HashSet, refinement: Refinement ): [HS.HashSet>, HS.HashSet] (self: HS.HashSet, predicate: (a: A) => boolean): [HS.HashSet, HS.HashSet] } = Dual.dual(2, (self: HS.HashSet, f: Predicate) => { const iterator = values(self) let next: IteratorResult const right = beginMutation(empty()) const left = beginMutation(empty()) while (!(next = iterator.next()).done) { const value = next.value if (f(value)) { add(right, value) } else { add(left, value) } } return [endMutation(left), endMutation(right)] as const })