From 6585bbd9dd21005cfc395d2f3104aec1211e5203 Mon Sep 17 00:00:00 2001 From: Fadi Shawki Date: Mon, 29 Jan 2024 18:25:08 +0100 Subject: [PATCH] 2024/01/29 - Move some old Ray stuff into OnOrbits.tsx to make it independent of the refactor for now --- _temp/OrbitMinesExplorer.tsx | 433 ---- .../src/routes/papers/2023.OnOrbits.tsx | 2028 ++++++++++++++++- 2 files changed, 2023 insertions(+), 438 deletions(-) diff --git a/_temp/OrbitMinesExplorer.tsx b/_temp/OrbitMinesExplorer.tsx index 5e36535..68f5ae3 100644 --- a/_temp/OrbitMinesExplorer.tsx +++ b/_temp/OrbitMinesExplorer.tsx @@ -15,64 +15,8 @@ export const add_ = (...a: number[][]): [number, number, number] => { } return res as [number, number, number]; } -export const add = (a: number[], b: number[]): [number, number, number] => [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; export const sub = (a: number[], b: number[]): [number, number, number] => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; -export const torus = { - // Radius of the torus, from the center of the torus to the center of the tube. Default is 1. - radius: 3, color: "orange", segments: 200, tube: { width: 1, segments: 200 }, -} -export const Continuation = ( - { - color = torus.color, - radius = torus.radius, - arc = Math.PI * 2, - position - }: any) => - - -export const Loop = ( - { color = "#FFFF55", - on = "orange", - position = [0, 0, 0], - initial = position, - terminal = add(position, [0, 30, 0]), - scale = 1.5, - radius = 15, - segments = 200 - }: any -) => { - // const geometry = new TorusGeometry(radius, torus.tube.width, torus.segments, torus.tube.segments, Math.PI * 4); - // - // const vertices = geometry.getAttribute('position').array; - // const points: any = []; - // for (let i = 0; i < vertices.length; i += 3) { - // points.push([vertices[i], vertices[i + 1], vertices[i + 2]]); - // } - const points: [number, number, number][] = []; - for (let i = 0; i < segments; i++) { - const angle = ((i / segments) * Math.PI * 2) + Math.PI / 2; // STARTS AT THE TOP - const x = radius * Math.cos(angle); - const y = radius * Math.sin(angle); - - if (i > 5 && i < segments - 6) - points.push([x, y, 0]) - } - console.log(points) - - const vertex = add(position, [0, -radius, 0]); - const continuation = add(position, [0, radius, 0]); - - return - - - - -} export const Curve = ( { color = "#FFFF55", position = [0, 0, 0], @@ -99,20 +43,6 @@ export const Curve = ( } -const line = { width: 2, length: 1, color: "orange", } -export const Line = ({ start, mid, end, scale, color = line.color }: any) => - - -export const circle = { radius: 3, color: "orange", segments: 30, } -export const Vertex = ({ position, color = circle.color }: any) => - - const BinaryValue = ({ boolean, position }: any) => { if (boolean) return @@ -144,58 +74,6 @@ const BinaryValue = ({ boolean, position }: any) => { } -export const BinarySuperposition = ({ position = [0, 0, 0], scale = 1.5 }: any) => { - const halfTorus = (torus.radius + (torus.tube.width / 2)); - - const up = add(position, [0, 20 + halfTorus, 0]); - const down = add(position, [0, -20, 0]); - - const left = add(down, [-halfTorus, 0, 0]); - const right = add(down, [+halfTorus, 0, 0]); - - return <> - {/**/} - - - - - - - - - - - -} - /** * Temporary Ray visualization till the visualization is incorporated into the editor (Basically when Visualization = Ray) * @@ -391,317 +269,6 @@ export const SimpleRenderedRay = ( } } -// In principle, this should be anything, this is just for the initial setup -export const RenderedRay = ( - props: { reference: Ray.Any } & { position?: [number, number, number], initial?: [number, number, number], terminal?: [number, number, number], scale?: number, color?: string } -) => { - const { - position = [0, 0, 0], - reference, - scale = 1, - color = 'orange', - initial = add(position, [-20, 0, 0]), - terminal = add(position, [20, 0, 0]), - } = props; - const left = initial; - const right = terminal; - - if (reference.is_none() || reference.is_none()) - return <> - - const vertex = reference.self; - - const Rendered = () => { - - - const type = vertex.as_reference().type; - switch (type) { - case RayType.INITIAL: { - /** - * [ |--] - */ - if (vertex.vertex.is_none()) { - return - } else { - const possible_continuations = vertex.vertex; - - if (!possible_continuations.as_reference().is_terminal()) - return - // - // if (vertex.terminal.store.rendered) - // return <> - - return - } - } - case RayType.TERMINAL: { - if (vertex.vertex.is_none()) { - return - } else { - const possible_continuations = vertex.vertex; - - // if (!possible_continuations.as_reference().is_initial()) - // return - return <> - - // return - } - } - case RayType.REFERENCE: { - if (vertex.as_reference().is_none()) // empty reference - return - - // throw 'Not Implemented' - return - } - case RayType.VERTEX: { - const tilt = -10; // TODO; Generally should use some equivalencing in the 3d-frames for this once setup is in place (perpsective/camera) if in threejs.. - // const left = add(position, [-80 + tilt, 0, 0]); - // const right = add(position, [80 - tilt, 0, 0]); - - // const initial_side = { - // continuation: add(left, [tilt, 0, 0]), - // initial: add(left, [30 + tilt, 15, 0]), - // terminal: add(left, [30 - tilt, -15, 0]), - // - // initial_continuation: add(left, [30 + tilt * 2, 15 + 15, 0]), - // terminal_continuation: add(left, [30 - tilt * 2, -15 - 15, 0]), - // } - // const terminal_side = { - // continuation: add(right, [-tilt, 0, 0]), - // initial: add(right, [-30 + tilt, 15, 0]), - // terminal: add(right, [-30 - tilt, -15, 0]), - // - // initial_continuation: add(right, [-30 + tilt * 2, 15 + 15, 0]), - // terminal_continuation: add(right, [-30 - tilt * 2, -15 - 15, 0]), - // } - // - // const Sup = ({ position }: any) => { - // - // const left = add(position, [-20, 0, 0]); - // const right = add(position, [20, 0, 0]); - // - // return <> - // - // - // {/* Line now starts in the center of the torus tube */} - // - // {/**/} - // - // - // - // - // } - - // 55FF55 - // return <> - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // <> - // - // - // - // - // - // - // - // {/* 1C2127 quick for background */} - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // <> - // - // - // - // - // - // - // - // {/* 1C2127 quick for background */} - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - - // const left = add(position, [-20, 0, 0]); - // const right = add(position, [20, 0, 0]); - - const isVertical = position[1] !== left[1]; - - return <> - {/**/} - - {/**/} - {/**/} - {/**/} - {/**/} - - {/**/} - {/**/} - {/**/} - {/**/} - - {/**/} - - - {/* Line now starts in the center of the torus tube */} - {/*{isVertical*/} - {/* ? */} - : - {/*}*/} - - - - {/**/} - - {/**/} - - - {/*{_.sample([true, false])*/} - {/* ? */} - {/* : */} - {/*}*/} - - - - - {/*{<>*/} - {/* */} - - {/* /!* Line now starts in the center of the torus tube *!/*/} - {/* */} - - {/* /!**!/*/} - {/* /!**!/*/} - {/* */} - - {/* {_.sample([true, false])*/} - {/* ? */} - {/* : */} - {/* }*/} - - {/* */} - - {/*}*/} - - - - {/**/} - {/**/} - {/* SHOULD BE SMOOTH */} - {/**/} - - - } - } - } - - const render = - vertex.any.rendered = render; - return render; -} - diff --git a/orbitmines.com/src/routes/papers/2023.OnOrbits.tsx b/orbitmines.com/src/routes/papers/2023.OnOrbits.tsx index 02752ac..a920605 100644 --- a/orbitmines.com/src/routes/papers/2023.OnOrbits.tsx +++ b/orbitmines.com/src/routes/papers/2023.OnOrbits.tsx @@ -13,13 +13,12 @@ import Arc from "../../lib/paper/layout/Arc"; import Link from "../../lib/paper/layout/Link"; import {renderable} from "../../lib/typescript/React"; import {Divider, Intent, Tag} from "@blueprintjs/core"; -import {Center} from "@react-three/drei"; +import {CatmullRomLine, Center, Circle, QuadraticBezierLine, Torus} from "@react-three/drei"; import {Block} from "../../lib/syntax-highlighting/CodeBlock"; import {HorizontalLine} from "../../lib/paper/PaperContent"; -import CustomIcon from "../../lib/layout/icons/CustomIcon"; import REFERENCES from "../../profiles/FadiShawki/FadiShawki"; -import { CachedVisualizationCanvas } from '../../@orbitmines/Visualization'; -import Ray from '@orbitmines/rays'; +import {CachedVisualizationCanvas} from '../../@orbitmines/Visualization'; +import _ from 'lodash'; export const ON_ORBITS: Content = { reference: { @@ -2123,4 +2122,2023 @@ const OnOrbits = () => { ; } -export default OnOrbits; \ No newline at end of file +export default OnOrbits; + +const Vertex = ({ position, color = circle.color }: any) => + +// In principle, this should be anything, this is just for the initial setup +const RenderedRay = ( + props: { reference: Ray } & { position?: [number, number, number], initial?: [number, number, number], terminal?: [number, number, number], scale?: number, color?: string } +) => { + const { + position = [0, 0, 0], + reference, + scale = 1, + color = 'orange', + initial = add(position, [-20, 0, 0]), + terminal = add(position, [20, 0, 0]), + } = props; + const left = initial; + const right = terminal; + + if (reference.is_none() || reference.is_none()) + return <> + + const vertex = reference.self; + + const Rendered = () => { + + + const type = vertex.as_reference().type; + switch (type) { + case RayType.INITIAL: { + /** + * [ |--] + */ + if (vertex.vertex.is_none()) { + return + } else { + const possible_continuations = vertex.vertex; + + if (!possible_continuations.as_reference().is_terminal()) + return + // + // if (vertex.terminal.store.rendered) + // return <> + + return + } + } + case RayType.TERMINAL: { + if (vertex.vertex.is_none()) { + return + } else { + const possible_continuations = vertex.vertex; + + // if (!possible_continuations.as_reference().is_initial()) + // return + return <> + + // return + } + } + case RayType.REFERENCE: { + if (vertex.as_reference().is_none()) // empty reference + return + + // throw 'Not Implemented' + return + } + case RayType.VERTEX: { + const tilt = -10; // TODO; Generally should use some equivalencing in the 3d-frames for this once setup is in place (perpsective/camera) if in threejs.. + // const left = add(position, [-80 + tilt, 0, 0]); + // const right = add(position, [80 - tilt, 0, 0]); + + // const initial_side = { + // continuation: add(left, [tilt, 0, 0]), + // initial: add(left, [30 + tilt, 15, 0]), + // terminal: add(left, [30 - tilt, -15, 0]), + // + // initial_continuation: add(left, [30 + tilt * 2, 15 + 15, 0]), + // terminal_continuation: add(left, [30 - tilt * 2, -15 - 15, 0]), + // } + // const terminal_side = { + // continuation: add(right, [-tilt, 0, 0]), + // initial: add(right, [-30 + tilt, 15, 0]), + // terminal: add(right, [-30 - tilt, -15, 0]), + // + // initial_continuation: add(right, [-30 + tilt * 2, 15 + 15, 0]), + // terminal_continuation: add(right, [-30 - tilt * 2, -15 - 15, 0]), + // } + // + // const Sup = ({ position }: any) => { + // + // const left = add(position, [-20, 0, 0]); + // const right = add(position, [20, 0, 0]); + // + // return <> + // + // + // {/* Line now starts in the center of the torus tube */} + // + // {/**/} + // + // + // + // + // } + + // 55FF55 + // return <> + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // <> + // + // + // + // + // + // + // + // {/* 1C2127 quick for background */} + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // <> + // + // + // + // + // + // + // + // {/* 1C2127 quick for background */} + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + + // const left = add(position, [-20, 0, 0]); + // const right = add(position, [20, 0, 0]); + + const isVertical = position[1] !== left[1]; + + return <> + {/**/} + + {/**/} + {/**/} + {/**/} + {/**/} + + {/**/} + {/**/} + {/**/} + {/**/} + + {/**/} + + + {/* Line now starts in the center of the torus tube */} + {/*{isVertical*/} + {/* ? */} + : + {/*}*/} + + + + {/**/} + + {/**/} + + + {/*{_.sample([true, false])*/} + {/* ? */} + {/* : */} + {/*}*/} + + + + + {/*{<>*/} + {/* */} + + {/* /!* Line now starts in the center of the torus tube *!/*/} + {/* */} + + {/* /!**!/*/} + {/* /!**!/*/} + {/* */} + + {/* {_.sample([true, false])*/} + {/* ? */} + {/* : */} + {/* }*/} + + {/* */} + + {/*}*/} + + + + {/**/} + {/**/} + {/* SHOULD BE SMOOTH */} + {/**/} + + + } + } + } + + const render = + vertex.any.rendered = render; + return render; +} + +const BinarySuperposition = ({ position = [0, 0, 0], scale = 1.5 }: any) => { + const halfTorus = (torus.radius + (torus.tube.width / 2)); + + const up = add(position, [0, 20 + halfTorus, 0]); + const down = add(position, [0, -20, 0]); + + const left = add(down, [-halfTorus, 0, 0]); + const right = add(down, [+halfTorus, 0, 0]); + + return <> + {/**/} + + + + + + + + + + + +} + +const Line = ({ start, mid, end, scale, color = line.color }: any) => + + +const line = { width: 2, length: 1, color: "orange", } + +const Continuation = ( + { + color = torus.color, + radius = torus.radius, + arc = Math.PI * 2, + position + }: any) => + + +const Loop = ( + { color = "#FFFF55", + on = "orange", + position = [0, 0, 0], + initial = position, + terminal = add(position, [0, 30, 0]), + scale = 1.5, + radius = 15, + segments = 200 + }: any +) => { + // const geometry = new TorusGeometry(radius, torus.tube.width, torus.segments, torus.tube.segments, Math.PI * 4); + // + // const vertices = geometry.getAttribute('position').array; + // const points: any = []; + // for (let i = 0; i < vertices.length; i += 3) { + // points.push([vertices[i], vertices[i + 1], vertices[i + 2]]); + // } + const points: [number, number, number][] = []; + for (let i = 0; i < segments; i++) { + const angle = ((i / segments) * Math.PI * 2) + Math.PI / 2; // STARTS AT THE TOP + const x = radius * Math.cos(angle); + const y = radius * Math.sin(angle); + + if (i > 5 && i < segments - 6) + points.push([x, y, 0]) + } + console.log(points) + + const vertex = add(position, [0, -radius, 0]); + const continuation = add(position, [0, radius, 0]); + + return + + + + +} + + +/** + * Ray.ts (2024/01/18) + */ +// TODO: SHOULDNT CLASSIFY THESE? (And incorporate in Ray??) +enum RayType { + // NONE = ' ', + REFERENCE = 'REFERENCE: | ', + INITIAL = 'INITIAL: |-?', + TERMINAL = 'TERMINAL: ?-| ', + VERTEX = 'VERTEX: --|--', +} +type Boundary = RayType.INITIAL | RayType.TERMINAL; +const opposite = (boundary: Boundary): Boundary => boundary === RayType.INITIAL ? RayType.TERMINAL : RayType.INITIAL; + +type ParameterlessFunction = () => T; +type Arbitrary = (...args: any[]) => T; +type Constructor = new (...args: any[]) => T; +type ParameterlessConstructor = new () => T; + +// TODO: Merge with Arbitrary. +type Recursive = (T | Recursive)[]; +type Method = (...other: Recursive) => T; +type ArbitraryMethod = (ref: T) => Method; + +type SwitchCases< + T = Ray, + SwitchCase extends string | symbol | number = RayType, + TResult = string | ((self: T) => T) +> = { + [TCase in SwitchCase]?: TResult +} + +type Implementation = (ref: T) => T; + +/** + * https://en.wikipedia.org/wiki/Homoiconicity + */ +interface PossiblyHomoiconic> { + get self(): T; + is_reference: () => boolean + as_reference: () => T +} + +interface AbstractDirectionality { initial: Arbitrary, vertex: Arbitrary, terminal: Arbitrary } + +// TODO: better debug +type DebugResult = { [label: string]: DebugRay } +type DebugRay = { + label: string, + initial: string, + vertex: string, + terminal: string, + is_initial: boolean, + is_vertex: boolean, + is_terminal: boolean, + type: RayType, + _dirty_store: any +} + +/** + * JavaScript wrapper for a mutable value. It is important to realize that this is merely some simple JavaScript abstraction, and anything is assumed to be inherently mutable. + * + * All the methods defined here should be considered deprecated, are there to help with JavaScript implementation only. + * + * TODO: + * - Homotopy equivalence merely as some direction/reversibility constraint on some direction, ignoring additional structure (or incorporating it into the equiv) at the vertices. (Could be loosened where certain vertex-equivalences are also part of the homotopy) + * - Induced ignorance/equivalence along arbitrary rays. + * - Usual way of thinking about vertices is what the coninuations are here - phrase that somewhjere + * + * TODO: Any javascript class, allow warpper of function names around any ray, as a possible match + * TODO: All the methods defined here should be implemented in some Ray structure at some point + * + * TODO: Maybe want a way to destroy from one end, so that if other references try to look, they won't find additional structure. - More as a javascript implementation quirck if anything? + * + * TODO: Can do some workaround overloading through properties, at least for +/- + * + * TODO: Singlke keybind for now to show/hide the ray disambiguation or 'dead edges/..'/ + * + * + * + * TODO: All methods to 'step' variant - and an intuitive way to switch between modes + * - Through better Ray.___func + * - Transform all functions on Ray to that. (Perhaps use JavaScript generators by default (more intuitively?) - Just convert using JS.Generator) + * - No assumption of halting + * - Perhaps locally cache (for stuff like count?) - no way to ensure globally coherence + * + * TODO: Stylistic + * - Consistency of Arbitrary vs non-arbitrary. + * - Reorder methods in a sensible way. + * + */ +class Ray // Other possibly names: AbstractDirectionality, ..., ?? + implements + PossiblyHomoiconic, + + AsyncIterable, + Iterable + // Array + // Dict +{ + // TODO: Could make a case that setting the terminal is more of a map, defaulting/first checking the terminal before additional functionality is mapped over that. + + protected _initial: Arbitrary; get initial(): Ray { return this._initial(); } set initial(initial: Arbitrary) { this._initial = initial; } + protected _vertex: Arbitrary; get vertex(): Ray { return this._vertex(); } set vertex(vertex: Arbitrary) { this._vertex = vertex; } + protected _terminal: Arbitrary; get terminal(): Ray { return this._terminal(); } set terminal(terminal: Arbitrary) { this._terminal = terminal; } + + get self(): Ray { return this.vertex; }; set self(self: Arbitrary) { this.vertex = self; } + + constructor({ initial, vertex, terminal, }: Partial> = {}) { + this._initial = initial ?? Ray.None; + this._vertex = vertex ?? this.self_reference; // TODO: None, could also self-reference the ray on which it's defining to be None. Now it's just an ignorant loop. + this._terminal = terminal ?? Ray.None; + } + + /** [ |-?] */ is_initial = (): boolean => this.is_some() && this.self.initial.is_none(); + /** [--|--] */ is_vertex = (): boolean => !this.is_initial() && !this.is_terminal(); + /** [?-| ] */ is_terminal = (): boolean => this.is_some() && this.self.terminal.is_none(); + /** [ | ] */ is_reference = (): boolean => this.is_initial() && this.is_terminal(); + /** [?-| ] or [ |-?] */ is_boundary = (): boolean => !this.is_reference() && (this.is_initial() || this.is_terminal()); // TODO: IS !This.references necessary? + + get type(): RayType { + /** [ | ] */ if (this.is_reference()) return RayType.REFERENCE; + /** [ |-?] */ if (this.is_initial()) return RayType.INITIAL; + /** [?-| ] */ if (this.is_terminal()) return RayType.TERMINAL; + // /** [ ] */ if (this.is_empty()) return RayType.NONE; + /** [--|--] */ return RayType.VERTEX; + } + + /** + * This is basically what breaks the recursive structure. Imagine a Ray like this: [|--|--|]. There are several ways of interpreting it, either there's a boolean on initial, vertex, terminal; Some 'false' value, says there's nothing there. Some true value says there's something there. - Basically an Option, ..., Maybe as in certain languages. + * + * --- + * + * Another way of interpreting a possible way of implementing it, is no matter how much more detail we would like to ask, the only thing we ever see is the same structure again (if we ignore the difference of us asking about that additional structure, that's still a possible handle on some difference). + * + * As a way of saying/.../assuming: I only 'infinitely' assume it's only this structure, "it seems to halt here". Note that this is necessarily an assumption. No guarantee of this can be made. This is necessarily an equivalence, ..., ignorance. + * + * See more: https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Quite%20similarly%20to%20the%20loops%2C%20I%20could%20be%20ignorant%20of%20additional%20structure%20by%20assuming%20it%27s%20not%20there. + * + * --- + * + * Concretely, we use here "whatever the JavaScript engine run on" as the thing which has power over the equivalence assumption we use to halt programs. - The asymmetry which allows the engine to make a distinction between each object. + */ + is_none = (): boolean => Ray.is_orbit(this.self, this.self.self); + + /** + * Tries for "global coherence" - since we probably can't actually do that, practically this just means self-reference, were no change is assumed... + * + * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=And%20there%20we%20have%20it%2C%20an%20infinity%2C%20loop%2C%20...%2C%20orbit%20if%20we%20ignore%20the%20difference. + */ + static is_orbit = (a: Ray, b: Ray) => a === b; // is, ..., appears equal. + protected self_reference = () => this; + + is_some = (): boolean => !this.is_none(); + + /** + * Can be used to override default dereference behavior. + * + * TODO: This should probably be configurable on a more global setting. + * + * TODO: Difference between this.self and this.self.self.as_reference is??? + */ + get dereference() { return this.self.self.as_reference(); } + + // /** + // * Moves `this.self` and `this.self.self` to a new line. + // * + // * [ |--] this.self ----- this.self.self [--|--] + // * ______ (<- initial pointer) + // */ + // as_initial = (): Ray => { + // if (this.is_none()) { + // throw new PreventsImplementationBug('Should be implemented at some point ; Just return an empty vertex'); + // } + // if (this.dereference.is_none()) { + // // TODO: Need some intuition for this check + // const vertex = this.___as_vertex(); + // + // if (vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // return vertex.follow(Ray.directions.previous); + // } + // + // const [terminal_vertex, initial_vertex] = this.___as_vertices(); + // + // if (initial_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // if (terminal_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // initial_vertex.compose(terminal_vertex); + // + // // TODO BETTER DEBUG + // + // return initial_vertex.follow(Ray.directions.previous); + // } + // /** + // * Moves `this.self` and `this.self.self` to a new line. + // * + // * [ |--] this.self.self ----- this.self [--|--] + // * _____ (<- terminal pointer) + // */ + // as_terminal = (): Ray => { + // if (this.is_none()) { + // throw new PreventsImplementationBug('Should be implemented at some point ; Just return an empty vertex'); + // } + // if (this.dereference.is_none()) { + // // TODO: Need some intuition for this check + // const vertex = this.___as_vertex(); + // + // if (vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // return vertex.follow(); + // } + // + // const [initial_vertex, terminal_vertex] = this.___as_vertices(); + // + // if (initial_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // if (terminal_vertex.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(); + // + // initial_vertex.compose(terminal_vertex); + // + // // TODO BETTER DEBUG + // + // return terminal_vertex.follow(); + // } + // private ___as_vertices = (): [Ray, Ray] => { + // if (!Ray.is_orbit(this.self, this.self.self.self)) + // throw new PreventsImplementationBug('Is there a use-case for this? Probably not?'); //TODO + // + // // TODO NOTE: THE ORDER OF `this.self` first matters here. + // return [this.self.___as_vertex(), this.___as_vertex()]; + // } + // private ___as_vertex = (): Ray => { + // const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + // + // // this.self.self = vertex.self.as_arbitrary(); + // // vertex.self.self = this.self.as_arbitrary(); + // + // // return this.___ignorantly_equivalent(Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' })); + // + // return this.___ignorantly_equivalent(vertex); + // } + private ___ignorantly_equivalent = (ref: Ray): Ray => { + this.self.self = ref.self.as_arbitrary(); + ref.self.self = this.self.as_arbitrary(); + + return ref; + } + + /** [ ] */ static None = () => new Ray({ }).o({ }); + /** [--?--] */ static vertex = (value: Arbitrary = Ray.None) => { + /** [ ] */ const vertex = Ray.None(); + /** [-- ] */ vertex.initial = vertex.___empty_initial(); + /** [ ? ] */ vertex.vertex = value; + /** [ --] */ vertex.terminal = vertex.___empty_terminal(); + + /** [--?--] */ return vertex; + } + /** [ |-?] */ static initial = () => Ray.vertex().initial; + /** [?-| ] */ static terminal = () => Ray.vertex().terminal; + + // TODO; Temp placeholders for now - & BETTER DEBUG + ___empty_initial = () => new Ray({ vertex: Ray.None, terminal: this.as_arbitrary() }).o({ debug: 'initial ref'}).as_arbitrary(); + ___empty_terminal = () => new Ray({ vertex: Ray.None, initial: this.as_arbitrary() }).o({ debug: 'terminal ref'}).as_arbitrary(); + + /** A ray whose vertex references this Ray (ignorantly - 'this' doesn't know about it). **/ + /** [?????] -> [ | ] */ as_reference = (): Ray => new Ray({ vertex: this.as_arbitrary() }); + + // TODO: Difference between () => this & this.as_arbitrary , relevant for lazy/modular/ignorant structures etc.. + as_arbitrary = (): Arbitrary => () => this; + + /** + * TODO : COMPOSE EMPTY AS FIRST ELEMENT: + * if (initial.is_none()) { + * // 'Empty' vertex from this perspective. + * + * initial.vertex = terminal.as_arbitrary(); + * console.log('first element'); + * return terminal; + * } + */ + + // TODO: Test if references hold after equivalence/composition... + + + // TODO: Returns the ref, since it still holds the information on how they're not the same??? - Need some intuitive way of doing this? + // TODO a.equivalent(b).equivalent(c), in this case would be [[a, b]].equivalent(c) not [a, b, c].equivalent ??? + + // TODO: Should do, one timesteap ahead, collapse one reference, and then recursively call continues_with on the vlaue at the reference, until it yields something. + + // TODO AS += through property + // TODO: Generally, return something which knows where all continuations are. + // @alias('merge, 'continues_with', 'compose') + /** + * Compose as "Equivalence at Continuations": (can usually be done in parallel - not generally) + * - `A.compose(B)` = `(A.TERMINAL).equivalent(B.INITIAL)` + * - `A.compose(B).compose(C)` = `(A.TERMINAL).equivalent(B.INITIAL) & (B.TERMINAL).equivalent(C.INITIAL)` + * + * Another interesting connection: + * - `A.compose(B).compose(C)` = `(A.equivalent(B).equivalent(C)).dereference.(MISSING ALL FUNC).compose` + * + * @see "Continuations as Equivalence": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Constructing%20Continuations%20%2D%20Continuations%20as%20Equivalence + */ + static compose = Ray.___func(ref => { + let { initial, terminal} = ref.self; + + if (initial.as_reference().type !== RayType.REFERENCE || terminal.as_reference().type !== RayType.REFERENCE) + throw new PreventsImplementationBug(); + + // ${[...initial.self.initial.as_reference().all().js]} + if (initial.type !== RayType.VERTEX || terminal.type !== RayType.VERTEX) { + throw new PreventsImplementationBug(`[${initial.type}] - [${terminal.type}] - only composing vertices for now (${initial.self.initial.any.js} -> ${terminal.self.terminal.any.js})`); + } + + initial.follow().equivalent(terminal.follow(Ray.directions.previous)); + + // return ref; TODO + return terminal; + }); + compose = Ray.compose.as_method(this); + + // TODO: Cleanup + /** + * Equivalence as "Composing Vertices": "TODO: Is this right?: Equivalence at Continuations, inside a Vertex, is parallel composition, from the perspective of the usual direction defined at the Vertex (not generally parallel)" + * - `A.equivalent(B)` = `A.as_vertex().compose(B.as_vertex())` + * - `A.equivalent(B).equivalent(C)` = `A.as_vertex().compose(B.as_vertex()).compose(C.as_vertex())` + * + * An equivalence is best understood as the drawing of a single line between two things. Where those two things might have arbitrary structure around them, but we're not checking the (non-)existence of that structure. And thus: + * - An equivalence, is only a local equivalence, no global coherence of it can be guaranteed. (or: Changes of an equivalence are only applied locally, which could have global effects, but this isn't necessarily obvious). + * + * @see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=On%20Equivalences%20%26%20Inconsistencies + */ + static equivalent = Ray.___func(ref => { + let { initial, terminal} = ref.self; + + /** + * The simplest case, is where both sides are only aware of themselves (on .vertex). The only thing we need to do is turn an Orbit, to an Orbit which repeats every 2 steps, the intermediate step being the other thing. + * + * Or in textual terms something like: + * - A single Orbit: `(A.self = A) | (B.self = B)` (i.e. A.is_none && B.is_none) + * - To: `(A.self = B) | (B.self = A)` + * + * Basically turns `A` into a reference to `B`, and `B` into a reference to `A`. + */ + const ignorant_equivalence = (): Ray => { + return initial.___ignorantly_equivalent(terminal); + } + + // 2x Ray.None -> Turn into 2 empty references, referencing each-other. + if ( + initial.dereference.is_none() && terminal.dereference.is_none() + && !(initial.type === RayType.VERTEX && terminal.type === RayType.VERTEX) + ) { + // throw new PreventsImplementationBug(`${initial.type} / ${terminal.type}`) + return ignorant_equivalence(); + } + + // Two structures, which have `ref.self = Ray.None` -> Turn into two structures which are on a line in between them. + if (initial.dereference.is_none()) { + const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + vertex.self.self = initial.self.as_arbitrary(); + initial.self.self = vertex.self.as_arbitrary(); + + // initial.equivalent(terminal); + // return terminal; + } + if (terminal.dereference.is_none()) { + const vertex = Ray.vertex().o({ js: '___as_vertex' }).as_reference().o({ js: '___as_vertex.#' }); + vertex.self.self = terminal.self.as_arbitrary(); + terminal.self.self = vertex.self.as_arbitrary(); + + // initial.equivalent(terminal.___as_vertex()); + // return terminal; + } + + if ( + initial.dereference.type !== RayType.VERTEX + || terminal.dereference.type !== RayType.VERTEX + || initial.dereference.self === initial.self + || terminal.dereference.self === terminal.self + ) { + throw new PreventsImplementationBug('wut') + } + + if (initial.follow().type !== RayType.TERMINAL || terminal.follow(Ray.directions.previous).type !== RayType.INITIAL) { + throw new PreventsImplementationBug('wut2') + } + + // if (terminal.self.any.js === 'D') + // throw new PreventsImplementationBug(); + + initial.dereference.compose(terminal.dereference); + + return terminal; + + // initial.dereference.compose() + // return terminal; + }); + equivalent = Ray.equivalent.as_method(this); + + // static equivalent = Ray.___func(ref => { + // let { initial, terminal} = ref.self; + // + // /** + // * The simplest case, is where both sides are only aware of themselves (on .vertex). The only thing we need to do is turn an Orbit, to an Orbit which repeats every 2 steps, the intermediate step being the other thing. + // * + // * Or in textual terms something like: + // * - A single Orbit: `(A.self = A) | (B.self = B)` (i.e. A.is_none && B.is_none) + // * - To: `(A.self = B) | (B.self = A)` + // * + // * Basically turns `A` into a reference to `B`, and `B` into a reference to `A`. + // */ + // const ignorant_equivalence = (): Ray => { + // return initial.___ignorantly_equivalent(terminal); + // } + // + // // 2x Ray.None -> Turn into 2 empty references, referencing each-other. + // if (initial.is_none() && terminal.is_none()) + // return ignorant_equivalence(); + // + // // Two structures, which have `ref.self = Ray.None` -> Turn into two structures referencing each-other. + // if (initial.dereference.is_none() && terminal.dereference.is_none()) + // return ignorant_equivalence(); + // + // if ( + // (initial.is_vertex() && terminal.is_boundary()) + // || (terminal.is_vertex() && initial.is_boundary()) + // ) { + // throw new NotImplementedError(`Parallel composition: TODO`); + // } + // + // /** + // * - Splits the 'initial' side's vertex, into an iterable one, and returns a pointer to the initial side of that iterator. + // * + // * - Similarly, we do the opposite for the terminal, returning the terminal side of that iterator. + // * + // * - Then we're left with the 'beginning' of one iterator, and the 'end' of the other. And the only thing that's left to do, is draw a simple (ignorant) equivalence between the two. (Basically call this function again, and call {ignorant_equivalence}). + // * TODO: This could also be a line with some debug information. + // */ + // const a = initial.as_terminal(); + // const b = terminal.as_initial(); + // + // if (a.type !== RayType.TERMINAL) + // throw new PreventsImplementationBug(); + // if (b.type !== RayType.INITIAL) + // throw new PreventsImplementationBug(); + // + // if (!a.self.self.is_none()) + // throw new PreventsImplementationBug(`${b.self.self.any.js}`); + // if (!b.self.self.is_none()) + // throw new PreventsImplementationBug(`${b.self.self.any.js}`); + // + // a.equivalent(b); + // + // const ret = terminal; + // + // if (ret.type !== RayType.VERTEX) + // throw new PreventsImplementationBug(`${ret.type}`); + // + // return ret; + // }); + // equivalent = Ray.equivalent.as_method(this); + + // zip also compose??? + // [a, b, c] zip [d, e, f] zip [g, h, i] ... + // [[a,d,g],[b,e,h],[c,f,i]] + static zip = Ray.___func(ref => { + let { initial, terminal } = ref.self; + + if (initial.as_reference().type !== RayType.REFERENCE || terminal.as_reference().type !== RayType.REFERENCE) + throw new PreventsImplementationBug('TODO: Implement'); + + if (initial.type !== RayType.VERTEX || terminal.type !== RayType.VERTEX) + throw new PreventsImplementationBug('TODO: Implement'); + + throw new NotImplementedError(); + // initial.traverse() + // return new Ray({ + // + // }); + }); + zip = Ray.zip.as_method(this); + + // pop = (): Ray => { + // this.last().previous().all.terminal = (ref) => ref.___empty_terminal(); + // } + pop = (): Ray => this.___primitive_switch({ + [RayType.VERTEX]: () => { + const previous_vertex = this.self.initial.follow(Ray.directions.previous); + + if (this.is_none()) { + return this; // TODO; Already empty, perhaps throw + } + + return previous_vertex.___primitive_switch({ + [RayType.VERTEX]: () => { + console.log(previous_vertex) + // TODO: NONHACKY + + previous_vertex.self.terminal = new Ray({ vertex: Ray.None, initial: previous_vertex.self.as_arbitrary() }).o({ debug: 'terminal ref'}).as_arbitrary() + return previous_vertex; + } + }); + } + }); + + + // static sn = (step: Implementation): { + // as_method: ArbitraryMethod + // } => { + // + // return { + // as_method: Ray.___func(step).as_method + // } + // } + + /** + * Constructs a function accepting arbitrary structure based on one implementation of it. + * + * TODO: Is there some equivalent of this in computer science??? category theory?? + * + * a.compose(b).compose(c) = [a, b, c].compose = abc.compose = [[a1, a2], b, c].compose = [[a1, a2], b, [c1, c2]].compose = [[a1, [[[a2]]], [[[[]]], []]], b, [[[]], [], [c]]].compose = ... + */ + static ___func( + step: Implementation, + ): { + as_method: ArbitraryMethod + } { + return { + + /** + * Puts the Ray this is called with on a new Ray [initial = ref, ???, ???]. Then it places any structure it's applying a method to, on the terminal of this new Ray [initial = ref, ???, terminal = any] + */ + as_method: (ref: Ray): Method => (...any: Recursive): Ray => { + if (any === undefined || any.length === 0) + return step(ref); + + // TODO: This can be much better... + const first = (recursive?: Recursive): Ray | undefined => { + if (recursive === undefined) return; + // if (_.isObject(recursive)) return recursive as unknown as Ray; + + for (let r of recursive) { + if (r === undefined) continue; + if (_.isObject(r)) return r as unknown as Ray; + + // if (r instanceof Ray) + // throw new PreventsImplementationBug(); + + // @ts-ignore + const _first = first(r); + if (_first) + return _first; + } + } + + const _first = first(any); + + if (_first === undefined) + return step(ref); + + const pointer = new Ray({ + initial: () => ref, + terminal: () => _first + }); + + return step(pointer); + + // TODO: ANY CASE + // if (any.length === 1) { + // } + } + } + } + + /** + * Helper methods for commonly used directions + * + * TODO: Link to step-wise walk as any function - lazy, not traversing certain paths, etc.. (for last/..) + */ + static directions = { + next: (ref: Ray) => ref.self.terminal.as_reference(), + previous: (ref: Ray) => ref.self.initial.as_reference(), + } + + // TODO: Nicer one? ; Differentiate between ".next" and just "follow the pointer" ? + follow = (step: Implementation = Ray.directions.next): Ray => { + // let pointer = new Ray({ + // initial: () => this, + // terminal: () => step(this), + // }); TODO USE POINTER? + + return step(this); + } + + /** + * .next + */ + next = (step: Implementation = Ray.directions.next): Ray => { + // for (let next of this.___next({step})) { + // + // // return next; + // } + // + // return Ray.None(); + let pointer = new Ray({ + initial: () => this, + terminal: () => step(this), + }); + + pointer = pointer.step().step(); + + if (pointer.terminal.type !== RayType.VERTEX) + throw new NotImplementedError(`${pointer.terminal.type} / ${pointer.terminal.self.any.js}`); + + return pointer.terminal; + + // return Ray.___next(Ray.directions.next)(this); + } + has_next = (step: Implementation = Ray.directions.next): boolean => this.next(step).is_some(); + // @alias('end', 'result', 'back') + last = (step: Implementation = Ray.directions.next): Ray => { + const next = this.next(step); + return next.is_some() ? next.last(step) : this; + } + /** + * .previous (Just .next with a `Ray.directions.previous` default) + */ + previous = (step: Implementation = Ray.directions.previous): Ray => this.next(step); + has_previous = (step: Implementation = Ray.directions.previous): boolean => this.has_next(step); + // @alias('beginning', 'front') + first = (step: Implementation = Ray.directions.previous): Ray => this.last(step); + + // TODO: I Don't like this name, but it needs to get across that any equivalency, or any equivalency check for that necessarily, is local. And I want more equivalences, I run more of this method. + // TODO: For chyp used to compare [vtype, size] as domains, just type matching on the vertex. + is_vertex_equivalent = (b: Ray) => { + // TODO; in the case of a list, each individually, again, additional structure... + } + // TODO: Ignore the connection between the two, say a.equiv(b) within some Rule [a,b], ignore the existing of the connection in the Rule? What does it mean not to??? + + // TODO: Whether the thing is referenced on the vertex: do their vertices have some connection onm this direction? + is_equivalent = (b: Ray): boolean => { return false; } // TODOl: Current references assume you can't go inside vertex.. + // TODO implement .not?? + + get count(): Ray { return Number(this.as_array().length); } + + // TODO; Could return the ignorant reference to both instances, or just the result., .. + + /** + * TODO: Need more control over the (non-/)lazyness of copy. + * + * - The problem with a copy, is that in or to be generalizable, it needs to alter all references to the thing it's copying to itself - this cannot be done with certainty. + * + * - Additionally, a copy necessarily has some non-redundancy to it: + * @see "A copy is necessarily inconsistent": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=If%20I%20have%20one%20thing%20and%20I%20make%20a%20perfect%20copy + */ + // @alias('duplicate') + copy = (): Ray => { + // return this.self.as_reference(); // Copies the reference? + throw new NotImplementedError(); + + // const copy = new Ray({ + // initial: this.self._initial().as_reference().none_or(ref => ref.copy()).as_arbitrary(), + // vertex: this.self._vertex().as_reference().none_or(ref => ref.copy()).as_arbitrary(), + // }).o({ ___dirty_copy_buffer: {} }); + // // copy._initial = () => copy.any.___dirty_copy_buffer._initial ??= this.self._initial().as_reference().copy(); + // // copy._vertex = () => copy.any.___dirty_copy_buffer._vertex ??= this.self._vertex().as_reference().copy(); + // // copy._terminal = () => copy.any.___dirty_copy_buffer._terminal ??= this.self._terminal().as_reference().copy(); + // + // + // // TODO: Doesn't copy .any + // + // return copy.as_reference(); + } + + // none_or = (arbitrary: Implementation): Ray => this.is_none() ? Ray.None() : arbitrary(this); + + // @alias('converse', 'opposite', 'swap') + get reverse(): Ray { + const copy = this;//TODO.copy(); + + // TODO: Do we do this lazy by default? Just using refs??? - Or abstract this elsewhere to decide what to do + const swap = copy.initial; + copy.initial = copy.terminal.as_arbitrary(); + copy.terminal = swap.as_arbitrary(); + // TODO: This doesn't actually work + + return copy; + } + + /** + * TODO - Better 'value' here. (Use JS.Any??) + * + * TODO: All these should accept Ray values. + * + * .size, since .length is reserved by JavaScript. + * TODO: .size could be more tensor-like, arbitrary lengths.. + */ + // @alias('length', 'of_length') + static size = (of: number, value: any = undefined): Ray => { + let ret: Ray | undefined; + let current: Ray | undefined; + // TODO: Actual good implementation: Should be lazy + for (let i = 0; i < of; i++) { + const vertex = Ray.vertex().o({js: value}).as_reference(); + + if (!ret) { + current = ret = vertex; + } else { + current = current?.compose(vertex); + } + } + + if (!ret) + return Ray.None(); + + return ret; + } + static at = (index: number, of: number, value: any = undefined): Ray => { + return Ray.size(of, value).at(index); + } + /** + * Just uses length/size for permutation. TODO: More complex permutation/enumeration implementation should follow at some point. (@see https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=One%20of%20them%20could%20even%20be%20putting%20both%20our%20points%20on%20our%20selection for an example) + * + * @see "Combinatorics as Equivalence": https://orbitmines.com/papers/on-orbits-equivalence-and-inconsistencies#:~:text=Constructing%20Combinatorics%20%2D%20Combinatorics%20as%20Equivalence + */ + static permutation = (permutation: number | undefined, of: number): Ray => Ray.at( + // In the case of a bit: 2nd value for '1' (but could be the reverse, if our interpreter does this) + permutation ?? 0, + // In the case of a bit: Either |-*-| if no bit or |-*->-*-| if a bit. + permutation === undefined ? 1 : of + ) + + at = (steps: number | Ray | Arbitrary): Ray => { + if (!is_number(steps)) + throw new NotImplementedError('Not yet implemented for Rays.'); + + // TODO: Actual good implementation - also doesn't support modular like this + const array = [...this.traverse( + steps < 0 ? Ray.directions.previous : Ray.directions.next + )]; + + steps = Math.abs(steps); + + return array.length > steps && steps >= 0 ? ( + array[steps] ?? Ray.None() // TODO FIX: Probably a JavaScript quirck with some weird numbers, just failsafe to None. + ) : Ray.None(); + } + + // const hexadecimal = (hexadecimal?: string): Arbitrary> => permutation(hexadecimal ? parseInt(hexadecimal, 16) : undefined, 16); + + // TODO: Should give the program that does the mapping, not the result, and probably implemented as 'compile/traverse' + map = (mapping: (ray: Ray) => Ray | any): Ray => { throw new NotImplementedError(); } + // filter = (mapping: (ray: Ray) => Ray | any): Ray => { throw new NotImplementedError(); } + get clear(): Ray { throw new NotImplementedError(); } + + // TODO: Generalize these functions to: + // + // TODO: +default, in the case of Initial/Terminal = Ray.None, to which the default sometimes is nothing. Or in the case of min/max it's 0. + + + // TODO: being called min.x needs to return the min value within that entire structure. + + // [this.vertices().x.max(), this.edges().x.max()].max() + // [this.vertices().x.min(), this.edges().x.max()].max() + // TODO: Indicies not corresponding the the directionality defined, are probably on another abstraction layer described this way. More accurately, they're directly connected, and on a separate layer with more stuff in between... + get index(): Ray { throw new NotImplementedError(); } + // TODO: Can probably generate these on the fly, or cache them automatically + min = (_default: 0): Ray => { throw new NotImplementedError(); } + max = (_default: 0): Ray => { throw new NotImplementedError(); } + + // TODO: FIND OUT IF SOMEONE HAS A NAME FOR THIS + // apply = (func: Ray) => { + + // TODO: Combine into generalized [x, min/max()] - preserve terminal/initial structure + // TODO: ray#apply. + // TODO: FROM COMPOSER + /** + * const func = [min(), '', max()] + * + * const [min_x, max_x] = [ + * // Compute the min x-coordinate of the edges and vertices in the other graph. + * compose.terminal.x.min(), // min_other + * + * // Compute the max x-coordinate of the edges and vertices in this graph. + * compose.initial.x.max(), // max_self + * ] + */ + // } + + // ___compute = () + + *traverse(step: Implementation = Ray.directions.next): Generator { + // TODO: Also to ___func?? + + if (this.type !== RayType.VERTEX) + throw new NotImplementedError(`[${this.type}]`); + + yield *this.___next({step}); + } + + static pointer = (initial: Ray, step: Implementation): Ray => new Ray({ + initial: () => initial, + terminal: () => step(initial) + }); + + private next_pointer = (step: Implementation) => { + const { self: history, terminal: current } = this; + + return new Ray({ + initial: current.as_arbitrary(), + vertex: history.as_arbitrary(), + terminal: () => current.follow(step) + }); + }; + + /** + * VERTEX (current) + * | + * v + * + * ? <-- Pointer B + * [--| ] <-- INITIAL/TERMINAL (previous) + * ? <-- Pointer A + */ + private branch = (): [Ray, Ray] => { + const { initial: previous, terminal: current } = this; + + if (!previous.is_boundary()) + throw new PreventsImplementationBug('Only branching off INITIAL/TERMINAL -> VERTEX for now.'); + if (current.type !== RayType.VERTEX) + throw new PreventsImplementationBug('Only branching off INITIAL/TERMINAL -> VERTEX for now.'); + + return [ + this.next_pointer(Ray.directions.previous), + this.next_pointer(Ray.directions.next) + ]; + } + + *___next({ + step = Ray.directions.next, + } = {}): Generator { + for (let pointer of this.___traverse({step})) { + + // TODO: You can do this non-locally with a pass over the history. This way it's local, but we''ll have to find a good example of why this might not go that well. (As this would match to any empty vertices, and maybe more) + + // TODO: Could also check for none.. + const { initial: previous, terminal: current } = pointer; + + if (previous.is_vertex() && !Ray.is_orbit(previous.self, current)) { + yield pointer.initial; + } + } + } + *___map(map: (vertex: Ray) => T, { + step = Ray.directions.next, + } = {}): Generator { + for (let vertex of this.___next({step})) { + yield map(vertex); + } + } + + /** + * TODO: Not happy with this... + */ + *___traverse({ + step = Ray.directions.next, + should_branch = (pointer: Ray) => { + const { initial: previous, terminal: current } = pointer; + return previous.is_boundary() && current.is_vertex() && Ray.is_orbit(current.self, previous); + }, + branch = (pointer: Ray): [Ray, Ray] => pointer.branch(), + filter = (pointer: Ray): boolean => true, + next = (pointers: Ray[]): Ray => pointers[0], + remove = (pointers: Ray[], pointer: Ray) => delete pointers[0], + } = {}): Generator { + const pointers: Ray[] = [ + Ray.vertex(Ray.pointer(this, step).as_arbitrary()) + ]; // TODO COuld be a ray; + + while (true) { + + const ref = next(pointers); + if (ref === undefined) { + // TODO: Could just keep trying... + break; + } + let { self: pointer } = ref; + + if (!filter(pointer)) { + remove(pointers, pointer); + continue; + } + + yield pointer; + + pointer = pointer.step(); + + if (pointer.terminal.is_none()) { + remove(pointers, pointer); + continue; + } + + if (should_branch(pointer)) { + const [a, b] = branch(pointer); + + ref.self = a.as_arbitrary(); + pointers.push(Ray.vertex(b.as_arbitrary())); + } else { + ref.self = pointer.as_arbitrary(); + } + } + } + + /** + * + * + * TODO: switch/match Should be abstracted into Ray? + */ + static step = (ref: Ray) => { + const { initial } = ref; + + /** + * Should return vertex, for one possible next step + * Initial for many + * Terminal for none + * Reference for ??? + */ + + /** + * Dereferencing is likely in many cases quickly subject to infinite stepping. + * + * REFERENCE -> Dereference (this.self.self) + * INITIAL/INITIAL -> Dereference (this.self.terminal) + * TERMINAL/TERMINAL -> Dereference (this.self.initial) + * VERTEX/VERTEX -> ??? + * + * - Could be that this means that there's no continuation, a self-reference defined here, or it's some mechanism of halting. + * + * - TODO: Simple example of infinitely finding terminals, or a reference to 'nothing - infinitely'. + * - TODO: Could return both dereference sides as possible options + */ + + const next_pointer = (terminal: Ray, next: Arbitrary) => new Ray({ + initial: () => terminal, + vertex: () => ref, + terminal: next, + }); + + /** + * INITIAL/TERMINAL -> possible previous - TERMINAL.self.initial (pass to step) + * TERMINAL/INITIAL -> possible next - INITIAL.self.terminal (pass to step) + */ + const follow_direction = (terminal: Ray): Ray => next_pointer(terminal, () => terminal.___primitive_switch({ + [RayType.INITIAL]: Ray.directions.next, + [RayType.TERMINAL]: Ray.directions.previous, + })); + + const dereference = (terminal: Ray) => next_pointer(terminal, () => terminal.dereference); + + /** + * TERMINAL -> VERTEX (next: VERTEX -> INITIAL) + * INITIAL -> VERTEX (next: VERTEX -> TERMINAL) + */ + const arbitrary_continuations = (terminal: Ray): Ray => next_pointer(terminal, () => initial.___primitive_switch({ + [RayType.INITIAL]: (initial) => Ray.directions.next(terminal), + [RayType.TERMINAL]: (initial) => Ray.directions.previous(terminal), + })); + + const boundary = (boundary: Boundary) => (terminal: Ray): Ray => terminal.___primitive_switch({ + + /** + * Many possible continuations (from the perspective of initial = TERMINAL) + * + * From something, we arrived at some TERMINAL/INITIAL, which at its `.self`, holds a VERTEX. + * [ ? ] + * [--|--][--| ] <-- ref superposed with ref.self + * [ ? ] + */ + [RayType.VERTEX]: arbitrary_continuations, + + /** + * A possible continuation + * + * (INITIAL -> TERMINAL) + * (TERMINAL -> INITIAL) + */ + [boundary]: follow_direction, + [opposite(boundary)]: follow_direction, + + [RayType.REFERENCE]: dereference, + }); + + return initial.___primitive_switch({ + + /** + * VERTEX -> VERTEX + * TODO Could be an ignorant continuation (as in, the terminal does not have the initial vertex on its .initial). Or you could interpret this as saying, oh this should be a vertex, no information about the continuation definition in between? + * + * VERTEX -> TERMINAL + * If we're going in the terminal direction (from the perspective of the initial = VERTEX) + * [--|--][--| ] <-- (VERTEX -> TERMINAL) + * + * VERTEX -> INITIAL + * If we're going in the initial direction (from the perspective of the initial = VERTEX) + * [ |--][--|--] <-- (VERTEX -> INITIAL) + * + * VERTEX -> REFERENCE + * TODO ??? + */ + [RayType.VERTEX]: (initial) => dereference(ref.terminal), + + [RayType.INITIAL]: (initial) => boundary(RayType.INITIAL)(ref.terminal), + [RayType.TERMINAL]: (initial) => boundary(RayType.TERMINAL)(ref.terminal), + + [RayType.REFERENCE]: dereference, + }); + } + step= Ray.___func(Ray.step).as_method(this); + + // TODO; Maybe replace switch with 'zip'?, What are the practical differences? + protected ___primitive_switch = (cases: SwitchCases): Ray => { + const _case = cases[this.type]; + + if (_case === undefined || _.isString(_case)) + { // @ts-ignore + throw new PreventsImplementationBug(_case ?? `Unhandled switch case; [${this.type}]`); + } + + return _case(this); + } + + /** + * JavaScript, possible compilations - TODO: Could have enumeratd possibilities, but just ignore that for now. + */ + // JS.AsyncGenerator + async *[Symbol.asyncIterator](): AsyncGenerator { yield *this.traverse(); } + // JS.Generator + *[Symbol.iterator](): Generator { yield *this.traverse(); } + // JS.AsyncGenerator + as_async_generator = (): AsyncGenerator => this[Symbol.asyncIterator](); + // JS.AsyncIterator + as_async_iterator = (): AsyncIterator => this.as_async_generator(); + // JS.Iterator + as_generator = (): Generator => this[Symbol.iterator](); + // JS.AsyncIterator + as_iterator = (): Iterator => this.as_generator(); + // JS.Array + as_array = (): Ray[] => [...this]; + // JS.String + toString = (): string => this.as_string(); + as_string = (): string => this.as_array().map(ref => ref.any.js).join(','); // TODO: PROPER + + as_int = (): number => { throw new NotImplementedError(); } + as_number = this.as_int; + + /** + * + * TODO: + * - This needs something much smarter at some point... + */ + all = (step: Implementation = Ray.directions.next): { [key: string | symbol]: Ray } & any => { + return new Proxy(this, { + + get(self: Ray, p: string | symbol, receiver: any): any { + + return self.___map(ref => ref.any[p], {step}); + }, + + /** + * Can't overload things like '-=' for anything but things that return numbers... ; So just apply a general function instead. + */ + set(self: Ray, p: string | symbol, newValue: any, receiver: any): boolean { + for (let ref of self.___next({step})) { // TODO; This needs to either be dynamically, or just a simple shut-off for circular ones. + ref.any[p] = is_function(newValue) ? newValue(ref.any[p]) : newValue; + } + + return true; + }, + + + deleteProperty(self: Ray, p: string | symbol): boolean { + throw new NotImplementedError(); + + return true; + } + // TODO: What do these other methods on Proxy do??? + }); + + } + + /** + * Move to a JavaScript object, which will handle any complexity of existing JavaScript objects, and allows one to abstract any values contained in the {vertex} to the usual JavaScript interface. - More usual to how one thinks about functions, ..., properties. + */ + get any(): { [key: string | symbol]: Ray } & any { return this.self.proxy(); } + get ___any(): { [key: string | symbol]: Ray } & any { return this.proxy(); } + cast = (): T => { throw new NotImplementedError(); } // TODO this.proxy(); + + /** + * Used for chaining JavaScript-provided properties + * + * TODO: DOESNT FOLLOW .ANY PATTERN? + */ + o = (object: { [key: string | symbol]: any }): Ray => { + _.keys(object).forEach(key => this.proxy()[key] = object[key]); // TODO: Can be prettier, TODO: map to Ray equivalents and add to vertices.. + return this; + } + + // All these are dirty + o2 = ({ initial, vertex, terminal }: any): Ray => { + if (initial) this.initial.o(initial); + if (vertex) this.o(vertex); + if (terminal) this.terminal.o(terminal); + + return this; + } + + protected property = (property: string | symbol, _default?: any): any => this.any[property] ??= (_default ?? Ray.None()); // TODO: Can this be prettier?? + + protected _proxy: any; + protected _dirty_store: { [key: string | symbol]: object } = {} + protected proxy = (constructor?: ParameterlessConstructor): T & { [key: string | symbol]: Ray } => { // TODO: + // TODO: IMPLEMENT SPLAT... {...ray.any} + return this._proxy ??= new Proxy(this, { + + get(self: Ray, p: string | symbol, receiver: any): any { + + // throw new NotImplementedError(); + return self._dirty_store[p]; + // return self.as_arbitrary(); + }, + set(self: Ray, p: string | symbol, newValue: any, receiver: any): boolean { + // throw new NotImplementedError(); + self._dirty_store[p] = newValue; + + return true; + }, + + deleteProperty(self: Ray, p: string | symbol): boolean { + if (!(p in self._dirty_store)) { + return false; + } + + delete self._dirty_store[p]; + return true; + } + // TODO: What do these other methods on Proxy do??? + }) as T; + } + + /** + * + * - Don't assume we can track back any reference to this thing. Just destroy it, set everything to None. And let anything else deal with the consequences of the deletion. + * + * TODO: + * - Could lazily try to find references. + * - Implement on proxy for 'delete ray' + */ + delete = (): Ray => { + this.self.initial = Ray.None; + this.self.self = this.self.self_reference; + this.self.terminal = Ray.None; + // TODO: REMOVE THESE + this.self._proxy = undefined; + this.self._dirty_store = {}; + + // Removes the current reference to it. + this.self = this.self_reference; + + return this; + } + + //TODO USED FOR DEBUG NOW + move = (func: (self: Ray) => Ray, memory: boolean, Interface: Ray): Ray => { + const target_ray = func(this.self); + + const target = target_ray.as_reference().o({ + ...this._dirty_store, + position: + target_ray.any.position + ?? this.any.position + ?? Ray.POSITION_OF_DOOM + }); + console.log('move', `${this.self.label.split(' ')[0]} -> ${target.self.label.split(' ')[0]}`); + + if (memory) { + if (!target_ray.any.traversed) { + Interface.___any.rays.push(target); + target_ray.any.traversed = true; + } + } else { + Interface.___any.rays = [target]; + } + + return target; + } + + static POSITION_OF_DOOM = [0, 100, 0] + + // TODO: Abstract away as compilation + render_options = (Interface: Ray): Required => { + return ({ + position: + this.any.position + ?? (this.is_none() ? Ray.POSITION_OF_DOOM : Ray.POSITION_OF_DOOM), + rotation: + this.any.rotation + ?? [0, 0, 0], + scale: + this.any.scale + ?? (this.is_none() ? 1.5 : 1.5), + color: + (Ray.is_orbit(Interface.___any.selection.self, this.self) && Interface.___any.cursor.tick) ? '#AAAAAA' // TODO: Should do lines as well, line render should prefer based on level of description.. (flat line only vertices, then render for the vertex?) + : ( + this.any.color + ?? (this.is_none() ? 'red' : { + [RayType.VERTEX]: 'orange', + [RayType.TERMINAL]: '#FF5555', + [RayType.INITIAL]: '#5555FF', + [RayType.REFERENCE]: '#555555', + }[this.type] + ) + ) + }); + } + + ___dirty_all(c: Ray[]): Ray[] { + if (c.filter(a => a.label === this.label).length !== 0) { + return c; + } + + c.push(this); + + if (this.initial.as_reference().is_some()) + this.initial.___dirty_all(c); + if (this.vertex.as_reference().is_some()) + this.vertex.___dirty_all(c); + if (this.terminal.as_reference().is_some()) + this.terminal.___dirty_all(c); + + return c; + } + + // TODO: DOESNT DO ON .SELF + debug = (c: DebugResult): DebugRay => { + if (c[this.label] !== undefined) + return c[this.label]!; + + const of = (ray: Ray): string => { + if (ray.as_reference().is_none()) return 'None'; + + ray.debug(c); + return ray.label; + } + + const obj: any = { label: this.label }; + c[this.label] = obj; + + obj.label = this.label; + obj.initial = of(this.initial); + obj.vertex = of(this.vertex); + obj.terminal = of(this.terminal); + obj.type = this.as_reference().type; + obj.is_initial = this.as_reference().is_initial(); + obj.is_vertex = this.as_reference().is_vertex(); + obj.is_terminal = this.as_reference().is_terminal(); + obj._dirty_store = this._dirty_store; + + return obj; + } + + /** + * TODO: This should be constructed at the vertex and in general unsolvable + */ + static _label: number = 0; + get label(): string { + if (this.any.label !== undefined) + return this.any.label; + + return this.any.label = `"${Ray._label++} (${this.any.debug?.toString() ?? '?'})})"`; + } + + push_back = (b: Ray) => this.last().compose(b); + push_front = (b: Ray) => this.first().compose(b); + + // [index: number]: Ray; + + // length: number; + // + // concat(...items: ConcatArray[]): Ray[]; + // concat(...items: (ConcatArray | Ray)[]): Ray[]; + // concat(...items: (ConcatArray | Ray)[]): Ray[] { + // return []; + // } + // + // copyWithin(target: number, start: number, end?: number): this { + // return undefined; + // } + // + // entries(): IterableIterator<[number, Ray]> { + // return undefined; + // } + // + // every(predicate: (value: Ray, index: number, array: Ray[]) => value is S, thisArg?: any): this is S[]; + // every(predicate: (value: Ray, index: number, array: Ray[]) => unknown, thisArg?: any): boolean; + // every(predicate, thisArg?: any): any { + // } + // + // fill(value: Ray, start?: number, end?: number): this { + // return undefined; + // } + // + // filter(predicate: (value: Ray, index: number, array: Ray[]) => value is S, thisArg?: any): S[]; + // filter(predicate: (value: Ray, index: number, array: Ray[]) => unknown, thisArg?: any): Ray[]; + // filter(predicate, thisArg?: any): any { + // } + // + // find(predicate: (value: Ray, index: number, obj: Ray[]) => value is S, thisArg?: any): S | undefined; + // find(predicate: (value: Ray, index: number, obj: Ray[]) => unknown, thisArg?: any): Ray | undefined; + // find(predicate, thisArg?: any): any { + // } + // + // findIndex(predicate: (value: Ray, index: number, obj: Ray[]) => unknown, thisArg?: any): number { + // return 0; + // } + // + // forEach(callbackfn: (value: Ray, index: number, array: Ray[]) => void, thisArg?: any): void { + // } + // + // indexOf(searchElement: Ray, fromIndex?: number): number { + // return 0; + // } + // + // join(separator?: string): string { + // return ""; + // } + // + // keys(): IterableIterator { + // return undefined; + // } + // + // lastIndexOf(searchElement: Ray, fromIndex?: number): number { + // return 0; + // } + // + // map(callbackfn: (value: Ray, index: number, array: Ray[]) => U, thisArg?: any): U[] { + // return []; + // } + // + // pop(): Ray | undefined { + // return undefined; + // } + // + // push(...items: Ray[]): number { + // return 0; + // } + // + // reduce(callbackfn: (previousValue: Ray, currentValue: Ray, currentIndex: number, array: Ray[]) => Ray): Ray; + // reduce(callbackfn: (previousValue: Ray, currentValue: Ray, currentIndex: number, array: Ray[]) => Ray, initialValue: Ray): Ray; + // reduce(callbackfn: (previousValue: U, currentValue: Ray, currentIndex: number, array: Ray[]) => U, initialValue: U): U; + // reduce(callbackfn, initialValue?): any { + // } + // + // reduceRight(callbackfn: (previousValue: Ray, currentValue: Ray, currentIndex: number, array: Ray[]) => Ray): Ray; + // reduceRight(callbackfn: (previousValue: Ray, currentValue: Ray, currentIndex: number, array: Ray[]) => Ray, initialValue: Ray): Ray; + // reduceRight(callbackfn: (previousValue: U, currentValue: Ray, currentIndex: number, array: Ray[]) => U, initialValue: U): U; + // reduceRight(callbackfn, initialValue?): any { + // } + // + // reverse(): Ray[] { + // return []; + // } + // + // shift(): Ray | undefined { + // return undefined; + // } + // + // slice(start?: number, end?: number): Ray[] { + // return []; + // } + // + // some(predicate: (value: Ray, index: number, array: Ray[]) => unknown, thisArg?: any): boolean { + // return false; + // } + // + // sort(compareFn?: (a: Ray, b: Ray) => number): this { + // return undefined; + // } + // + // splice(start: number, deleteCount?: number): Ray[]; + // splice(start: number, deleteCount: number, ...items: Ray[]): Ray[]; + // splice(start: number, deleteCount?: number, ...items: Ray[]): Ray[] { + // return []; + // } + // + // unshift(...items: Ray[]): number { + // return 0; + // } + // + // values(): IterableIterator { + // return undefined; + // } + // + // findLast(predicate: (value: Ray, index: number, array: Ray[]) => value is S, thisArg?: any): S | undefined; + // findLast(predicate: (value: Ray, index: number, array: Ray[]) => unknown, thisArg?: any): Ray | undefined; + // findLast(predicate, thisArg?: any): any { + // } + // + // findLastIndex(predicate: (value: Ray, index: number, array: Ray[]) => unknown, thisArg?: any): number { + // return 0; + // } + // + // flat(depth?: D): FlatArray[] { + // return []; + // } + // + // flatMap(callback: (this: This, value: Ray, index: number, array: Ray[]) => (ReadonlyArray | U), thisArg?: This): U[] { + // return []; + // } + // + // includes(searchElement: Ray, fromIndex?: number): boolean { + // return false; + // } + // + // toReversed(): Ray[] { + // return []; + // } + // + // toSorted(compareFn?: (a: Ray, b: Ray) => number): Ray[] { + // return []; + // } + // + // toSpliced(start: number, deleteCount: number, ...items: Ray[]): Ray[]; + // toSpliced(start: number, deleteCount?: number): Ray[]; + // toSpliced(start: number, deleteCount?: number, ...items: Ray[]): Ray[] { + // return []; + // } + // + // with(index: number, value: Ray): Ray[] { + // return []; + // } + + +} + +// default = (fn: () => any): any => self.match({ +// Some: (a) => a, +// None: () => fn() +// }) +// + + +/** + * + * Important to remember this is just one particular structure to which it can be mapped, there are probably many (TODO infinitely?) others. + * + * Not to be considered as a perfect mapping of JavaScript functionality - merely a practical one. + */ + + const Boolean = (boolean: boolean): Ray => { + // |-false->-true-| (could of course also be reversed) + const _false = Ray.vertex().o({ js: false }); + const _true = Ray.vertex().o({ js: true }); + _false.compose(_true); + + return (boolean ? _true : _false).as_reference(); + } + // const bit = (bit?: boolean): Arbitrary> => permutation(bit ? 1 : 0, 2); + const Bit = Boolean; + + const Iterable = (iterable: Iterable): Ray => Iterator(iterable[Symbol.iterator]()); + + const Iterator = (iterator: Iterator): Ray => { + // [ |--] + + const next = (initial: Ray): Ray => { + const iterator_result = iterator.next(); + const is_terminal = iterator_result.done === true; + + if (is_terminal) { + // We're done, this is the end of the iterator + + // vertex: could have something at the vertex which defines the "end of the iterator" - but we don't here. + const terminal = new Ray({ + initial: () => initial + }); + // initial.compose(() => terminal.as_reference()); + + // if (initial.is_some()) + // initial.terminal = () => terminal; // TODO REPEAT FROM BELOW + + return terminal; + } + + const current: Ray = new Ray({ + // initial: () => new Ray(), + initial: () => initial, + vertex: () => Any(iterator_result.value), + terminal: () => next(current) + }).o({js: iterator_result.value}); + + // initial.compose(() => current.as_reference()); + if (initial.is_some()) + initial.terminal = () => current; + + return current; + } + + const ray_iterator = Ray.None().o({ js: iterator }); + ray_iterator.terminal = () => next(ray_iterator); + + // This indicates we're passing a reference, since traversal logic will be defined at its vertex - what it's defining. + return ray_iterator.as_reference(); + } + + const Generator = (generator: Generator): Ray => Iterable(generator); + + // TODO Could have parallel threads in general. + // const AsyncGenerator = (generator: AsyncGenerator): Ray => { + // // [ |--] + // return JS.Iterable(generator); + // } + + const Number = (number: number): Ray => { + throw new NotImplementedError(); + } + + const Function = (func: Arbitrary): Ray => { + throw new NotImplementedError(); + } + + const Object = (object: object): Ray => Ray.vertex().o(object); + + const Any = (any: any): Ray => { + if (any === null || any === undefined) return Any(any); + if (is_boolean(any)) return Boolean(any); + if (is_number(any)) return Number(any); + if (is_iterable(any)) return Iterable(any); // || is_array(any)) + if (is_function(any)) return Function(any); + if (is_object(any)) return Object(any); + + // TODO + // return JS.Any(any); + return Ray.vertex().o({js: any}); + } + + const is_boolean = (_object: any): _object is boolean => _.isBoolean(_object); + const is_number = (_object: any): _object is number => _.isNumber(_object); + const is_object = (_object: any): _object is object => _.isObject(_object); + const is_iterable = (_object: any): _object is Iterable => Symbol.iterator in Object(_object); + const is_async_iterable = (_object: any): _object is AsyncIterable => Symbol.asyncIterator in Object(_object); + const is_array = (_object: any): _object is T[] => _.isArray(_object); + const is_async = (_object: any) => _.has(_object, 'then') && is_function(_.get(_object, 'then')); // TODO, Just an ugly check + + const is_error = (_object: any): _object is Error => _.isError(_object); + const is_function = (_object: any): _object is ((...args: any[]) => any) => _.isFunction(_object); + +class NotImplementedError extends Error {} +class PreventsImplementationBug extends Error {} + +/** + * Temporary Ray visualization till the visualization is incorporated into the editor (Basically when Visualization = Ray) + * + * TODO; Generalize to Ray - should be embedded on the vertex, or on another layer of description, where the interface is a rewrite rule + */ +type InterfaceOptions = { + position?: [number, number, number], + rotation?: [number, number, number], + scale?: number, + color?: string +} +type Options = { + initial?: InterfaceOptions, + vertex?: InterfaceOptions, + terminal?: InterfaceOptions, +} + +const torus = { + // Radius of the torus, from the center of the torus to the center of the tube. Default is 1. + radius: 3, color: "orange", segments: 200, tube: { width: 1, segments: 200 }, +} +const add = (a: number[], b: number[]): [number, number, number] => [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; + +const circle = { radius: 3, color: "orange", segments: 30, }