root notes [uts-001O]

Draft notes [uts-0001]

Home [index]

I'm Utensil Song, my Forester notes are hosted here, mostly about math and tech.

If you are new to Forester, you may wish to check out Some tips about Forester.

Learning diary [uts-0018]

I wish to keep a learning diary, to keep track of partial reading progress, and things learned during making things.

Lost notes [uts-0014]

The following are normal leaf notes that are not transcluded by any note, and are not captured by root notes or Draft notes, thus might be lost.

[lm-macros]

[tt-003B]

A complete layer-first method [frap-0017]

The three-layer model in the complete cube account turns a human solve into a sequence of stabilizer problems. We keep the fixed frame and write \(U,D,F,B,R,L\) for clockwise quarter-turns as seen from the named face. A prime is an inverse and a superscript \(2\) is a half-turn; a word is read from left to right. The bottom face \(D\) is the first layer, so its center fixes the target colour and the four side centres fix the cross alignment. The notation and face-view convention agree with the cube notation notes.

A block-building or Roux-style organization can reduce the number of stages for an experienced solver, but it changes the recognition and preservation cases. It should be compared through the same questions—what is fixed, what subgroup remains, which effects are checked, and what is remembered—rather than by treating a lower move count as a complete measure of usability. The cube resource notes collect contrasting organizations for that later comparison.

A worked replay and projected lower bound [frap-001B]

The fixed-frame model has a concrete replay on a three-layer puzzle. Work in the fixed-frame HTM convention of the complete cube account: a face quarter-turn, its inverse, or a half-turn costs one move, and words are read from left to right. Start at the solved state and apply the declared scramble

