import * as Equal from "../Equal.js" import { dual } from "../Function.js" import * as Hash from "../Hash.js" import type { HashMap } from "../HashMap.js" import type * as HS from "../HashSet.js" import { format, NodeInspectSymbol, toJSON } from "../Inspectable.js" import { pipeArguments } from "../Pipeable.js" import type { Predicate, Refinement } from "../Predicate.js" import { hasProperty } from "../Predicate.js" import type { NoInfer } from "../Types.js" import * as HM from "./hashMap.js" const HashSetSymbolKey = "effect/HashSet" /** @internal */ export const HashSetTypeId: HS.TypeId = Symbol.for(HashSetSymbolKey) 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.cached( this, Hash.combine(Hash.hash(this._keyMap))(Hash.hash(HashSetSymbolKey)) ) }, [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 format(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 => hasProperty(u, HashSetTypeId) 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< (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< (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, B>): (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( 2, (self: HS.HashSet, refinement: Refinement): self is HS.HashSet => !some(self, (a) => !refinement(a)) ) /** @internal */ export const isSubset = 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< (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< (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< (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< (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< (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< (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< (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< (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< (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< (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< (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: { (refinement: Refinement, B>): (self: HS.HashSet) => HS.HashSet (predicate: Predicate>): (self: HS.HashSet) => HS.HashSet (self: HS.HashSet, refinement: Refinement): HS.HashSet (self: HS.HashSet, predicate: Predicate): HS.HashSet } = dual(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, B> ): (self: HS.HashSet) => [excluded: HS.HashSet>, satisfying: HS.HashSet] ( predicate: Predicate> ): (self: HS.HashSet) => [excluded: HS.HashSet, satisfying: HS.HashSet] ( self: HS.HashSet, refinement: Refinement ): [excluded: HS.HashSet>, satisfying: HS.HashSet] (self: HS.HashSet, predicate: Predicate): [excluded: HS.HashSet, satisfying: HS.HashSet] } = dual(2, (self: HS.HashSet, predicate: Predicate): [excluded: HS.HashSet, satisfying: HS.HashSet] => { 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 (predicate(value)) { add(right, value) } else { add(left, value) } } return [endMutation(left), endMutation(right)] })