// ets_tracing: off import "../Operator/index.js" import { Stack } from "../Stack/index.js" import * as St from "../Structural/index.js" const _brand = Symbol() export function isFreeAssociative(self: unknown): self is FreeAssociative { return typeof self === "object" && self != null && _brand in self } export class IEmpty { readonly _tag = "Empty"; readonly [_brand] = _brand get [St.hashSym](): number { return St.hash(toArray(this)) } [St.equalsSym](that: unknown): boolean { return isFreeAssociative(that) && St.equals(toArray(this), toArray(that)) } } export class IElement { readonly _tag = "Element"; readonly [_brand] = _brand constructor(readonly element: A) {} get [St.hashSym](): number { return St.hash(toArray(this)) } [St.equalsSym](that: unknown): boolean { return isFreeAssociative(that) && St.equals(toArray(this), toArray(that)) } } export class IConcat { readonly _tag = "Concat"; readonly [_brand] = _brand constructor(readonly left: FreeAssociative, readonly right: FreeAssociative) {} } export type FreeAssociative = IEmpty | IElement | IConcat export function init(): FreeAssociative { return new IEmpty() } export function of(a: A): FreeAssociative { return new IElement(a) } export function concat( r: FreeAssociative ): (l: FreeAssociative) => FreeAssociative { return (l) => new IConcat(l, r) } export function concat_( l: FreeAssociative, r: FreeAssociative ): FreeAssociative { return new IConcat(l, r) } export function append(a: A): (_: FreeAssociative) => FreeAssociative { return (_) => new IConcat(_, new IElement(a)) } export function append_(_: FreeAssociative, a: A): FreeAssociative { return new IConcat(_, new IElement(a)) } export function prepend(a: A): (_: FreeAssociative) => FreeAssociative { return (_) => new IConcat(new IElement(a), _) } export function prepend_(_: FreeAssociative, a: A): FreeAssociative { return new IConcat(new IElement(a), _) } export function toArray(_: FreeAssociative): readonly A[] { const as = [] let current: FreeAssociative | undefined = _ let stack: Stack> | undefined = undefined while (typeof current !== "undefined") { switch (current._tag) { case "Empty": { current = undefined break } case "Element": { as.push(current.element) current = undefined break } case "Concat": { const p: any = stack stack = new Stack(current.right, p) current = current.left break } } if (typeof current === "undefined") { if (typeof stack !== "undefined") { current = stack.value stack = stack.previous } } } return as }