\[w=R\ U\ R'\ U'\ F\ R\ U\ R'\ U'\ F'.\]

The inverse word is

\[w^{-1}=F\ U\ R\ U'\ R'\ F'\ U\ R\ U'\ R'.\]

A sticker-level replay of the six fixed-frame face maps sends the solved state to the recorded scramble state and then returns to solved. The check represents each of the 54 pairs in how the cube models connect as a slot, applies the six quarter-turn permutations and their inverses, and enumerates the projected corner states breadth first through depth two. The token-by-token states and hashes are preserved in the replay receipt. Running the tracked replay checker with `node scripts/check-frap-b9b.js trees/frap-001B-replay.json` reproduces and compares the stored `replay_log`. The complete cube account supplies the move convention; the replay, hashes and 262-state count are the deterministic calculation reported here. The second replay is therefore a certificate for the move convention and the inverse, not merely an animation of a familiar sequence.

Computer solving and efficiency [frap-0018]

A computer solver answers a sharper question than a human method: from a recorded state, what is the shortest path to solved, or what short path can be found quickly? The answer depends on the move metric, the state coordinates, and the information retained by the search. We use the fixed-frame ordinary 3-layer action from the complete cube account, with the six outer face turns as generators. The methods chapter supplies the group-action graph viewpoints; here they become finite search procedures.

Computer solving therefore extends the earlier construction in three controlled directions: exact graph distances on a small quotient, admissible lower bounds for the full state space, and subgroup reductions that trade completeness of the search for speed. These distinctions let a later return to play report a solver's output without confusing a valid solve, a short solve, and a certified optimum.

This chapter is a mathematical specification, not a Lean-certified solver. For comparison, rubik-lean4 at commit b8a5a09 formalizes the fixed-frame move and sticker correspondence, while rubiks-cube-group at commit 3baebd73 formalizes the legal-state subgroup; neither repository is used here as a formal proof of the search, diameter, or optimality claims above.

From move sequences to play [frap-0006]

A formal move description becomes useful only when a player can apply it and reproduce its result. Play gives the mathematics an immediate test of intelligibility: a notation should identify a move unambiguously, a displayed turn should agree with the stated action, and replaying a sequence should reproduce the same state. Scrambling, undoing, recognizing a case, and following a solution all depend on those agreements.

Further connections [frap-0007]

The total-orientation character can expose a constraint, but what survives when we change the local frame or the dimension? Framed orientations and transfer and dimension develop the first connections: wreath product records positions and local transport, while abelianized characters detect constraints when the allowed generators preserve them [marchetti2025rubik, Section 3.1]. Finite-index transfer organizes related calculations through coset representatives [mayer2015artin, Sections 3.1--3.3]. The dimension calculation explains how a local abelianization can lose this detecting power without deciding reachability.

The following outlooks ask different questions about transport and functions on a move group. Each needs its own construction; neither is a prerequisite for continuing to the layered cubes.

How the cube models connect [frap-001A]

An abstract cube turn records incidence positions and restricts a shape symmetry to one facet. A layered-cube turn records cubies, stickers, frames and allowed layers. For the ordinary three-layer cube these are two descriptions of the same action; the map below identifies their coordinates.

Label transport and topology [frap-0014]

A sequence of puzzle moves may return pieces to their positions while changing their orientations. The first question is what local return map records that change; the cycle criterion computes it. A related question asks how labels move between neighboring simplices. The projectivity construction of Izmestiev and Joswig makes that question precise, and the complete unfolding records the topology of one such transport system [izmestiev2002branched, Sections 2.1--2.3.3].

A comparison between these constructions must name the fiber, the local transport map, and the allowed paths before relating topology to puzzle orientation. The projectivity example supplies one transport vocabulary; the framed cube account supplies a different finite-state calculation. A bridge between them would require an explicit map between those data and a proof that it preserves composition, return maps, and the claimed topology.

Methods and search [frap-0005]

Once the constraints are known, a solver still needs a reasoned way to choose the next move. A solving method organizes a sequence of smaller goals: at each stage, some information has been fixed and some remains free. The useful mathematical question is which moves accomplish the next goal while preserving what has already been achieved. The stabilizer of the recorded data, as in the general construction MulAction.stabilizer, gives language to that requirement; successive stages suggest a chain of subgroups. Conjugation relocates the effect of a known sequence; commutators can help isolate effects, whose actual support must be checked in each example. Salkinder uses conjugates and commutators in the cube setting [salkinder2021nxnxn, Sections 2 and 3.2].

Explaining why a method works and judging how well it serves a player are related but different tasks. A complete method needs an argument that its stages cover the remaining cases and terminate at a solved state. Practical use also depends on recognition, memorization, and ease of execution. Computational search raises further questions about time, memory, and solution length.

Cayley graphs describe move sequences as paths between group elements [pan2021fourier, Section 4, Definition 4]; actions on partial information lead to Schreier graphs. The generic labeled-edge construction is Quiver.SchreierGraph: a move labels an edge from a state to its image. For the left action of a group on itself, this is the Cayley viewpoint. These declarations supply stabilizers and action graphs; they do not establish a puzzle-solving method. Such descriptions become useful when comparing reduction strategies or searching for shorter sequences. Any length comparison must specify the allowed moves and their costs. The goal is to connect a method's observable effect with its mathematical explanation. Scrambling and search functions describe a related use of functions on the move group. The complete layer-first construction is given in a complete layer-first method, while computer solving and efficiency turns these graphs into exact searches, lower bounds and subgroup reductions. This chapter keeps the method next to the graph and stabilizer language needed to analyze it.

Scrambling, spectra and search coordinates [frap-0015]

Random moves raise a different question from solving: when, and how quickly, does a scramble lose information about its starting state? A random walk depends on its allowed moves and their probabilities. We use the fixed-frame three-layer move group from the complete cube account, and write \(G\) for its reachable state group. A walk with step law \(\mu \) has distribution

\[\Pr [X_k=g]=\mu ^{*k}(g)\]

from the solved state, where convolution records the successive moves.

The displayed parity bounds and finite-operator framework are the results established here. They do not claim a full-cube mixing time, a universal scramble length, or a reproduced machine-learning benchmark. Those would require a declared move law, reachable component, statistical or spectral analysis, and independently checked computational evidence.

Status [spin-000S]

Currently it's a work-in-progress.

The complete three-layer cube [frap-0016]

The all-size law has two reasons to be followed by a separate three-layer account. A \(3\times 3\times 3\) cube has no moving centers or coupled edges, and its familiar three tests can be checked directly on the six outer-face generators. We use a fixed frame and unmarked centers. A labelled state is therefore

\[(\sigma ,x;\tau ,z)\in S_8\times (\mathbb Z_3)^8 \times S_{12}\times (\mathbb Z_2)^{12}.\]

Here \(\sigma \) and \(\tau \) record corner and edge positions. The corner frame uses the U/D sticker to record a twist in \(\mathbb Z_3\); an edge frame uses the U/D or F/B sticker convention to record a flip in \(\mathbb Z_2\). The six centers fix the frame but have no recorded orientation. This is the ordinary cube model, rather than a supercube model with marked centers.

The family of layered cubes [frap-0003]

To pass from one cube size to another, we first fix a physical model. Let \(C_N\) be an \(N\times N\times N\) array of unit cubies, with coordinates \((i,j,k)\in {1,...,N}^3\). A visible cubie is a surface cell, so at least one coordinate is \(1\) or \(N\). A cell with three boundary coordinates is a corner; with exactly two it is an edge; with exactly one it is a center. This is a description of positions, before we decide which labels a player can see.

The resulting counts are immediate: there are \(8\) corners, \(12(N-2)\) edge cubies, and \(6(N-2)^2\) center cubies. Their sum is the number of surface cells. The count already explains why changing \(N\) is more than making a larger drawing. New edge and center positions appear, and those positions can fall into different move orbits. Bonzio, Loi and Peruzzi use this cubie decomposition in their general model [bonzio2018nxnxn, Sections 2--3]; Salkinder gives a parallel orbit and configuration analysis [salkinder2021nxnxn, Section 2].

The abstract-to-cube handoff is made explicit in how the cube models connect. In the \(N=3\) case, the 54 facet-colored incidence positions group into corner and edge sticker fibers, with six fixed centers supplying the frame; the resulting action is the fixed-frame coordinate model used later. For larger \(N\), the new center and coupled-edge orbits prevent this from being a single ordinary-cube coordinate space.

The first law for the layered cube [frap-0004]

The layered model needs a reachability test. A tuple of permutations and orientations can describe a dismantled cube without being obtainable by legal slice turns. We now choose the labelled, fixed-frame state space from the layered-cube model and state the first law for that space. The theorem is a statement about labelled cubies; an ordinary colour pattern is its observation after equal-looking labels have been forgotten.

Which moves survive a quotient? [frap-0019]

A quotient of observations is useful only when the moves still have a well-defined action on the observations. Let \(X\) be a declared sticker-state set, let \(G\) act on \(X\), and let the surjection \(q:X\twoheadrightarrow Y\) identify the observations we want to retain (equivalently, take \(Y=q(X)\)). The move \(g\in G\) preserves the fibers of \(q\) when

\[q(x)=q(x')\Longrightarrow q(gx)=q(gx')\qquad (x,x'\in X).\]

Exactly in this case there is a unique induced map \(\bar g:Y\to Y\) with

\[q\circ g=\bar g\circ q.\]

If \(g\) is a bijection and \(X\) is finite, then \(\bar g\) is surjective and therefore bijective. In an infinite setting, require the inverse move to preserve fibers as well. Thus, for the finite cube action below, the fiber-preserving bijections form a subgroup; a quotient does not automatically inherit the whole puzzle action.

The practical rule is short: state the state space, the quotient map and the move action before saying that a move survives identification. The antipodal calculation supplies both a negative and a positive finite example, while preserving the distinction between a shape cover and a compatible puzzle action.

Some tips about Forester [uts-000T]

Technical experiments [uts-0010]

The following are technical experiments on what kind of rich content I can include in my forester notes. See my post on this for more background. Some of my Technical notes (draft) might have additional information.

Lean 4 code highlighting [uts-0003]

The following code is taken from pygments to test Lean 4 code highlighting, with only wrapped strings removed because it confuses forester.

/- Copyright (c) 2017 Johannes Hölzl. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Johannes Hölzl -/ import Mathlib.Order.Chain #align_import order.zorn from "leanprover-community/mathlib"@"46a64b5b4268c594af770c44d9e502afc6a515cb" /-! # Zorn's lemmas This file proves several formulations of Zorn's Lemma. ## Variants The primary statement of Zorn's lemma is `exists_maximal_of_chains_bounded`. Then it is specialized to particular relations: * `(≤)` with `zorn_partialOrder` * `(⊆)` with `zorn_subset` * `(⊇)` with `zorn_superset` Lemma names carry modifiers: * `₀`: Quantifies over a set, as opposed to over a type. * `_nonempty`: Doesn't ask to prove that the empty chain is bounded and lets you give an element that will be smaller than the maximal element found (the maximal element is no smaller than any other element, but it can also be incomparable to some). ## How-to This file comes across as confusing to those who haven't yet used it, so here is a detailed walkthrough: 1. Know what relation on which type/set you're looking for. See Variants above. You can discharge some conditions to Zorn's lemma directly using a `_nonempty` variant. 2. Write down the definition of your type/set, put a `suffices : ∃ m, ∀ a, m ≺ a → a ≺ m, { ... },` (or whatever you actually need) followed by an `apply some_version_of_zorn`. 3. Fill in the details. This is where you start talking about chains. A typical proof using Zorn could look like this (TODO: update to mathlib4) ```lean lemma zorny_lemma : zorny_statement := begin let s : Set α := {x | whatever x}, suffices : ∃ x ∈ s, ∀ y ∈ s, y ⊆ x → y = x, -- or with another operator { exact proof_post_zorn }, apply zorn_subset, -- or another variant rintro c hcs hc, obtain rfl | hcnemp := c.eq_empty_or_nonempty, -- you might need to disjunct on c empty or not { exact ⟨edge_case_construction, proof_that_edge_case_construction_respects_whatever, proof_that_edge_case_construction_contains_all_stuff_in_c⟩ }, exact ⟨construction, proof_that_construction_respects_whatever, proof_that_construction_contains_all_stuff_in_c⟩, end ``` ## Notes Originally ported from Isabelle/HOL. The [original file](https://isabelle.in.tum.de/dist/library/HOL/HOL/Zorn.html) was written by Jacques D. Fleuriot, Tobias Nipkow, Christian Sternagel. -/ open Classical Set variable {α β : Type*} {r : α → α → Prop} {c : Set α} /-- Local notation for the relation being considered. -/ local infixl:50 " ≺ " => r /-- **Zorn's lemma** If every chain has an upper bound, then there exists a maximal element. -/ theorem exists_maximal_of_chains_bounded (h : ∀ c, IsChain r c → ∃ ub, ∀ a ∈ c, a ≺ ub) (trans : ∀ {a b c}, a ≺ b → b ≺ c → a ≺ c) : ∃ m, ∀ a, m ≺ a → a ≺ m := have : ∃ ub, ∀ a ∈ maxChain r, a ≺ ub := h _ <| maxChain_spec.left let ⟨ub, (hub : ∀ a ∈ maxChain r, a ≺ ub)⟩ := this ⟨ub, fun a ha => have : IsChain r (insert a <| maxChain r) := maxChain_spec.1.insert fun b hb _ => Or.inr <| trans (hub b hb) ha hub a <| by rw [maxChain_spec.right this (subset_insert _ _)] exact mem_insert _ _⟩ #align exists_maximal_of_chains_bounded exists_maximal_of_chains_bounded /-- A variant of Zorn's lemma. If every nonempty chain of a nonempty type has an upper bound, then there is a maximal element. -/ theorem exists_maximal_of_nonempty_chains_bounded [Nonempty α] (h : ∀ c, IsChain r c → c.Nonempty → ∃ ub, ∀ a ∈ c, a ≺ ub) (trans : ∀ {a b c}, a ≺ b → b ≺ c → a ≺ c) : ∃ m, ∀ a, m ≺ a → a ≺ m := exists_maximal_of_chains_bounded (fun c hc => (eq_empty_or_nonempty c).elim (fun h => ⟨Classical.arbitrary α, fun x hx => (h ▸ hx : x ∈ (∅ : Set α)).elim⟩) (h c hc)) trans #align exists_maximal_of_nonempty_chains_bounded exists_maximal_of_nonempty_chains_bounded section Preorder variable [Preorder α] theorem zorn_preorder (h : ∀ c : Set α, IsChain (· ≤ ·) c → BddAbove c) : ∃ m : α, ∀ a, m ≤ a → a ≤ m := exists_maximal_of_chains_bounded h le_trans #align zorn_preorder zorn_preorder theorem zorn_nonempty_preorder [Nonempty α] (h : ∀ c : Set α, IsChain (· ≤ ·) c → c.Nonempty → BddAbove c) : ∃ m : α, ∀ a, m ≤ a → a ≤ m := exists_maximal_of_nonempty_chains_bounded h le_trans #align zorn_nonempty_preorder zorn_nonempty_preorder theorem zorn_preorder₀ (s : Set α) (ih : ∀ c ⊆ s, IsChain (· ≤ ·) c → ∃ ub ∈ s, ∀ z ∈ c, z ≤ ub) : ∃ m ∈ s, ∀ z ∈ s, m ≤ z → z ≤ m := let ⟨⟨m, hms⟩, h⟩ := @zorn_preorder s _ fun c hc => let ⟨ub, hubs, hub⟩ := ih (Subtype.val '' c) (fun _ ⟨⟨_, hx⟩, _, h⟩ => h ▸ hx) (by rintro _ ⟨p, hpc, rfl⟩ _ ⟨q, hqc, rfl⟩ hpq refine' hc hpc hqc fun t => hpq (Subtype.ext_iff.1 t)) ⟨⟨ub, hubs⟩, fun ⟨y, hy⟩ hc => hub _ ⟨_, hc, rfl⟩⟩ ⟨m, hms, fun z hzs hmz => h ⟨z, hzs⟩ hmz⟩ #align zorn_preorder₀ zorn_preorder₀ theorem zorn_nonempty_preorder₀ (s : Set α) (ih : ∀ c ⊆ s, IsChain (· ≤ ·) c → ∀ y ∈ c, ∃ ub ∈ s, ∀ z ∈ c, z ≤ ub) (x : α) (hxs : x ∈ s) : ∃ m ∈ s, x ≤ m ∧ ∀ z ∈ s, m ≤ z → z ≤ m := by -- Porting note: the first three lines replace the following two lines in mathlib3. -- The mathlib3 `rcases` supports holes for proof obligations, this is not yet implemented in 4. -- rcases zorn_preorder₀ ({ y ∈ s | x ≤ y }) fun c hcs hc => ?_ with ⟨m, ⟨hms, hxm⟩, hm⟩ -- · exact ⟨m, hms, hxm, fun z hzs hmz => hm _ ⟨hzs, hxm.trans hmz⟩ hmz⟩ have H := zorn_preorder₀ ({ y ∈ s | x ≤ y }) fun c hcs hc => ?_ · rcases H with ⟨m, ⟨hms, hxm⟩, hm⟩ exact ⟨m, hms, hxm, fun z hzs hmz => hm _ ⟨hzs, hxm.trans hmz⟩ hmz⟩ · rcases c.eq_empty_or_nonempty with (rfl | ⟨y, hy⟩) · exact ⟨x, ⟨hxs, le_rfl⟩, fun z => False.elim⟩ · rcases ih c (fun z hz => (hcs hz).1) hc y hy with ⟨z, hzs, hz⟩ exact ⟨z, ⟨hzs, (hcs hy).2.trans <| hz _ hy⟩, hz⟩ #align zorn_nonempty_preorder₀ zorn_nonempty_preorder₀ theorem zorn_nonempty_Ici₀ (a : α) (ih : ∀ c ⊆ Ici a, IsChain (· ≤ ·) c → ∀ y ∈ c, ∃ ub, ∀ z ∈ c, z ≤ ub) (x : α) (hax : a ≤ x) : ∃ m, x ≤ m ∧ ∀ z, m ≤ z → z ≤ m := by let ⟨m, _, hxm, hm⟩ := zorn_nonempty_preorder₀ (Ici a) (fun c hca hc y hy ↦ ?_) x hax · exact ⟨m, hxm, fun z hmz => hm _ (hax.trans <| hxm.trans hmz) hmz⟩ · have ⟨ub, hub⟩ := ih c hca hc y hy; exact ⟨ub, (hca hy).trans (hub y hy), hub⟩ #align zorn_nonempty_Ici₀ zorn_nonempty_Ici₀ end Preorder section PartialOrder variable [PartialOrder α] theorem zorn_partialOrder (h : ∀ c : Set α, IsChain (· ≤ ·) c → BddAbove c) : ∃ m : α, ∀ a, m ≤ a → a = m := let ⟨m, hm⟩ := zorn_preorder h ⟨m, fun a ha => le_antisymm (hm a ha) ha⟩ #align zorn_partial_order zorn_partialOrder theorem zorn_nonempty_partialOrder [Nonempty α] (h : ∀ c : Set α, IsChain (· ≤ ·) c → c.Nonempty → BddAbove c) : ∃ m : α, ∀ a, m ≤ a → a = m := let ⟨m, hm⟩ := zorn_nonempty_preorder h ⟨m, fun a ha => le_antisymm (hm a ha) ha⟩ #align zorn_nonempty_partial_order zorn_nonempty_partialOrder theorem zorn_partialOrder₀ (s : Set α) (ih : ∀ c ⊆ s, IsChain (· ≤ ·) c → ∃ ub ∈ s, ∀ z ∈ c, z ≤ ub) : ∃ m ∈ s, ∀ z ∈ s, m ≤ z → z = m := let ⟨m, hms, hm⟩ := zorn_preorder₀ s ih ⟨m, hms, fun z hzs hmz => (hm z hzs hmz).antisymm hmz⟩ #align zorn_partial_order₀ zorn_partialOrder₀ theorem zorn_nonempty_partialOrder₀ (s : Set α) (ih : ∀ c ⊆ s, IsChain (· ≤ ·) c → ∀ y ∈ c, ∃ ub ∈ s, ∀ z ∈ c, z ≤ ub) (x : α) (hxs : x ∈ s) : ∃ m ∈ s, x ≤ m ∧ ∀ z ∈ s, m ≤ z → z = m := let ⟨m, hms, hxm, hm⟩ := zorn_nonempty_preorder₀ s ih x hxs ⟨m, hms, hxm, fun z hzs hmz => (hm z hzs hmz).antisymm hmz⟩ #align zorn_nonempty_partial_order₀ zorn_nonempty_partialOrder₀ end PartialOrder theorem zorn_subset (S : Set (Set α)) (h : ∀ c ⊆ S, IsChain (· ⊆ ·) c → ∃ ub ∈ S, ∀ s ∈ c, s ⊆ ub) : ∃ m ∈ S, ∀ a ∈ S, m ⊆ a → a = m := zorn_partialOrder₀ S h #align zorn_subset zorn_subset theorem zorn_subset_nonempty (S : Set (Set α)) (H : ∀ c ⊆ S, IsChain (· ⊆ ·) c → c.Nonempty → ∃ ub ∈ S, ∀ s ∈ c, s ⊆ ub) (x) (hx : x ∈ S) : ∃ m ∈ S, x ⊆ m ∧ ∀ a ∈ S, m ⊆ a → a = m := zorn_nonempty_partialOrder₀ _ (fun _ cS hc y yc => H _ cS hc ⟨y, yc⟩) _ hx #align zorn_subset_nonempty zorn_subset_nonempty theorem zorn_superset (S : Set (Set α)) (h : ∀ c ⊆ S, IsChain (· ⊆ ·) c → ∃ lb ∈ S, ∀ s ∈ c, lb ⊆ s) : ∃ m ∈ S, ∀ a ∈ S, a ⊆ m → a = m := (@zorn_partialOrder₀ (Set α)ᵒᵈ _ S) fun c cS hc => h c cS hc.symm #align zorn_superset zorn_superset theorem zorn_superset_nonempty (S : Set (Set α)) (H : ∀ c ⊆ S, IsChain (· ⊆ ·) c → c.Nonempty → ∃ lb ∈ S, ∀ s ∈ c, lb ⊆ s) (x) (hx : x ∈ S) : ∃ m ∈ S, m ⊆ x ∧ ∀ a ∈ S, a ⊆ m → a = m := @zorn_nonempty_partialOrder₀ (Set α)ᵒᵈ _ S (fun _ cS hc y yc => H _ cS hc.symm ⟨y, yc⟩) _ hx #align zorn_superset_nonempty zorn_superset_nonempty /-- Every chain is contained in a maximal chain. This generalizes Hausdorff's maximality principle. -/ theorem IsChain.exists_maxChain (hc : IsChain r c) : ∃ M, @IsMaxChain _ r M ∧ c ⊆ M := by -- Porting note: the first three lines replace the following two lines in mathlib3. -- The mathlib3 `obtain` supports holes for proof obligations, this is not yet implemented in 4. -- obtain ⟨M, ⟨_, hM₀⟩, hM₁, hM₂⟩ := -- zorn_subset_nonempty { s | c ⊆ s ∧ IsChain r s } _ c ⟨Subset.rfl, hc⟩ have H := zorn_subset_nonempty { s | c ⊆ s ∧ IsChain r s } ?_ c ⟨Subset.rfl, hc⟩ · obtain ⟨M, ⟨_, hM₀⟩, hM₁, hM₂⟩ := H exact ⟨M, ⟨hM₀, fun d hd hMd => (hM₂ _ ⟨hM₁.trans hMd, hd⟩ hMd).symm⟩, hM₁⟩ rintro cs hcs₀ hcs₁ ⟨s, hs⟩ refine' ⟨⋃₀cs, ⟨fun _ ha => Set.mem_sUnion_of_mem ((hcs₀ hs).left ha) hs, _⟩, fun _ => Set.subset_sUnion_of_mem⟩ rintro y ⟨sy, hsy, hysy⟩ z ⟨sz, hsz, hzsz⟩ hyz obtain rfl | hsseq := eq_or_ne sy sz · exact (hcs₀ hsy).right hysy hzsz hyz cases' hcs₁ hsy hsz hsseq with h h · exact (hcs₀ hsz).right (h hysy) hzsz hyz · exact (hcs₀ hsy).right hysy (h hzsz) hyz #align is_chain.exists_max_chain IsChain.exists_maxChain -- other bits of tricky syntax variable {ι A B : Type*} (𝒜 : ι → A) (ℬ : ι → B) #check `𝒜.a #check ``𝒜 #check List.get! #check 1.0 + 2. + 0.3e1 #check "\ This is\na \ wrapped string." @[to_additive "See note [foo]"] lemma mul_one : sorry := sorry

Test TwistySim [uts-016I]

For docs, see TwistySim.

TwistySim is the work of Conrad Rider, licensed under the Creative Commons Attribution-ShareAlike 4.0 International License (CC BY-SA 4.0).

Test Twizzle [uts-016H]

R U2' R2 U' R2 U' R2 U2' R y' M2' U M U2 M' U M2
- API: cubing.js and full API reference
- available options for TwistyPlayer: twisty-player-config
- demo for TwistyAlgVier: twisty-alg-viewer demo for use of alg viewer, inspired by A supplement to "Commutators in the Rubik's Cube Group"

test 3D Bifurcation Diagram [uts-001C]

Adapted from ⧉:

#storage atomic_storage array<atomic<i32>>

const MaxSamples = 256.0;
const FOV = 0.6;
const PI = 3.14159265;
const TWO_PI = 6.28318530718;
const STEP = 0.01;
const LARGENUM = 1e10;
const ATOMIC_SCALE = 1024.0;

struct Camera
{
  pos: float3,
  cam: float3x3,
  fov: float,
  size: float2
}

struct Ray
{
    ro: float3,
    rd: float3,
}

var<private> camera : Camera;
var<private> state : uint4;

fn pcg4d(a: uint4) -> uint4
{
	var v = a * 1664525u + 1013904223u;
    v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z;
    v = v ^  ( v >> uint4(16u) );
    v.x += v.y*v.w; v.y += v.z*v.x; v.z += v.x*v.y; v.w += v.y*v.z;
    return v;
}

fn rand4() -> float4
{
    state = pcg4d(state);
    return float4(state)/float(0xffffffffu);
}

fn nrand4(sigma: float, mean: float4) -> float4
{
    let Z = rand4();
    return mean + sigma * sqrt(-2.0 * log(Z.xxyy)) *
           float4(cos(TWO_PI * Z.z),sin(TWO_PI * Z.z),cos(TWO_PI * Z.w),sin(TWO_PI * Z.w));
}

fn udir(rng: float2) -> float3
{
    let r = float2(2.*PI*rng.x, acos(2.*rng.y - 1.0));
    let c = cos(r);
    let s = sin(r);
    return float3(c.x*s.y, s.x*s.y, c.y);
}

fn disk(rng: float2) -> float2
{
    return float2(sin(TWO_PI*rng.x), cos(TWO_PI*rng.x))*sqrt(rng.y);
}

fn Rotate(t: float) -> float2x2
{
    return float2x2(
        cos(t), sin(t),
      - sin(t), cos(t),
    );
}

fn RotXY(x: float3, t: float) -> float3
{
    return float3(Rotate(t)*x.xy, x.z);
}

fn GetCameraMatrix(ang: float2) -> float3x3
{
    let x_dir = float3(cos(ang.x)*sin(ang.y), cos(ang.y), sin(ang.x)*sin(ang.y));
    let y_dir = normalize(cross(x_dir, float3(0.0,1.0,0.0)));
    let z_dir = normalize(cross(y_dir, x_dir));
    return float3x3(-x_dir, y_dir, z_dir);
}

fn SetCamera()
{
    let screen_size = int2(textureDimensions(screen));
    let screen_size_f = float2(screen_size);
    let ang = float2(mouse.pos.xy)*float2(-TWO_PI, PI)/screen_size_f + float2(0.4, 0.4);

    camera.fov = FOV;
    camera.cam = GetCameraMatrix(ang);
    camera.pos = - (camera.cam*float3(8.0*custom.Radius+0.5,0.0,0.0));
    camera.size = screen_size_f;
}

//project to clip space
fn Project(cam: Camera, p: float3) -> float3
{
    let td = distance(cam.pos, p);
    let dir = (p - cam.pos)/td;
    let screen = dir*cam.cam;
    return float3(screen.yz*cam.size.y/(cam.fov*screen.x) + 0.5*cam.size,screen.x*td);
}

const max_iterations = 256;
const color_thresholds = float4(255.0, 130.0, 80.0, 255.0);


fn AdditiveBlend(color: float3, depth: float, index: int)
{
    let scaledColor = int3(floor(ATOMIC_SCALE*color/(depth*depth + 0.2) + rand4().xyz));

    if(scaledColor.x>0)
    {
        atomicAdd(&atomic_storage[index*4+0], scaledColor.x);
    }

    if(scaledColor.y>0)
    {
        atomicAdd(&atomic_storage[index*4+1], scaledColor.y);
    }

    if(scaledColor.z>0)
    {
        atomicAdd(&atomic_storage[index*4+2], scaledColor.z);
    }
}

fn RasterizePoint(pos: float3, color: float3)
{
    let screen_size = int2(camera.size);
    let projectedPos = Project(camera, pos);
    let screenCoord = int2(projectedPos.xy+0.5*rand4().xy);

    //outside of our view
    if(screenCoord.x < 0 || screenCoord.x >= screen_size.x ||
        screenCoord.y < 0 || screenCoord.y >= screen_size.y || projectedPos.z < 0.0)
    {
        return;
    }

    let idx = screenCoord.x + screen_size.x * screenCoord.y;
    AdditiveBlend(color, projectedPos.z, idx);
}


fn saturate(x: f32) -> f32 {
    return min(1.0, max(0.0, x));
}

fn saturate_vec3(x: vec3<f32>) -> vec3<f32> {
    return min(vec3<f32>(1.0, 1.0, 1.0), max(vec3<f32>(0.0, 0.0, 0.0), x));
}

fn bump3y(x: vec3<f32>, yoffset: vec3<f32>) -> vec3<f32> {
    var y: vec3<f32> = vec3<f32>(1.0, 1.0, 1.0) - x * x;
    y = saturate_vec3(y - yoffset);
    return y;
}

fn spectral_zucconi(w: f32) -> vec3<f32> {
    let x: f32 = saturate((w - 400.0) / 300.0);

    let cs: vec3<f32> = vec3<f32>(3.54541723, 2.86670055, 2.29421995);
    let xs: vec3<f32> = vec3<f32>(0.69548916, 0.49416934, 0.28269708);
    let ys: vec3<f32> = vec3<f32>(0.02320775, 0.15936245, 0.53520021);

    return bump3y(cs * (x - xs), ys);
}

fn hue(v: float) -> float3 {
    return .6 + .6 * cos(6.3 * v + float3(0.,23.,21.));
}

fn bifurcation(iters: i32) {
    var p = rand4().xyz * float3(4.0, 4.0, 5.0) - float3(2.0, 2.0, -0.25);
    let s = rand4().x;

    let alpha = custom.Alpha;
    let beta = custom.Beta + custom.BetaAnim*(0.5 * sin(time.elapsed) + 0.5) + custom.BetaS*s;


    for (var j: i32 = 0; j <= iters; j = j + 1) {
        let p0 = p;
        p.x = p.z - p0.y*(beta * p0.x + (1.0 - beta) * p0.y);
        p.y = p0.x + p0.y * p0.y * alpha;

        if(j < iters - int(custom.Samples*MaxSamples + 1.0)) {continue;}
        var color = spectral_zucconi(350 + 350.0*s);
        color = pow(color, vec3(1.0));
        RasterizePoint(float3(1,1,2.5)*(p - float3(0.5, 0.5, 1.5)), 32.0*color/(custom.Samples*MaxSamples + 1.0));
    }
}

@compute @workgroup_size(16, 16)
fn Clear(@builtin(global_invocation_id) id: uint3) {
    let screen_size = int2(textureDimensions(screen));
    let idx0 = int(id.x) + int(screen_size.x * int(id.y));

    atomicStore(&atomic_storage[idx0*4+0], 0);
    atomicStore(&atomic_storage[idx0*4+1], 0);
    atomicStore(&atomic_storage[idx0*4+2], 0);
    atomicStore(&atomic_storage[idx0*4+3], 0);
}

@compute @workgroup_size(16, 16)
fn Rasterize(@builtin(global_invocation_id) id: uint3)
{
    SetCamera();

    //RNG state
    state = uint4(id.x, id.y, id.z, uint(custom.NoiseAnimation)*time.frame);

    bifurcation(int(MaxSamples*2.0));
}

fn Sample(pos: int2) -> float3
{
    let screen_size = int2(textureDimensions(screen));
    let idx = pos.x + screen_size.x * pos.y;

    var color: float3;

    let x = float(atomicLoad(&atomic_storage[idx*4+0]));
    let y = float(atomicLoad(&atomic_storage[idx*4+1]));
    let z = float(atomicLoad(&atomic_storage[idx*4+2]));

    color = float3(x,y,z)/ATOMIC_SCALE;

    return abs(color);
}

@compute @workgroup_size(16, 16)
fn FinalPass(@builtin(global_invocation_id) id: uint3)
{
    let screen_size = uint2(textureDimensions(screen));

    // Prevent overdraw for workgroups on the edge of the viewport
    if (id.x >= screen_size.x || id.y >= screen_size.y) { return; }

    // Pixel coordinates (centre of pixel, origin at bottom left)
    let fragCoord = float2(float(id.x) + .5, float(id.y) + .5);


    let oldColor = textureLoad(pass_in, int2(id.xy), 0, 0);

    var color = float4(Sample(int2(id.xy)), 1.0);


    if(mouse.click != 1)
    {
       color += oldColor * custom.Accumulation;
    }

    let exposed = 1.0 - exp(-5.0*custom.Exposure*color.xyz/color.w);

    // Output to buffer
    textureStore(pass_out, int2(id.xy), 0, color);


    textureStore(screen, int2(id.xy), float4(exposed, 1.));
}

test GiNaC (WASM) [uts-001H]

This example evaluates symbolic expressions locally in the browser.

Computing symbolic expressions…

test Keenan Crane's style for rendering edge and shade [uts-001D]

Adapted from ⧉:

#define EDGE_WIDTH 0.12
#define RAYMARCH_ITERATIONS 35
#define SHADOW_ITERATIONS 40
#define SHADOW_STEP 1.0
#define SHADOW_SMOOTHNESS 256.0
#define SHADOW_DARKNESS 0.75

// Distance functions from iquilezles.org
float fSubtraction(float a, float b) {return max(-a,b);}
float fIntersection(float d1, float d2) {return max(d1,d2);}
void fUnion(inout float d1, float d2) {d1 = min(d1,d2);}
float pSphere(vec3 p, float s) {return length(p)-s;}
float pRoundBox(vec3 p, vec3 b, float r) {return length(max(abs(p)-b,0.0))-r;}
float pTorus(vec3 p, vec2 t) {vec2 q = vec2(length(p.xz)-t.x,p.y); return length(q)-t.y;}
float pTorus2(vec3 p, vec2 t) {vec2 q = vec2(length(p.xy)-t.x,p.z); return length(q)-t.y;}
float pCapsule(vec3 p, vec3 a, vec3 b, float r) {vec3 pa = p - a, ba = b - a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); return length( pa - ba*h ) - r;}

float mySDF(vec3 p) {
  float x = p.x;
  float y = p.y;
  float z = p.z;

  float x2 = pow(x, 2.);
  float y2 = pow(y, 2.);
  float z2 = pow(z, 2.);

  float x3 = pow(x, 3.);
  float y3 = pow(y, 3.);
  float z3 = pow(z, 3.);

  float x4 = pow(x, 4.);
  float y4 = pow(y, 4.);
  float z4 = pow(z, 4.);

  // return (y-x2)*(z-x3);

  // return 5.*(z2+y3-y4-x2*y2);

  // return (y2-x2-z2);

  return (x2-z2*y2+y3);
}

float distf(vec3 p)
{
  float d = 100000.0;

  fUnion(d, pRoundBox(vec3(0,0,10) + p, vec3(21,21,1), 1.0));
  fUnion(d, pSphere(vec3(10,10,0) + p, 8.0));
  fUnion(d, pSphere(vec3(16,0,4) + p, 4.0));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(15,15,-6.5), 1.5));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(5,15,-6.5), 1.5));
  fUnion(d, pCapsule(p, vec3(10,10,12), vec3(10,5,-6.5), 1.5));
  fUnion(d, pTorus(vec3(15,-15,0) + p, vec2(6,2)));
  fUnion(d, pTorus2(vec3(10,-15,0) + p, vec2(6,2)));
  fUnion(d, pRoundBox(vec3(-10,10,-2) + p, vec3(1,1,9), 1.0));
  fUnion(d, pRoundBox(vec3(-10,10,-4) + p, vec3(0.5,6,0.5), 1.0));
  fUnion(d, pRoundBox(vec3(-10,10,2) + p, vec3(6,0.5,0.5), 1.0));

  // d = mySDF(p);
  // d = fIntersection(d, pSphere(p,15.0));

  return d;
}


vec3 grad(vec3 p)
{
  const float eps = 0.01;
  float m;
    vec3 d_distf = vec3( (distf(vec3(p.x-eps,p.y,p.z)) - distf(vec3(p.x+eps,p.y,p.z))),
                   (distf(vec3(p.x,p.y-eps,p.z)) - distf(vec3(p.x,p.y+eps,p.z))),
                   (distf(vec3(p.x,p.y,p.z-eps)) - distf(vec3(p.x,p.y,p.z+eps)))
         );
    return d_distf / (2.*eps);
}

vec3 grad2(vec3 p)
{
  const float eps = 0.01;
  float m;
    vec3 d_grad = vec3( (grad(vec3(p.x-eps,p.y,p.z)) - grad(vec3(p.x+eps,p.y,p.z))).x,
                   (grad(vec3(p.x,p.y-eps,p.z)) - grad(vec3(p.x,p.y+eps,p.z))).y,
                   (grad(vec3(p.x,p.y,p.z-eps)) - grad(vec3(p.x,p.y,p.z+eps)).z)
         );
    return d_grad / (2.*eps);
}

vec3 normal(vec3 p)
{
    return normalize(grad(p));
}


vec4 raymarch(vec3 from, vec3 increment)
{
  const float maxDist = 200.0;
  const float minDist = 0.001;
  const int maxIter = RAYMARCH_ITERATIONS;

  float dist = 0.0;

  float lastDistEval = 1e10;
  float edge = 0.0;

  for(int i = 0; i < maxIter; i++) {
    vec3 pos = (from + increment * dist);
    float distEval = distf(pos);

    if (lastDistEval < EDGE_WIDTH && distEval > lastDistEval + 0.001) {
      edge = 1.0;
    }


    if (distEval < minDist) {
      break;
    }

    dist += distEval;
    if (distEval < lastDistEval) lastDistEval = distEval;
  }

  float mat = 1.0;
  if (dist >= maxDist) mat = 0.0;

  return vec4(dist, mat, edge, 0);
}

float shadow(vec3 from, vec3 increment)
{
  const float minDist = 1.0;

  float res = 1.0;
  float t = 1.0;
  for(int i = 0; i < SHADOW_ITERATIONS; i++) {
    float h = distf(from + increment * t);
    if(h < minDist)
      return 0.0;

      res = min(res, SHADOW_SMOOTHNESS * h / t);
      t += SHADOW_STEP;
    }
    return res;
}

float rand(float x)
{
  return fract(sin(x) * 43758.5453);
}

float triangle(float x)
{
  return abs(1.0 - mod(abs(x), 2.0)) * 2.0 - 1.0;
}

// Camera localized normal
vec3 campos, camup;
vec3 localNormal(vec3 p, vec3 rd) {
    vec3 n = normal(p), ln;
    vec3 side = cross(campos, camup);
    return vec3(dot(n,  side), dot(n,  camup), dot(n,  -rd));
}

float time;
vec4 getPixel(vec2 p, vec3 from, vec3 increment, vec3 light)
{
  vec4 c = raymarch(from, increment);
  vec3 hitPos = from + increment * c.x;
  vec3 normalDir = normal(hitPos);


  float diffuse = 1.0 + min(0.0, dot(normalDir, -light));
  float inshadow = (1.0 - shadow(hitPos, -light)) * SHADOW_DARKNESS;

  // diffuse = max(diffuse, inshadow);

  // if it's not edge, and no contact
  if (c.z != 1.0 && c.y == 0.0) return vec4(0.96, 0.94, 0.87,1);

  float low = 0.05;
  float high = 0.95;

  diffuse = diffuse > high ? 1.0 : (diffuse > low ? low : diffuse);

  vec4 mCol = mix(vec4(vec3(0.78, 0.76, 0.78) * 1.15,1), vec4(0.70, 0.68, 0.75,1), diffuse);

  float gridStep = 1.0;

  // optional chess style grid
  // mCol = mix(mCol,vec4(0.,0.,0.,1.0),0.1*mod(floor(hitPos.x/gridStep)+floor(hitPos.y/gridStep)+floor(hitPos.z/gridStep),2.));

  float dt = dot(vec3(0., 0., 1.), normalDir);

  float eps_high = 0.02;
  float eps_low = 0.00000; //1;

  // vec3 n = vec4(normalDir, 0.).xyz;
  vec3 n = localNormal(hitPos, increment); // grad(hitPos); // localNormal(hitPos, increment);
  vec3 ez = vec3(0.,0.,0.1);

  // https://www.shadertoy.com/view/fsGXzc

  const float MAX_DIST = 200.0;
  float depth = distance(from, hitPos); // /MAX_DIST;

    // I've mostly just copied and pasted Evan's code.
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Compute curvature
	vec3 dx = dFdx(n);
	vec3 dy = dFdy(n);
	vec3 xneg = n - dx;
	vec3 xpos = n + dx;
	vec3 yneg = n - dy;
	vec3 ypos = n + dy;
	float curvature = (cross(xneg, xpos).y - cross(yneg, ypos).x) * 4.0 / (depth);

  // curvature debug
  // return vec4(vec3(1,0.9,0.8) * 0.7 * (abs(curvature) * 500.),1);
  ;
  vec4 curve_color = vec4(mix(vec3(1,0.9,0.8) * 0.5, vec3(1.), 0.5), 1.); // * length(grad(hitPos).xy) //  * abs(curvature) * 200.

  float curvature_eps = 0.0001;
  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).z) > eps_low && ( length(grad(hitPos).z) < eps_high * abs(curvature) * 100. ) ) {
      return curve_color;
  }

  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).y) > eps_low && ( length(grad(hitPos).y) < eps_high * abs(curvature) * 100.) ) {
      return curve_color;
  }

  if(c.z != 1. && abs(curvature) > curvature_eps && length(grad(hitPos).x) > eps_low && ( length(grad(hitPos).x) < eps_high * abs(curvature) * 100.) ) {
      return curve_color;
  }

  if(c.z != 1. && length(grad(hitPos).z) > eps_low && ( length(grad(hitPos).z) < eps_high) ) {
      // return vec4(mix(vec3(1,0.9,0.8) * 0.5, vec3(1.), 0.5), 1.); // * (abs(curvature) * 1000.),1);
  }

  // .z is edge
  mCol = mix(mCol,vec4(vec3(1,0.9,0.8) * 0.5,1),c.z);

  return mCol;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  time = floor(iTime * 16.0) / 16.0;
  // pixel position
  vec2 q = fragCoord.xy / iResolution.xy;
  vec2 p = -1.0+2.0*q;
  p.x *= -iResolution.x/iResolution.y;

  // mouse
  vec2 mo = iMouse.xy/iResolution.xy;
  vec2 m = iMouse.xy / iResolution.xy;
  if (iMouse.x == 0.0 && iMouse.y == 0.0) {
    m = vec2(time * 0.06 + 1.67, 0.78);
  }
  m = -1.0 + 2.0 * m;
  m *= vec2(4.0,-0.75);
  m.y += 0.75;

  // camera position
  float dist = 65.0;
  vec3 ta = vec3(0,0,0);
  vec3 ro = vec3(cos(m.x) * cos(m.y) * dist, sin(m.x) * cos(m.y) * dist, sin(m.y) * dist);
  vec3 light = vec3(cos(m.x - 2.27) * 50.0, sin(m.x - 2.27) * 50.0, -20.0);

  // camera direction
  vec3 cw = normalize( ta-ro );
  vec3 cp = vec3( 0.0, 0.0, 1.0 );
  vec3 cu = normalize( cross(cw,cp) );
  vec3 cv = normalize( cross(cu,cw) );
  vec3 rd = normalize( p.x*cu + p.y*cv + 2.5*cw );
  campos = -cw, camup = cv;

  // calculate color
  vec4 col = getPixel(p, ro, rd, normalize(light));
  col = pow(col, vec4(1.0 / 2.2));
  col = col * 1.8 - 0.8;
  fragColor = col;
}

Color extracted with the help of ⧉.

See also:

- toon shader
- GLSL Fragment Shader: Sobel Edge Detection
- Cross hatching WebGL shader
- Penumbra Maps: Approximate Soft Shadows in Real-Time
- stackgl/glsl-lighting-walkthrough

test Rhai scripting [uts-001P]

For now, open the console to see the output.

test Three.js and shaders [uts-0015]

test algorithms [uts-001F]

test compute shaders [uts-001E]

test d3-graphviz [uts-001Q]

Same as test graphviz, but with d3-graphviz. There is no obvious difference as zooming and panning are disabled to avoid distraction.

Adapted from ⧉:

strict digraph "" { graph [bgcolor=transparent]; node [label="\N", penwidth=1.8 ]; edge [arrowhead=vee]; QuadraticForm [color=green, fillcolor="#B0ECA3", label=QuadraticForm, shape=box, style=filled]; LinearMap [color=green, fillcolor="#B0ECA3", label=LinearMap, shape=box, style=filled]; Dual [color=green, fillcolor="#B0ECA3", label=Dual, shape=box, style=filled]; LinearMap -> Dual [style=dashed]; TensorAlgebra [color=green, fillcolor="#B0ECA3", label=TensorAlgebra, shape=box, style=filled]; LinearMap -> TensorAlgebra [style=dashed]; Group [color=green, fillcolor="#B0ECA3", label=Group, shape=box, style=filled]; Ring [color=green, fillcolor="#B0ECA3", label=Ring, shape=box, style=filled]; Group -> Ring [style=dashed]; Module [color=green, fillcolor="#B0ECA3", label=Module, shape=box, style=filled]; Ring -> Module [style=dashed]; DivisionRing [color=green, fillcolor="#B0ECA3", label=DivisionRing, shape=box, style=filled]; Ring -> DivisionRing [style=dashed]; RingHom [color=green, fillcolor="#B0ECA3", label=RingHom, shape=box, style=filled]; Ring -> RingHom [style=dashed]; Module -> LinearMap [style=dashed]; VectorSpace [color=green, fillcolor="#B0ECA3", label=VectorSpace, shape=box, style=filled]; Module -> VectorSpace [style=dashed]; Algebra [color=green, fillcolor="#B0ECA3", label=Algebra, shape=box, style=filled]; Module -> Algebra [style=dashed]; RingQuot [color=green, fillcolor="#B0ECA3", label=RingQuot, shape=box, style=filled]; Module -> RingQuot [style=dashed]; BilinForm [color=green, fillcolor="#B0ECA3", label=BilinForm, shape=box, style=filled]; Module -> BilinForm [style=dashed]; FreeAlgebra [color=green, fillcolor="#B0ECA3", label=FreeAlgebra, shape=box, style=filled]; Algebra -> FreeAlgebra [style=dashed]; RingQuot -> FreeAlgebra [style=dashed]; BilinForm -> QuadraticForm [style=dashed]; DivisionRing -> VectorSpace [style=dashed]; RingHom -> LinearMap [style=dashed]; RingHom -> Algebra [style=dashed]; RingHom -> RingQuot [style=dashed]; AlgHom [color=green, fillcolor="#B0ECA3", label=AlgHom, shape=box, style=filled]; RingHom -> AlgHom [style=dashed]; FreeAlgebra -> TensorAlgebra [style=dashed]; Monoid [color=green, fillcolor="#B0ECA3", label=Monoid, shape=box, style=filled]; Monoid -> Ring [style=dashed];}

Adapted from ⧉:

digraph { graph [rankdir=LR]; node [shape=record]; 0 [label="0 | [&bull; S, $]\n[S &rarr; &bull; a S b, $]\n[S &rarr; &bull;, $]"]; 1 [label="1 | [S &bull;, $]"]; 2 [label="2 | [S &rarr; a &bull; S b, $]\n[S &rarr; &bull; a S b, b]\n[S &rarr; &bull;, b]"]; 3 [label="3 | [S &rarr; a S &bull; b, $]"]; 4 [label="4 | [S &rarr; a &bull; S b, b]\n[S &rarr; &bull; a S b, b]\n[S &rarr; &bull;, b]"]; 5 [label="5 | [S &rarr; a S b &bull;, $]"]; 6 [label="6 | [S &rarr; a S &bull; b, b]"]; 7 [label="7 | [S &rarr; a S b &bull;, b]"]; 0 -> 1 [label=S]; 0 -> 2 [label=a]; 2 -> 3 [label=S]; 2 -> 4 [label=a]; 3 -> 5 [label=b]; 4 -> 6 [label=S]; 4 -> 4 [label=a]; 6 -> 7 [label=b]; }

Adapted from ⧉:

digraph G { node [shape=plaintext fontname="Arial"]; 0 -> 1 [arrowhead=none]; 1 -> 2 [arrowhead=none]; 2 -> 7 [arrowhead=none]; 7 -> 8 [arrowhead=none]; 8 -> 9 [arrowhead=none]; 8 -> 10 [arrowhead=none]; 9 -> 10 [color="#aaaaaa" constraint=false]; 10 -> 11 [arrowhead=none]; 10 -> 12 [arrowhead=none]; 11 -> 12 [color="#aaaaaa" constraint=false]; 7 -> 13 [arrowhead=none]; 8 -> 13 [color="#aaaaaa" constraint=false]; 13 -> 14 [arrowhead=none]; 7 -> 15 [arrowhead=none]; 13 -> 15 [color="#aaaaaa" constraint=false]; 15 -> 16 [arrowhead=none]; 15 -> 17 [arrowhead=none]; 16 -> 17 [color="#aaaaaa" constraint=false]; 2 -> 3 [arrowhead=none]; 7 -> 3 [color="#aaaaaa" constraint=false]; 3 -> 4 [arrowhead=none]; 2 -> 5 [arrowhead=none]; 3 -> 5 [color="#aaaaaa" constraint=false]; 5 -> 6 [arrowhead=none]; 2 -> 18 [arrowhead=none]; 5 -> 18 [color="#aaaaaa" constraint=false]; 18 -> 19 [arrowhead=none]; 19 -> 20 [arrowhead=none]; 19 -> 21 [arrowhead=none]; 20 -> 21 [color="#aaaaaa" constraint=false]; 18 -> 22 [arrowhead=none]; 19 -> 22 [color="#aaaaaa" constraint=false]; }

The same, but randomly choose a layout from one of 'circo', 'dot', 'fdp', 'sfdp', 'neato', 'osage', 'patchwork', 'twopi':

digraph G { node [shape=plaintext fontname="Arial"]; 0 -> 1 [arrowhead=none]; 1 -> 2 [arrowhead=none]; 2 -> 7 [arrowhead=none]; 7 -> 8 [arrowhead=none]; 8 -> 9 [arrowhead=none]; 8 -> 10 [arrowhead=none]; 9 -> 10 [color="#aaaaaa" constraint=false]; 10 -> 11 [arrowhead=none]; 10 -> 12 [arrowhead=none]; 11 -> 12 [color="#aaaaaa" constraint=false]; 7 -> 13 [arrowhead=none]; 8 -> 13 [color="#aaaaaa" constraint=false]; 13 -> 14 [arrowhead=none]; 7 -> 15 [arrowhead=none]; 13 -> 15 [color="#aaaaaa" constraint=false]; 15 -> 16 [arrowhead=none]; 15 -> 17 [arrowhead=none]; 16 -> 17 [color="#aaaaaa" constraint=false]; 2 -> 3 [arrowhead=none]; 7 -> 3 [color="#aaaaaa" constraint=false]; 3 -> 4 [arrowhead=none]; 2 -> 5 [arrowhead=none]; 3 -> 5 [color="#aaaaaa" constraint=false]; 5 -> 6 [arrowhead=none]; 2 -> 18 [arrowhead=none]; 5 -> 18 [color="#aaaaaa" constraint=false]; 18 -> 19 [arrowhead=none]; 19 -> 20 [arrowhead=none]; 19 -> 21 [arrowhead=none]; 20 -> 21 [color="#aaaaaa" constraint=false]; 18 -> 22 [arrowhead=none]; 19 -> 22 [color="#aaaaaa" constraint=false]; }

test egglog [uts-001I]

This example runs a local egglog fixed-point computation in the browser.

Computing Fibonacci…

test graphviz [uts-001L]

Adapted from ⧉:

strict digraph "" { graph [bgcolor=transparent]; node [label="\N", penwidth=1.8 ]; edge [arrowhead=vee]; QuadraticForm [color=green, fillcolor="#B0ECA3", label=QuadraticForm, shape=box, style=filled]; LinearMap [color=green, fillcolor="#B0ECA3", label=LinearMap, shape=box, style=filled]; Dual [color=green, fillcolor="#B0ECA3", label=Dual, shape=box, style=filled]; LinearMap -> Dual [style=dashed]; TensorAlgebra [color=green, fillcolor="#B0ECA3", label=TensorAlgebra, shape=box, style=filled]; LinearMap -> TensorAlgebra [style=dashed]; Group [color=green, fillcolor="#B0ECA3", label=Group, shape=box, style=filled]; Ring [color=green, fillcolor="#B0ECA3", label=Ring, shape=box, style=filled]; Group -> Ring [style=dashed]; Module [color=green, fillcolor="#B0ECA3", label=Module, shape=box, style=filled]; Ring -> Module [style=dashed]; DivisionRing [color=green, fillcolor="#B0ECA3", label=DivisionRing, shape=box, style=filled]; Ring -> DivisionRing [style=dashed]; RingHom [color=green, fillcolor="#B0ECA3", label=RingHom, shape=box, style=filled]; Ring -> RingHom [style=dashed]; Module -> LinearMap [style=dashed]; VectorSpace [color=green, fillcolor="#B0ECA3", label=VectorSpace, shape=box, style=filled]; Module -> VectorSpace [style=dashed]; Algebra [color=green, fillcolor="#B0ECA3", label=Algebra, shape=box, style=filled]; Module -> Algebra [style=dashed]; RingQuot [color=green, fillcolor="#B0ECA3", label=RingQuot, shape=box, style=filled]; Module -> RingQuot [style=dashed]; BilinForm [color=green, fillcolor="#B0ECA3", label=BilinForm, shape=box, style=filled]; Module -> BilinForm [style=dashed]; FreeAlgebra [color=green, fillcolor="#B0ECA3", label=FreeAlgebra, shape=box, style=filled]; Algebra -> FreeAlgebra [style=dashed]; RingQuot -> FreeAlgebra [style=dashed]; BilinForm -> QuadraticForm [style=dashed]; DivisionRing -> VectorSpace [style=dashed]; RingHom -> LinearMap [style=dashed]; RingHom -> Algebra [style=dashed]; RingHom -> RingQuot [style=dashed]; AlgHom [color=green, fillcolor="#B0ECA3", label=AlgHom, shape=box, style=filled]; RingHom -> AlgHom [style=dashed]; FreeAlgebra -> TensorAlgebra [style=dashed]; Monoid [color=green, fillcolor="#B0ECA3", label=Monoid, shape=box, style=filled]; Monoid -> Ring [style=dashed];}

Adapted from ⧉:

digraph { graph [rankdir=LR]; node [shape=record]; 0 [label="0 | [&bull; S, $]\n[S &rarr; &bull; a S b, $]\n[S &rarr; &bull;, $]"]; 1 [label="1 | [S &bull;, $]"]; 2 [label="2 | [S &rarr; a &bull; S b, $]\n[S &rarr; &bull; a S b, b]\n[S &rarr; &bull;, b]"]; 3 [label="3 | [S &rarr; a S &bull; b, $]"]; 4 [label="4 | [S &rarr; a &bull; S b, b]\n[S &rarr; &bull; a S b, b]\n[S &rarr; &bull;, b]"]; 5 [label="5 | [S &rarr; a S b &bull;, $]"]; 6 [label="6 | [S &rarr; a S &bull; b, b]"]; 7 [label="7 | [S &rarr; a S b &bull;, b]"]; 0 -> 1 [label=S]; 0 -> 2 [label=a]; 2 -> 3 [label=S]; 2 -> 4 [label=a]; 3 -> 5 [label=b]; 4 -> 6 [label=S]; 4 -> 4 [label=a]; 6 -> 7 [label=b]; }

Adapted from ⧉:

digraph G { node [shape=plaintext fontname="Arial"]; 0 -> 1 [arrowhead=none]; 1 -> 2 [arrowhead=none]; 2 -> 7 [arrowhead=none]; 7 -> 8 [arrowhead=none]; 8 -> 9 [arrowhead=none]; 8 -> 10 [arrowhead=none]; 9 -> 10 [color="#aaaaaa" constraint=false]; 10 -> 11 [arrowhead=none]; 10 -> 12 [arrowhead=none]; 11 -> 12 [color="#aaaaaa" constraint=false]; 7 -> 13 [arrowhead=none]; 8 -> 13 [color="#aaaaaa" constraint=false]; 13 -> 14 [arrowhead=none]; 7 -> 15 [arrowhead=none]; 13 -> 15 [color="#aaaaaa" constraint=false]; 15 -> 16 [arrowhead=none]; 15 -> 17 [arrowhead=none]; 16 -> 17 [color="#aaaaaa" constraint=false]; 2 -> 3 [arrowhead=none]; 7 -> 3 [color="#aaaaaa" constraint=false]; 3 -> 4 [arrowhead=none]; 2 -> 5 [arrowhead=none]; 3 -> 5 [color="#aaaaaa" constraint=false]; 5 -> 6 [arrowhead=none]; 2 -> 18 [arrowhead=none]; 5 -> 18 [color="#aaaaaa" constraint=false]; 18 -> 19 [arrowhead=none]; 19 -> 20 [arrowhead=none]; 19 -> 21 [arrowhead=none]; 20 -> 21 [color="#aaaaaa" constraint=false]; 18 -> 22 [arrowhead=none]; 19 -> 22 [color="#aaaaaa" constraint=false]; }

The same, but randomly choose a layout from one of 'circo', 'dot', 'fdp', 'sfdp', 'neato', 'osage', 'patchwork', 'twopi':

digraph G { node [shape=plaintext fontname="Arial"]; 0 -> 1 [arrowhead=none]; 1 -> 2 [arrowhead=none]; 2 -> 7 [arrowhead=none]; 7 -> 8 [arrowhead=none]; 8 -> 9 [arrowhead=none]; 8 -> 10 [arrowhead=none]; 9 -> 10 [color="#aaaaaa" constraint=false]; 10 -> 11 [arrowhead=none]; 10 -> 12 [arrowhead=none]; 11 -> 12 [color="#aaaaaa" constraint=false]; 7 -> 13 [arrowhead=none]; 8 -> 13 [color="#aaaaaa" constraint=false]; 13 -> 14 [arrowhead=none]; 7 -> 15 [arrowhead=none]; 13 -> 15 [color="#aaaaaa" constraint=false]; 15 -> 16 [arrowhead=none]; 15 -> 17 [arrowhead=none]; 16 -> 17 [color="#aaaaaa" constraint=false]; 2 -> 3 [arrowhead=none]; 7 -> 3 [color="#aaaaaa" constraint=false]; 3 -> 4 [arrowhead=none]; 2 -> 5 [arrowhead=none]; 3 -> 5 [color="#aaaaaa" constraint=false]; 5 -> 6 [arrowhead=none]; 2 -> 18 [arrowhead=none]; 5 -> 18 [color="#aaaaaa" constraint=false]; 18 -> 19 [arrowhead=none]; 19 -> 20 [arrowhead=none]; 19 -> 21 [arrowhead=none]; 20 -> 21 [color="#aaaaaa" constraint=false]; 18 -> 22 [arrowhead=none]; 19 -> 22 [color="#aaaaaa" constraint=false]; }

test markdownit [uts-000M]

We test hybrid markdown with forester markup:

We wish to use

- external link: markdown-it demo
- wiki link: test tikz drawing
- citations: [nakahira2023diagrammatic]
- cross-references: § [uts-000E]

The following tests a raw markdown-it renderer with no plugins, adapted from markdown-it demo:

# h1 Heading 8-)
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading


## Horizontal Rules

___

---

***


## Typographic replacements

Enable typographer option to see result.

(c) (C) (r) (R) (tm) (TM) (p) (P) +-

test.. test... test..... test?..... test!....

!!!!!! ???? ,,  -- ---

"Smartypants, double quotes" and 'single quotes'


## Emphasis

**This is bold text**

__This is bold text__

*This is italic text*

_This is italic text_

~~Strikethrough~~


## Blockquotes


> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.


## Lists

Unordered

+ Create a list by starting a line with `+`, `-`, or `*`
+ Sub-lists are made by indenting 2 spaces:
  - Marker character change forces new list start:
    * Ac tristique libero volutpat at
    + Facilisis in pretium nisl aliquet
    - Nulla volutpat aliquam velit
+ Very easy!

Ordered

1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa


1. You can use sequential numbers...
1. ...or keep all the numbers as `1.`

Start numbering with offset:

57. foo
1. bar


## Code

Inline `code`

Indented code

    // Some comments
    line 1 of code
    line 2 of code
    line 3 of code


Block code "fences"

```
Sample text here...
```

Syntax highlighting

``` js
var foo = function (bar) {
  return bar++;
};

console.log(foo(5));
```

## Tables

| Option | Description |
| ------ | ----------- |
| data   | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext    | extension to be used for dest files. |

Right aligned columns

| Option | Description |
| ------:| -----------:|
| data   | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext    | extension to be used for dest files. |


## Links

[link text](http://dev.nodeca.com)

[link with title](http://nodeca.github.io/pica/demo/ "title text!")

Autoconverted link https://github.com/nodeca/pica (enable linkify to see)


## Images

![Minion](https://octodex.github.com/images/minion.png)
![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")

Like links, Images also have a footnote style syntax

![Alt text][id]

With a reference later in the document defining the URL location:

[id]: https://octodex.github.com/images/dojocat.jpg  "The Dojocat"


## Plugins

The killer feature of `markdown-it` is very effective support of
[syntax plugins](https://www.npmjs.org/browse/keyword/markdown-it-plugin).


### [Emojies](https://github.com/markdown-it/markdown-it-emoji)

> Classic markup: :wink: :cry: :laughing: :yum:
>
> Shortcuts (emoticons): :-) :-( 8-) ;)

see [how to change output](https://github.com/markdown-it/markdown-it-emoji#change-output) with twemoji.


### [Subscript](https://github.com/markdown-it/markdown-it-sub) / [Superscript](https://github.com/markdown-it/markdown-it-sup)

- 19^th^
- H~2~O


### [\<ins>](https://github.com/markdown-it/markdown-it-ins)

++Inserted text++


### [\<mark>](https://github.com/markdown-it/markdown-it-mark)

==Marked text==


### [Footnotes](https://github.com/markdown-it/markdown-it-footnote)

Footnote 1 link[^first].

Footnote 2 link[^second].

Inline footnote^[Text of inline footnote] definition.

Duplicated footnote reference[^second].

[^first]: Footnote **can have markup**

    and multiple paragraphs.

[^second]: Footnote text.


### [Definition lists](https://github.com/markdown-it/markdown-it-deflist)

Term 1

:   Definition 1
with lazy continuation.

Term 2 with *inline markup*

:   Definition 2

        { some code, part of Definition 2 }

    Third paragraph of definition 2.

_Compact style:_

Term 1
  ~ Definition 1

Term 2
  ~ Definition 2a
  ~ Definition 2b


### [Abbreviations](https://github.com/markdown-it/markdown-it-abbr)

This is HTML abbreviation example.

It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on.

*[HTML]: Hyper Text Markup Language

### [Custom containers](https://github.com/markdown-it/markdown-it-container)

::: warning
*here be dragons*
:::

test markmap [uts-016G]

The following example is adapted from markmap: Try it out.

# markmap ## Links - [Website](https://markmap.js.org/) - [GitHub](https://github.com/gera2ld/markmap) ## Related Projects - [coc-markmap](https://github.com/gera2ld/coc-markmap) for Neovim - [markmap-vscode](https://marketplace.visualstudio.com/items?itemName=gera2ld.markmap-vscode) for VSCode - [eaf-markmap](https://github.com/emacs-eaf/eaf-markmap) for Emacs ## Features Note that if blocks and lists appear at the same level, the lists will be ignored. ### Lists - **strong** ~~del~~ *italic* ==highlight== - `inline code` - [x] checkbox - Katex: $x = {-b \pm \sqrt{b^2-4ac} \over 2a}$ <!-- markmap: fold --> - [More Katex Examples](#?d=gist:af76a4c245b302206b16aec503dbe07b:katex.md) - Now we can wrap very very very very long text based on `maxWidth` option - Ordered list 1. item 1 2. item 2 ### Blocks ```js console.log('hello, JavaScript') ``` | Products | Price | |-|-| | Apple | 4 | | Banana | 2 | ![](https://markmap.js.org/favicon.png)

test mermaid [uts-016F]

We test hybrid markdown with forester markup and mermaid diagrams.

They should render to a mermaid diagram or fail gracefully as the mermaid source code.

The following examples are taken from various DeepWiki generated by AI or official Mermaid examples.

graph TD
    RaftNode["raftNode"] --> WAL["Write-Ahead Log (WAL)"]
    RaftNode --> MemoryStorage["raft.MemoryStorage"]
    MemoryStorage --> Snapshot["snap.Snapshotter"]
    WAL --> DiskStorage["Storage on Disk"]
    Snapshot --> DiskStorage

    subgraph "Log Compaction"
        Snapshot -->|"Creates"| SnapshotFile["Snapshot File"]
        SnapshotFile -->|"Allows truncation of"| WAL
    end
sequenceDiagram
    participant App as "Your Application"
    participant Config as "embed.Config"
    participant Etcd as "embed.Etcd"
    participant Server as "etcdserver.EtcdServer"

    App->>Config: NewConfig()
    Note over Config: Configure options
    App->>Config: Customize configuration
    App->>Etcd: StartEtcd(config)
    Etcd->>Config: Validate()
    Etcd->>Server: NewServer(serverConfig)
    Etcd->>Server: Start()
    Etcd-->>App: Return Etcd instance
    App->>Etcd: Server.ReadyNotify()
    Note over App,Etcd: Wait for server to be ready
    App->>Etcd: Use etcd server
classDiagram
    class Config {
        +Name string
        +Dir string
        +WalDir string
        +ListenPeerUrls []url.URL
        +ListenClientUrls []url.URL
        +AdvertisePeerUrls []url.URL
        +AdvertiseClientUrls []url.URL
        +ClientTLSInfo transport.TLSInfo
        +PeerTLSInfo transport.TLSInfo
        +ClusterState string
        +InitialCluster string
        +InitialClusterToken string
        +LogOutput[] string
        +LogLevel string
        +Logger *zap.Logger
        +AuthToken string
        +ServerFeatureGate featuregate.FeatureGate
        +...other fields
    }

    Config --> ServerConfig
    Config --> ClientConfig
    Config --> SecurityConfig

    class ServerConfig {
        +Name string
        +DataDir string
        +...other fields
    }

    class ClientConfig {
        +ConnectionType ClientConnType
        +CertAuthority bool
        +AutoTLS bool
        +...other fields
    }

    class SecurityConfig {
        +CertFile string
        +KeyFile string
        +TrustedCAFile string
        +...other fields
    }
stateDiagram-v2
    [*] --> Still
    Still --> [*]
    Still --> Moving
    Moving --> Still
    Moving --> Crash
    Crash --> [*]
erDiagram
    CUSTOMER }|..|{ DELIVERY-ADDRESS : has
    CUSTOMER ||--o{ ORDER : places
    CUSTOMER ||--o{ INVOICE : "liable for"
    DELIVERY-ADDRESS ||--o{ ORDER : receives
    INVOICE ||--|{ ORDER : covers
    ORDER ||--|{ ORDER-ITEM : includes
    PRODUCT-CATEGORY ||--|{ PRODUCT : contains
    PRODUCT ||--o{ ORDER-ITEM : "ordered in"
%%{init: { 'logLevel': 'debug', 'timeline': {'disableMulticolor': true}}}%%
timeline
    title History of Social Media Platform
      2002 : LinkedIn
      2004 : Facebook : Google
      2005 : YouTube
      2006 : Twitter
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
journey
    title My working day
    section Go to work
      Make tea: 5: Me
      Go upstairs: 3: Me
      Do work: 1: Me, Cat
    section Go home
      Go downstairs: 5: Me
      Sit down: 3: Me
gantt
    title A Gantt Diagram
    dateFormat MM-DD
    section sec 1
    task1     : a1, 2014-01-01, 3d
    task2     : after a1, 2d
    section sec 2
    task3      : 2014-01-02, 2d
    task4      : 4d

test penrose [uts-000Q]

The following combines Penrose examples walk-on-spheres/laplace-estimator and curve-examples/blobs:

test pikchr [uts-000W]

The following example is from ⧉:

MK: box "Makefile" ht .3 rad 0.2 down A: arrow; "downloads" at last.e + (.3, 0) color 0xaaaaaa; move to A.s "pikchr.c"; right; arrow; CC: box "emcc" fill AliceBlue ht .3; arrow; "pikchr.js" down; A: arrow <- dashed; "loads" at last.e + (.2, 0) color 0xaaaaaa; move to A.s; down ellipse "example.html" fill whitesmoke; arrow from MK.e then right until even with CC then to CC.n rad 0.5 "runs" at last.ne + (.2,.1) color 0xaaaaaa;

The following is from my blog post Transformers: from self-attention to performance optimizations:

up TB: [ IN: text "Input" A_in: arrow LN_pre1: box "LayerNorm" bold \ wid 0.8 ht 0.4 rad 0.1 \ fill 0xf2f4c1 A_qkv: arrow ht 60% K: box "K" ht 0.2 wid 0.2 MHSA: box "MHSA" bold \ same as LN_pre1\ fill 0xffe2bb arrow ht 50% Res1: text "&#8853;" big big A_attn: arrow ht 60% LN_pre2: box "LayerNorm" bold same as LN_pre1 arrow ht 50% FFN: box "FFN" bold \ same as LN_pre1\ fill 0xc2e8f7 arrow ht 50% Res2: text "&#8853;" big big arrow ht 50% OUT: text "Output" Q: box "Q" with right at K.left same as K V: box "V" with left at K.right same as K arrow from A_qkv.c left until even with Q then to Q.start rad 0.03 arrow from A_qkv.c right until even with V then to V.start rad 0.03 arrow from A_in.c \ right 0.6 \ then up until even with Res1 \ then to Res1.right rad 0.1 arrow from A_attn.c \ right 0.6 \ then up until even with Res2 \ then to Res2.right rad 0.1 ] TB_border: box behind TB \ wid TB.wid+0.5 ht TB.ht-0.7 rad 0.1 \ at TB.c \ fill 0xf3f3f4 TB_background: box behind TB_border \ wid TB_border.wid+1 ht TB_border.ht+1 rad 0.1 \ at TB.c \ fill 0xf3f3f4 \ color 0xf3f3f4

test syntax highlighting [uts-000Y]

This is an additional test to Lean 4 code highlighting, which should load language highlighting syntax from CDN on demand to avoid a huge bundle of all languages.

test tikz drawing [uts-000E]

This is a series of experiments with TikZ drawing to ensure my `diagrams.tex` has sufficient preambles to render them. Most of them are adapted to minimize dependencies, and fix wierd render issues.

test typst [uts-000R]

Note: The Typst files and their imports must be placed under typst, as it's set up as the root directory for Typst imports.

test uwal [uts-001J]

This example doubles a small vector with a local WebGPU compute shader.

Running the GPU computation…
§ [uts-0012][uts-0012]

Technical notes (draft) [uts-0012]

Try some alternative browsers [uts-016N]

I decided to widen my horizon about browsers. After browsing a few subreddits, I have chosen these 3 browsers:

- Arc
    - Chromium based
    - the team has moved on to Dia, an AI browser
    - but people still miss the UX of Arc, it seems to be one of a kind, so I have to know
    - unlikely to be open-source
    - Mac/iOS/Windows, no Linux
- Orion
    - WebKit based, this engine is the best hope for degoogle
    - will be open-source
    - not mature yetA
    - Mac/iOS, Linux WIP, no Windows/Android
- Floorp
    - Firefox based
    - open-source
    - Mac/Windows/Linux, no mobile

How to run perf in a colima container [uts-016L]

Just to note down that it's not straightforward to get perf working.

Here's what worked finally:

First, the container needs to be started with proper capabilities:

```bash
docker run --rm -it --platform linux/amd64 --cap-add PERFMON ubuntu:jammy bash
```

Secondly, some flags need to be set from the VM:

```bash
# this would start a shell in the VM, outside of containers
colima ssh

# don't know why, sudo doesn't work, but it works in a root shell
# -1 or 2 both work
sudo bash -c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'
```

Lastly, install perf in the container:

```bash
apt install -y linux-tools-$(uname -r) linux-cloud-tools-$(uname -r)
```

Then the following works:

```bash
perf record -g echo 1
perf report -g graph,0.5,caller
```

Note also that you can't pass `"echo 1"` because perf will failed to find a file named "echo 1".

Learned this while working on Trying Zig's self-hosted x86 backend on Apple Silicon and before submitting this issue about unable to run `poop` in `colima` containers.

Follow-up:

It's important to check whether certain perf counters actually work:

```
sleep 10 &
perf stat -p $! -e "cpu-cycles,instructions,cache-references,cache-misses,branch-misses"
```

On a machine that supports corresponding counters, it should give something like:

```
 Performance counter stats for process id '101011':

            511488      cpu-cycles                                                  
            280194      instructions              #    0.55  insn per cycle         
             16193      cache-references                                            
              6161      cache-misses              #   38.047 8730      branch-misses                                               

      10.009515156 seconds time elapsed
```

On an unsupported machine, the output would be like:

```
 Performance counter stats for process id '552':

   <not supported>      cpu-cycles
   <not supported>      instructions
   <not supported>      cache-references
   <not supported>      cache-misses
   <not supported>      branch-misses

       6.491105847 seconds time elapsed
```

Resources about Rubik's Cube [uts-016J]

Notes on Rubik's abstract polytopes develop the mathematics from abstract puzzles to layered cubes, solving methods, and play.

- speed solving
    - Roux (a.k.a as bridging)
        - Learning the Roux method
        - Simple Roux Method of Solving Rubik's Cube
        - Introduction - Roux Tutorial
            - uses TwistySim
    - ZZ
        - Example Solves - Beginner ZZ Tutorial
- notation
    - Rubik's cube notation by Sebastiano Tronto
    - 3x3x3 notation
        - which clarifies naming of `E`, `S`, `r` etc.
- papers
    - The Generalized Rubik’s Cube: Combinatorial Insights Through Group Theory
        - it cites Nxnxn rubik’s cubes and god’s number[salkinder2021nxnxn]
        - has its own notation for nxnxn cubies
        - works out many details about general nxnxn cubes and their corresponding group theory
    - Commutators in the Rubik’s Cube Group
        - uses notations like `UR`, `FUR` to denote edges and corners, which I prefered
        - nice diagrams
        - demo with Twizzle
    - Generalising the configurations of an N×N×N Rubik’s Cube
        - aimed to find an expression for the total number of possible configurations of an N×N×N Rubik’s Cube
        - uses notations like `ur`, `fur`, `u_1` for 3x3x3 and 4x4x4 cubes
        - generalize to N×N×N with 3x3x3-style edges and 4x4x4-style corners
- solving with linear algebra
    - asked by this Reddit post
    - Mathematics of the Rubik’s cube by W. D. Joyner in 1996
    - Non-Commutative Gaussian Elimination and Rubik’s Cube
- Test TwistySim
- Test Twizzle

My setup with 4 screens and 2 Macs [uts-016B]

Recently, a MacBookPro and a Mac Mini, both are M4 Pro, have become my primary productivity tools. Along with them, I have 3 monitors, plus the one comes with MacBookPro, counts as 4 screens in total. On top of that, I have the logi MX Keys keyboard and MX Master 3S suite, and an Apple Magic Trackpad.

It turns out that I need to come up with a productive setup for them.

## TL;DR as an ASCII art

```
                                              +---------+
                                              |         |
  +-----------------+   +-----------------+   |  Tall   |
  |    Reference    |   |     Primary     |   |         |
  |    Display      |   |     Display     |   | Display |
  +-----------------+   +-----------------+   |         |
                                              |         |
        +------+           +------------+     +---------+
        | Mini |           |    MBP     |
        +------+           +------------+
                           | [Keyboard] |
                           |   [Pad]    |
                           +------------+
                            [ MX Keys   ]       [MX 3S]
                              [Pad]
```

## Screen arrangement

Above the screen of MacBookPro, I have my primary display, which serves as the main workspace for coding, browsing etc. The second display is placed on its left, on the same height, hosting stuff for reference. Both are horizontal and wide.

The third display (I would like to call it the "tall display") is placed on the right, yet vertically, with its middle part aligned with the primary display, it might host IM, PDFs, AI assistants, or any stuff that are tall, including vertically stacked windows that requires less width.

## Mac-display correspondence

Mac Mini is placed under the second display, and it's connected to the first 2 displays, and all of them if desired.

MacBookPro is connected to the tall display, and optionally also to the primary display.

## Keyboard and mouse

I could just use the keyboard and trackpad of MacBookPro, and they can travel to the nearby Mac Mini.

As I have muscle memory that's already used to MacBookPro's keyboard, MX Keys would be a good fit, as the keys are flatter, the key distance is also shorter.

I'm so used to use the trackpad to scroll, zoom, switching between spaces, and move cursor during coding, so that I would either put MX Keys on top of the keyboard of MacBookPro so I can reach its trackpad effortlessly, or I would just MX Keys and the Magic Trackpad beneath it, with the same relative location like the MacBookPro's keyboard and trackpad.

When I work with diagrams, slides, or UIs that require meticulous mouse control, I would use the MX Master 3S mouse. Its side wheel is configured to send `Ctrl+Left` and `Ctrl+Right`, and side buttons are configured to send `Ctrl+Up` and `Ctrl+Down`, both serving space switching.

The logi keyboard and mouse supports switch between 3 devices, so I can switch between 2 Macs. Unfortunately, one doesn't follow the other to switch, so I would rely on Mac's native support for keyboard and mouse sharing, and it goes like `2->1->MacBookPro->Tall`, so there is minimal extra travel between 1 and tall.

One might noticed that such a setup is highly redundant, but this provides me with maximum flexibility and productivity:

- In coding mode, I could have both hands on the keyboard, with the left hand sometimes on the trackpad
- In browing mode, my left hand is on the trackpad, and the right hand is on the mouse
- In diagramming mode, both hands are also on the keyboard, with the right hand sometimes on the mouse

## Thunderbolt bridge

I also have a Thunderbolt bridge, by connecting 2 Macs with a Thunderbolt cable, and following this guide. This would create a secure and fast connection between them for file transfer, screen sharing, distributed AI inference etc.

Such a bridge could also be used in MX Flow, but it has certain delay each time the cursor moves between the 2 Macs, so I didn't use it.

Organizing files [uts-002J]

I wish to note down my exploration of solutions to organize massive amount of (mostly binary media) files.

## Wishlist

- Performant
    - it should be able to handle huge amount of files, with cached metadata, thumbnails, etc. so I can browse over their contents quickly
    - batch operations to files on NAS should be as performant as local files, e.g. using client-server architecture, using rsync to determine the diff, etc.
- Data integrity
    - it should be able to use versioning, deduplication, etc. to ensure data integrity so I can mess with the files (mostly moving around, renaming) without worrying about losing them and their metadata such as timestamp, and this should be efficient in terms of storage
- Semi-auto tagging
    - I should be able to tag files manually, in batch, and set rules to tag files automatically with metadata or based on content (using ML models)
    - preferably, tags could have its own hierarchy
    - tags should not be using filename or metadata, but rather a separate database
- Open-source, freemium or affordable one-time purchase
    - in anycase, I should not be locked in, and the data should be exportable, so I can script

## Candidate solutions

- git-annex
    - Pros
        - it maintains a versioned record for files, can even track offline media
    - Cons
        - it only have access to the file contents when they are present at the registered location
        - even with the web UI, it's not realy user-friendly
- DataLad
    - it builds on git-annex, but still a CLI
- kopia
    - Pros
        - backup, versioning, deduplication, encryption, compression, error correction etc.
        - it can mount or restore any snapshot to a directory
        - there is official support for syncing the repo, making it reliable to backup the backup
    - Cons
        - can't really check the history of a file
        - relies on other tools for checking file location changes
        - lost the time added, preserving only the modification time
- Commander One
    - dual-pane file manager, trying out
- `dua -i`
    - it can analyze disk usage recursively, interactively
    - I can mark files for deletion
- jw
    - Pros
        - it can calculate the hashes of files in deep directories really quick
        - I use it to check the integrity of files after copying files
    - Cons
        - it doesn't check file renaming/moving
- VeraCrypt
    - Pros
        - useful if you just want to encrypt files in a container
    - Cons
        - it's inconvenient to mount in docker
- Garage
    - Pros
        - it's a S3-compatible storage
        - it works with git-annex and kopia
- macFUSE
    - needed to mount various filesystems
- photo management
    - HomeGallery
    - librephotos
    - digiKam
    - PhotoPrism
    - Immich
    - Lychee
- asset management
    - Eagle 4
    - Pixcall
    - Billfish

Notes on modern C++ programming [uts-002H]

useful tools and key mappings for TUI [uts-002G]

some useful key mappings in Neovim [uts-002F]

initial survey on headless render/testing for WebGL [uts-001N]

I want to be able to run headless render or visual regression tests, both locally where GPU is available, and on CI where GPU has to be emulated.

The ingredients seems to be Storybook+Playwright+Xvfb.

- use OrbStack as local docker engine
- use act (usage) to run GitHub Actions locally
- use Storybook to building pages and components in isolation
- use Playwright to verify the visual appearance on different browsers
- use Xvfb to emulate a display server

If I'm a bit more ambitious, I would like to be able to do the same for WebGPU. At least we do know that Xvfb support OpenGL via mesa (which also supports Vulkan etc.), and there are some libraries to run WebGPU via WebGL 2 (e.g. wgpu).

Some resources I found so far:

- Effortless CI Integration: Running Playwright Tests with GitHub Actions uses `xvfb-run`
- `playwright`'s own CI uses `xvfb-run`
- Headless chrome – testing WebGL using playwright demonstrates WegGL testing when GPU is available, see also its writeup
- Options to enable GPU Hardware Acceleration in headeless mode
- use xquartz with PyVirtualDisplay on Mac
- use libosmesa with Chrome from 2016
- use VirtualGL with Xvfb ⧉
For an earlier survey on headless testing, see SSR of WebGL.

SSR of WebGL [uts-0013]


## Server-side rendering of WebGL

Combining `xvfb-run`, headless-gl, three.js, should make it possible, e.g. see Headless rendering with THREE.js, see also:

- sharp for rendering to various image formats
- three.js + headless.gl rendering with texturing in CoffeeScript
- How to run OpenGL based tests on Github Actions

## Plotly.js

I wish to draw a style similar to Project contour profiles onto a graph by Matplotlib, which plots a 3D surface then its contour profiles projected to surrounding planes.

Plotly.js has a similar example as Surface Plot With Contours.

Plotly requires a `div` element to render the plot, it will initialize a WebGL-capable `canvas` to do so. It has support for using virtual WebGL context but no native way to specify a `canvas` from the outside.

When running in headless mode from node, we will typically use jsdom which in turn uses node-canvas that's not WebGL-capable. There is an open issue to detect the presence of node-canvas-webgl and use it, as it integrations `node-canvas` and headless-gl. Another idea is to make NPM packages `get-canvas-context` and in turn `webgl-context` to handle this.

A commercial plot libraries `LightningChart JS` has made Headless server use support for itself, which could be used as a reference.

Should I Render Three.js to Video on the Client or Server? has some discussions on this topic.

initial survey on writing Rust in Forester [uts-0011]

I wish to be able to call rust libraries that are WASM compatible to use it for both BSR and SSR.

Here is my initial survey:

- wasm-pack and wasm-bindgen are the two main tools for building and binding rust to javascript, more info see awesome-rust-and-webassembly
- I wish to write one-file Rust and specify the dependencies in the file, rust-script is similar in spirit but it's for server-side execution.
- I wish to find prior art on doing this with bun, found bun-ffi-rust and hyperimport, but again, it's for server-side execution.

some links for learning shaders [uts-000H]

some links for rendering implicit surfaces [uts-000G]


# readings: raymarching on implicit surfaces

- [singh2009real] has reviews on related work, an algorithm and many examples. But it has no code.
- Ray Marching and Signed Distance Functions is a good introduction to ray-marching and signed distance functions, and the author learns from Inigo Quilez.
- Rendering Worlds with Two Triangles with raytracing on the GPU in 4096 bytes by Iñigo Quilez in 2008
- Raymarching Distance Fields in 2013
- raymarching terrains by Iñigo Quilez in 2002
- Volumetric Rendering: Raymarching in 2016
- Painting with Math: A Gentle Study of Raymarching in 2023, which has interative illustration of ray-marching algorithm, and shader examples from simple to complex
- CUDA Ray Marching by Jonathan Granskog
- Slides on GPU Ray Marching by Alex Benton in 2017–18, with more related materials here.
- Ray Tracing Deterministic 3-D Fractals in 1989
- Ray-marching Thurston geometries in 2022
- PU-Ray: Domain-Independent Point Cloud Upsampling via Ray Marching on Neural Implicit Surface in 2024
- Ray Tracing Harmonic Functions in 2024
- Lipschitz-agnostic, efficient and accurate rendering of implicit surfaces in 2024
- What is raytracing/raymarching/raycasting
- Ray Marching in JS, the source code has a link to the principle behind ray marching shadows: soft shadows in raymarched SDFs - 2010
- Raymarching Voxel Rendering in 2017
- UC Berkeley CS184/284A Lecture 9: Ray Tracing & Acceleration Structures in 2024
- Third Year and MSc Interactive Computer Graphics in 2008
- Raymarching Distance Fields in 2013
- Perfecting anti-aliasing on signed distance functions in 2025

# libraries and tools: raymarching on implicit surfaces

- SURFER renders implicit surfaces using ray-tracing, the result is stunning well. It also has a Java library with many examples.
- Raycasting Implicit Surfaces based on CindyJS is Web-based implicit surface renderer using ray-tracing, and the source code is best suited for reproducing and learning. It also has a rich set of examples.
- Raycasting a surface of degree 4 is a relatively simpler example of ray-tracing implicit surfaces with source here.
- mla has a series of shaders from simple to complicated to render the Kummer surface, see here. mla has also ported a tiny ray tracer here which could be a starting point.
- A simpler but more versatile ray-tracer for implicit surfaces is Implicit surfaces created by Rogolop.
- 3D Bifurcation Diagram
- MA1104-implicitplot has a naive implicit surface plotter.
- usegpu.live demo of ploting implicit surface
- Surface Plotter makes use of Paul Bourke's Marching Cubes algorithm with edge table, to turn implicit surfaces into polygonal meshes. See Marching Cubes and its references.

## Implicit reconstruction or polygonization

- A Heat Method for Generalized Signed Distance in 2024
- Robust Creation of Implicit Surfaces from Polygonal Meshes in 2002
- Curvature Dependent Polygonization of Implicit Surfaces in 2004
- Implicit Meshes for Modeling and Reconstruction in 2018
- Differential Geometry in Neural Implicits in 2022
- Differentiable Iterated Function Systems in 2024
- Adaptive grid generation for discretizing implicit complexes in 2024
- SDFGen: A simple commandline utility to generate grid-based signed distance field (level set) generator from triangle meshes, using code from Robert Bridson's website. Found from Merging Meshes with Signed Distance Fields.
- sdf: Generate 3D meshes based on SDFs (signed distance functions) with a dirt simple Python API.
- SDFStudio: A Unified Framework for Surface Reconstruction

For learning shaders, see some links for learning shaders.

Convert some of the links to citations.

I would like to have a formal specification of the raymarching algorithm (in algorithm2e or algorithmicx+algpseudocode or even algpseudocodex), and find propositions worth proving.

Distributing my binaries [uts-016P]

- Repology
- brew for mac & linux
    - personal tap repo: see
        - Taps (Third-Party Repositories) — Homebrew Documentation
        - Adding Software to Homebrew — Homebrew Documentation
        - `create`, `bottle` in brew(1), also `bump*`
- Windows or all platforms
    - Aqua private registery
        - then supported by mise or maybe more
        - see Develop a Registry | aqua

Using Zig to build more [uts-016O]

- C/C++ projects
    - All Your Codebase | Loris Cro's Blog
        - All Your Codebase
            - no GiNaC yet
- help rust to cross-compile
    - rust-cross/cargo-zigbuild
        - Compile Cargo project with zig as linker for easier cross compiling
- help PyPI to store less
    - The Python Package Index Should Get Rid Of Its Training Wheels | Loris Cro's Blog
    - zig is also available as a PyPI package
        - ziglang · PyPI
- zig alternative to PyO3/maturin: Build and publish crates with pyo3, cffi and uniffi bindings as well as rust binaries as python packages
    - spiraldb/ziggy-pydust: A toolkit for building Python extensions in Zig.
        - the orginal template that ueses poetry
        - the community template that uses uv and hatchling
            - where hatchling seems to be a better build backend than setup tools post 3.12 for projects with native extensions and custom build hooks
            - see also Why Hatch? - Hatch

root notes [uts-001O]

Transclusion loop detected, rendering stopped.