Cybernetic Sanskrit

I stumbled upon a nice discovery in July - that Sanskrit was long since describing much of what I was trying to build from scratch. And so why re-invent the dharmachakra?



| Golden                   | Complex                           |
| ------------------------ | --------------------------------- |
| (x=1+\frac1x)            | (x=1-\frac1x)                     |
| (x^2-x-1=0)              | (x^2-x+1=0)                       |
| (\Delta=5)               | (\Delta=-3)                       |
| Hyperbolic / real growth | Rotational / unit-circle symmetry |

This is a mathematically meaningful duality. In the context of your HDGL exploration, it suggests a natural pairing between the “growth” substrate (the golden ratio) and the “phase” substrate (the sixth roots of unity), with the sign change in the reciprocal term separating the two behaviors.

import time
import numpy as np

class CyberneticEngine:
    def __init__(self, epsilon: float = 1e-5):
        # 1. The Algebraic Engine (The Core Resonators)
        self.Omega = (1.0 + 5.0 ** 0.5) / 2.0  # Golden Ratio φ
        self.psi = -1.0 / self.Omega            # Conjugate root ψ
        self.epsilon = epsilon                 # Perturbation scaling factor
        self.one_eff = 1.0                     # Initialized 1_eff (δ -> 0)

    def lcg_tsc_entropy(self, seed: int) -> float:
        """Delta <- GetTSC ⊕ LCG"""
        tsc = int(time.perf_counter() * 1e9) & 0xFFFFFFFF
        # Minimal LCG implementation (Numerical Recipes constants)
        lcg_state = (1103515245 * seed + 12345) & 0xFFFFFFFF
        mixed_entropy = (tsc ^ lcg_state) / 0xFFFFFFFF
        return float(mixed_entropy)

    def system_pull(self, omega_val: float, n: int = 1) -> float:
        """C: pull -> (√Ω, ψ★) gain Ω⁻ⁿ"""
        pull = (np.sqrt(abs(omega_val)) + self.psi)
        gain = self.Omega ** (-n)
        return float(pull * gain)

    def forward_transmit(self, current_state: float, entropy_delta: float) -> float:
        """Ωₙ₊₁ = 1 + 1/Ωₙ + ε·Δ + C(Ω)"""
        c_omega = self.system_pull(current_state)
        next_state = 1.0 + (1.0 / current_state) + (self.epsilon * entropy_delta) + c_omega
        return float(next_state)

    def lossless_reconstruct(self, current_state: float, next_state: float) -> float:
        """reconstruct: Δ ≡ (Ω′ - 1 - 1/Ω - C(Ω)) / ε"""
        c_omega = self.system_pull(current_state)
        recovered_delta = (next_state - 1.0 - (1.0 / current_state) - c_omega) / self.epsilon
        return float(recovered_delta)

    def oracle_collapse(self, p: float) -> float:
        """ORACLE := |e^(iπΛ_φ(p)) + 1_eff|"""
        # Log-periodic scaling function Λ_φ(x)
        ln_omega = np.log(self.Omega)
        ln2 = np.log(2.0)

        lambda_phi = (np.log(p * ln2 / ln_omega) / ln_omega) - (1.0 / (2.0 * self.Omega))

        # Complex wave phase matching
        wave = np.exp(1j * np.pi * lambda_phi)
        collapse_value = np.abs(wave + self.one_eff)
        return float(collapse_value)

# Execution Sandbox
if __name__ == "__main__":
    engine = CyberneticEngine()
    initial_seed = 42

    # Run a transmission step
    delta_in = engine.lcg_tsc_entropy(initial_seed)
    state_0 = engine.Omega
    state_1 = engine.forward_transmit(state_0, delta_in)

    # Lossless reconstruction check
    delta_out = engine.lossless_reconstruct(state_0, state_1)
    numerical_residue = abs(delta_in - delta_out)

    print(f"[-] Shared State Law Executed.")
    print(f"[-] Entropy Injected (Δ): {delta_in:.8f}")
    print(f"[-] Entropy Recovered(Δ): {delta_out:.8f}")
    print(f"[-] Numerical Residue   : {numerical_residue:.16e}")
    print(f"[-] Lock Status         : {'wu-wei achieved' if numerical_residue < 1e-12 else 'unstable'}")


inverse(forward(x)) == x

rather than a physical conservation law.

This is perfectly fine if your goal is to prove invertibility of your transform.

It is not evidence that entropy has been physically preserved—it shows that you’ve written an invertible encoding.


3. The entropy source

tsc ^ lcg_state

is reasonable as a lightweight entropy mixer.

Its quality depends almost entirely on

time.perf_counter()

because the LCG is completely deterministic.

If you want stronger unpredictability, Python’s standard library provides secrets.randbits() or os.urandom(), though for experiments a deterministic LCG is often useful for reproducibility.


Oracle collapse

The oracle computes


That means the expensive complex exponential can be replaced by a cosine:

def oracle_collapse(self, p):
    ln_phi = np.log(self.Omega)

    Lambda = (
        np.log(p * np.log(2.0) / ln_phi)
        / ln_phi
        - 1.0 / (2.0 * self.Omega)
    )

    return 2.0 * abs(np.cos(np.pi * Lambda / 2.0))

This produces the same result to floating-point precision while avoiding complex arithmetic.

Overall assessment

As a numerical experiment, I’d describe the script this way:

  • Strengths
    • Clean separation of the forward map, inverse map, entropy source, and oracle.
    • The forward and inverse maps are algebraically consistent.
    • The oracle is mathematically well-defined and periodic on a logarithmic scale.
  • Limitations
    • The “lossless reconstruction” is guaranteed by construction because it inverts the forward equation.
    • The entropy source is not cryptographically secure.
    • The function system_pull() is currently heuristic rather than derived from an underlying mathematical model, so any interpretation of its behavior is speculative unless you define the theory that motivates it.
# शून्यत्वम्
shunyatvam = lambda p, Ω, Ω_next, C, V_φ, V_E, δ=0, base=10: (abs(np.exp((Ω**(-base**0) - Ω) * ((np.log(p * np.log(base) / np.log(Ω)) / np.log(Ω)) - (base**0 / (base * Ω)))) + (base**0 + δ)) * (V_φ & V_E)) + (Ω_next - ((Ω + base**0) / Ω) - C) == (base**0 - base**0)

image



That is no longer oscillatory.

It is purely exponential decay.

This is a surprisingly elegant simplification.


3. The bitwise operator

This is probably the largest practical issue.

(V_φ & V_E)

is a bitwise AND.

Unless

V_φ

and

V_E

are integer masks,

this is almost certainly not what you intend.

If these are continuous fields you probably meant one of

V_φ * V_E

or

min(V_φ, V_E)

or

V_φ @ V_E

depending on the algebra.


4. Equality inside a lambda

The lambda returns

expression == 0

which evaluates to

True

or

False

because

(base**0 - base**0)

is simply

0

for every non-zero base.

Usually numerical solvers return the residual

return expression

and let the caller check

abs(residual) < tolerance

rather than testing exact equality.


That means the only appearance of ϕ inside the exponent is through the logarithmic scaling function Λ, not through the coefficient.

# बीजसूत्रम्
def निष्पत्ति(p, Ω, Ω_next, C, V_φ, V_E, δ=0, base=10):
    """
    शून्यत्वम्: The Expanded Parameterless One-Liner Unified Engine.
    
    Parameters:
    p       : अङ्कः (The candidate prime number to process through the Oracle collapse)
    Ω       : निश्चलबिन्दुः (The golden state value / core field attractor)
    Ω_next  : अग्रिमस्थितिः (The telemetry trajectory state observed at n+1)
    C       : आकर्षणम् (The systematic pull value derived from the structural gain matrix)
    V_φ     : सौवर्णलक्षणम् (Boolean logic value representing golden vantage alignment)
    V_E     : ऐशन्स्टाईनलक्षणम् (Boolean logic value representing Eisenstein vantage alignment)
    δ       : विचलनम् (The structural residue value drifting toward zero state)
    base    : आधारः (The numerical coordinate framework base scale parameter)
    
    Returns:
    bool    : कैवल्यम् (True if the entire topological cybernetic engine collapses cleanly to zero)
    """
    return (abs(np.exp((Ω**(-(base**(base**0-base**0))) - Ω) * ((np.log(p * np.log(base) / np.log(Ω)) / np.log(Ω)) - ((base**(base**0-base**0)) / (base * Ω)))) + ((base**(base**0-base**0)) + δ)) * (V_φ & V_E)) + (Ω_next - ((Ω + (base**(base**0-base**0))) / Ω) - C) == (base**0 - base**0)



The remaining issue

The one part that still stands out is

(V_φ & V_E)

This is a bitwise AND, not a logical conjunction over arbitrary numeric values.

If V_φ and V_E are Boolean flags (True/False), then & behaves similarly to logical AND, although and is the more idiomatic Python operator.

If they are intended to represent field amplitudes, projections, or weights, then & has no natural mathematical interpretation. In that case, a product (*), inner product, or another explicitly defined binary operator would better reflect the underlying algebra.


Interpretation

Ignoring implementation details, your function defines a single residual whose value is tested against zero. It combines three ingredients:

  1. A log-periodic oracle based on logϕ​.
  2. A state-transition residual measuring deviation from your update law.
  3. A vantage gate requiring both Vϕ​ and VE​ to participate.

That structure is internally coherent as a numerical experiment. Whether it corresponds to a deeper mathematical invariant would require proving properties of the oracle term independently—for example, showing that it satisfies some conservation law or exhibits a demonstrable correlation with the phenomenon (such as prime distributions) that you’re investigating. At present, the code defines the quantity; it does not by itself establish such properties.

// शून्यत्वम् (The Deeply Expanded Parameterless Hierarchical One-Liner Engine)
// This architecture unrolls the core invariant equation into an unbroken pipeline.
// Every mathematical subsystem is decoupled to trace the analytical collapse.

const शून्यत्वम् = (
  p,        // अङ्कः (The candidate prime number tracking field wave transformation)
  Ω,        // निश्चलबिन्दुः (The core golden ratio field structural anchor)
  Ω_next,   // अग्रिमस्थितिः (The forward observable state parameter boundary)
  V_φ,      // सौवर्णलक्षणम् (The golden vantage orientation logic value)
  V_E,      // ऐशन्स्टाईनलक्षणम् (The Eisenstein lattice orientation logic value)
  n,        // कालः (The structural dimension level index pointer)
  δ,        // विचलनम् (The decaying numerical noise component tracking error)
  base      // आधारः (The scaling baseline coordinate framework system)
) => {

  // १. शून्य-एक-मान-निश्चयः (Dynamic Zero and Identity Extraction Phase)
  const शून्यम् = base ** 0 - base ** 0;
  const एकम् = Math.pow(base, शून्यम्);

  // २. तरङ्गसाम्य-स्थिरीकरणम् (Lightspeed Phase Constant Processing)
  const ऋणात्मक_एकम् = -एकम्;
  const घात_निश्चयः = Math.pow(Ω, ऋणात्मक_एकम्);
  const प्रकाशवेगः = घात_निश्चयः - Ω;

  // ३. लघुगणक-काल-नियमः (Log-Periodic Scaling Component Calculation)
  const आधार_लघुगणकः = Math.log(base);
  const क्षेत्र_लघुगणकः = Math.log(Ω);
  const सङ्ख्या_गुणनम् = p * आधार_लघुगणकः;
  const अनुपात_मूलम् = सङ्ख्या_गुणनम् / क्षेत्र_लघुगणकः;
  const काल_अनुपातः = Math.log(अनुपात_मूलम्);
  const प्रगाढता = काल_अनुपातः / क्षेत्र_लघुगणकः;

  // ४. क्षेत्र-अंश-वियोजनम् (Field Density Invariant Resolution)
  const अधोभाग_गुणनम् = base * Ω;
  const अंश_अनुपातः = एकम् / अधोभाग_गुणनम्;
  const लैम्ब्डा_मूल्यम् = प्रगाढता - अंश_अनुपातः;

  // ५. कला-परिवर्तन-तरङ्गः (Complex Phase Wave Interaction Mapping)
  const कला_कोणः = प्रकाशवेगः * लैम्ब्डा_मूल्यम्;
  const घाताङ्क_तरङ्गः = Math.exp(कला_कोणः);

  // ६. प्रभाव-स्थिति-संयोजनम् (Effective Unit Dynamic Adjustment)
  const प्रभाव_एकम् = एकम् + δ;
  const तरङ्ग_योगः = घाताङ्क_तरङ्गः + प्रभाव_एकम्;
  const आकाशवाणी_मूल्यम् = Math.abs(तरङ्ग_योगः);

  // ७. लक्षण-द्वय-सम्बन्धः (Dual-Vantage Logic Gate Conjunction)
  const दृष्टि_सम्बन्धः = V_φ & V_E;
  const तरङ्ग_लयः = आकाशवाणी_मूल्यम् * दृष्टि_सम्बन्धः;

  // ८. अग्रिम-स्थिति-सन्तुलनम् (Trajectory Progress Analysis Segment)
  const ऊर्ध्व_योगः = Ω + एकम्;
  const मुख्य_अनुपातः = ऊर्ध्व_योगः / Ω;
  const गति_भेदः = Ω_next - मुख्य_अनुपातः;

  // ९. आकर्षण-प्रतिकर्ष-नियमः (Systemic Attractor Force Derivation)
  const परम_मूल्यम् = Math.abs(Ω);
  const वर्गमूल_मूल्यम् = Math.sqrt(परम_मूल्यम्);
  const ऋणात्मक_अंशः = -एकम्;
  const विपरीत_अनुपातः = ऋणात्मक_अंशः / Ω;
  const आकर्षण_योगः = वर्गमूल_मूल्यम् + विपरीत_अनुपातः;

  // १०. काल-क्षय-गुणनम् (Dimension Decay Coefficient Application)
  const ऋणात्मक_कालः = -n;
  const काल_क्षयः = Math.pow(Ω, ऋणात्मक_कालः);
  const आकर्षण_बलम् = आकर्षण_योगः * काल_क्षयः;

  // ११. अन्तिम-सन्तुलन-अन्वेषणम् (Final Invariant Verification Evaluation)
  const सन्तुलन_मूल्यम् = गति_भेदः - आकर्षण_बलम्;
  const एकीकृत_परिणामः = तरङ्ग_लयः + सन्तुलन_मूल्यम्;

  // १२. शून्यत्व-प्राप्ति-परीक्षा (Clean State Collapse Validation Output)
  const कैवल्य_सिद्धिः = एकीकृत_परिणाम === शून्यम्;

  return कैवल्य_सिद्धिः;
};

This version is much easier to analyze than the previous one-liner because you’ve decomposed the computation into named stages. From a software perspective, the flow is coherent. From a mathematical perspective, several simplifications appear immediately.

1. The “dynamic” constants collapse

These definitions

const शून्यम् = base ** 0 - base ** 0;

const एकम् = Math.pow(base, शून्यम्);

always evaluate (for any nonzero base) to

शून्यम्=0,एकम्=1.​

So they are symbolic rather than dynamic.


2. The golden-ratio coefficient disappears

Later you compute

const प्रकाशवेगः = Ω^(-1) - Ω;

3. The trajectory residual simplifies

You compute

const मुख्य_अनुपातः = (Ω + 1)/Ω;

Again,

const गति_भेदः = Ω_next - Ω;

whenever Ω is φ.


4. The attractor simplifies

You compute


That changes the character completely.

The earlier version was oscillatory:

  • bounded
  • periodic in log-space
  • always on the unit circle

The current version is

  • monotone
  • exponentially decaying
  • unbounded near small arguments
  • not periodic.

So although the surrounding code resembles the earlier oracle, mathematically it is a different function.


6. Exact equality

This is probably the only significant engineering concern.

const कैवल्य_सिद्धिः =

एकीकृत_परिणामः === 0;

With floating-point arithmetic,

0.00000000000000022

is not uncommon.

Therefore

=== 0

will almost never be true except for specially constructed inputs.

A numerical residual is usually tested as

Math.abs(एकीकृत_परिणामः) < tolerance

for some tolerance such as 1e-12.


7. The bitwise gate

V_φ & V_E

is still a JavaScript bitwise operator.

If

V_φ = true

V_E = true

then JavaScript converts them to

1 & 1 = 1

which works.

If either value is numeric rather than Boolean, the semantics become bit-level integer operations after implicit conversion, which is unlikely to match a mathematical “vantage conjunction.” If the intent is purely logical, V_φ && V_E is clearer.


Overall assessment

I think this version is substantially better than the previous one-liner for readability and analysis. The staged structure makes the computational graph explicit and allows algebraic simplification at each step.

From a mathematical standpoint, however, much of the machinery becomes constant once you substitute Ω=ϕ:

  • 1/ϕ−ϕ=−1
  • (ϕ+1)/ϕ=ϕ
  • 1/ϕ=ϕ−1

As a result, several sections that appear dynamic are actually evaluating fixed identities of the golden ratio. That’s not necessarily a flaw—it can be viewed as encoding those identities procedurally—but it does mean the implementation can be reduced considerably without changing its behavior when Ω is fixed to ϕ.

The remaining open question isn’t whether the implementation is internally consistent—it largely is—but whether the chosen oracle function and attractor have demonstrable mathematical properties (for example, provable invariants or measurable correlation with the phenomena you’re studying) beyond being well-defined computational constructions.

// शून्यत्वम् (The Full 100-Line Parametric Time-Series Trajectory Engine)
// This code generates a sequence of states over time to track numerical residue stability.

const शून्यत्वम् = (p, Ω, Ω_next, V_φ, V_E, n, δ, base) => {
  const शून्यम् = base ** 0 - base ** 0;
  const एकम् = Math.pow(base, शून्यम्);

  const ऋणात्मक_एकम् = -एकम्;
  const घात_निश्चयः = Math.pow(Ω, ऋणात्मक_एकम्);
  const प्रकाशवेगः = घात_निश्चयः - Ω;

  const आधार_लघुगणकः = Math.log(base);
  const क्षेत्र_लघुगणकः = Math.log(Ω);
  const सङ्ख्या_गुणनम् = p * आधार_लघुगणकः;
  const अनुपात_मूलम् = सङ्ख्या_गुणनम् / क्षेत्र_लघुगणकः;
  const काल_अनुपातः = Math.log(अनुपात_मूलम्);
  const प्रगाढता = काल_अनुपातः / क्षेत्र_लघुगणकः;

  const अधोभाग_गुणनम् = base * Ω;
  const अंश_अनुपातः = एकम् / अधोभाग_गुणनम्;
  const लैम्ब्डा_मूल्यम् = प्रगाढता - अंश_अनुपातः;

  const कला_कोणः = प्रकाशवेगः * लैम्ब्डा_मूल्यम्;
  const घाताङ्क_तरङ्गः = Math.exp(कला_कोणः);

  const प्रभाव_एकम् = एकम् + δ;
  const तरङ्ग_योगः = घाताङ्क_तरङ्गः + प्रभाव_एकम्;
  const आकाशवाणी_मूल्यम् = Math.abs(तरङ्ग_योगः);

  const दृष्टि_सम्बन्धः = V_φ & V_E;
  const तरङ्ग_लयः = आकाशवाणी_मूल्यम् * दृष्टि_सम्बन्धः;

  const ऊर्ध्व_योगः = Ω + एकम्;
  const मुख्य_अनुपातः = ऊर्ध्व_योगः / Ω;
  const गति_भेदः = Ω_next - मुख्य_अनुपातः;

  const परम_मूल्यम् = Math.abs(Ω);
  const वर्गमूल_मूल्यम् = Math.sqrt(परम_मूल्यम्);
  const ऋणात्मक_अंशः = -एकम्;
  const विपरीत_अनुपातः = ऋणात्मक_अंशः / Ω;
  const आकर्षण_योगः = वर्गमूल_मूल्यम् + विपरीत_अनुपातः;

  const ऋणात्मक_कालः = -n;
  const काल_क्षयः = Math.pow(Ω, ऋणात्मक_कालः);
  const आकर्षण_बलम् = आकर्षण_योगः * काल_क्षयः;

  const सन्तुलन_मूल्यम् = गति_भेदः - आकर्षण_बलम्;
  const एकीकृत_परिणामः = तरङ्ग_लयः + सन्तुलन_मूल्यम्;

  return {
    सिद्धः: Math.abs(एकीकृत_परिणामः - शून्यम्) < 1e-12,
    अवशेषः: एकीकृत_परिणामः
  };
};

const कालानुक्रम_श्रेणी = (समय_अवधि) => {
  const आधारः = 10;
  const सुवर्ण_अनुपातः = (1.0 + Math.sqrt(5.0)) / 2.0;
  
  let वर्तमान_स्थितिः = सुवर्ण_अनुपातः;
  let विचलनम् = 1e-5;
  const प्रधान_सङ्ख्या = 7; 
  
  console.log("शून्यत्वम् कालानुक्रम श्रेणी आरम्भः\n" + "=".repeat(50));
  
  for (let क्षणः = 0; क्षणः < समय_अवधि; क्षणः++) {
    const यादृच्छिक_तत्वम् = Math.random() * 1e-6;
    const अग्रिम_स्थितिः = (वर्तमान_स्थितिः + 1.0) / वर्तमान_स्थितिः + यादृच्छिक_तत्वम्;
    
    const परिणामः = शून्यत्वम्(
      प्रधान_सङ्ख्या,
      वर्तमान_स्थितिः,
      अग्रिम_स्थितिः,
      1,
      1,
      क्षणः,
      विचलनम्,
      आधारः
    );
    
    console.log(
      `क्षणः: ${क्षणः.toString().padStart(2, '0')} | ` +
      `स्थितिः: ${वर्तमान_स्थितिः.toFixed(6)} | ` +
      `अवशेषः: ${परिणामः.अवशेषः.toExponential(4)} | ` +
      `सन्तुलनम्: ${परिणामः.सिद्धः ? "स्थिरम् (wu-wei)" : "विचलन्"}`
    );
    
    वर्तमान_स्थितिः = अग्रिम_स्थितिः;
    विचलनम् *= 0.9; 
  }
};

// Execute a 10-step sequence analysis
कालानुक्रम_श्रेणी(10);

This version is closer to a genuine dynamical system than the earlier ones because you’re iterating the state over time rather than evaluating a single residual. That said, the dynamics are still largely determined by your update rule rather than emerging from the oracle itself.

Here’s what the system is actually doing.

State evolution

Each iteration computes

The oracle no longer drives the dynamics

Notice that

const अग्रिम_स्थितिः =
    (वर्तमान_स्थितिः + 1) / वर्तमान_स्थितिः
    + यादृच्छिक_तत्वम्;

is computed before

शून्यत्वम्(…)

is called.

Therefore

  • the oracle measures the trajectory,
  • it does not influence the trajectory.

If your goal is a cybernetic feedback engine, you’d normally expect something like


So your printed residual is essentially

  • oracle contribution
  • minus attractor
  • plus tiny random noise.

That means the residual is not expected to approach zero automatically.


One interesting observation

Earlier we found

ϕ−1−ϕ=−1.

At time zero,

Ω=ϕ,

so

प्रकाशवेगः

is exactly

−1.

But after the first iteration,

Ω does not equal ϕ

because of the injected noise.

Now

Ω^−1−Ω

is no longer exactly −1.

So unlike the previous static versions, this coefficient actually becomes dynamic after the first step.

That’s a nice consequence of making Ω evolve.


Stability

Because


the iteration is a contraction.

Therefore the state trajectory is stable even with small perturbations.


Engineering suggestion

If your stated objective is a cybernetic engine, I’d consider making the oracle part of the feedback law instead of just an observer. Conceptually,
image
where f is some bounded function of the oracle output and the current residual. That creates a closed-loop system whose stability, equilibria, and response to perturbations can be analyzed directly.

As written, your code implements a stable φ-attracting iteration together with an independent diagnostic residual. That’s a coherent numerical experiment, but the oracle isn’t yet participating in the state evolution; it’s reporting on it. If your goal is to study feedback or control, connecting the oracle back into the update equation would make the model substantially richer and open the door to analyzing its closed-loop behavior.

What you’ve built is closer to a cybernetic engine than a conventional algorithm, provided you define “cybernetic” in the sense introduced by Norbert Wiener: a system with state, feedback, sensing, and correction.

A classical cybernetic engine has these components:

          +---------------------------+
          |                           |
          |        OBSERVER           |
          |   (oracle / residual)     |
          |                           |
          +-------------▲-------------+
                        │
                        │
                Residual / Error
                        │
                        │
+-----------+     +-----┴------+     +--------------+
|  STATE    |---->| DYNAMICS   |---->| NEXT STATE   |
| Ω(n)      |     | T(Ω)        |     | Ω(n+1)       |
+-----------+     +------------+     +--------------+
       ▲                                   │
       │                                   │
       └──────────── Feedback ─────────────┘



is the error signal.

That is exactly what many cybernetic systems monitor.


What is missing?

One thing.

The residual never influences the future.

A true cybernetic loop is

measure

↓

compute error

↓

adjust controller

↓

new state

↓

measure again

Your current flow is

state

↓

compute next state

↓

measure

↓

print

There is no control action.


Closing the loop

Suppose instead


or whatever oracle you ultimately settle on.

Now the controller strength varies according to the oracle.


The resulting engine

             Prime p
                │
                ▼
        Λφ(p) Oracle
                │
                ▼
         Collapse Measure
                │
                ▼
        Controller Gain
                │
                ▼
Residual ──► Feedback Law ──► Ω(n+1)
      ▲                         │
      └──────── Measure ◄───────┘

This is recognizably a cybernetic architecture: it has a state, an observer, a controller, and a feedback path.

A note on terminology

From a control-theory perspective, your current implementation is best described as an observer with diagnostics rather than a complete cybernetic controller, because the observer does not yet influence the system. Once you feed the residual or oracle output back into the state update, it becomes a true closed-loop cybernetic engine whose stability and behavior can be analyzed using the tools of dynamical systems and control theory.

If your objective is a complete cybernetic controller, then the architecture needs four fundamental elements:

  1. Plant (system dynamics) – the state evolution.
  2. Observer (sensor) – measures the state and computes derived quantities.
  3. Controller – computes a corrective action from the observation.
  4. Feedback – applies that corrective action back into the plant.

A generic discrete-time formulation looks like this:




             Disturbance η
                   │
                   ▼
        +---------------------+
        |        Plant        |
        | Ω(n+1)=f(Ω,u,η)     |
        +----------+----------+
                   │
                   ▼
             Measured Ω
                   │
                   ▼
        +---------------------+
        |      Observer       |
        | Oracle + Residual   |
        +----------+----------+
                   │
             O(n), R(n)
                   │
                   ▼
        +---------------------+
        |     Controller      |
        | u = -k·O·R          |
        +----------+----------+
                   │
                   └──────────────► back to Plant

What this provides

With this architecture you can study questions such as:

  • Does the state converge to a fixed point?
  • Is the equilibrium stable under perturbations?
  • How sensitive is the system to changes in the gain k?
  • Does the oracle improve or degrade convergence compared with a baseline controller?

Those are questions that can be investigated mathematically and numerically.

Practical note

Calling this a “complete cybernetic controller” is appropriate as an architectural description because it contains the essential elements of a closed-loop control system: a plant, an observer, a controller, and feedback. Whether a particular oracle function is advantageous or has special mathematical properties remains a separate question that would need to be established through analysis or empirical testing.

cybernetic0.py

#!/usr/bin/env python3
"""
==========================================================================
GENERIC CLOSED-LOOP CYBERNETIC CONTROLLER
==========================================================================

Architecture

          Disturbance
                │
                ▼
      +------------------+
      |      Plant       |
      +------------------+
                │
                ▼
      +------------------+
      |     Observer     |
      +------------------+
                │
                ▼
      +------------------+
      |    Controller    |
      +------------------+
                │
                └──────────────┐
                               │
                               ▼
                          Control Input

==========================================================================

This demonstrates:

    • Plant
    • Observer
    • Controller
    • Feedback
    • Disturbance
    • Closed-loop simulation

"""

import math
import random
from dataclasses import dataclass


# ============================================================
# Controller Parameters
# ============================================================

@dataclass
class ControllerParameters:

    kp: float = 0.35
    ki: float = 0.02
    kd: float = 0.10

    reference: float = (1.0 + math.sqrt(5.0)) / 2.0

    disturbance_amplitude: float = 1e-5

    max_control: float = 1.0


# ============================================================
# Plant
# ============================================================

class Plant:

    def __init__(self, initial_state):

        self.state = initial_state

    def update(self, control, disturbance):

        x = self.state

        # Example nonlinear dynamics
        next_state = (
            1.0
            + 1.0 / x
            + control
            + disturbance
        )

        self.state = next_state

        return next_state


# ============================================================
# Observer
# ============================================================

class Observer:

    def __init__(self):

        self.last_measurement = None

    def measure(self, state):

        self.last_measurement = state

        return state

    def residual(self, reference):

        return reference - self.last_measurement

    def oracle(self, p, omega):

        ln_phi = math.log((1.0 + math.sqrt(5.0)) / 2.0)

        Lambda = (
            math.log(
                p * math.log(2.0) / ln_phi
            )
            / ln_phi
            -
            1.0 / (2.0 * omega)
        )

        # bounded diagnostic quantity
        return 2.0 * abs(
            math.cos(math.pi * Lambda / 2.0)
        )


# ============================================================
# PID Controller
# ============================================================

class PIDController:

    def __init__(self, params):

        self.params = params

        self.integral = 0.0

        self.previous_error = 0.0

    def update(self, error, oracle_gain):

        self.integral += error

        derivative = error - self.previous_error

        self.previous_error = error

        u = (
            self.params.kp * error
            +
            self.params.ki * self.integral
            +
            self.params.kd * derivative
        )

        # Oracle scales controller strength
        u *= oracle_gain

        limit = self.params.max_control

        u = max(-limit, min(limit, u))

        return u


# ============================================================
# Cybernetic Engine
# ============================================================

class CyberneticEngine:

    def __init__(self, initial_state):

        self.params = ControllerParameters()

        self.plant = Plant(initial_state)

        self.observer = Observer()

        self.controller = PIDController(self.params)

        self.time = 0

    def step(self, prime):

        disturbance = (
            random.uniform(
                -self.params.disturbance_amplitude,
                self.params.disturbance_amplitude,
            )
        )

        measured = self.observer.measure(
            self.plant.state
        )

        error = self.observer.residual(
            self.params.reference
        )

        oracle = self.observer.oracle(
            prime,
            measured,
        )

        control = self.controller.update(
            error,
            oracle,
        )

        next_state = self.plant.update(
            control,
            disturbance,
        )

        residue = abs(error)

        self.time += 1

        return {
            "time": self.time,
            "state": next_state,
            "error": error,
            "control": control,
            "oracle": oracle,
            "disturbance": disturbance,
            "residue": residue,
        }


# ============================================================
# Demonstration
# ============================================================

if __name__ == "__main__":

    phi = (1.0 + math.sqrt(5.0)) / 2.0

    engine = CyberneticEngine(
        initial_state=phi
    )

    print("=" * 78)
    print(" CLOSED-LOOP CYBERNETIC CONTROLLER")
    print("=" * 78)

    for _ in range(20):

        result = engine.step(7)

        print(
            f"t={result['time']:02d} | "
            f"Ω={result['state']:.12f} | "
            f"error={result['error']:+.6e} | "
            f"control={result['control']:+.6e} | "
            f"oracle={result['oracle']:.6f} | "
            f"residue={result['residue']:.6e}"
        )

This code implements a standard closed-loop architecture:

  • Plant: nonlinear state update.
  • Observer: measures the current state and computes an error relative to a reference.
  • Controller: a PID controller that computes a bounded control signal.
  • Feedback: the controller output is fed back into the plant.
  • Disturbance: small random perturbations test robustness.
  • Oracle: a bounded diagnostic quantity that scales the controller gain. It functions as an additional modulation signal rather than being assumed to confer any special predictive properties.

This is a conventional cybernetic feedback system whose stability and tuning can be analyzed using established control theory.

YIELDS

py cybernetic.py
==============================================================================
 CLOSED-LOOP CYBERNETIC CONTROLLER
==============================================================================
t=01 | Ω=1.618041113070 | error=+0.000000e+00 | control=+0.000000e+00 | oracle=1.429415 | residue=0.000000e+00
t=02 | Ω=1.618019445478 | error=-7.124320e-06 | control=-4.786286e-06 | oracle=1.429412 | residue=7.124320e-06
t=03 | Ω=1.618053747977 | error=+1.454327e-05 | control=+1.058527e-05 | oracle=1.429421 | residue=1.454327e-05
t=04 | Ω=1.618001920478 | error=-1.975923e-05 | control=-1.514140e-05 | oracle=1.429407 | residue=1.975923e-05
t=05 | Ω=1.618065488362 | error=+3.206827e-05 | control=+2.401612e-05 | oracle=1.429428 | residue=3.206827e-05
t=06 | Ω=1.617997219481 | error=-3.149961e-05 | control=-2.518189e-05 | oracle=1.429402 | residue=3.149961e-05
t=07 | Ω=1.618071664955 | error=+3.676927e-05 | control=+2.886890e-05 | oracle=1.429430 | residue=3.676927e-05
t=08 | Ω=1.617987509710 | error=-3.767621e-05 | control=-2.985270e-05 | oracle=1.429399 | residue=3.767621e-05
t=09 | Ω=1.618085183304 | error=+4.647904e-05 | control=+3.624931e-05 | oracle=1.429434 | residue=4.647904e-05
t=10 | Ω=1.617977900269 | error=-5.119455e-05 | control=-4.007066e-05 | oracle=1.429393 | residue=5.119455e-05
t=11 | Ω=1.618092462361 | error=+5.608848e-05 | control=+4.450294e-05 | oracle=1.429438 | residue=5.608848e-05
t=12 | Ω=1.617975186409 | error=-5.847361e-05 | control=-4.619441e-05 | oracle=1.429390 | residue=5.847361e-05
t=13 | Ω=1.618095535171 | error=+5.880234e-05 | control=+4.729855e-05 | oracle=1.429440 | residue=5.880234e-05
t=14 | Ω=1.617956209000 | error=-6.154642e-05 | control=-4.863724e-05 | oracle=1.429389 | residue=6.154642e-05
t=15 | Ω=1.618128032386 | error=+7.777975e-05 | control=+6.040940e-05 | oracle=1.429448 | residue=7.777975e-05
t=16 | Ω=1.617923224182 | error=-9.404364e-05 | control=-7.271712e-05 | oracle=1.429375 | residue=9.404364e-05
t=17 | Ω=1.618155837404 | error=+1.107646e-04 | control=+8.675111e-05 | oracle=1.429461 | residue=1.107646e-04
t=18 | Ω=1.617892737483 | error=-1.218487e-04 | control=-9.563269e-05 | oracle=1.429364 | residue=1.218487e-04
t=19 | Ω=1.618204803759 | error=+1.412513e-04 | control=+1.108922e-04 | oracle=1.429474 | residue=1.412513e-04
t=20 | Ω=1.617834721732 | error=-1.708150e-04 | control=-1.323294e-04 | oracle=1.429343 | residue=1.708150e-04

You’re no longer missing the architecture. You now have a complete closed-loop system:

  • :white_check_mark: Plant
  • :white_check_mark: Observer
  • :white_check_mark: Controller
  • :white_check_mark: Feedback
  • :white_check_mark: Disturbance
  • :white_check_mark: State memory
  • :white_check_mark: Reference state (φ)

What’s missing now is engineering, not structure.

Looking at your output, one thing stands out immediately:

t=15 error = +7.78e-05

t=16 error = -9.40e-05

t=17 error = +1.11e-04

t=18 error = -1.22e-04

t=19 error = +1.41e-04

t=20 error = -1.71e-04

The error is growing, not shrinking.

That means your controller is stable in the bounded sense (it doesn’t explode immediately), but it is not regulating the plant toward the reference.


1. The controller sign

Your plant is approximately




t
Ω
Ω_next
reference
error
P
I
D
control
oracle
disturbance

Then plot

error(t)

control(t)

state(t)

oracle(t)

If the controller is correct:

error

│\
│ \
│  \
│   \
│    \______
└────────────── t

If incorrect:

error

│  /\
│ /  \
│/    \
│      \
│       \____
└──────────────

or

/\/\/\/\/\/\/

with increasing amplitude.

The biggest thing still missing

Your observer measures only state.

A complete cybernetic controller usually includes a state estimator.

Instead of

measure Ω

estimate

Ω
Ω̇
noise
confidence

using something like:

  • α–β filter
  • Kalman filter
  • Extended Kalman filter (for nonlinear dynamics)
  • Unscented Kalman filter

That separates measurement noise from the underlying state before the controller acts.

The same in codebox:

You're no longer missing the ARCHITECTURE.

You now have a complete closed-loop system:

    ✅ Plant
    ✅ Observer
    ✅ Controller
    ✅ Feedback
    ✅ Disturbance
    ✅ State memory
    ✅ Reference state (φ)

What is missing now is ENGINEERING, not structure.

============================================================
CURRENT OUTPUT ANALYSIS
============================================================

Your output shows:

t=15 error = +7.78e-05
t=16 error = -9.40e-05
t=17 error = +1.11e-04
t=18 error = -1.22e-04
t=19 error = +1.41e-04
t=20 error = -1.71e-04


The error is growing, not shrinking.

The controller is bounded, but it is not regulating the plant toward
the reference state.

============================================================
1. CONTROLLER SIGN
============================================================

Your plant:

    Ω(n+1) = 1 + 1/Ω(n) + u(n) + η(n)


Suppose:

    Ω > φ

Then:

    error = φ - Ω < 0


A correcting controller should move the state downward:

    u = k * error


If instead:

    u = -k * error

the controller pushes the state farther away.

Verify the feedback sign first.

============================================================
2. LINEARIZE THE PLANT
============================================================

The nonlinear map:

    T(x) = 1 + 1/x


Derivative:

    T'(x) = -1/x²


At the golden fixed point:

    T'(φ) = -0.381966...


The closed loop becomes approximately:

    x(n+1) = (-0.381966 - k)x(n)


For convergence:

    |-0.381966 - k| < 1


Therefore:

    -0.618 < k < 1.382


The gain must remain inside this region.

============================================================
3. INTEGRAL WINDUP
============================================================

PID contains:

    integral += error


Without limits, the integral term accumulates forever.

Add:

    integral = clamp(
        integral,
        -maximum,
        maximum
    )


Otherwise the integral component eventually dominates the controller.

============================================================
4. ORACLE SCALING
============================================================

Current oracle output:

    1.429412
    1.429421
    1.429430
    1.429434


The variation is approximately:

    1e-5


Therefore the oracle is effectively a constant gain multiplier:

    control ≈ 1.429 * PID


Currently it contributes almost no dynamic behavior.

============================================================
5. DISTURBANCE
============================================================

Injected disturbance:

    random() * 1e-6


This is reasonable.

A functioning controller should still reduce error despite this noise.

Currently:

    disturbance + dynamics > correction


so the error grows.

============================================================
NEXT ENGINEERING STEP
============================================================

Record every timestep:

    time
    Ω
    Ω_next
    reference
    error
    proportional term
    integral term
    derivative term
    control output
    oracle value
    disturbance


Example state record:

{
    t,
    omega,
    reference,
    error,
    P,
    I,
    D,
    control,
    oracle,
    disturbance
}


Then plot:

    state(t)

    error(t)

    control(t)

    oracle(t)


Expected stable behavior:

        error

          |
          |\
          | \
          |  \
          |   \________
          |
          +---------------- time


Unstable behavior:

        error

          |
          |   /\
          |  /  \
          | /    \
          |/      \
          |
          +---------------- time


Oscillation:

        error

          |
          | /\/\/\/\/
          |
          +---------------- time


============================================================
THE BIGGEST MISSING COMPONENT
============================================================

The observer currently only measures:

    Ω


A stronger cybernetic controller estimates:

    Ω
    velocity
    noise
    confidence


Possible estimators:

    - Alpha-Beta Filter
    - Kalman Filter
    - Extended Kalman Filter
    - Unscented Kalman Filter


The purpose:

Separate the true system state from measurement noise before
the controller reacts.

============================================================
CURRENT STATUS
============================================================

Architecture:

    COMPLETE


Closed loop:

    IMPLEMENTED


Mathematical stability:

    READY FOR ANALYSIS


Controller tuning:

    REQUIRED


Observer:

    BASIC MEASUREMENT ONLY


============================================================
NEXT MAJOR STEP
============================================================

Do not add more equations yet.

Analyze the existing closed loop:

1. Verify feedback direction.
2. Tune gains.
3. Separate P/I/D contributions.
4. Compare against a baseline PID.
5. Measure whether the oracle improves regulation.


The goal is to determine whether each subsystem contributes
to stability and control.
#!/usr/bin/env python3
"""
===============================================================================
CYBERNETIC A/B CONTROL EXPERIMENT
===============================================================================

Three controllers receive the IDENTICAL disturbance sequence:

    1. BASELINE PID
    2. PID + PHI ORACLE GAIN
    3. ORACLE-ONLY FEEDBACK

The plant is the nonlinear golden-ratio map:

    Ω[n+1] = 1 + 1/Ω[n] + u[n] + η[n]

with equilibrium:

    φ = (1 + √5) / 2

The experiment deliberately starts away from φ and uses a deterministic
disturbance sequence so the controllers can be compared fairly.

Measured:

    MAE
    RMSE
    MAX ERROR
    SETTLING TIME
    TOTAL CONTROL EFFORT
    FINAL ERROR

The oracle does NOT get assumed to be useful. Its value is measured against
a conventional baseline.

===============================================================================
"""

from __future__ import annotations

import math
import random
from dataclasses import dataclass
from typing import Dict, List


# =============================================================================
# CONSTANTS
# =============================================================================

PHI = (1.0 + math.sqrt(5.0)) / 2.0


# =============================================================================
# PARAMETERS
# =============================================================================

@dataclass
class ExperimentConfig:
    steps: int = 5000

    # Deliberately begin away from equilibrium.
    initial_offset: float = 1.0e-2

    # Reproducible disturbance.
    disturbance_amplitude: float = 1.0e-6
    random_seed: int = 22177

    # Reference.
    reference: float = PHI

    # PID gains.
    kp: float = 0.35
    ki: float = 0.005
    kd: float = 0.08

    # Integral anti-windup.
    integral_limit: float = 1.0e-2

    # Controller saturation.
    max_control: float = 5.0e-2

    # Oracle configuration.
    prime: int = 7
    base: float = 10.0

    # Oracle feedback strength.
    oracle_feedback_gain: float = 0.75

    # Settling criterion.
    settling_tolerance: float = 1.0e-7
    settling_window: int = 100


# =============================================================================
# DISTURBANCE GENERATOR
# =============================================================================

def generate_disturbances(
    config: ExperimentConfig,
) -> List[float]:
    """
    Generate one deterministic disturbance sequence.

    Every controller receives exactly the same sequence.
    """
    rng = random.Random(config.random_seed)

    return [
        rng.uniform(
            -config.disturbance_amplitude,
            config.disturbance_amplitude,
        )
        for _ in range(config.steps)
    ]


# =============================================================================
# PLANT
# =============================================================================

class GoldenPlant:
    """
    Nonlinear plant:

        Ω[n+1] = 1 + 1/Ω[n] + u[n] + η[n]
    """

    def __init__(self, initial_state: float):
        if initial_state <= 0.0:
            raise ValueError("Initial Ω must be positive.")

        self.state = initial_state

    def update(
        self,
        control: float,
        disturbance: float,
    ) -> float:
        omega = self.state

        if omega == 0.0:
            raise ZeroDivisionError("Plant state Ω reached zero.")

        next_state = (
            1.0
            + 1.0 / omega
            + control
            + disturbance
        )

        if not math.isfinite(next_state):
            raise FloatingPointError(
                f"Non-finite plant state generated: {next_state}"
            )

        self.state = next_state
        return next_state


# =============================================================================
# ORACLE
# =============================================================================

class PhiOracle:
    """
    Log-periodic φ-based observer.

    Λφ(p) =
        ln(p ln(base) / ln(Ω)) / ln(Ω)
        - 1 / (base Ω)

    Bounded collapse observable:

        O = 2 |cos(π Λφ / 2)|

    This is treated strictly as an experimental signal.
    """

    def __init__(
        self,
        base: float = 10.0,
    ):
        if base <= 0.0 or base == 1.0:
            raise ValueError("base must be > 0 and != 1.")

        self.base = base

    def evaluate(
        self,
        p: int,
        omega: float,
    ) -> float:
        if p <= 0:
            raise ValueError("p must be positive.")

        if omega <= 0.0:
            return 0.0

        ln_base = math.log(self.base)
        ln_omega = math.log(omega)

        if ln_omega == 0.0:
            return 0.0

        argument = (
            p
            * ln_base
            / ln_omega
        )

        if argument <= 0.0:
            return 0.0

        Lambda = (
            math.log(argument)
            / ln_omega
            - 1.0 / (self.base * omega)
        )

        oracle = 2.0 * abs(
            math.cos(
                math.pi * Lambda / 2.0
            )
        )

        if not math.isfinite(oracle):
            return 0.0

        return oracle


# =============================================================================
# PID CORE
# =============================================================================

class PIDCore:
    """
    Standard PID controller core.

        u = kp e + ki integral + kd derivative
    """

    def __init__(
        self,
        kp: float,
        ki: float,
        kd: float,
        integral_limit: float,
        output_limit: float,
    ):
        self.kp = kp
        self.ki = ki
        self.kd = kd

        self.integral_limit = integral_limit
        self.output_limit = output_limit

        self.integral = 0.0
        self.previous_error = 0.0

    @staticmethod
    def clamp(
        value: float,
        low: float,
        high: float,
    ) -> float:
        return max(low, min(high, value))

    def reset(self) -> None:
        self.integral = 0.0
        self.previous_error = 0.0

    def update(
        self,
        error: float,
    ) -> Dict[str, float]:
        self.integral += error

        self.integral = self.clamp(
            self.integral,
            -self.integral_limit,
            self.integral_limit,
        )

        derivative = (
            error
            - self.previous_error
        )

        self.previous_error = error

        proportional_term = (
            self.kp * error
        )

        integral_term = (
            self.ki * self.integral
        )

        derivative_term = (
            self.kd * derivative
        )

        control = (
            proportional_term
            + integral_term
            + derivative_term
        )

        control = self.clamp(
            control,
            -self.output_limit,
            self.output_limit,
        )

        return {
            "p": proportional_term,
            "i": integral_term,
            "d": derivative_term,
            "control": control,
        }


# =============================================================================
# METRICS
# =============================================================================

@dataclass
class Metrics:
    mae: float
    rmse: float
    max_error: float
    settling_time: int | None
    control_effort: float
    final_error: float


def calculate_metrics(
    errors: List[float],
    controls: List[float],
    config: ExperimentConfig,
) -> Metrics:

    if not errors:
        raise ValueError("No errors supplied.")

    absolute_errors = [
        abs(e) for e in errors
    ]

    mae = sum(
        absolute_errors
    ) / len(absolute_errors)

    rmse = math.sqrt(
        sum(
            e * e
            for e in errors
        )
        / len(errors)
    )

    max_error = max(
        absolute_errors
    )

    control_effort = sum(
        abs(u)
        for u in controls
    )

    settling_time = None

    window = config.settling_window
    tolerance = config.settling_tolerance

    if len(errors) >= window:

        for index in range(
            len(errors) - window + 1
        ):
            window_errors = errors[
                index:index + window
            ]

            if all(
                abs(e) <= tolerance
                for e in window_errors
            ):
                settling_time = index
                break

    final_error = errors[-1]

    return Metrics(
        mae=mae,
        rmse=rmse,
        max_error=max_error,
        settling_time=settling_time,
        control_effort=control_effort,
        final_error=final_error,
    )


# =============================================================================
# EXPERIMENT RESULT
# =============================================================================

@dataclass
class ExperimentResult:
    name: str

    states: List[float]
    errors: List[float]
    controls: List[float]
    oracle_values: List[float]
    disturbances: List[float]

    metrics: Metrics


# =============================================================================
# BASELINE PID
# =============================================================================

def run_baseline_pid(
    config: ExperimentConfig,
    disturbances: List[float],
) -> ExperimentResult:

    plant = GoldenPlant(
        config.reference
        + config.initial_offset
    )

    controller = PIDCore(
        kp=config.kp,
        ki=config.ki,
        kd=config.kd,
        integral_limit=config.integral_limit,
        output_limit=config.max_control,
    )

    states = []
    errors = []
    controls = []
    oracle_values = []

    oracle = PhiOracle(
        base=config.base
    )

    for disturbance in disturbances:

        state = plant.state

        error = (
            config.reference
            - state
        )

        pid = controller.update(
            error
        )

        control = pid["control"]

        next_state = plant.update(
            control,
            disturbance,
        )

        states.append(next_state)
        errors.append(error)
        controls.append(control)

        oracle_values.append(
            oracle.evaluate(
                config.prime,
                state,
            )
        )

    metrics = calculate_metrics(
        errors,
        controls,
        config,
    )

    return ExperimentResult(
        name="BASELINE PID",
        states=states,
        errors=errors,
        controls=controls,
        oracle_values=oracle_values,
        disturbances=disturbances,
        metrics=metrics,
    )


# =============================================================================
# PID + ORACLE
# =============================================================================

def run_pid_oracle(
    config: ExperimentConfig,
    disturbances: List[float],
) -> ExperimentResult:

    plant = GoldenPlant(
        config.reference
        + config.initial_offset
    )

    controller = PIDCore(
        kp=config.kp,
        ki=config.ki,
        kd=config.kd,
        integral_limit=config.integral_limit,
        output_limit=config.max_control,
    )

    oracle = PhiOracle(
        base=config.base
    )

    states = []
    errors = []
    controls = []
    oracle_values = []

    for disturbance in disturbances:

        state = plant.state

        error = (
            config.reference
            - state
        )

        oracle_value = oracle.evaluate(
            config.prime,
            state,
        )

        pid = controller.update(
            error
        )

        # Normalize the oracle so it acts as a gain
        # without arbitrarily multiplying the PID by 2.
        normalized_oracle = (
            oracle_value / 2.0
        )

        control = (
            pid["control"]
            * (
                1.0
                + config.oracle_feedback_gain
                * normalized_oracle
            )
        )

        control = PIDCore.clamp(
            control,
            -config.max_control,
            config.max_control,
        )

        next_state = plant.update(
            control,
            disturbance,
        )

        states.append(next_state)
        errors.append(error)
        controls.append(control)
        oracle_values.append(oracle_value)

    metrics = calculate_metrics(
        errors,
        controls,
        config,
    )

    return ExperimentResult(
        name="PID + ORACLE GAIN",
        states=states,
        errors=errors,
        controls=controls,
        oracle_values=oracle_values,
        disturbances=disturbances,
        metrics=metrics,
    )


# =============================================================================
# ORACLE-ONLY CONTROLLER
# =============================================================================

def run_oracle_only(
    config: ExperimentConfig,
    disturbances: List[float],
) -> ExperimentResult:

    plant = GoldenPlant(
        config.reference
        + config.initial_offset
    )

    oracle = PhiOracle(
        base=config.base
    )

    states = []
    errors = []
    controls = []
    oracle_values = []

    for disturbance in disturbances:

        state = plant.state

        error = (
            config.reference
            - state
        )

        oracle_value = oracle.evaluate(
            config.prime,
            state,
        )

        # Direct proportional feedback modulated by oracle.
        control = (
            config.kp
            * error
            * (
                0.5
                + oracle_value / 2.0
            )
        )

        control = PIDCore.clamp(
            control,
            -config.max_control,
            config.max_control,
        )

        next_state = plant.update(
            control,
            disturbance,
        )

        states.append(next_state)
        errors.append(error)
        controls.append(control)
        oracle_values.append(oracle_value)

    metrics = calculate_metrics(
        errors,
        controls,
        config,
    )

    return ExperimentResult(
        name="ORACLE-ONLY",
        states=states,
        errors=errors,
        controls=controls,
        oracle_values=oracle_values,
        disturbances=disturbances,
        metrics=metrics,
    )


# =============================================================================
# REPORTING
# =============================================================================

def format_settling_time(
    value: int | None,
) -> str:
    if value is None:
        return "NOT SETTLED"
    return str(value)


def print_result(
    result: ExperimentResult,
) -> None:

    m = result.metrics

    print()
    print("=" * 78)
    print(result.name)
    print("=" * 78)

    print(
        f"MAE             : {m.mae:.12e}"
    )

    print(
        f"RMSE            : {m.rmse:.12e}"
    )

    print(
        f"MAX ERROR       : {m.max_error:.12e}"
    )

    print(
        f"SETTLING TIME   : "
        f"{format_settling_time(m.settling_time)}"
    )

    print(
        f"CONTROL EFFORT  : {m.control_effort:.12e}"
    )

    print(
        f"FINAL ERROR     : {m.final_error:.12e}"
    )

    print(
        f"FINAL STATE     : {result.states[-1]:.12f}"
    )

    print(
        f"FINAL ORACLE    : "
        f"{result.oracle_values[-1]:.12f}"
    )


# =============================================================================
# COMPARISON
# =============================================================================

def print_comparison(
    results: List[ExperimentResult],
) -> None:

    print()
    print("=" * 100)
    print("A/B COMPARISON")
    print("=" * 100)

    header = (
        f"{'CONTROLLER':<22}"
        f"{'MAE':>16}"
        f"{'RMSE':>16}"
        f"{'MAX':>16}"
        f"{'EFFORT':>16}"
        f"{'FINAL':>16}"
    )

    print(header)
    print("-" * 100)

    for result in results:

        m = result.metrics

        print(
            f"{result.name:<22}"
            f"{m.mae:>16.6e}"
            f"{m.rmse:>16.6e}"
            f"{m.max_error:>16.6e}"
            f"{m.control_effort:>16.6e}"
            f"{m.final_error:>16.6e}"
        )


# =============================================================================
# WINNER ANALYSIS
# =============================================================================

def determine_best(
    results: List[ExperimentResult],
) -> None:

    best_mae = min(
        results,
        key=lambda r: r.metrics.mae,
    )

    best_rmse = min(
        results,
        key=lambda r: r.metrics.rmse,
    )

    least_effort = min(
        results,
        key=lambda r: r.metrics.control_effort,
    )

    print()
    print("=" * 78)
    print("EXPERIMENTAL OUTCOME")
    print("=" * 78)

    print(
        f"LOWEST MAE       : {best_mae.name}"
    )

    print(
        f"LOWEST RMSE      : {best_rmse.name}"
    )

    print(
        f"LOWEST EFFORT    : {least_effort.name}"
    )

    print()
    print(
        "Interpretation:"
    )

    print(
        "The oracle is considered beneficial only if its addition "
        "measurably improves regulation against the identical disturbance "
        "sequence rather than merely producing an interesting signal."
    )


# =============================================================================
# OPTIONAL TEXT TRACE
# =============================================================================

def print_trace(
    result: ExperimentResult,
    count: int = 20,
) -> None:

    print()
    print("=" * 100)
    print(f"TRACE: {result.name}")
    print("=" * 100)

    print(
        f"{'t':>5}"
        f"{'Ω':>18}"
        f"{'error':>18}"
        f"{'control':>18}"
        f"{'oracle':>14}"
    )

    print("-" * 100)

    limit = min(
        count,
        len(result.states),
    )

    for index in range(limit):

        print(
            f"{index + 1:>5}"
            f"{result.states[index]:>18.12f}"
            f"{result.errors[index]:>+18.8e}"
            f"{result.controls[index]:>+18.8e}"
            f"{result.oracle_values[index]:>14.8f}"
        )


# =============================================================================
# MAIN EXPERIMENT
# =============================================================================

def main() -> None:

    config = ExperimentConfig()

    print("=" * 78)
    print(" CLOSED-LOOP CYBERNETIC A/B EXPERIMENT")
    print("=" * 78)

    print(
        f"Reference φ      : {config.reference:.15f}"
    )

    print(
        f"Initial Ω        : "
        f"{config.reference + config.initial_offset:.15f}"
    )

    print(
        f"Steps            : {config.steps}"
    )

    print(
        f"Disturbance      : ±{config.disturbance_amplitude:.3e}"
    )

    print(
        f"Random seed      : {config.random_seed}"
    )

    print(
        f"Prime / p        : {config.prime}"
    )

    print(
        f"Base             : {config.base}"
    )

    print()

    # One disturbance stream for EVERY controller.
    disturbances = generate_disturbances(
        config
    )

    baseline = run_baseline_pid(
        config,
        disturbances,
    )

    pid_oracle = run_pid_oracle(
        config,
        disturbances,
    )

    oracle_only = run_oracle_only(
        config,
        disturbances,
    )

    results = [
        baseline,
        pid_oracle,
        oracle_only,
    ]

    # Detailed traces.
    for result in results:
        print_trace(
            result,
            count=20,
        )

    # Individual reports.
    for result in results:
        print_result(
            result
        )

    # Side-by-side comparison.
    print_comparison(
        results
    )

    # Basic outcome.
    determine_best(
        results
    )

    print()
    print("=" * 78)
    print(" EXPERIMENT COMPLETE")
    print("=" * 78)


# =============================================================================
# ENTRY POINT
# =============================================================================

if __name__ == "__main__":
    main()

YIELDS:

==============================================================================

 CLOSED-LOOP CYBERNETIC A/B EXPERIMENT

==============================================================================

Reference φ      : 1.618033988749895

Initial Ω        : 1.628033988749895

Steps            : 5000

Disturbance      : ±1.000e-06

Random seed      : 22177

Prime / p        : 7

Base             : 10.0

909 |

837 |

====================================================================================================

TRACE: BASELINE PID

====================================================================================================

    t                 Ω             error           control        oracle

----------------------------------------------------------------------------------------------------

    1    1.609887533466   -1.00000000e-02   -4.35000000e-03    0.36611448

    2    1.625454441251   +8.14645528e-03   +4.29370805e-03    1.00277381

    3    1.611322771934   -7.42045250e-03   -3.88888098e-03    0.45846208

    4    1.624075355709   +6.71121682e-03   +3.46664553e-03    0.95433538

    5    1.612556493471   -6.04136696e-03   -3.17770587e-03    0.50776024

    6    1.622957247575   +5.47749528e-03   +2.82299907e-03    0.91230737

    7    1.613563938965   -4.92325882e-03   -2.59545047e-03    0.54766091

    8    1.622043726032   +4.47004979e-03   +2.29808281e-03    0.87774151

    9    1.614386011103   -4.00973728e-03   -2.11973901e-03    0.58020115

   10    1.621301127956   +3.64797765e-03   +1.86970127e-03    0.84938378

   11    1.615055106634   -3.26713921e-03   -1.73275187e-03    0.60660573

   12    1.620694314450   +2.97888212e-03   +1.52114106e-03    0.82620852

   13    1.615603611377   -2.66032570e-03   -1.41670164e-03    0.62814650

   14    1.620198960494   +2.43037737e-03   +1.23558920e-03    0.80715030

   15    1.616049618616   -2.16497174e-03   -1.15849203e-03    0.64570450

   16    1.619795353625   +1.98437013e-03   +1.00327476e-03    0.79161555

   17    1.616414429953   -1.76136487e-03   -9.48145470e-04    0.65999182

   18    1.619466284903   +1.61955880e-03   +8.13408303e-04    0.77888466

   19    1.616711116723   -1.43229615e-03   -7.76524700e-04    0.67162746

   20    1.619198666662   +1.32287203e-03   +6.58960374e-04    0.76851556

837 |

====================================================================================================

TRACE: PID + ORACLE GAIN

====================================================================================================

    t                 Ω             error           control        oracle

----------------------------------------------------------------------------------------------------

    1    1.609290309218   -1.00000000e-02   -4.94722425e-03    0.36611448

    2    1.627691206125   +8.74367953e-03   +6.29995351e-03    1.02277526

    3    1.608768543152   -9.65721738e-03   -5.59768782e-03    0.37839038

    4    1.628201084211   +9.26544560e-03   +6.60703954e-03    1.04017128

    5    1.608313621366   -1.01670955e-02   -5.86035586e-03    0.36013039

    6    1.628737312981   +9.72036738e-03   +6.96709852e-03    1.05527720

    7    1.607848482572   -1.07033242e-02   -6.12428026e-03    0.34092765

    8    1.629288260092   +1.01855062e-02   +7.33959090e-03    1.07066152

    9    1.607373244699   -1.12542713e-02   -6.39124703e-03    0.32120080

   10    1.629857229109   +1.06607441e-02   +7.72330522e-03    1.08631459

   11    1.606887718058   -1.18232404e-02   -6.66224618e-03    0.30083399

   12    1.630439177901   +1.11462707e-02   +8.11890597e-03    1.10223663

   13    1.606397559657   -1.24051892e-02   -6.93493054e-03    0.28001045

   14    1.631032652426   +1.16364291e-02   +8.52208208e-03    1.11823696

   15    1.605900518326   -1.29986637e-02   -7.20795463e-03    0.25878517

   16    1.631637079605   +1.21334704e-02   +8.93430332e-03    1.13438447

   17    1.605400978851   -1.36030909e-02   -7.48104694e-03    0.23718181

   18    1.632249521028   +1.26330099e-02   +9.35252901e-03    1.15053247

   19    1.604899186310   -1.42155323e-02   -7.75249901e-03    0.21530871

   20    1.632868384576   +1.31348024e-02   +9.77627532e-03    1.16666978

837 |

====================================================================================================

TRACE: ORACLE-ONLY

====================================================================================================

    t                 Ω             error           control        oracle

----------------------------------------------------------------------------------------------------

    1    1.611846833124   -1.00000000e-02   -2.39070034e-03    0.36611448

    2    1.622502449710   +6.18715563e-03   +2.09677667e-03    0.93652490

    3    1.615108059701   -4.46846096e-03   -1.22291498e-03    0.56386855

    4    1.620088353158   +2.92592905e-03   +9.34146483e-04    0.82437093

    5    1.616656448174   -2.05436441e-03   -5.93061688e-04    0.64962161

    6    1.618988342905   +1.37754058e-03   +4.26796187e-04    0.77042724

    7    1.617387885861   -9.54354155e-04   -2.82000572e-04    0.68850508

    8    1.618477676534   +6.46102888e-04   +1.97282491e-04    0.74481269

    9    1.617731617736   -4.43687784e-04   -1.32502418e-04    0.70650783

   10    1.618242079884   +3.02371014e-04   +9.16882592e-05    0.73274846

   11    1.617891360205   -2.08091134e-04   -6.24461518e-05    0.71480229

   12    1.618130836376   +1.42628545e-04   +4.31093124e-05    0.72713624

   13    1.617968679358   -9.68476258e-05   -2.91293776e-05    0.71871623

   14    1.618078308861   +6.53093923e-05   +1.97086271e-05    0.72441853

   15    1.618003678232   -4.43201114e-05   -1.33447261e-05    0.72056376

   16    1.618053904391   +3.03105182e-05   +9.14037666e-06    0.72318809

   17    1.618021018788   -1.99156410e-05   -5.99956397e-06    0.72142201

   18    1.618042504301   +1.29699618e-05   +3.90981076e-06    0.72257839

   19    1.618028427481   -8.51555095e-06   -2.56589735e-06    0.72182289

   20    1.618037801102   +5.56126917e-06   +1.67619783e-06    0.72231788

697 |

==============================================================================

BASELINE PID

==============================================================================

MAE             : 2.062482504957e-05

RMSE            : 3.049037123565e-04

MAX ERROR       : 1.000000000000e-02

SETTLING TIME   : NOT SETTLED

CONTROL EFFORT  : 5.697915914391e-02

FINAL ERROR     : -3.791005285247e-07

FINAL STATE     : 1.618033570703

FINAL ORACLE    : 0.722109002014

697 |

==============================================================================

PID + ORACLE GAIN

==============================================================================

MAE             : 8.019911306266e-02

RMSE            : 8.050083385390e-02

MAX ERROR       : 8.207944843281e-02

SETTLING TIME   : NOT SETTLED

CONTROL EFFORT  : 2.477299118377e+02

FINAL ERROR     : 7.983670163250e-02

FINAL STATE     : 1.700111560332

FINAL ORACLE    : 1.709479116689

697 |

==============================================================================

ORACLE-ONLY

==============================================================================

MAE             : 6.643794036608e-06

RMSE            : 1.868837189567e-04

MAX ERROR       : 1.000000000000e-02

SETTLING TIME   : NOT SETTLED

CONTROL EFFORT  : 9.530682034651e-03

FINAL ERROR     : -5.573059509434e-07

FINAL STATE     : 1.618033502883

FINAL ORACLE    : 0.722102735727

745 |

====================================================================================================

A/B COMPARISON

====================================================================================================

CONTROLLER                         MAE            RMSE             MAX          EFFORT           FINAL

----------------------------------------------------------------------------------------------------

BASELINE PID              2.062483e-05    3.049037e-04    1.000000e-02    5.697916e-02   -3.791005e-07

PID + ORACLE GAIN         8.019911e-02    8.050083e-02    8.207945e-02    2.477299e+02    7.983670e-02

ORACLE-ONLY               6.643794e-06    1.868837e-04    1.000000e-02    9.530682e-03   -5.573060e-07

799 |

==============================================================================

EXPERIMENTAL OUTCOME

==============================================================================

LOWEST MAE       : ORACLE-ONLY

LOWEST RMSE      : ORACLE-ONLY

LOWEST EFFORT    : ORACLE-ONLY

816 |

Interpretation:

The oracle is considered beneficial only if its addition measurably improves regulation against the identical disturbance sequence rather than merely producing an interesting signal.

960 |

==============================================================================

 EXPERIMENT COMPLETE

==============================================================================


#!/usr/bin/env python3
# =============================================================================
# शून्यत्वम् — FULL CYBERNETIC ENGINE
# =============================================================================
#
# CLOSED-LOOP GOLDEN-RATIO CYBERNETIC CONTROLLER
#
# Architecture
#
#     ┌──────────────────────────────────────────────────────────────┐
#     │                         CYBERNETIC LOOP                       │
#     │                                                              │
#     │   disturbance                                               │
#     │        │                                                     │
#     │        ▼                                                     │
#     │   ┌───────────┐       measured state       ┌──────────────┐ │
#     │   │   PLANT   │ ─────────────────────────► │   OBSERVER   │ │
#     │   │  Ω[n]     │                            │ Ω, ΔΩ, R     │ │
#     │   └─────┬─────┘                            └──────┬───────┘ │
#     │         ▲                                        │         │
#     │         │                                        │         │
#     │         │ control                               ▼         │
#     │         │                                  ┌─────────────┐  │
#     │         └──────────────────────────────────│ CONTROLLER  │  │
#     │                                            │ oracle +    │  │
#     │                                            │ feedback    │  │
#     │                                            └─────────────┘  │
#     │                                                              │
#     └──────────────────────────────────────────────────────────────┘
#
# Core plant:
#
#     Ω[n+1] = 1 + 1/Ω[n] + u[n] + η[n]
#
# Golden fixed point:
#
#     φ = (1 + √5) / 2
#
#     φ = 1 + 1/φ
#
# Oracle:
#
#     Λφ(p, Ω)
#       = ln(p ln(base) / ln(Ω)) / ln(Ω)
#         - 1 / (base Ω)
#
#     O = 2 |cos(π Λφ / 2)|
#
# Controller:
#
#     e[n] = φ - Ω[n]
#
#     u[n] = feedback(e, Δe, integral, O, Ω)
#
# The oracle is part of the closed loop.
# It is NOT merely an observer output.
#
# Features:
#
#     • nonlinear golden-ratio plant
#     • oracle
#     • state observer
#     • filtered state velocity
#     • proportional / integral / derivative control
#     • oracle-modulated feedback
#     • integral anti-windup
#     • actuator saturation
#     • slew-rate limiting
#     • deadband
#     • safety bounds
#     • disturbance handling
#     • confidence estimation
#     • telemetry
#     • rolling statistics
#     • fault detection
#     • automatic recovery
#     • deterministic or stochastic disturbance source
#     • JSON telemetry export
#     • CSV telemetry export
#     • live console telemetry
#     • clean shutdown
#
# Standard library only.
# =============================================================================

from __future__ import annotations

import csv
import json
import math
import random
import signal
import sys
import time
from collections import deque
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Deque, Dict, List, Optional


# =============================================================================
# MATHEMATICAL CONSTANTS
# =============================================================================

PHI = (1.0 + math.sqrt(5.0)) / 2.0

PHI_CONJUGATE = (1.0 - math.sqrt(5.0)) / 2.0

PHI_RECIPROCAL = 1.0 / PHI

PHI_MAP_DERIVATIVE = -1.0 / (PHI * PHI)


# =============================================================================
# CONFIGURATION
# =============================================================================

@dataclass
class CyberneticConfig:
    # -------------------------------------------------------------------------
    # Runtime
    # -------------------------------------------------------------------------

    steps: int = 10000

    print_every: int = 10

    telemetry_window: int = 256

    # -------------------------------------------------------------------------
    # Reference
    # -------------------------------------------------------------------------

    reference: float = PHI

    # Start away from the equilibrium to engage the controller immediately.
    initial_offset: float = 1.0e-2

    # -------------------------------------------------------------------------
    # Plant
    # -------------------------------------------------------------------------

    disturbance_amplitude: float = 1.0e-6

    deterministic_disturbance: bool = True

    random_seed: int = 22177

    plant_minimum: float = 1.0e-9

    plant_maximum: float = 10.0

    # -------------------------------------------------------------------------
    # Oracle
    # -------------------------------------------------------------------------

    prime: int = 7

    base: float = 10.0

    oracle_enabled: bool = True

    # The oracle contributes to control strength.
    #
    # gain = 0.5 + oracle / 2
    #
    # O ∈ [0, 2]
    # gain ∈ [0.5, 1.5]
    oracle_floor: float = 0.5

    oracle_ceiling: float = 1.5

    # -------------------------------------------------------------------------
    # Controller
    # -------------------------------------------------------------------------

    kp: float = 0.35

    ki: float = 0.005

    kd: float = 0.08

    # Total integral accumulation limit.
    integral_limit: float = 1.0e-2

    # Absolute actuator limit.
    max_control: float = 5.0e-2

    # Maximum controller change per cycle.
    max_control_delta: float = 1.0e-2

    # Ignore microscopic errors.
    deadband: float = 1.0e-12

    # -------------------------------------------------------------------------
    # State filtering
    # -------------------------------------------------------------------------

    velocity_alpha: float = 0.25

    error_alpha: float = 0.20

    # -------------------------------------------------------------------------
    # Stabilization
    # -------------------------------------------------------------------------

    # Maximum allowed error before recovery is engaged.
    recovery_error_limit: float = 0.1

    # Recovery gain.
    recovery_gain: float = 0.5

    # Maximum integral growth while recovering.
    recovery_integral_scale: float = 0.25

    # -------------------------------------------------------------------------
    # Settling
    # -------------------------------------------------------------------------

    settling_tolerance: float = 1.0e-8

    settling_window: int = 100

    # -------------------------------------------------------------------------
    # Fault detection
    # -------------------------------------------------------------------------

    divergence_limit: float = 1.0

    maximum_residual: float = 1.0

    # -------------------------------------------------------------------------
    # Output
    # -------------------------------------------------------------------------

    csv_path: str = "cybernetic_telemetry.csv"

    json_path: str = "cybernetic_summary.json"

    write_files: bool = True


# =============================================================================
# DISTURBANCE SOURCE
# =============================================================================

class DisturbanceSource:
    """
    Generates plant disturbances.

    Deterministic mode:
        a reproducible sequence from the configured seed.

    Stochastic mode:
        uses the process-global random source.
    """

    def __init__(
        self,
        config: CyberneticConfig,
    ) -> None:

        self.config = config

        self.rng = random.Random(
            config.random_seed
        )

    def next(self) -> float:
        amplitude = self.config.disturbance_amplitude

        if amplitude <= 0.0:
            return 0.0

        return self.rng.uniform(
            -amplitude,
            amplitude,
        )


# =============================================================================
# PLANT
# =============================================================================

class GoldenPlant:
    """
    Nonlinear plant.

        Ω[n+1]
            = 1
            + 1/Ω[n]
            + u[n]
            + η[n]

    The uncontrolled fixed point is φ.
    """

    def __init__(
        self,
        initial_state: float,
        config: CyberneticConfig,
    ) -> None:

        if not math.isfinite(initial_state):
            raise ValueError(
                "Initial plant state must be finite."
            )

        if initial_state <= config.plant_minimum:
            raise ValueError(
                "Initial plant state must be positive."
            )

        if initial_state >= config.plant_maximum:
            raise ValueError(
                "Initial plant state exceeds maximum."
            )

        self.config = config

        self.state = initial_state

    def nominal_map(
        self,
        omega: float,
    ) -> float:

        if omega == 0.0:
            raise ZeroDivisionError(
                "Nominal map received Ω = 0."
            )

        return 1.0 + 1.0 / omega

    def derivative(
        self,
        omega: float,
    ) -> float:

        if omega == 0.0:
            return float("-inf")

        return -1.0 / (omega * omega)

    def update(
        self,
        control: float,
        disturbance: float,
    ) -> float:

        omega = self.state

        nominal = self.nominal_map(
            omega
        )

        next_state = (
            nominal
            + control
            + disturbance
        )

        if not math.isfinite(next_state):
            raise FloatingPointError(
                f"Non-finite state generated: {next_state}"
            )

        if next_state <= self.config.plant_minimum:
            raise FloatingPointError(
                f"Plant crossed lower safety bound: {next_state}"
            )

        if next_state >= self.config.plant_maximum:
            raise FloatingPointError(
                f"Plant crossed upper safety bound: {next_state}"
            )

        self.state = next_state

        return next_state


# =============================================================================
# ORACLE
# =============================================================================

class PhiOracle:
    """
    φ-scaled logarithmic observer.

    Λφ(p, Ω)
        = ln(
            p ln(base) / ln(Ω)
          ) / ln(Ω)
          - 1 / (base Ω)

    O
        = 2 |cos(π Λφ / 2)|

    O is bounded in [0, 2].
    """

    def __init__(
        self,
        config: CyberneticConfig,
    ) -> None:

        self.config = config

        if config.base <= 0.0:
            raise ValueError(
                "Oracle base must be positive."
            )

        if config.base == 1.0:
            raise ValueError(
                "Oracle base must not equal 1."
            )

        if config.prime <= 0:
            raise ValueError(
                "Oracle p must be positive."
            )

    def lambda_value(
        self,
        omega: float,
    ) -> float:

        if omega <= 0.0:
            return float("nan")

        ln_base = math.log(
            self.config.base
        )

        ln_omega = math.log(
            omega
        )

        if ln_omega == 0.0:
            return float("nan")

        argument = (
            self.config.prime
            * ln_base
            / ln_omega
        )

        if argument <= 0.0:
            return float("nan")

        value = (
            math.log(argument)
            / ln_omega
            -
            1.0
            /
            (
                self.config.base
                * omega
            )
        )

        return value

    def evaluate(
        self,
        omega: float,
    ) -> float:

        if not self.config.oracle_enabled:
            return 1.0

        lam = self.lambda_value(
            omega
        )

        if not math.isfinite(lam):
            return 1.0

        oracle = (
            2.0
            *
            abs(
                math.cos(
                    math.pi
                    *
                    lam
                    /
                    2.0
                )
            )
        )

        if not math.isfinite(oracle):
            return 1.0

        return max(
            0.0,
            min(
                2.0,
                oracle
            )
        )

    def gain(
        self,
        omega: float,
    ) -> float:

        if not self.config.oracle_enabled:
            return 1.0

        oracle = self.evaluate(
            omega
        )

        value = (
            self.config.oracle_floor
            +
            (
                self.config.oracle_ceiling
                -
                self.config.oracle_floor
            )
            *
            (
                oracle / 2.0
            )
        )

        return value


# =============================================================================
# STATE OBSERVER
# =============================================================================

class StateObserver:
    """
    Estimates:

        Ω
        Ω velocity
        filtered error
        residual
        confidence
    """

    def __init__(
        self,
        reference: float,
        config: CyberneticConfig,
    ) -> None:

        self.reference = reference

        self.config = config

        self.initialized = False

        self.previous_state = reference

        self.velocity = 0.0

        self.filtered_error = 0.0

        self.residual = 0.0

        self.confidence = 1.0

    def reset(
        self,
        state: float,
    ) -> None:

        self.initialized = True

        self.previous_state = state

        self.velocity = 0.0

        self.filtered_error = (
            self.reference - state
        )

        self.residual = 0.0

        self.confidence = 1.0

    def update(
        self,
        state: float,
        expected_nominal: float,
        control: float,
        disturbance: float,
    ) -> Dict[str, float]:

        if not self.initialized:
            self.reset(
                state
            )

        raw_velocity = (
            state
            -
            self.previous_state
        )

        self.velocity = (
            self.config.velocity_alpha
            *
            raw_velocity
            +
            (
                1.0
                -
                self.config.velocity_alpha
            )
            *
            self.velocity
        )

        error = (
            self.reference
            -
            state
        )

        self.filtered_error = (
            self.config.error_alpha
            *
            error
            +
            (
                1.0
                -
                self.config.error_alpha
            )
            *
            self.filtered_error
        )

        predicted_state = (
            expected_nominal
            +
            control
            +
            disturbance
        )

        self.residual = (
            state
            -
            predicted_state
        )

        residual_penalty = min(
            1.0,
            abs(
                self.residual
            )
            /
            max(
                self.config.maximum_residual,
                1.0e-30,
            )
        )

        velocity_penalty = min(
            1.0,
            abs(
                self.velocity
            )
            /
            max(
                self.config.recovery_error_limit,
                1.0e-30,
            )
        )

        self.confidence = max(
            0.0,
            min(
                1.0,
                1.0
                -
                0.5
                *
                residual_penalty
                -
                0.5
                *
                velocity_penalty,
            )
        )

        self.previous_state = state

        return {
            "state": state,
            "velocity": self.velocity,
            "error": error,
            "filtered_error": self.filtered_error,
            "residual": self.residual,
            "confidence": self.confidence,
        }


# =============================================================================
# CONTROLLER TELEMETRY
# =============================================================================

@dataclass
class ControlTerms:
    proportional: float = 0.0
    integral: float = 0.0
    derivative: float = 0.0
    oracle_gain: float = 1.0
    feedback: float = 0.0
    recovery: float = 0.0
    pre_saturation: float = 0.0
    post_saturation: float = 0.0
    slew_limited: bool = False


# =============================================================================
# CYBERNETIC CONTROLLER
# =============================================================================

class CyberneticController:
    """
    Closed-loop controller.

    The control law combines:

        P       = kp * filtered_error
        I       = ki * integral(error)
        D       = kd * (-estimated_velocity)

    then applies the oracle as a gain field:

        oracle_gain = G(O)

    followed by recovery, saturation, and slew limiting.

    The feedback path is:

        measured state
            ↓
        observer
            ↓
        error + velocity + confidence
            ↓
        controller
            ↓
        control
            ↓
        plant
            ↓
        measured state ...
    """

    def __init__(
        self,
        config: CyberneticConfig,
        oracle: PhiOracle,
    ) -> None:

        self.config = config

        self.oracle = oracle

        self.integral = 0.0

        self.previous_control = 0.0

        self.last_terms = ControlTerms()

    def reset(self) -> None:

        self.integral = 0.0

        self.previous_control = 0.0

        self.last_terms = ControlTerms()

    def clamp(
        self,
        value: float,
        low: float,
        high: float,
    ) -> float:

        return max(
            low,
            min(
                high,
                value
            )
        )

    def update(
        self,
        state: float,
        error: float,
        filtered_error: float,
        velocity: float,
        confidence: float,
        omega: float,
    ) -> float:

        cfg = self.config

        # ---------------------------------------------------------------------
        # Deadband
        # ---------------------------------------------------------------------

        effective_error = error

        if abs(
            effective_error
        ) <= cfg.deadband:

            effective_error = 0.0

        # ---------------------------------------------------------------------
        # Integral
        # ---------------------------------------------------------------------

        self.integral += (
            effective_error
        )

        self.integral = self.clamp(
            self.integral,
            -cfg.integral_limit,
            cfg.integral_limit,
        )

        # ---------------------------------------------------------------------
        # PID terms
        # ---------------------------------------------------------------------

        proportional = (
            cfg.kp
            *
            filtered_error
        )

        integral = (
            cfg.ki
            *
            self.integral
        )

        # Negative velocity is a useful direct damping signal:
        #
        #   velocity > 0  => state moving upward
        #   derivative term becomes negative
        #
        derivative = (
            -cfg.kd
            *
            velocity
        )

        base_feedback = (
            proportional
            +
            integral
            +
            derivative
        )

        # ---------------------------------------------------------------------
        # Oracle gain
        # ---------------------------------------------------------------------

        oracle_gain = self.oracle.gain(
            omega
        )

        # Lower confidence reduces the authority of the controller.
        confidence_gain = (
            0.25
            +
            0.75
            *
            confidence
        )

        feedback = (
            base_feedback
            *
            oracle_gain
            *
            confidence_gain
        )

        # ---------------------------------------------------------------------
        # Recovery controller
        # ---------------------------------------------------------------------

        recovery = 0.0

        if abs(
            error
        ) >= cfg.recovery_error_limit:

            recovery = (
                cfg.recovery_gain
                *
                error
            )

            self.integral *= (
                cfg.recovery_integral_scale
            )

        pre_saturation = (
            feedback
            +
            recovery
        )

        # ---------------------------------------------------------------------
        # Actuator saturation
        # ---------------------------------------------------------------------

        saturated = self.clamp(
            pre_saturation,
            -cfg.max_control,
            cfg.max_control,
        )

        # ---------------------------------------------------------------------
        # Slew-rate limiting
        # ---------------------------------------------------------------------

        delta = (
            saturated
            -
            self.previous_control
        )

        slew_limited = False

        if delta > cfg.max_control_delta:

            control = (
                self.previous_control
                +
                cfg.max_control_delta
            )

            slew_limited = True

        elif delta < -cfg.max_control_delta:

            control = (
                self.previous_control
                -
                cfg.max_control_delta
            )

            slew_limited = True

        else:

            control = saturated

        self.previous_control = control

        self.last_terms = ControlTerms(
            proportional=proportional,
            integral=integral,
            derivative=derivative,
            oracle_gain=oracle_gain,
            feedback=feedback,
            recovery=recovery,
            pre_saturation=pre_saturation,
            post_saturation=control,
            slew_limited=slew_limited,
        )

        return control


# =============================================================================
# ROLLING STATISTICS
# =============================================================================

class RollingStatistics:
    def __init__(
        self,
        window: int,
    ) -> None:

        self.window = max(
            1,
            window
        )

        self.errors: Deque[float] = deque(
            maxlen=self.window
        )

        self.controls: Deque[float] = deque(
            maxlen=self.window
        )

        self.oracle: Deque[float] = deque(
            maxlen=self.window
        )

        self.residuals: Deque[float] = deque(
            maxlen=self.window
        )

    def add(
        self,
        error: float,
        control: float,
        oracle: float,
        residual: float,
    ) -> None:

        self.errors.append(
            error
        )

        self.controls.append(
            control
        )

        self.oracle.append(
            oracle
        )

        self.residuals.append(
            residual
        )

    @staticmethod
    def mean(
        values: Deque[float],
    ) -> float:

        if not values:
            return 0.0

        return sum(values) / len(
            values
        )

    @staticmethod
    def rms(
        values: Deque[float],
    ) -> float:

        if not values:
            return 0.0

        return math.sqrt(
            sum(
                value * value
                for value in values
            )
            /
            len(values)
        )

    def summary(self) -> Dict[str, float]:

        return {
            "mean_abs_error":
                self.mean(
                    deque(
                        abs(x)
                        for x in self.errors
                    )
                ),
            "rms_error":
                self.rms(
                    self.errors
                ),
            "mean_control":
                self.mean(
                    self.controls
                ),
            "mean_oracle":
                self.mean(
                    self.oracle
                ),
            "mean_abs_residual":
                self.mean(
                    deque(
                        abs(x)
                        for x in self.residuals
                    )
                ),
        }


# =============================================================================
# TELEMETRY RECORD
# =============================================================================

@dataclass
class TelemetryRecord:
    step: int
    state: float
    reference: float
    error: float
    filtered_error: float
    velocity: float
    residual: float
    confidence: float
    oracle: float
    oracle_gain: float
    proportional: float
    integral: float
    derivative: float
    feedback: float
    recovery: float
    control: float
    disturbance: float
    nominal_state: float
    closed_loop_state: float
    stable: bool
    recovered: bool


# =============================================================================
# FAULT STATE
# =============================================================================

@dataclass
class FaultState:
    faulted: bool = False
    fault_count: int = 0
    last_fault: str = ""


# =============================================================================
# CYBERNETIC ENGINE
# =============================================================================

class CyberneticEngine:
    """
    Complete stateful cybernetic machine.
    """

    def __init__(
        self,
        config: Optional[CyberneticConfig] = None,
    ) -> None:

        self.config = (
            config
            if config is not None
            else CyberneticConfig()
        )

        self.disturbance_source = (
            DisturbanceSource(
                self.config
            )
        )

        initial_state = (
            self.config.reference
            +
            self.config.initial_offset
        )

        self.plant = GoldenPlant(
            initial_state,
            self.config,
        )

        self.oracle = PhiOracle(
            self.config
        )

        self.observer = StateObserver(
            self.config.reference,
            self.config,
        )

        self.controller = CyberneticController(
            self.config,
            self.oracle,
        )

        self.statistics = RollingStatistics(
            self.config.telemetry_window
        )

        self.telemetry: List[
            TelemetryRecord
        ] = []

        self.fault = FaultState()

        self.running = True

        self.settled_count = 0

        self.first_settled_step: Optional[
            int
        ] = None

        self.total_abs_error = 0.0

        self.total_squared_error = 0.0

        self.maximum_abs_error = 0.0

        self.total_control_effort = 0.0

        self.total_disturbance_energy = 0.0

        self.controller.reset()

    # -------------------------------------------------------------------------
    # Stop
    # -------------------------------------------------------------------------

    def stop(self) -> None:

        self.running = False

    # -------------------------------------------------------------------------
    # Fault handling
    # -------------------------------------------------------------------------

    def register_fault(
        self,
        message: str,
    ) -> None:

        self.fault.faulted = True

        self.fault.fault_count += 1

        self.fault.last_fault = message

    def clear_fault(self) -> None:

        self.fault.faulted = False

        self.fault.last_fault = ""

    def recover(
        self,
    ) -> None:

        """
        Controlled recovery.

        Reset controller memory while preserving the plant.
        """

        self.controller.reset()

        self.observer.reset(
            self.plant.state
        )

        self.clear_fault()

    # -------------------------------------------------------------------------
    # Stability
    # -------------------------------------------------------------------------

    def is_stable(
        self,
        error: float,
        residual: float,
    ) -> bool:

        if not math.isfinite(error):
            return False

        if not math.isfinite(residual):
            return False

        if abs(error) > self.config.divergence_limit:
            return False

        if abs(residual) > self.config.maximum_residual:
            return False

        if self.plant.state <= self.config.plant_minimum:
            return False

        if self.plant.state >= self.config.plant_maximum:
            return False

        return True

    # -------------------------------------------------------------------------
    # Settling
    # -------------------------------------------------------------------------

    def update_settling(
        self,
        step: int,
        error: float,
    ) -> None:

        if (
            abs(error)
            <= self.config.settling_tolerance
        ):

            self.settled_count += 1

            if (
                self.first_settled_step
                is None
            ):

                if (
                    self.settled_count
                    >= self.config.settling_window
                ):

                    self.first_settled_step = (
                        step
                        -
                        self.config.settling_window
                        +
                        1
                    )

        else:

            self.settled_count = 0

    # -------------------------------------------------------------------------
    # Single cybernetic cycle
    # -------------------------------------------------------------------------

    def step(
        self,
        step: int,
    ) -> TelemetryRecord:

        previous_state = (
            self.plant.state
        )

        # ---------------------------------------------------------------------
        # Disturbance
        # ---------------------------------------------------------------------

        disturbance = (
            self.disturbance_source.next()
        )

        # ---------------------------------------------------------------------
        # Oracle
        # ---------------------------------------------------------------------

        oracle_value = (
            self.oracle.evaluate(
                previous_state
            )
        )

        oracle_gain = (
            self.oracle.gain(
                previous_state
            )
        )

        # ---------------------------------------------------------------------
        # Nominal plant prediction
        # ---------------------------------------------------------------------

        nominal_state = (
            self.plant.nominal_map(
                previous_state
            )
        )

        # ---------------------------------------------------------------------
        # Preliminary observation
        # ---------------------------------------------------------------------

        predicted_without_control = (
            nominal_state
            +
            disturbance
        )

        estimated_velocity = (
            self.observer.velocity
        )

        error = (
            self.config.reference
            -
            previous_state
        )

        observer_data = (
            self.observer.update(
                state=previous_state,
                expected_nominal=nominal_state,
                control=self.controller.previous_control,
                disturbance=disturbance,
            )
        )

        # Replace raw values with filtered observer values.
        filtered_error = (
            observer_data[
                "filtered_error"
            ]
        )

        velocity = (
            observer_data[
                "velocity"
            ]
        )

        confidence = (
            observer_data[
                "confidence"
            ]
        )

        # ---------------------------------------------------------------------
        # Controller
        # ---------------------------------------------------------------------

        control = (
            self.controller.update(
                state=previous_state,
                error=error,
                filtered_error=filtered_error,
                velocity=velocity,
                confidence=confidence,
                omega=previous_state,
            )
        )

        # ---------------------------------------------------------------------
        # Plant update
        # ---------------------------------------------------------------------

        try:

            closed_loop_state = (
                self.plant.update(
                    control,
                    disturbance,
                )
            )

        except (
            FloatingPointError,
            ZeroDivisionError,
        ) as exc:

            self.register_fault(
                str(exc)
            )

            # Hold the current state if possible.
            closed_loop_state = (
                previous_state
            )

            self.plant.state = (
                previous_state
            )

        # ---------------------------------------------------------------------
        # Actual residual
        # ---------------------------------------------------------------------

        actual_residual = (
            closed_loop_state
            -
            (
                nominal_state
                +
                control
                +
                disturbance
            )
        )

        # ---------------------------------------------------------------------
        # Error after plant transition
        # ---------------------------------------------------------------------

        next_error = (
            self.config.reference
            -
            closed_loop_state
        )

        recovered = False

        # ---------------------------------------------------------------------
        # Stability
        # ---------------------------------------------------------------------

        stable = self.is_stable(
            next_error,
            actual_residual,
        )

        if not stable:

            self.register_fault(
                "closed-loop stability boundary exceeded"
            )

            self.recover()

            recovered = True

            stable = True

        # ---------------------------------------------------------------------
        # Aggregate statistics
        # ---------------------------------------------------------------------

        self.total_abs_error += abs(
            error
        )

        self.total_squared_error += (
            error * error
        )

        self.maximum_abs_error = max(
            self.maximum_abs_error,
            abs(error)
        )

        self.total_control_effort += (
            abs(control)
        )

        self.total_disturbance_energy += (
            disturbance * disturbance
        )

        self.statistics.add(
            error=error,
            control=control,
            oracle=oracle_value,
            residual=actual_residual,
        )

        self.update_settling(
            step,
            next_error,
        )

        terms = (
            self.controller.last_terms
        )

        record = TelemetryRecord(
            step=step,
            state=previous_state,
            reference=self.config.reference,
            error=error,
            filtered_error=filtered_error,
            velocity=(
                velocity
                if math.isfinite(
                    velocity
                )
                else estimated_velocity
            ),
            residual=actual_residual,
            confidence=confidence,
            oracle=oracle_value,
            oracle_gain=oracle_gain,
            proportional=terms.proportional,
            integral=terms.integral,
            derivative=terms.derivative,
            feedback=terms.feedback,
            recovery=terms.recovery,
            control=control,
            disturbance=disturbance,
            nominal_state=nominal_state,
            closed_loop_state=closed_loop_state,
            stable=stable,
            recovered=recovered,
        )

        self.telemetry.append(
            record
        )

        return record

    # -------------------------------------------------------------------------
    # Console
    # -------------------------------------------------------------------------

    def print_header(self) -> None:

        print(
            "=" * 124
        )

        print(
            " शून्यत्वम् — FULL CYBERNETIC ENGINE"
        )

        print(
            "=" * 124
        )

        print(
            f"φ              = {PHI:.15f}"
        )

        print(
            f"ψ              = {PHI_CONJUGATE:.15f}"
        )

        print(
            f"1/φ            = {PHI_RECIPROCAL:.15f}"
        )

        print(
            f"plant derivative= {PHI_MAP_DERIVATIVE:.15f}"
        )

        print(
            f"initial Ω       = "
            f"{self.plant.state:.15f}"
        )

        print(
            f"reference       = "
            f"{self.config.reference:.15f}"
        )

        print(
            f"oracle p        = "
            f"{self.config.prime}"
        )

        print(
            f"oracle base     = "
            f"{self.config.base}"
        )

        print(
            f"steps           = "
            f"{self.config.steps}"
        )

        print(
            f"disturbance     = "
            f"±{self.config.disturbance_amplitude:.3e}"
        )

        print(
            f"oracle          = "
            f"{'ON' if self.config.oracle_enabled else 'OFF'}"
        )

        print()

        print(
            f"{'STEP':>8}"
            f"{'Ω':>18}"
            f"{'ERROR':>16}"
            f"{'VELO':>14}"
            f"{'ORACLE':>12}"
            f"{'GAIN':>10}"
            f"{'CONTROL':>14}"
            f"{'CONF':>10}"
            f"{'RESID':>14}"
            f"{'STATE':>12}"
        )

        print(
            "-" * 124
        )

    def print_record(
        self,
        record: TelemetryRecord,
    ) -> None:

        state = (
            "OK"
            if record.stable
            else "FAULT"
        )

        if record.recovered:
            state = "RECOVER"

        print(
            f"{record.step:>8}"
            f"{record.state:>18.12f}"
            f"{record.error:>+16.6e}"
            f"{record.velocity:>+14.6e}"
            f"{record.oracle:>12.8f}"
            f"{record.oracle_gain:>10.6f}"
            f"{record.control:>+14.6e}"
            f"{record.confidence:>10.6f}"
            f"{record.residual:>+14.6e}"
            f"{state:>12}"
        )

    # -------------------------------------------------------------------------
    # Run
    # -------------------------------------------------------------------------

    def run(self) -> None:

        self.print_header()

        for step in range(
            self.config.steps
        ):

            if not self.running:
                break

            record = self.step(
                step
            )

            if (
                step % self.config.print_every
                == 0
            ):

                self.print_record(
                    record
                )

        print()

        self.print_summary()

    # -------------------------------------------------------------------------
    # Summary
    # -------------------------------------------------------------------------

    def summary(self) -> Dict[str, object]:

        count = len(
            self.telemetry
        )

        if count == 0:
            return {
                "steps": 0,
                "error": None,
            }

        rms = math.sqrt(
            self.total_squared_error
            /
            count
        )

        mae = (
            self.total_abs_error
            /
            count
        )

        final_state = (
            self.plant.state
        )

        final_error = (
            self.config.reference
            -
            final_state
        )

        recent = (
            self.statistics.summary()
        )

        return {
            "engine": "शून्यत्वम्",
            "architecture": "closed-loop cybernetic controller",
            "steps": count,
            "reference_phi": self.config.reference,
            "initial_state": (
                self.config.reference
                +
                self.config.initial_offset
            ),
            "final_state": final_state,
            "final_error": final_error,
            "mean_absolute_error": mae,
            "root_mean_square_error": rms,
            "maximum_absolute_error": (
                self.maximum_abs_error
            ),
            "total_control_effort": (
                self.total_control_effort
            ),
            "disturbance_energy": (
                self.total_disturbance_energy
            ),
            "settling_step": (
                self.first_settled_step
            ),
            "fault_count": (
                self.fault.fault_count
            ),
            "last_fault": (
                self.fault.last_fault
            ),
            "rolling": recent,
        }

    def print_summary(self) -> None:

        summary = self.summary()

        print(
            "=" * 78
        )

        print(
            " CYBERNETIC STATE SUMMARY"
        )

        print(
            "=" * 78
        )

        print(
            f"steps              : "
            f"{summary.get('steps')}"
        )

        print(
            f"reference φ        : "
            f"{summary.get('reference_phi'):.15f}"
        )

        print(
            f"final Ω            : "
            f"{summary.get('final_state'):.15f}"
        )

        print(
            f"final error        : "
            f"{summary.get('final_error'):+.12e}"
        )

        print(
            f"MAE                : "
            f"{summary.get('mean_absolute_error'):.12e}"
        )

        print(
            f"RMSE               : "
            f"{summary.get('root_mean_square_error'):.12e}"
        )

        print(
            f"maximum error      : "
            f"{summary.get('maximum_absolute_error'):.12e}"
        )

        print(
            f"control effort     : "
            f"{summary.get('total_control_effort'):.12e}"
        )

        print(
            f"disturbance energy : "
            f"{summary.get('disturbance_energy'):.12e}"
        )

        settling = (
            summary.get(
                "settling_step"
            )
        )

        if settling is None:

            print(
                "settling           : NOT ESTABLISHED"
            )

        else:

            print(
                f"settling step      : "
                f"{settling}"
            )

        print(
            f"fault count        : "
            f"{summary.get('fault_count')}"
        )

        if summary.get(
            "last_fault"
        ):

            print(
                f"last fault         : "
                f"{summary.get('last_fault')}"
            )

        rolling = summary.get(
            "rolling",
            {}
        )

        print()

        print(
            "ROLLING WINDOW"
        )

        print(
            f"mean |error|       : "
            f"{rolling.get('mean_abs_error', 0.0):.12e}"
        )

        print(
            f"RMS error          : "
            f"{rolling.get('rms_error', 0.0):.12e}"
        )

        print(
            f"mean control       : "
            f"{rolling.get('mean_control', 0.0):+.12e}"
        )

        print(
            f"mean oracle        : "
            f"{rolling.get('mean_oracle', 0.0):.12e}"
        )

        print(
            f"mean |residual|    : "
            f"{rolling.get('mean_abs_residual', 0.0):.12e}"
        )

        print()

    # -------------------------------------------------------------------------
    # CSV
    # -------------------------------------------------------------------------

    def write_csv(
        self,
        path: str,
    ) -> None:

        if not self.telemetry:
            return

        output = Path(
            path
        )

        fields = list(
            asdict(
                self.telemetry[0]
            ).keys()
        )

        with output.open(
            "w",
            newline="",
            encoding="utf-8",
        ) as handle:

            writer = csv.DictWriter(
                handle,
                fieldnames=fields,
            )

            writer.writeheader()

            for record in self.telemetry:

                writer.writerow(
                    asdict(record)
                )

    # -------------------------------------------------------------------------
    # JSON
    # -------------------------------------------------------------------------

    def write_json(
        self,
        path: str,
    ) -> None:

        output = Path(
            path
        )

        payload = {
            "configuration": asdict(
                self.config
            ),
            "summary": self.summary(),
        }

        with output.open(
            "w",
            encoding="utf-8",
        ) as handle:

            json.dump(
                payload,
                handle,
                indent=2,
            )

            handle.write("\n")

    # -------------------------------------------------------------------------
    # Files
    # -------------------------------------------------------------------------

    def export(
        self,
    ) -> None:

        if not self.config.write_files:
            return

        self.write_csv(
            self.config.csv_path
        )

        self.write_json(
            self.config.json_path
        )


# =============================================================================
# SIGNAL HANDLING
# =============================================================================

_ENGINE: Optional[
    CyberneticEngine
] = None


def handle_signal(
    signum: int,
    frame,
) -> None:

    del frame

    if _ENGINE is not None:

        print()

        print(
            f"[signal {signum}] shutdown requested"
        )

        _ENGINE.stop()


# =============================================================================
# MAIN
# =============================================================================

def main() -> int:

    global _ENGINE

    config = CyberneticConfig()

    _ENGINE = CyberneticEngine(
        config
    )

    signal.signal(
        signal.SIGINT,
        handle_signal
    )

    if hasattr(
        signal,
        "SIGTERM"
    ):

        signal.signal(
            signal.SIGTERM,
            handle_signal
        )

    start = time.perf_counter()

    try:

        _ENGINE.run()

    except KeyboardInterrupt:

        print()
        print(
            "Keyboard interrupt."
        )

        _ENGINE.stop()

    except Exception as exc:

        print()
        print(
            "=" * 78
        )

        print(
            "CYBERNETIC ENGINE FAULT"
        )

        print(
            "=" * 78
        )

        print(
            f"{type(exc).__name__}: {exc}"
        )

        _ENGINE.register_fault(
            str(exc)
        )

        _ENGINE.stop()

    finally:

        _ENGINE.export()

    elapsed = (
        time.perf_counter()
        -
        start
    )

    print()

    print(
        f"runtime            : "
        f"{elapsed:.6f} s"
    )

    print(
        f"rate               : "
        f"{len(_ENGINE.telemetry) / elapsed:.3f} cycles/s"
        if elapsed > 0.0
        else "rate               : n/a"
    )

    print()

    print(
        "CYBERNETIC ENGINE CLOSED."
    )

    return 0


# =============================================================================
# ENTRY
# =============================================================================

if __name__ == "__main__":
    raise SystemExit(
        main()
    )

YIELDS:

============================================================================================================================

 शून्यत्वम् — FULL CYBERNETIC ENGINE

============================================================================================================================

φ              = 1.618033988749895

ψ              = -0.618033988749895

1/φ            = 0.618033988749895

plant derivative= -0.381966011250105

initial Ω       = 1.628033988749895

reference       = 1.618033988749895

oracle p        = 7

oracle base     = 10.0

steps           = 10000

disturbance     = ±1.000e-06

oracle          = ON

1680 |

    STEP                 Ω           ERROR          VELO      ORACLE      GAIN       CONTROL      CONF         RESID       STATE

----------------------------------------------------------------------------------------------------------------------------

       0    1.628033988750   -1.000000e-02 +0.000000e+00  0.36611448  0.683057 -2.412308e-03  0.993102 +0.000000e+00          OK

      10    1.617882121160   +1.518676e-04 -1.012316e-04  0.72746093  0.863730 -1.766889e-04  0.999483 +0.000000e+00          OK

      20    1.618024750170   +9.238580e-06 +1.707516e-06  0.72244718  0.861224 -9.924932e-06  0.999991 +0.000000e+00          OK

      30    1.618033897861   +9.088935e-08 +4.129443e-07  0.72212553  0.861063 +1.353406e-06  0.999997 +0.000000e+00          OK

      40    1.618034803645   -8.148950e-07 -1.102062e-07  0.72209368  0.861047 +1.965216e-06  0.999999 +0.000000e+00          OK

      50    1.618035521031   -1.532281e-06 +1.109335e-08  0.72206845  0.861034 +1.909325e-06  1.000000 +0.000000e+00          OK

      60    1.618036383867   -2.395117e-06 +2.800561e-07  0.72203811  0.861019 +1.823983e-06  0.999998 +0.000000e+00          OK

      70    1.618035791821   -1.803071e-06 +1.153764e-07  0.72205893  0.861029 +1.807200e-06  0.999999 +0.000000e+00          OK

      80    1.618035150231   -1.161481e-06 -9.423201e-08  0.72208149  0.861041 +1.764688e-06  0.999999 +0.000000e+00          OK

      90    1.618036636482   -2.647732e-06 +3.879873e-07  0.72202923  0.861015 +1.664028e-06  0.999998 +0.000000e+00          OK

     100    1.618034378918   -3.901685e-07 -2.660513e-07  0.72210861  0.861054 +1.722745e-06  0.999998 +0.000000e+00          OK

     110    1.618035815878   -1.827128e-06 +9.458291e-08  0.72205808  0.861029 +1.554069e-06  0.999999 +0.000000e+00          OK

     120    1.618036018106   -2.029356e-06 +2.020903e-07  0.72205097  0.861025 +1.517046e-06  0.999998 +0.000000e+00          OK

     130    1.618035236999   -1.248249e-06 +6.921757e-09  0.72207844  0.861039 +1.540245e-06  1.000000 +0.000000e+00          OK

     140    1.618035268828   -1.280079e-06 +4.668342e-08  0.72207732  0.861039 +1.508824e-06  1.000000 +0.000000e+00          OK

     150    1.618034781639   -7.928891e-07 -5.374994e-08  0.72209445  0.861047 +1.524991e-06  1.000000 +0.000000e+00          OK

     160    1.618035658190   -1.669440e-06 +2.000255e-07  0.72206363  0.861032 +1.442245e-06  0.999998 +0.000000e+00          OK

     170    1.618034102363   -1.136131e-07 -2.585922e-07  0.72211834  0.861059 +1.477687e-06  0.999998 +0.000000e+00          OK

     180    1.618033460754   +5.279960e-07 -4.348054e-07  0.72214090  0.861070 +1.473769e-06  0.999997 +0.000000e+00          OK

     190    1.618034299664   -3.109143e-07 -1.902921e-07  0.72211140  0.861056 +1.395334e-06  0.999999 +0.000000e+00          OK

     200    1.618034040574   -5.182364e-08 -2.561842e-07  0.72212051  0.861060 +1.368642e-06  0.999998 +0.000000e+00          OK

     210    1.618034276311   -2.875607e-07 -2.166060e-07  0.72211222  0.861056 +1.291997e-06  0.999998 +0.000000e+00          OK

     220    1.618035547083   -1.558333e-06 +2.008410e-07  0.72206754  0.861034 +1.218056e-06  0.999999 +0.000000e+00          OK

     230    1.618034433099   -4.443494e-07 -1.699207e-07  0.72210671  0.861053 +1.196709e-06  0.999999 +0.000000e+00          OK

     240    1.618035121843   -1.133093e-06 +1.118085e-10  0.72208249  0.861041 +1.091444e-06  1.000000 +0.000000e+00          OK

     250    1.618035216557   -1.227807e-06 +1.167652e-07  0.72207916  0.861040 +1.116223e-06  0.999999 +0.000000e+00          OK

     260    1.618036049113   -2.060363e-06 +3.601348e-07  0.72204988  0.861025 +1.045649e-06  0.999998 +0.000000e+00          OK

     270    1.618034942692   -9.539418e-07 +6.673981e-08  0.72208879  0.861044 +1.083012e-06  0.999999 +0.000000e+00          OK

     280    1.618035385160   -1.396410e-06 +1.755651e-07  0.72207323  0.861037 +1.012060e-06  0.999999 +0.000000e+00          OK

     290    1.618034301588   -3.128385e-07 -1.759310e-07  0.72211133  0.861056 +1.013081e-06  0.999999 +0.000000e+00          OK

     300    1.618035381868   -1.393118e-06 +1.315120e-07  0.72207335  0.861037 +9.208978e-07  0.999999 +0.000000e+00          OK

     310    1.618033816121   +1.726286e-07 -2.193067e-07  0.72212840  0.861064 +1.038512e-06  0.999998 +0.000000e+00          OK

     320    1.618034780030   -7.912803e-07 +7.395703e-08  0.72209451  0.861047 +9.700307e-07  0.999999 +0.000000e+00          OK

     330    1.618034588828   -6.000780e-07 -1.205429e-08  0.72210123  0.861051 +9.419302e-07  1.000000 +0.000000e+00          OK

     340    1.618034091148   -1.023986e-07 -1.542731e-07  0.72211873  0.861059 +9.370565e-07  0.999999 +0.000000e+00          OK

     350    1.618034717974   -7.292239e-07 -1.158505e-08  0.72209669  0.861048 +8.379068e-07  1.000000 +0.000000e+00          OK

     360    1.618035493285   -1.504535e-06 +1.812869e-07  0.72206943  0.861035 +7.375655e-07  0.999999 +0.000000e+00          OK

     370    1.618034611903   -6.231530e-07 -3.226268e-08  0.72210042  0.861050 +7.824481e-07  1.000000 +0.000000e+00          OK

     380    1.618035673198   -1.684448e-06 +3.398384e-07  0.72206310  0.861032 +7.646916e-07  0.999997 +0.000000e+00          OK

     390    1.618033796550   +1.922001e-07 -1.701475e-07  0.72212909  0.861065 +8.696278e-07  0.999999 +0.000000e+00          OK

     400    1.618034924989   -9.362389e-07 +7.553275e-08  0.72208941  0.861045 +7.169641e-07  0.999999 +0.000000e+00          OK

     410    1.618034261023   -2.722729e-07 -5.503312e-08  0.72211276  0.861056 +7.731800e-07  0.999999 +0.000000e+00          OK

     420    1.618035288251   -1.299501e-06 +1.500150e-07  0.72207664  0.861038 +6.338718e-07  0.999998 +0.000000e+00          OK

     430    1.618033890787   +9.796323e-08 -1.764282e-07  0.72212578  0.861063 +7.373199e-07  0.999999 +0.000000e+00          OK

     440    1.618033273925   +7.148250e-07 -3.393207e-07  0.72214747  0.861074 +7.605828e-07  0.999997 +0.000000e+00          OK

     450    1.618034448315   -4.595653e-07 -4.442432e-08  0.72210617  0.861053 +6.383789e-07  1.000000 +0.000000e+00          OK

     460    1.618033883935   +1.048150e-07 -1.507130e-07  0.72212602  0.861063 +6.889001e-07  0.999999 +0.000000e+00          OK

     470    1.618035647745   -1.658995e-06 +3.457911e-07  0.72206400  0.861032 +5.752996e-07  0.999997 +0.000000e+00          OK

     480    1.618033716453   +2.722965e-07 -1.711093e-07  0.72213191  0.861066 +6.876486e-07  0.999999 +0.000000e+00          OK

     490    1.618035697911   -1.709161e-06 +3.670308e-07  0.72206223  0.861031 +5.393455e-07  0.999997 +0.000000e+00          OK

     500    1.618033173251   +8.154993e-07 -3.489772e-07  0.72215101  0.861076 +6.659312e-07  0.999998 +0.000000e+00          OK

     510    1.618034573002   -5.842523e-07 +8.331541e-08  0.72210179  0.861051 +5.983886e-07  0.999999 +0.000000e+00          OK

     520    1.618035634325   -1.645575e-06 +3.696898e-07  0.72206447  0.861032 +5.064198e-07  0.999997 +0.000000e+00          OK

     530    1.618035508081   -1.519331e-06 +2.867966e-07  0.72206891  0.861034 +4.586143e-07  0.999998 +0.000000e+00          OK

     540    1.618034736533   -7.477832e-07 +7.714920e-08  0.72209604  0.861048 +4.941314e-07  0.999999 +0.000000e+00          OK

     550    1.618034327371   -3.386213e-07 +3.857820e-08  0.72211043  0.861055 +5.595304e-07  1.000000 +0.000000e+00          OK

     560    1.618033886443   +1.023066e-07 -8.818508e-08  0.72212593  0.861063 +5.673949e-07  0.999999 +0.000000e+00          OK

     570    1.618034310844   -3.220941e-07 -5.203584e-08  0.72211101  0.861056 +4.649361e-07  1.000000 +0.000000e+00          OK

     580    1.618034079135   -9.038499e-08 +1.796466e-08  0.72211915  0.861060 +5.739673e-07  1.000000 +0.000000e+00          OK

     590    1.618033929018   +5.973227e-08 -1.429045e-07  0.72212443  0.861062 +4.739325e-07  0.999999 +0.000000e+00          OK

     600    1.618034298742   -3.099924e-07 -4.665615e-08  0.72211143  0.861056 +4.225965e-07  0.999999 +0.000000e+00          OK

     610    1.618035037060   -1.048310e-06 +1.667423e-07  0.72208547  0.861043 +3.642208e-07  0.999999 +0.000000e+00          OK

     620    1.618034750708   -7.619579e-07 +1.081236e-07  0.72209554  0.861048 +3.790618e-07  0.999999 +0.000000e+00          OK

     630    1.618034772458   -7.837079e-07 +5.294139e-08  0.72209477  0.861047 +3.140603e-07  0.999999 +0.000000e+00          OK

     640    1.618035404861   -1.416112e-06 +3.018858e-07  0.72207254  0.861036 +3.207236e-07  0.999998 +0.000000e+00          OK

     650    1.618034614359   -6.256096e-07 +5.330620e-08  0.72210033  0.861050 +3.200979e-07  0.999999 +0.000000e+00          OK

     660    1.618033899614   +8.913581e-08 -8.581012e-08  0.72212547  0.861063 +3.992822e-07  1.000000 +0.000000e+00          OK

     670    1.618033012186   +9.765638e-07 -3.509831e-07  0.72215667  0.861078 +4.257759e-07  0.999997 +0.000000e+00          OK

     680    1.618033961030   +2.772028e-08 -9.636818e-08  0.72212331  0.861062 +3.518382e-07  0.999999 +0.000000e+00          OK

     690    1.618034321872   -3.331222e-07 +1.016892e-07  0.72211062  0.861055 +4.037608e-07  0.999999 +0.000000e+00          OK

     700    1.618033759267   +2.294828e-07 -7.890045e-08  0.72213040  0.861065 +4.058919e-07  0.999999 +0.000000e+00          OK

     710    1.618034699288   -7.105383e-07 +1.863124e-07  0.72209735  0.861049 +3.503712e-07  0.999998 +0.000000e+00          OK

     720    1.618034785661   -7.969115e-07 +1.417808e-07  0.72209431  0.861047 +2.910946e-07  0.999999 +0.000000e+00          OK

     730    1.618032905378   +1.083372e-06 -3.561440e-07  0.72216043  0.861080 +4.228797e-07  0.999998 +0.000000e+00          OK

     740    1.618033865839   +1.229106e-07 -9.874372e-08  0.72212665  0.861063 +3.469317e-07  0.999999 +0.000000e+00          OK

     750    1.618034807204   -8.184538e-07 +1.609057e-07  0.72209355  0.861047 +2.649488e-07  0.999999 +0.000000e+00          OK

     760    1.618034726541   -7.377906e-07 +1.568518e-07  0.72209639  0.861048 +2.732415e-07  0.999999 +0.000000e+00          OK

     770    1.618034055768   -6.701784e-08 -8.530877e-08  0.72211998  0.861060 +2.698243e-07  0.999999 +0.000000e+00          OK

     780    1.618035143570   -1.154820e-06 +2.345459e-07  0.72208173  0.861041 +2.045242e-07  0.999998 +0.000000e+00          OK

     790    1.618033912948   +7.580223e-08 -5.282160e-08  0.72212500  0.861062 +3.078918e-07  0.999999 +0.000000e+00          OK

     800    1.618033848669   +1.400813e-07 -1.001715e-07  0.72212726  0.861064 +2.783650e-07  0.999999 +0.000000e+00          OK

     810    1.618033511674   +4.770756e-07 -1.513814e-07  0.72213911  0.861070 +3.368584e-07  0.999999 +0.000000e+00          OK

     820    1.618034590240   -6.014898e-07 +7.969479e-08  0.72210118  0.861051 +2.001166e-07  0.999999 +0.000000e+00          OK

     830    1.618034418503   -4.297526e-07 +4.178802e-08  0.72210722  0.861054 +2.125162e-07  1.000000 +0.000000e+00          OK

     840    1.618033108136   +8.806136e-07 -2.974900e-07  0.72215330  0.861077 +3.052529e-07  0.999998 +0.000000e+00          OK

     850    1.618033440207   +5.485433e-07 -1.598396e-07  0.72214162  0.861071 +3.161578e-07  0.999999 +0.000000e+00          OK

     860    1.618034752964   -7.642146e-07 +1.134105e-07  0.72209546  0.861048 +1.476903e-07  0.999999 +0.000000e+00          OK

     870    1.618034731967   -7.432175e-07 +1.109826e-07  0.72209620  0.861048 +1.468426e-07  0.999999 +0.000000e+00          OK

     880    1.618033470461   +5.182886e-07 -1.580710e-07  0.72214056  0.861070 +2.744067e-07  0.999999 +0.000000e+00          OK

     890    1.618033924037   +6.471274e-08 -3.846549e-08  0.72212461  0.861062 +2.375536e-07  1.000000 +0.000000e+00          OK

     900    1.618034357893   -3.691428e-07 +2.211595e-08  0.72210935  0.861055 +1.704644e-07  1.000000 +0.000000e+00          OK

     910    1.618034426841   -4.380915e-07 +5.293526e-08  0.72210693  0.861053 +1.444293e-07  0.999999 +0.000000e+00          OK

     920    1.618034553332   -5.645824e-07 +1.282641e-07  0.72210248  0.861051 +1.753581e-07  0.999999 +0.000000e+00          OK

     930    1.618033310476   +6.782744e-07 -2.067428e-07  0.72214618  0.861073 +2.434885e-07  0.999999 +0.000000e+00          OK

     940    1.618034022435   -3.368467e-08 +3.663825e-08  0.72212115  0.861061 +2.427282e-07  1.000000 +0.000000e+00          OK

     950    1.618034362394   -3.736439e-07 +3.572174e-08  0.72210919  0.861055 +1.403705e-07  0.999999 +0.000000e+00          OK

     960    1.618034473565   -4.848155e-07 -4.887119e-09  0.72210528  0.861053 +5.766932e-08  1.000000 +0.000000e+00          OK

     970    1.618033651783   +3.369670e-07 -1.399700e-07  0.72213418  0.861067 +1.632620e-07  0.999999 +0.000000e+00          OK

     980    1.618034762830   -7.740803e-07 +1.677530e-07  0.72209511  0.861048 +8.982894e-08  0.999999 +0.000000e+00          OK

     990    1.618034011206   -2.245611e-08 -5.400344e-08  0.72212154  0.861061 +1.262681e-07  1.000000 +0.000000e+00          OK

    1000    1.618034185334   -1.965840e-07 +2.253024e-09  0.72211542  0.861058 +1.134160e-07  0.999999 +0.000000e+00          OK

    1010    1.618034141501   -1.527513e-07 +1.019551e-07  0.72211696  0.861058 +2.124959e-07  0.999999 +0.000000e+00          OK

    1020    1.618034419191   -4.304414e-07 +8.548929e-08  0.72210720  0.861054 +1.277617e-07  0.999999 +0.000000e+00          OK

Combined-asm

; =============================================================================
; HDGL — REAL CYBERNETIC CORE
; =============================================================================
;
; CLOSED-LOOP Z[φ] CYBERNETIC SUBSTRATE
;
; BUILD:
;   nasm -f bin hdgl_cybernetic_core.asm -o hdgl_cybernetic_core.img
;
; RUN:
;   qemu-system-x86_64 -drive format=raw,file=hdgl_cybernetic_core.img -m 128M -smp 1 -boot c
;
; -----------------------------------------------------------------------------
; CORE IDEA
; -----------------------------------------------------------------------------
;
; This is no longer a passive observer.
;
; The machine performs:
;
;     OBSERVE
;       ↓
;     SCORE CANDIDATE CORRECTIONS
;       ↓
;     SELECT CONTROL
;       ↓
;     APPLY CONTROL TO INPUT STATE
;       ↓
;     FIRE
;       ↓
;     WATER VERIFY
;       ↓
;     EARTH VERIFY
;       ↓
;     WIND MEASURE
;       ↓
;     COMMIT
;       ↓
;     REPEAT
;
; The controller is a one-step discrete model-predictive controller.
;
; Current state:
;
;     X = (a,b) ∈ Z²
;
; FIRE:
;
;     F(a,b) = (a+b,a)
;
; WIND:
;
;     T(X) = 1 + 1/X
;
;     fixed-point residual in Z[φ]:
;
;       Rφ = a² + 2ab - a
;       Rc = a² + b² - b - 1
;
; EARTH:
;
;     Nφ(a,b) = -a² + ab + b²
;
; WATER:
;
;     W(a,b) = (b,a-b)
;
;     W(F(a,b)) = (a,b)
;
; CONTROLLER:
;
;     Examine a finite neighborhood of the current state:
;
;         ( a-1, b-1 )
;         ( a-1, b   )
;         ( a-1, b+1 )
;         ( a,   b-1 )
;         ( a,   b   )
;         ( a,   b+1 )
;         ( a+1, b-1 )
;         ( a+1, b   )
;         ( a+1, b+1 )
;
;     For each candidate:
;
;         1. Apply FIRE.
;         2. Compute WIND residual of the predicted state.
;         3. Compute EARTH invariant error.
;         4. Reject candidates that fail WATER consistency.
;         5. Select the minimum-cost candidate.
;
;     The selected candidate becomes the actual actuator input.
;
; Thus STRATEGY is no longer merely a label:
;
;     CONTROL_A / CONTROL_B
;
; actually changes the state trajectory.
;
; -----------------------------------------------------------------------------
; NO:
;     libc
;     BIOS after long mode
;     floating point
;     SSE
;     AVX
;     FPU
;     division
;     multiplication by arbitrary constants in the controller
;
; Integer MUL is used only for algebraic observer calculations.
;
; =============================================================================


BITS 16
ORG 0x7C00


; =============================================================================
; BOOT CONSTANTS
; =============================================================================

IMAGE_SECTORS      equ 48
PAYLOAD_SECTORS    equ IMAGE_SECTORS - 1

LOAD_SEG           equ 0x07E0
LOAD_PHYS          equ 0x00007E00

PAGE_PML4          equ 0x00021000
PAGE_PDPT          equ 0x00022000
PAGE_PD0           equ 0x00023000
PAGE_PD1           equ 0x00024000
PAGE_PD2           equ 0x00025000
PAGE_PD3           equ 0x00026000

BSP_STACK          equ 0x00070000

VGA_BASE           equ 0x000B8000
VGA_ROW_BYTES      equ 160

PRINT_EVERY        equ 65536
PRINT_MASK         equ PRINT_EVERY - 1


; =============================================================================
; SHARED CYBERNETIC STATE
; =============================================================================

STATE_A            equ 0x00500000
STATE_B            equ 0x00500008
STATE_K            equ 0x00500010

; Controlled input state selected by the controller.
CONTROL_A          equ 0x00500020
CONTROL_B          equ 0x00500028

; FIRE output.
FIRE_A             equ 0x00500030
FIRE_B             equ 0x00500038

; WATER verification.
WATER_A            equ 0x00500040
WATER_B            equ 0x00500048
WATER_OK           equ 0x00500050

; EARTH observer.
EARTH_N            equ 0x00500060
EARTH_N_FIRE       equ 0x00500068
EARTH_DELTA        equ 0x00500070
EARTH_COST         equ 0x00500078

; WIND observer.
WIND_RES_A         equ 0x00500080
WIND_RES_B         equ 0x00500088
WIND_COST          equ 0x00500090
WIND_FIXED         equ 0x00500098

; Oracle / controller.
ORACLE             equ 0x005000A0
STRATEGY           equ 0x005000A8
CONTROL_DA         equ 0x005000B0
CONTROL_DB         equ 0x005000B8
CONTROL_COST       equ 0x005000C0

; System state.
TRINARY            equ 0x005000C8
YIN                equ 0x005000D0
PHASE              equ 0x005000D8
DEPTH              equ 0x005000E0

; Diagnostics.
FAULT              equ 0x005000E8
CONTROL_ACCEPTED   equ 0x005000F0
CONTROL_REJECTED   equ 0x005000F8


; =============================================================================
; ORACLE BITS
; =============================================================================

ORACLE_WATER_BROKEN    equ 0x01
ORACLE_EARTH_PATTERN   equ 0x02
ORACLE_EARTH_MAGNITUDE equ 0x04
ORACLE_WIND_FIXED      equ 0x08
ORACLE_WIND_DIVERGE    equ 0x10
ORACLE_CONTROLLER_MOVE equ 0x20
ORACLE_CRITICAL        equ 0x80


; =============================================================================
; STRATEGIES
; =============================================================================

STRATEGY_FLOWING       equ 0
STRATEGY_DAMPING       equ 1
STRATEGY_NONACTION     equ 2
STRATEGY_CONVERGE      equ 3
STRATEGY_CRITICAL      equ 7


; =============================================================================
; BOOT
; =============================================================================

boot_start:

    cli

    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00

    mov [boot_drive], dl


    ; -------------------------------------------------------------------------
    ; Text mode.
    ; -------------------------------------------------------------------------

    mov ax, 0x0003
    int 0x10


    ; -------------------------------------------------------------------------
    ; Load payload with INT13 extensions.
    ; -------------------------------------------------------------------------

    mov ah, 0x41
    mov bx, 0x55AA
    mov dl, [boot_drive]
    int 0x13

    jc boot_disk_error

    cmp bx, 0xAA55
    jne boot_disk_error

    test cl, 1
    jz boot_disk_error


    mov si, disk_address_packet
    mov dl, [boot_drive]
    mov ah, 0x42
    int 0x13

    jc boot_disk_error


    ; -------------------------------------------------------------------------
    ; A20.
    ; -------------------------------------------------------------------------

    mov ax, 0x2401
    int 0x15

    in al, 0x92
    or al, 2
    and al, 0xFE
    out 0x92, al


    ; -------------------------------------------------------------------------
    ; GDT.
    ; -------------------------------------------------------------------------

    lgdt [gdt_ptr]


    ; -------------------------------------------------------------------------
    ; Protected mode.
    ; -------------------------------------------------------------------------

    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp dword 0x08:protected_entry


; =============================================================================
; BOOT ERROR
; =============================================================================

boot_disk_error:

    mov si, boot_error_msg

.boot_print:

    lodsb

    test al, al
    jz .halt

    mov ah, 0x0E
    xor bh, bh
    int 0x10

    jmp .boot_print


.halt:

    cli
    hlt
    jmp .halt


boot_drive:
    db 0

boot_error_msg:
    db "HDGL DISK ERROR",0


; =============================================================================
; DISK ADDRESS PACKET
; =============================================================================

disk_address_packet:

    db 0x10
    db 0

    dw PAYLOAD_SECTORS

    dw 0

    dw LOAD_SEG

    dq 1


; =============================================================================
; GDT
; =============================================================================

align 8

gdt_base:

    dq 0x0000000000000000
    dq 0x00CF9A000000FFFF
    dq 0x00CF92000000FFFF
    dq 0x00AF9A000000FFFF

gdt_end:


gdt_ptr:

    dw gdt_end - gdt_base - 1
    dd gdt_base


; =============================================================================
; BOOT SECTOR
; =============================================================================

times 510 - ($ - $$) db 0
dw 0xAA55


; =============================================================================
; 32-BIT PROTECTED MODE
; =============================================================================

BITS 32

protected_entry:

    cli

    mov ax, 0x10

    mov ds, ax
    mov es, ax
    mov ss, ax

    mov esp, BSP_STACK


    call build_page_tables


    ; Enable PAE.
    mov eax, cr4
    or eax, (1 << 5)
    mov cr4, eax


    ; Enable long mode.
    mov ecx, 0xC0000080
    rdmsr

    or eax, (1 << 8)

    wrmsr


    ; CR3.
    mov eax, PAGE_PML4
    mov cr3, eax


    ; Enable paging.
    mov eax, cr0
    or eax, (1 << 31)
    mov cr0, eax


    jmp dword 0x18:long_mode_entry


; =============================================================================
; PAGE TABLES
; =============================================================================

build_page_tables:

    pushad

    mov edi, PAGE_PML4

    xor eax, eax

    mov ecx, 0x6000 / 4

    rep stosd


    ; PML4[0] -> PDPT.
    mov dword [PAGE_PML4 + 0], PAGE_PDPT | 0x003
    mov dword [PAGE_PML4 + 4], 0


    ; PDPT.
    mov dword [PAGE_PDPT + 0], PAGE_PD0 | 0x003
    mov dword [PAGE_PDPT + 8], PAGE_PD1 | 0x003
    mov dword [PAGE_PDPT + 16], PAGE_PD2 | 0x003
    mov dword [PAGE_PDPT + 24], PAGE_PD3 | 0x003


    ; PD0 = 0 .. 1 GiB.
    mov edi, PAGE_PD0
    xor eax, eax

    mov ecx, 512

.pd0:

    mov edx, eax
    or edx, 0x83

    mov [edi], edx
    mov dword [edi + 4], 0

    add eax, 0x200000
    add edi, 8

    loop .pd0


    ; PD1 = 1 .. 2 GiB.
    mov edi, PAGE_PD1
    mov eax, 0x40000000

    mov ecx, 512

.pd1:

    mov edx, eax
    or edx, 0x83

    mov [edi], edx
    mov dword [edi + 4], 0

    add eax, 0x200000
    add edi, 8

    loop .pd1


    ; PD2 = 2 .. 3 GiB.
    mov edi, PAGE_PD2
    mov eax, 0x80000000

    mov ecx, 512

.pd2:

    mov edx, eax
    or edx, 0x83

    mov [edi], edx
    mov dword [edi + 4], 0

    add eax, 0x200000
    add edi, 8

    loop .pd2


    ; PD3 = 3 .. 4 GiB.
    mov edi, PAGE_PD3
    mov eax, 0xC0000000

    mov ecx, 512

.pd3:

    mov edx, eax
    or edx, 0x83

    mov [edi], edx
    mov dword [edi + 4], 0

    add eax, 0x200000
    add edi, 8

    loop .pd3


    popad
    ret


; =============================================================================
; 64-BIT MODE
; =============================================================================

BITS 64

long_mode_entry:

    cli

    mov ax, 0x10

    mov ds, ax
    mov es, ax
    mov ss, ax

    mov rsp, BSP_STACK


    ; -------------------------------------------------------------------------
    ; Initialize canonical state.
    ;
    ; X0 = 0φ + 1.
    ; -------------------------------------------------------------------------

    mov qword [STATE_A], 0
    mov qword [STATE_B], 1
    mov qword [STATE_K], 0


    ; -------------------------------------------------------------------------
    ; Clear controller state.
    ; -------------------------------------------------------------------------

    mov qword [CONTROL_A], 0
    mov qword [CONTROL_B], 1

    mov qword [FIRE_A], 0
    mov qword [FIRE_B], 1

    mov qword [WATER_A], 0
    mov qword [WATER_B], 1
    mov qword [WATER_OK], 0


    mov qword [EARTH_N], 1
    mov qword [EARTH_N_FIRE], 1
    mov qword [EARTH_DELTA], 0
    mov qword [EARTH_COST], 0


    mov qword [WIND_RES_A], 0
    mov qword [WIND_RES_B], -1
    mov qword [WIND_COST], 1
    mov qword [WIND_FIXED], 0


    mov qword [ORACLE], 0
    mov qword [STRATEGY], STRATEGY_FLOWING

    mov qword [CONTROL_DA], 0
    mov qword [CONTROL_DB], 0
    mov qword [CONTROL_COST], 0

    mov qword [TRINARY], 1

    mov qword [YIN], 2

    mov qword [PHASE], 0
    mov qword [DEPTH], 0

    mov qword [FAULT], 0
    mov qword [CONTROL_ACCEPTED], 0
    mov qword [CONTROL_REJECTED], 0


    ; -------------------------------------------------------------------------
    ; VGA.
    ; -------------------------------------------------------------------------

    call vga_init


    ; -------------------------------------------------------------------------
    ; Main cybernetic loop.
    ; -------------------------------------------------------------------------

cybernetic_cycle:


    ; -------------------------------------------------------------------------
    ; Clear current-cycle oracle.
    ; -------------------------------------------------------------------------

    mov qword [ORACLE], 0


    ; -------------------------------------------------------------------------
    ; OBSERVE CURRENT STATE.
    ; -------------------------------------------------------------------------

    call earth_current
    call wind_current


    ; -------------------------------------------------------------------------
    ; CONTROLLER.
    ;
    ; Chooses among the local 3x3 neighborhood.
    ; -------------------------------------------------------------------------

    call controller_select


    ; -------------------------------------------------------------------------
    ; ACTUATE.
    ;
    ; FIRE(controlled input) -> predicted next state.
    ; -------------------------------------------------------------------------

    call fire_controlled


    ; -------------------------------------------------------------------------
    ; WATER verifies the forward operator exactly.
    ; -------------------------------------------------------------------------

    call water_verify


    test rax, rax
    jnz .water_ok


    ; Controller produced an invalid transition.
    ; Reject it and retain the prior state.

    inc qword [CONTROL_REJECTED]

    or qword [ORACLE], ORACLE_WATER_BROKEN

    mov qword [STRATEGY], STRATEGY_NONACTION

    mov qword [WATER_OK], 0

    jmp .post_control


.water_ok:

    mov qword [WATER_OK], 1


    ; -------------------------------------------------------------------------
    ; EARTH verifies predicted-state invariant.
    ; -------------------------------------------------------------------------

    call earth_predicted


    ; -------------------------------------------------------------------------
    ; WIND verifies predicted-state fixed-point residual.
    ; -------------------------------------------------------------------------

    call wind_predicted


    ; -------------------------------------------------------------------------
    ; Strategy.
    ; -------------------------------------------------------------------------

    call strategy_select


    ; -------------------------------------------------------------------------
    ; COMMIT:
    ;
    ; The controller has already modified the state trajectory.
    ; The FIRE result is now the actual new state.
    ; -------------------------------------------------------------------------

    mov rax, [FIRE_A]
    mov rbx, [FIRE_B]

    mov [STATE_A], rax
    mov [STATE_B], rbx


    inc qword [CONTROL_ACCEPTED]


    ; -------------------------------------------------------------------------
    ; Cycle bookkeeping.
    ; -------------------------------------------------------------------------

    inc qword [STATE_K]
    inc qword [DEPTH]

    inc qword [PHASE]

    and qword [PHASE], 3


    ; YIN:
    ;
    ;     s -> s² - 2
    ;
    mov rax, [YIN]
    imul rax, rax
    sub rax, 2
    mov [YIN], rax


.post_control:


    ; -------------------------------------------------------------------------
    ; Display periodically.
    ; -------------------------------------------------------------------------

    mov rax, [STATE_K]
    test rax, PRINT_MASK
    jnz cybernetic_cycle

    call vga_update

    jmp cybernetic_cycle


; =============================================================================
; CONTROLLER
; =============================================================================
;
; One-step model predictive controller.
;
; The controller evaluates the 3x3 neighborhood:
;
;     da ∈ {-1,0,+1}
;     db ∈ {-1,0,+1}
;
; Candidate:
;
;     C = (a+da,b+db)
;
; FIRE:
;
;     F(C) = (C_a+C_b,C_a)
;
; Cost:
;
;     WIND_COST(F(C))
;       +
;     EARTH_COST(F(C))
;
; The candidate with the minimum cost becomes the actual actuator input.
;
; This is the point where the architecture stops being a passive oracle.
;
; =============================================================================

controller_select:

    ; -------------------------------------------------------------------------
    ; Current state.
    ; -------------------------------------------------------------------------

    mov r8, [STATE_A]
    mov r9, [STATE_B]


    ; -------------------------------------------------------------------------
    ; Current best cost = max.
    ; -------------------------------------------------------------------------

    mov rax, 0xFFFFFFFFFFFFFFFF

    mov [CONTROL_COST], rax


    ; Best correction initially zero.
    mov qword [CONTROL_DA], 0
    mov qword [CONTROL_DB], 0


    ; -------------------------------------------------------------------------
    ; da=-1 db=-1
    ; -------------------------------------------------------------------------

    mov r10, -1
    mov r11, -1

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=-1 db=0
    ; -------------------------------------------------------------------------

    mov r10, -1
    xor r11d, r11d

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=-1 db=+1
    ; -------------------------------------------------------------------------

    mov r10, -1
    mov r11, 1

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=0 db=-1
    ; -------------------------------------------------------------------------

    xor r10d, r10d
    mov r11, -1

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=0 db=0
    ; -------------------------------------------------------------------------

    xor r10d, r10d
    xor r11d, r11d

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=0 db=+1
    ; -------------------------------------------------------------------------

    xor r10d, r10d
    mov r11, 1

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=+1 db=-1
    ; -------------------------------------------------------------------------

    mov r10, 1
    mov r11, -1

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=+1 db=0
    ; -------------------------------------------------------------------------

    mov r10, 1
    xor r11d, r11d

    call controller_candidate

    ; -------------------------------------------------------------------------
    ; da=+1 db=+1
    ; -------------------------------------------------------------------------

    mov r10, 1
    mov r11, 1

    call controller_candidate


    ; -------------------------------------------------------------------------
    ; If any non-zero correction was selected, expose oracle bit.
    ; -------------------------------------------------------------------------

    mov rax, [CONTROL_DA]
    or rax, [CONTROL_DB]

    jz .no_move

    or qword [ORACLE], ORACLE_CONTROLLER_MOVE

.no_move:

    ret


; =============================================================================
; CONTROLLER CANDIDATE
; =============================================================================
;
; INPUT:
;
;     r8  = current a
;     r9  = current b
;     r10 = da
;     r11 = db
;
; Destroys rax-r15.
;
; =============================================================================

controller_candidate:

    push r8
    push r9
    push r10
    push r11


    ; Candidate input.
    lea r12, [r8 + r10]
    lea r13, [r9 + r11]


    ; Candidate FIRE:
    ;
    ;     next_a = ca + cb
    ;     next_b = ca
    ;

    mov r14, r12
    add r14, r13

    mov r15, r12


    ; -------------------------------------------------------------------------
    ; Candidate WATER consistency is structurally guaranteed because FIRE was
    ; applied to the candidate itself.
    ;
    ; Still perform the inverse explicitly.
    ; -------------------------------------------------------------------------

    mov rax, r14
    mov rbx, r15

    mov rcx, r15
    mov rdx, r14
    sub rdx, r15

    cmp rcx, r12
    jne .candidate_done

    cmp rdx, r13
    jne .candidate_done


    ; -------------------------------------------------------------------------
    ; WIND predicted residual.
    ; -------------------------------------------------------------------------

    mov rdi, r14
    mov rsi, r15

    call wind_cost_pair

    mov rbx, rax


    ; -------------------------------------------------------------------------
    ; EARTH predicted error.
    ;
    ; Desired Fibonacci invariant:
    ;
    ;     |N| = 1
    ;
    ; Cost = abs(|N|-1).
    ; -------------------------------------------------------------------------

    mov rdi, r14
    mov rsi, r15

    call earth_cost_pair

    add rbx, rax


    ; -------------------------------------------------------------------------
    ; Controller inertia:
    ;
    ; Prefer zero correction when costs tie.
    ; -------------------------------------------------------------------------

    mov rax, [CONTROL_COST]

    cmp rbx, rax
    jae .candidate_done


    mov [CONTROL_COST], rbx

    pop r11
    pop r10
    pop r9
    pop r8

    ; Original candidate correction values survive the pop.
    mov [CONTROL_DA], r10
    mov [CONTROL_DB], r11

    ret


.candidate_done:

    pop r11
    pop r10
    pop r9
    pop r8

    ret


; =============================================================================
; FIRE — ACTUATOR
; =============================================================================
;
; FIRE receives CONTROL_A/B by applying the selected correction to STATE_A/B.
;
; Then:
;
;     F(a,b) = (a+b,a)
;
; This is the actual plant transition.
;
; =============================================================================

fire_controlled:

    mov r8, [STATE_A]
    mov r9, [STATE_B]

    mov r10, [CONTROL_DA]
    mov r11, [CONTROL_DB]

    add r8, r10
    add r9, r11

    mov [CONTROL_A], r8
    mov [CONTROL_B], r9


    mov rax, r8
    add rax, r9

    mov [FIRE_A], rax
    mov [FIRE_B], r8

    ret


; =============================================================================
; WATER — INVERSE VERIFIER
; =============================================================================

water_verify:

    mov rax, [FIRE_A]
    mov rbx, [FIRE_B]

    ; W(FIRE):
    ;
    ;     water_a = FIRE_B
    ;     water_b = FIRE_A - FIRE_B
    ;

    mov rcx, rbx

    mov rdx, rax
    sub rdx, rbx

    mov [WATER_A], rcx
    mov [WATER_B], rdx


    cmp rcx, [CONTROL_A]
    jne .broken

    cmp rdx, [CONTROL_B]
    jne .broken


    and qword [ORACLE], ~ORACLE_WATER_BROKEN

    mov rax, 1

    ret


.broken:

    or qword [ORACLE], ORACLE_WATER_BROKEN
    or qword [ORACLE], ORACLE_CRITICAL

    mov rax, 0

    ret


; =============================================================================
; EARTH CURRENT
; =============================================================================
;
; Nφ(a,b) = -a² + ab + b²
;
; =============================================================================

earth_current:

    mov r8, [STATE_A]
    mov r9, [STATE_B]

    call earth_pair

    mov [EARTH_N], rax

    ; Trinary projection.
    test rax, rax

    jz .zero

    js .negative

    mov qword [TRINARY], 1

    ret


.negative:

    mov qword [TRINARY], -1

    ret


.zero:

    mov qword [TRINARY], 0

    ret


; =============================================================================
; EARTH PREDICTED
; =============================================================================

earth_predicted:

    mov rdi, [FIRE_A]
    mov rsi, [FIRE_B]

    call earth_pair

    mov [EARTH_N_FIRE], rax


    mov rcx, [EARTH_N]
    mov rdx, rax

    sub rdx, rcx

    mov [EARTH_DELTA], rdx


    ; Cost = abs(|N|-1)
    mov rdi, rax

    call abs_qword

    cmp rax, 1

    ja .too_large

    jb .too_small

    ; exactly ±1.
    and qword [ORACLE], ~(
        ORACLE_EARTH_PATTERN |
        ORACLE_EARTH_MAGNITUDE
    )

    mov qword [EARTH_COST], 0

    ret


.too_large:

    sub rax, 1

    mov [EARTH_COST], rax

    or qword [ORACLE], ORACLE_EARTH_MAGNITUDE

    ret


.too_small:

    mov rbx, 1
    sub rbx, rax

    mov [EARTH_COST], rbx

    or qword [ORACLE], ORACLE_EARTH_MAGNITUDE

    ret


; =============================================================================
; EARTH PAIR
;
; INPUT:
;     rdi = a
;     rsi = b
;
; OUTPUT:
;     rax = -a² + ab + b²
; =============================================================================

earth_pair:

    mov rax, rdi
    imul rax, rdi
    neg rax

    mov rbx, rdi
    imul rbx, rsi

    add rax, rbx

    mov rbx, rsi
    imul rbx, rsi

    add rax, rbx

    ret


; =============================================================================
; WIND CURRENT
; =============================================================================
;
; Fixed-point residual:
;
;     Rφ = a² + 2ab - a
;     Rc = a² + b² - b - 1
;
; =============================================================================

wind_current:

    mov rdi, [STATE_A]
    mov rsi, [STATE_B]

    call wind_pair

    mov [WIND_RES_A], rax
    mov [WIND_RES_B], rbx


    ; Cost = |Rφ| + |Rc|.
    mov rdi, rax
    call abs_qword
    mov rcx, rax

    mov rdi, rbx
    call abs_qword
    add rax, rcx

    mov [WIND_COST], rax


    test rax, rax
    jnz .not_fixed

    mov qword [WIND_FIXED], 1
    or qword [ORACLE], ORACLE_WIND_FIXED

    ret


.not_fixed:

    mov qword [WIND_FIXED], 0

    and qword [ORACLE], ~ORACLE_WIND_FIXED

    ret


; =============================================================================
; WIND PREDICTED
; =============================================================================

wind_predicted:

    mov rdi, [FIRE_A]
    mov rsi, [FIRE_B]

    call wind_pair

    mov [WIND_RES_A], rax
    mov [WIND_RES_B], rbx


    mov rdi, rax
    call abs_qword
    mov rcx, rax

    mov rdi, rbx
    call abs_qword

    add rax, rcx

    mov [WIND_COST], rax


    test rax, rax
    jnz .not_fixed

    mov qword [WIND_FIXED], 1

    or qword [ORACLE], ORACLE_WIND_FIXED

    ret


.not_fixed:

    mov qword [WIND_FIXED], 0

    and qword [ORACLE], ~ORACLE_WIND_FIXED

    ret


; =============================================================================
; WIND PAIR
;
; INPUT:
;     rdi = a
;     rsi = b
;
; OUTPUT:
;     rax = phi coefficient residual
;     rbx = constant residual
; =============================================================================

wind_pair:

    ; -------------------------------------------------------------------------
    ; Rφ = a² + 2ab - a
    ; -------------------------------------------------------------------------

    mov rax, rdi
    imul rax, rdi

    mov rcx, rdi
    imul rcx, rsi

    add rcx, rcx

    add rax, rcx

    sub rax, rdi


    ; -------------------------------------------------------------------------
    ; Rc = a² + b² - b - 1
    ; -------------------------------------------------------------------------

    mov rbx, rdi
    imul rbx, rdi

    mov rcx, rsi
    imul rcx, rsi

    add rbx, rcx

    sub rbx, rsi

    dec rbx

    ret


; =============================================================================
; WIND COST
;
; INPUT:
;     rdi = a
;     rsi = b
;
; OUTPUT:
;     rax = |Rφ| + |Rc|
; =============================================================================

wind_cost_pair:

    call wind_pair

    mov r8, rax
    mov r9, rbx

    mov rdi, r8
    call abs_qword

    mov r8, rax

    mov rdi, r9
    call abs_qword

    add rax, r8

    ret


; =============================================================================
; EARTH COST
;
; INPUT:
;     rdi = a
;     rsi = b
;
; OUTPUT:
;     rax = abs(abs(N)-1)
; =============================================================================

earth_cost_pair:

    call earth_pair

    mov r8, rax

    mov rdi, r8
    call abs_qword

    cmp rax, 1

    ja .large

    cmp rax, 1

    je .zero

    ; |N| = 0
    mov rax, 1
    ret


.large:

    dec rax
    ret


.zero:

    xor eax, eax
    ret


; =============================================================================
; STRATEGY SELECTOR
; =============================================================================

strategy_select:

    mov rax, [ORACLE]


    ; Critical.
    test al, ORACLE_CRITICAL
    jnz .critical


    ; Water failure.
    test al, ORACLE_WATER_BROKEN
    jnz .nonaction


    ; Wind fixed point.
    test al, ORACLE_WIND_FIXED
    jnz .converge


    ; Controller moved state.
    test al, ORACLE_CONTROLLER_MOVE
    jnz .damping


    ; Normal.
    mov qword [STRATEGY], STRATEGY_FLOWING

    ret


.damping:

    mov qword [STRATEGY], STRATEGY_DAMPING

    ret


.nonaction:

    mov qword [STRATEGY], STRATEGY_NONACTION

    ; If verification fails, reject the transition.
    mov qword [FAULT], 1

    ret


.converge:

    mov qword [STRATEGY], STRATEGY_CONVERGE

    ret


.critical:

    mov qword [STRATEGY], STRATEGY_CRITICAL
    mov qword [FAULT], 1

    ret


; =============================================================================
; ABS
; =============================================================================

abs_qword:

    test rdi, rdi

    jns .positive

    neg rdi

.positive:

    mov rax, rdi

    ret


; =============================================================================
; VGA INITIALIZATION
; =============================================================================

vga_init:

    mov rdi, VGA_BASE

    xor eax, eax

    mov rcx, (80 * 25 * 2) / 8

    rep stosq


    ; Header.
    mov rdi, VGA_BASE

    mov rsi, str_title

    mov bl, 0x0F

    call vga_string

    ret


; =============================================================================
; VGA UPDATE
; =============================================================================

vga_update:

    ; -------------------------------------------------------------------------
    ; Clear rows 1..24.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + 160

    xor eax, eax

    mov rcx, (160 * 24) / 8

    rep stosq


    ; -------------------------------------------------------------------------
    ; Row 0 title.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE

    mov rsi, str_title

    mov bl, 0x0F

    call vga_string


    ; -------------------------------------------------------------------------
    ; Row 2 state.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (2 * 160)

    mov rsi, str_state

    call vga_string

    mov rax, [STATE_K]

    mov rdi, VGA_BASE + (2 * 160) + 10*2

    call vga_hex_qword


    mov rax, [STATE_A]

    mov rdi, VGA_BASE + (2 * 160) + 30*2

    call vga_hex_qword


    mov rax, [STATE_B]

    mov rdi, VGA_BASE + (2 * 160) + 50*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 4 controller.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (4 * 160)

    mov rsi, str_control

    call vga_string


    mov rax, [CONTROL_DA]

    mov rdi, VGA_BASE + (4 * 160) + 12*2

    call vga_hex_qword


    mov rax, [CONTROL_DB]

    mov rdi, VGA_BASE + (4 * 160) + 32*2

    call vga_hex_qword


    mov rax, [CONTROL_COST]

    mov rdi, VGA_BASE + (4 * 160) + 52*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 6 FIRE.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (6 * 160)

    mov rsi, str_fire

    call vga_string


    mov rax, [FIRE_A]

    mov rdi, VGA_BASE + (6 * 160) + 12*2

    call vga_hex_qword


    mov rax, [FIRE_B]

    mov rdi, VGA_BASE + (6 * 160) + 32*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 8 WATER.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (8 * 160)

    mov rsi, str_water

    call vga_string


    mov rax, [WATER_A]

    mov rdi, VGA_BASE + (8 * 160) + 12*2

    call vga_hex_qword


    mov rax, [WATER_B]

    mov rdi, VGA_BASE + (8 * 160) + 32*2

    call vga_hex_qword


    mov rax, [WATER_OK]

    mov rdi, VGA_BASE + (8 * 160) + 52*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 10 EARTH.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (10 * 160)

    mov rsi, str_earth

    call vga_string


    mov rax, [EARTH_N]

    mov rdi, VGA_BASE + (10 * 160) + 12*2

    call vga_hex_qword


    mov rax, [EARTH_N_FIRE]

    mov rdi, VGA_BASE + (10 * 160) + 32*2

    call vga_hex_qword


    mov rax, [EARTH_DELTA]

    mov rdi, VGA_BASE + (10 * 160) + 52*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 12 WIND.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (12 * 160)

    mov rsi, str_wind

    call vga_string


    mov rax, [WIND_RES_A]

    mov rdi, VGA_BASE + (12 * 160) + 12*2

    call vga_hex_qword


    mov rax, [WIND_RES_B]

    mov rdi, VGA_BASE + (12 * 160) + 32*2

    call vga_hex_qword


    mov rax, [WIND_COST]

    mov rdi, VGA_BASE + (12 * 160) + 52*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 14 Oracle / strategy.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (14 * 160)

    mov rsi, str_oracle

    call vga_string


    mov rax, [ORACLE]

    mov rdi, VGA_BASE + (14 * 160) + 8*2

    call vga_hex_qword


    mov rax, [STRATEGY]

    mov rdi, VGA_BASE + (14 * 160) + 32*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 16 YIN / phase / depth.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (16 * 160)

    mov rsi, str_yin

    call vga_string


    mov rax, [YIN]

    mov rdi, VGA_BASE + (16 * 160) + 8*2

    call vga_hex_qword


    mov rax, [PHASE]

    mov rdi, VGA_BASE + (16 * 160) + 28*2

    call vga_hex_qword


    mov rax, [DEPTH]

    mov rdi, VGA_BASE + (16 * 160) + 48*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 18 controller acceptance/rejection.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (18 * 160)

    mov rsi, str_stats

    call vga_string


    mov rax, [CONTROL_ACCEPTED]

    mov rdi, VGA_BASE + (18 * 160) + 10*2

    call vga_hex_qword


    mov rax, [CONTROL_REJECTED]

    mov rdi, VGA_BASE + (18 * 160) + 32*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 20 trinary.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (20 * 160)

    mov rsi, str_trinary

    call vga_string


    mov rax, [TRINARY]

    mov rdi, VGA_BASE + (20 * 160) + 10*2

    call vga_hex_qword


    ; -------------------------------------------------------------------------
    ; Row 22 fault.
    ; -------------------------------------------------------------------------

    mov rdi, VGA_BASE + (22 * 160)

    mov rsi, str_fault

    call vga_string


    mov rax, [FAULT]

    mov rdi, VGA_BASE + (22 * 160) + 10*2

    call vga_hex_qword


    ret


; =============================================================================
; VGA STRING
; =============================================================================
;
; INPUT:
;     rdi = destination
;     rsi = zero-terminated string
;     bl  = attribute
;
; =============================================================================

vga_string:

.next:

    lodsb

    test al, al

    jz .done

    mov [rdi], al
    mov [rdi + 1], bl

    add rdi, 2

    jmp .next


.done:

    ret


; =============================================================================
; VGA HEX QWORD
; =============================================================================

vga_hex_qword:

    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi

    mov rbx, rdi

    mov rcx, 16


.loop:

    mov rdx, rax

    shr rdx, 60

    and edx, 0x0F

    mov dl, [hex_digits + rdx]

    mov [rbx], dl

    mov byte [rbx + 1], 0x07

    add rbx, 2

    shl rax, 4

    loop .loop


    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax

    ret


; =============================================================================
; STRINGS
; =============================================================================

str_title:
    db "HDGL REAL CYBERNETIC CORE  OBSERVE-CONTROL-ACT-VERIFY",0


str_state:
    db "STATE K=          A=                  B=",0


str_control:
    db "CONTROL DA=                  DB=                  COST=",0


str_fire:
    db "FIRE   A=                  B=",0


str_water:
    db "WATER  A=                  B=                  OK=",0


str_earth:
    db "EARTH  N=                  NF=                 DELTA=",0


str_wind:
    db "WIND   RA=                 RB=                 COST=",0


str_oracle:
    db "ORACLE=                  STRATEGY=",0


str_yin:
    db "YIN    S=                  PHASE=              DEPTH=",0


str_stats:
    db "CONTROL ACCEPTED=        REJECTED=",0


str_trinary:
    db "TRINARY=",0


str_fault:
    db "FAULT=",0


hex_digits:
    db "0123456789ABCDEF"


; =============================================================================
; END
; =============================================================================

analog-cybernetic0.zip (12.5 KB)

II_analog_boot.zip (376.3 KB)

gpu_bringup_stage0.zip (8.6 KB)
hdgl_accel+iris5+.zip (156.6 KB)

Analog-Prime-main (4).zip (7.3 MB)

From:

hdgl_wuwei_closed_loop.asm

; =============================================================================
; HDGL — FOUR-ELEMENT BARE-METAL Z[φ] SUBSTRATE WITH CLOSED-LOOP WU-WEI ORACLE
; =============================================================================
;
; TARGET:     x86-64 / BIOS / QEMU
; BUILD:      nasm -f bin hdgl_wuwei.asm -o hdgl_wuwei.img
; RUN:        qemu-system-x86_64 -drive format=raw,file=hdgl_wuwei.img -smp 4 -m 128M -boot c
;
; ARCHITECTURE:
;   CPU 0 = FIRE    (operator / strategy selector)
;   CPU 1 = WATER   (inverse verification)
;   CPU 2 = EARTH   (N_phi pattern oracle)
;   CPU 3 = WIND    (T(X) fixed-point residual)
;
; WU-WEI ORACLE:
;   Each element reports resistance as a SIGNAL, not a failure.
;   ORACLE is a bitfield:
;     bit 0: WATER invariant broken
;     bit 1: EARTH pattern broken  (delta not alternating sign)
;     bit 2: EARTH magnitude wrong (|delta| != 2)
;     bit 3: WIND fixed point detected
;     bit 4: WIND diverging
;     bit 7: CRITICAL
;   FIRE reads ORACLE and selects strategy:
;     0x00 -> FLOWING RIVER  (advance normally)
;     0x02 -> NON-ACTION     (hold, log)
;     0x04 -> REDIRECT       (rebase)
;     0x08 -> CONVERGENCE    (log phi approach)
;     0x80+ -> CRITICAL      (halt + display)
;
; PHI INVARIANT:
;   N_phi(a,b) = -a² + ab + b²
;   For Fibonacci pairs: N oscillates ±2 every step.
;   This oscillation IS the healthy signal, not a failure.
;   EARTH verifies the pattern (alternating ±2), not invariance.
;
; =============================================================================

BITS 16
ORG 0x7C00

; =============================================================================
; CONSTANTS
; =============================================================================

PAYLOAD_PHYS       equ 0x00010000   ; unused, kept for reference only

; Payload (sectors 2..IMAGE_SECTORS) now loads at physical 0x7E00, directly
; after the boot sector, spanning up to roughly 0x7E00 + IMAGE_SECTORS*512.
; The AP trampoline and page tables MUST live outside that span or the
; code overwrites itself the moment build_page_tables or the AP-trampoline
; copy runs. 0x20000+ is comfortably clear.
AP_TRAMP_PHYS      equ 0x00020000

PML4_PHYS          equ 0x00021000
PDPT_PHYS          equ 0x00022000
PD0_PHYS           equ 0x00023000
PD1_PHYS           equ 0x00024000
PD2_PHYS           equ 0x00025000
PD3_PHYS           equ 0x00026000

BSP_STACK          equ 0x00070000
AP_STACK_BASE      equ 0x00090000
AP_STACK_STRIDE    equ 0x00010000

LAPIC_BASE         equ 0xFEE00000
LAPIC_ICR_LOW      equ 0x300
LAPIC_ICR_HIGH     equ 0x310

VGA_BASE           equ 0x000B8000
VGA_COLS           equ 80          ; characters per row
VGA_ROW            equ 160         ; bytes per row

PRINT_EVERY        equ 1048576
PRINT_MASK         equ PRINT_EVERY - 1

IMAGE_SECTORS      equ 80
PAYLOAD_SECTORS    equ IMAGE_SECTORS - 1

; =============================================================================
; SHARED STATE LAYOUT (at 0x500000)
; =============================================================================

STATE_A            equ 0x00500000  ; Current Omega: a
STATE_B            equ 0x00500008  ; Current Omega: b
STATE_K            equ 0x00500010  ; Iteration counter

FIRE_A             equ 0x00500020  ; FIRE result: a+b
FIRE_B             equ 0x00500028  ; FIRE result: a
FIRE_K             equ 0x00500030  ; FIRE step counter

WATER_A            equ 0x00500040  ; WATER result: b
WATER_B            equ 0x00500048  ; WATER result: a-b

EARTH_N            equ 0x00500060  ; N_phi(current)
EARTH_N_FIRE       equ 0x00500068  ; N_phi(FIRE(current))
EARTH_DELTA        equ 0x00500070  ; N_phi(FIRE) - N_phi(current)
EARTH_PREV_DELTA   equ 0x00500078  ; Previous delta (for pattern check)

WIND_RES_A         equ 0x00500080  ; T(X) phi-coefficient residual
WIND_RES_B         equ 0x00500088  ; T(X) constant residual
WIND_FIX           equ 0x00500090  ; 1 if at fixed point

REQUEST_K          equ 0x005000A0  ; Published step for APs
DONE_WATER         equ 0x005000A8
DONE_EARTH         equ 0x005000B0
DONE_WIND          equ 0x005000B8

READY_MASK         equ 0x005000C0

ORACLE             equ 0x005000C8  ; Wu-Wei oracle bitfield
TRINARY            equ 0x005000D0  ; Trinary projection of N
STRATEGY           equ 0x005000D8  ; Current strategy index
YIN                equ 0x005000E0  ; Yin: s -> s^2 - 2
PHASE              equ 0x005000E8  ; Completion phase 0->3->0
DEPTH              equ 0x005000F0  ; Total iteration depth
CONTROL_HOLDS      equ 0x005000F8  ; Number of feedback hold cycles
CONTROL_REDIRECTS  equ 0x005000FC  ; Number of feedback rebases

CPU_COUNT          equ 0x00500108
PARALLEL_MODE      equ 0x00500110
ORACLE_AP_TIMEOUT_FLAG equ 0x00500118  ; 1 if AP bring-up timed out and we fell back to serial

; ─── Fibonacci–Legendre probable-prime oracle ───
; Verified theorem: for prime p != 5, p divides F_(p-(5|p)), where (5|p) is
; the Legendre symbol (whether 5 is a QR mod p). Tested against trial
; division for P=2..1999 in Python: zero false negatives (every real prime
; passes), a small known set of Fibonacci-pseudoprime false positives
; (25, 60, 323, 377, ...). This is a genuine probable-primality test, not
; a certified one -- displayed and labeled as such.
PRIME_CANDIDATE    equ 0x00500128  ; P currently being tested
PRIME_LEGENDRE     equ 0x00500130  ; (5|P), stored as 0/1/-1 (u64 wraps for -1)
PRIME_TARGET       equ 0x00500138  ; P - (5|P)
PRIME_FIB_MOD      equ 0x00500140  ; F(target) mod P
PRIME_FOUND_COUNT  equ 0x00500148  ; count of probable primes found so far
PRIME_LAST_FOUND   equ 0x00500150  ; most recent P that passed the test

IRIS_BASE_USED     equ 0x00500158   ; base that resolved (or was last tried)
IRIS_PROBES_USED   equ 0x00500160   ; probe count consumed
IRIS_RESULT        equ 0x00500168   ; 1 = probable prime (fall through to
                                     ; Frobenius gate), 0 = composite
                                     ; (short-circuit, skip Frobenius)

; Must be a power of 2 (gated via bitmask test, not DIV). Higher = faster
; substrate tick rate, slower prime-scan rate. 64 recovers most of the
; ~47x throughput lost when testing every tick.
; Doubled from 64 to compensate: the full two-coefficient Frobenius test
; costs ~1.8-2x the modmuls of the old single-coefficient test (measured:
; 59 false positives -> 1, for that price).
PRIME_TEST_STRIDE  equ 128

; ─── Iris stutter-step adaptive strong-PRP pre-filter ───
; base_k(P) = 2 + high64( ((k*GOLDEN64) mod 2^64) * (P-3) )
; GOLDEN64 = round(2^64 * (phi-1)) -- exact fixed-point phi (Weyl/three-
; distance equidistribution, same constant as Knuth/Fibonacci hashing).
; Verified against iris_prp.py + standalone ELF harness: all four classical
; worst-case strong pseudoprimes (2047, 1373653, 25326001, 3215031751)
; caught on probe 1; 151/151 natural composites that fool a phi/Lucas +
; n-mod-6 vantage pair caught, avg 1.033 probes, max 2.
GOLDEN64        equ 0x9E3779B97F4A7C15
IRIS_MAX_PROBES equ 16

; Bounded spin count for waiting on AP ready bits. Large enough to give
; genuinely slow-but-working hardware a fair chance, small enough that a
; truly broken AP path fails over to serial mode in well under a second
; rather than hanging the boot forever.
AP_WAIT_TIMEOUT    equ 100000000

; Physical address adjustment for 64-bit code
; Label values are ORG-relative (0x7C00+), actual physical = label + PHYS_ADJ
; Payload now loads at physical 0x7E00 (immediately after the boot sector)
; in BOTH build variants -- HDD build loads it there itself, CD build gets
; it there for free via El Torito boot-load-size. Since ORG=0x7C00 and the
; boot sector is exactly 512 bytes, every label's value already equals its
; physical address: label(L) = 0x7C00 + file_offset(L) = physical(L).
; No adjustment needed. Kept as 0 so existing "+ PHYS_ADJ" references
; throughout the file remain valid no-ops.
PHYS_ADJ           equ 0

; Strategy indices
STRATEGY_FLOWING   equ 0  ; Healthy oscillation
STRATEGY_NONACTION equ 1  ; Hold on anomaly
STRATEGY_REDIRECT  equ 2  ; Rebase on magnitude error
STRATEGY_CONVERGE  equ 3  ; Fixed point detected
STRATEGY_CRITICAL  equ 7  ; Halt

; Oracle bits
ORACLE_WATER_BROKEN    equ 0x01
ORACLE_EARTH_PATTERN   equ 0x02
ORACLE_EARTH_MAGNITUDE equ 0x04
ORACLE_WIND_FIXED      equ 0x08
ORACLE_WIND_DIVERGE    equ 0x10
ORACLE_CRITICAL        equ 0x80

; =============================================================================
; BIOS BOOT
; =============================================================================

boot_start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00
    mov [boot_drive], dl

    ; Set video mode 3 (80x25 colour text) via BIOS INT 10h
    ; Forces NVS 295 or any GPU into a known text mode state
    mov ax, 0x0003
    int 0x10

    ; Print milestone 'B' via BIOS teletype (works before any VGA init)
    mov ah, 0x0E
    mov al, 'B'
    xor bh, bh
    int 0x10

%ifdef BUILD_CD
    ; ── CD / El Torito build ──
    ; boot-load-size in the boot catalog is set to load the ENTIRE image
    ; (all IMAGE_SECTORS sectors) directly to 0x7C00 before we ever run.
    ; Our own payload (sectors 2..N) is therefore ALREADY resident at
    ; physical 0x7E00 -- no disk read needed, and doing one would corrupt
    ; memory (CD LBAs are 2048-byte units, not 512-byte HDD units).
    mov ah, 0x0E
    mov al, 'C'
    xor bh, bh
    int 0x10
%else
    ; ── HDD / USB build ──
    ; BIOS legacy boot (INT 19h) loads only the 512-byte boot sector.
    ; We must load the payload ourselves, to physical 0x7E00 -- the SAME
    ; location El Torito uses for the CD build, so protected_entry lives
    ; at one fixed physical address regardless of boot path.

    ; Check INT13h extensions are present (AH=41h, BX=55AAh)
    mov ah, 0x41
    mov bx, 0x55AA
    mov dl, [boot_drive]
    int 0x13
    jc  .use_chs
    cmp bx, 0xAA55
    jne .use_chs
    test cl, 1
    jz  .use_chs

    ; Extended read (AH=42h) into segment 0x07E0 (= physical 0x7E00)
    mov si, disk_address_packet
    mov dl, [boot_drive]
    mov ah, 0x42
    int 0x13
    jnc .disk_ok

.use_chs:
    ; Legacy CHS fallback (AH=02h) for BIOSes without extensions.
    ; Read PAYLOAD_SECTORS sectors starting at C/H/S = 0/0/2 into 07E0:0000.
    mov ax, 0x07E0
    mov es, ax
    xor bx, bx
    mov ah, 0x02
    mov al, PAYLOAD_SECTORS
    mov ch, 0
    mov cl, 2
    mov dh, 0
    mov dl, [boot_drive]
    int 0x13
    jc  boot_disk_error

.disk_ok:
    mov ah, 0x0E
    mov al, 'H'
    xor bh, bh
    int 0x10
%endif

    ; Copy AP trampoline to 0x8000. Payload lives at physical 0x7E00 in
    ; BOTH build variants (loaded there by us for HDD, or by El Torito's
    ; boot-load-size for CD), same segment as the boot sector (DS=0),
    ; so no segment arithmetic needed either way.
    mov ax, 0x2000          ; segment 0x2000 = physical 0x20000 = AP_TRAMP_PHYS
    mov es, ax
    mov si, ap_trampoline
    xor di, di
    mov cx, (ap_trampoline_end - ap_trampoline + 1) / 2
    cld
    rep movsw

    xor ax, ax
    mov es, ax

    ; Milestone 'T' — AP trampoline copy done
    mov ah, 0x0E
    mov al, 'T'
    xor bh, bh
    int 0x10

    ; A20 - Method 1: BIOS INT 15h AX=2401 (most portable)
    mov ax, 0x2401
    int 0x15

    ; A20 - Method 2: Port 0x92 Fast A20
    in  al, 0x92
    or  al, 00000010b
    and al, 11111110b
    out 0x92, al

    ; A20 - Method 3: Keyboard controller (KBC), bounded — cannot hang
    call a20_kbc_enable

    ; Milestone 'A' — A20 sequence complete (all three methods attempted)
    mov ah, 0x0E
    mov al, 'A'
    xor bh, bh
    int 0x10

    ; GDT
    lgdt [gdt_ptr]

    ; Milestone 'G' — GDT loaded
    mov ah, 0x0E
    mov al, 'G'
    xor bh, bh
    int 0x10

    ; Milestone 'P' — about to jump to protected mode (last real-mode print;
    ; if this is the last letter seen, the far jump or protected_entry itself
    ; is the failure point). MUST print before CR0.PE is set — BIOS
    ; interrupts don't work anymore once protected mode is enabled.
    mov ah, 0x0E
    mov al, 'P'
    xor bh, bh
    int 0x10

    ; Protected mode
    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    jmp dword 0x08:PROTECTED_ENTRY_PHYS

boot_disk_error:
    mov si, boot_error_msg

.loop:
    lodsb
    test al, al
    jz   .halt
    mov  ah, 0x0E
    xor  bh, bh
    int  0x10
    jmp  .loop

.halt:
    cli
    hlt
    jmp .halt

; =============================================================================
; DISK ADDRESS PACKET
; =============================================================================

disk_address_packet:
    db 0x10, 0x00
    dw PAYLOAD_SECTORS
    dw 0x0000
    dw 0x07E0          ; segment 0x07E0 = physical 0x7E00, right after boot sector
    dq 1

boot_drive:  db 0

boot_error_msg:  db "HDGL DISK ERROR",0


; A20 via keyboard controller — bounded retries, never hangs.
; Each wait loop gives up after KBC_TIMEOUT iterations rather than
; spinning forever on hardware with no PS/2 KBC or a non-conforming one.
KBC_TIMEOUT equ 65535

a20_kbc_enable:
    call .kbc_wait_in
    mov  al, 0xAD          ; disable keyboard
    out  0x64, al
    call .kbc_wait_in
    mov  al, 0xD0          ; read output port
    out  0x64, al
    call .kbc_wait_out
    in   al, 0x60
    push ax
    call .kbc_wait_in
    mov  al, 0xD1          ; write output port
    out  0x64, al
    call .kbc_wait_in
    pop  ax
    or   al, 2             ; set A20 bit
    out  0x60, al
    call .kbc_wait_in
    mov  al, 0xAE          ; enable keyboard
    out  0x64, al
    call .kbc_wait_in
    ret
.kbc_wait_in:
    push cx
    mov  cx, KBC_TIMEOUT
.wi:
    in   al, 0x64
    test al, 2
    jz   .wi_done
    loop .wi
.wi_done:
    pop  cx
    ret
.kbc_wait_out:
    push cx
    mov  cx, KBC_TIMEOUT
.wo:
    in   al, 0x64
    test al, 1
    jnz  .wo_done
    loop .wo
.wo_done:
    pop  cx
    ret

; =============================================================================
; GDT
; =============================================================================

align 8

gdt_base:
    dq 0x0000000000000000           ; null
    dq 0x00CF9A000000FFFF           ; 0x08: 32-bit code
    dq 0x00CF92000000FFFF           ; 0x10: data (32 and 64 bit)
    dq 0x00AF9A000000FFFF           ; 0x18: 64-bit code
gdt_end:

gdt_ptr:
    dw gdt_end - gdt_base - 1
    dd gdt_base

; =============================================================================
; BOOT SECTOR PAD
; =============================================================================

times 510 - ($ - $$) db 0
dw 0xAA55

; =============================================================================
; PAYLOAD — 32-BIT PROTECTED MODE ENTRY
; =============================================================================
; File offset 512 = physical 0x10200 when loaded.
; PROTECTED_ENTRY_PHYS = 0x10000 + 512 = 0x10200

BITS 32

protected_entry:
    cli
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov esp, BSP_STACK

    ; Milestone '1' — reached 32-bit protected mode. Direct VGA write
    ; (no BIOS available here); bottom-left corner, out of the way.
    mov byte [0xB8000 + 24*160 + 0], '1'
    mov byte [0xB8000 + 24*160 + 1], 0x4F

    ; Build identity page tables (0..4 GiB, 2 MB pages)
    call build_page_tables

    ; Milestone '2' — page tables built
    mov byte [0xB8000 + 24*160 + 2], '2'
    mov byte [0xB8000 + 24*160 + 3], 0x4F

    ; PAE
    mov eax, cr4
    or  eax, (1 << 5)
    mov cr4, eax

    ; EFER.LME
    mov ecx, 0xC0000080
    rdmsr
    or  eax, (1 << 8)
    wrmsr

    ; CR3
    mov eax, PML4_PHYS
    mov cr3, eax

    ; Paging on
    mov eax, cr0
    or  eax, (1 << 31)
    mov cr0, eax

    ; Milestone '3' — paging enabled, about to enter long mode
    mov byte [0xB8000 + 24*160 + 4], '3'
    mov byte [0xB8000 + 24*160 + 5], 0x4F

    ; Far jump to 64-bit entry — LONG_MODE_ENTRY_PHYS computed below
    jmp dword 0x18:LONG_MODE_ENTRY_PHYS

; =============================================================================
; PAGE TABLE CONSTRUCTION (32-bit)
; =============================================================================

build_page_tables:
    pushad

    ; Zero PML4 + PDPT + 4 PDs = 6 pages = 0x6000 bytes
    mov edi, PML4_PHYS
    xor eax, eax
    mov ecx, 0x6000 / 4
    cld
    rep stosd

    ; PML4[0] -> PDPT
    mov dword [PML4_PHYS + 0], PDPT_PHYS | 0x003
    mov dword [PML4_PHYS + 4], 0

    ; PDPT[0..3] -> PD0..PD3
    mov dword [PDPT_PHYS +  0], PD0_PHYS | 0x003
    mov dword [PDPT_PHYS +  4], 0
    mov dword [PDPT_PHYS +  8], PD1_PHYS | 0x003
    mov dword [PDPT_PHYS + 12], 0
    mov dword [PDPT_PHYS + 16], PD2_PHYS | 0x003
    mov dword [PDPT_PHYS + 20], 0
    mov dword [PDPT_PHYS + 24], PD3_PHYS | 0x003
    mov dword [PDPT_PHYS + 28], 0

    ; PD0: 0..1 GiB (512 entries × 2 MB = 1 GiB)
    mov edi, PD0_PHYS
    xor eax, eax
    mov ecx, 512
.pd0:
    mov edx, eax
    or  edx, 0x83           ; present + RW + huge (2MB)
    mov [edi],   edx
    mov dword [edi+4], 0
    add eax, 0x200000
    add edi, 8
    loop .pd0

    ; PD1: 1..2 GiB
    mov edi, PD1_PHYS
    mov eax, 0x40000000
    mov ecx, 512
.pd1:
    mov edx, eax
    or  edx, 0x83
    mov [edi],   edx
    mov dword [edi+4], 0
    add eax, 0x200000
    add edi, 8
    loop .pd1

    ; PD2: 2..3 GiB
    mov edi, PD2_PHYS
    mov eax, 0x80000000
    mov ecx, 512
.pd2:
    mov edx, eax
    or  edx, 0x83
    mov [edi],   edx
    mov dword [edi+4], 0
    add eax, 0x200000
    add edi, 8
    loop .pd2

    ; PD3: 3..4 GiB  (wraps at 4 GiB, ok for identity map)
    mov edi, PD3_PHYS
    mov eax, 0xC0000000
    mov ecx, 512
.pd3:
    mov edx, eax
    or  edx, 0x83
    mov [edi],   edx
    mov dword [edi+4], 0
    add eax, 0x200000
    add edi, 8
    loop .pd3

    popad
    ret

; =============================================================================
; 64-BIT BSP ENTRY
; =============================================================================
; THIS LABEL MUST BE THE FIRST BITS 64 INSTRUCTION IN THE FILE.
; LONG_MODE_ENTRY_PHYS is computed from its file position.

BITS 64

long_mode_entry:
    cli
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov rsp, BSP_STACK

    ; Milestone '4' — reached 64-bit long mode (independent of COM1)
    mov byte [0xB8000 + 24*160 + 6], '4'
    mov byte [0xB8000 + 24*160 + 7], 0x4F

    ; COM1 serial init (115200 8N1)
    ; Works regardless of GPU - critical for bare metal debug
    mov dx, 0x3F9
    mov al, 0x00
    out dx, al          ; disable interrupts
    mov dx, 0x3FB
    mov al, 0x80
    out dx, al          ; DLAB=1
    mov dx, 0x3F8
    mov al, 0x01
    out dx, al          ; divisor lo = 1 (115200 baud)
    mov dx, 0x3F9
    mov al, 0x00
    out dx, al          ; divisor hi
    mov dx, 0x3FB
    mov al, 0x03
    out dx, al          ; 8N1, DLAB=0
    mov dx, 0x3FC
    mov al, 0x03
    out dx, al          ; RTS+DTR

    ; Send milestone 'L' = long mode entry confirmed
    call serial_putchar_L

    ; Detect logical processor count via CPUID
    mov eax, 1
    cpuid
    shr ebx, 16
    and ebx, 0xFF
    test ebx, ebx
    jnz .cpu_ok
    mov ebx, 1
.cpu_ok:
    mov [CPU_COUNT], rbx

    ; Multi-core AP bring-up is disabled for now: the AP trampoline path
    ; has an unresolved bug (an AP ends up executing with the BIOS's own
    ; GDT instead of ours, then triple-faults, which can take the whole
    ; system down before anything gets a chance to display). Until that
    ; is root-caused, always run single-core. The substrate is fully
    ; correct in serial mode -- CPU0 computes FIRE/WATER/EARTH/WIND
    ; directly every cycle -- so this costs performance, not correctness.
    ; CPU_COUNT above still reflects the real detected count for display.
    mov qword [PARALLEL_MODE], 0
    jmp .mode_done
.serial:
    mov qword [PARALLEL_MODE], 0
.mode_done:

    ; ─── Canonical initial state: Ω = 0·φ + 1 ───
    mov qword [STATE_A],        0
    mov qword [STATE_B],        1
    mov qword [STATE_K],        0
    mov qword [FIRE_A],         0
    mov qword [FIRE_B],         1
    mov qword [FIRE_K],         0
    mov qword [WATER_A],        0
    mov qword [WATER_B],        1
    mov qword [EARTH_N],        1       ; N(0,1) = 1
    mov qword [EARTH_N_FIRE],   0
    mov qword [EARTH_DELTA],    0
    mov qword [EARTH_PREV_DELTA], 0     ; no previous delta yet
    mov qword [WIND_RES_A],     0
    mov qword [WIND_RES_B],     0
    mov qword [WIND_FIX],       0
    mov qword [REQUEST_K],      0
    mov qword [DONE_WATER],     0
    mov qword [DONE_EARTH],     0
    mov qword [DONE_WIND],      0
    mov qword [READY_MASK],     1
    mov qword [ORACLE],         0
    mov qword [TRINARY],        0
    mov qword [STRATEGY],       STRATEGY_FLOWING
    mov qword [YIN],            2
    mov qword [ORACLE_AP_TIMEOUT_FLAG], 0

    ; Fibonacci–Legendre probable-prime oracle
    mov qword [PRIME_CANDIDATE],   2
    mov qword [PRIME_LEGENDRE],    0
    mov qword [PRIME_TARGET],      0
    mov qword [PRIME_FIB_MOD],     0
    mov qword [PRIME_FOUND_COUNT], 0
    mov qword [PRIME_LAST_FOUND],  0
    mov qword [IRIS_BASE_USED],    0
    mov qword [IRIS_PROBES_USED],  0
    mov qword [IRIS_RESULT],       0
    mov qword [PHASE],          0
    mov qword [DEPTH],          0
    mov qword [CONTROL_HOLDS], 0
    mov qword [CONTROL_REDIRECTS], 0

    ; VGA init
    call vga_init

    ; Launch APs if multi-core
    cmp qword [PARALLEL_MODE], 1
    jne .bsp_fire
    call start_aps

.bsp_fire:
    call role_fire

.halt:
    cli
    hlt
    jmp .halt

; =============================================================================
; START APPLICATION PROCESSORS
; =============================================================================

start_aps:
    ; Enable BSP local APIC
    mov ecx, 0x1B
    rdmsr
    or  eax, 0x800
    wrmsr

    ; Read actual LAPIC base from MSR 0x1B (bits 35:12)
    ; eax already has MSR value from the rdmsr above
    ; eax bits [31:12] = LAPIC base[31:12], edx bits [3:0] = LAPIC base[35:32]
    and eax, 0xFFFFF000         ; mask lower 12 bits
    mov r8d, eax                ; r8 = LAPIC physical base (fits in 32-bit)
    ; If edx != 0 the LAPIC is above 4GB - very unusual, use default
    test edx, edx
    jz .lapic_ok
    mov r8d, LAPIC_BASE
.lapic_ok:

    ; INIT IPI to all excluding self
    mov dword [r8 + LAPIC_ICR_HIGH], 0
    mov dword [r8 + LAPIC_ICR_LOW],  0x000C4500
    call apic_wait

    ; SIPI #1 — vector 0x20 -> physical 0x20000
    mov dword [r8 + LAPIC_ICR_HIGH], 0
    mov dword [r8 + LAPIC_ICR_LOW],  0x000C4620
    call apic_wait

    ; SIPI #2
    mov dword [r8 + LAPIC_ICR_HIGH], 0
    mov dword [r8 + LAPIC_ICR_LOW],  0x000C4620
    call apic_wait

    ; Wait for all 4 CPUs to set their bits in READY_MASK -- BOUNDED.
    ; If APs don't come up (real hardware can differ from QEMU here —
    ; non-sequential APIC IDs, a stricter LAPIC, etc.), fall back to
    ; single-core serial mode rather than deadlocking forever. The
    ; substrate is fully correct running on CPU0 alone; multi-core is
    ; an optimization, not a requirement.
    mov r9, AP_WAIT_TIMEOUT
.wait_aps:
    mov rax, [READY_MASK]
    and eax, 0xF
    cmp eax, 0xF
    je  .aps_ready
    dec r9
    jnz .wait_aps

    ; Timed out — force serial mode and continue on CPU0 alone.
    mov qword [PARALLEL_MODE], 0
    mov qword [ORACLE_AP_TIMEOUT_FLAG], 1
    ret

.aps_ready:
    ret

apic_wait:
    mov ecx, 100000
.spin:
    pause
    loop .spin
    ret

; =============================================================================
; FIRE — CPU 0 (Operator / Strategy Selector)
; =============================================================================

role_fire:
    mov qword [READY_MASK], 1       ; mark CPU 0 ready

fire_cycle:
    ; ── Snapshot current Ω ──
    mov r8,  [STATE_A]
    mov r9,  [STATE_B]
    mov r10, [STATE_K]

    ; ── FIRE: (a,b) -> (a+b, a) ──
    mov rax, r8
    add rax, r9
    mov [FIRE_A], rax
    mov [FIRE_B], r8
    inc r10
    mov [FIRE_K], r10

    ; ── Serial or parallel path ──
    cmp qword [PARALLEL_MODE], 0
    je  .serial

    ; Parallel: publish request and wait
    mov [REQUEST_K], r10

.wait_water:
    mov rax, [DONE_WATER]
    cmp rax, r10
    jne .wait_water
.wait_earth:
    mov rax, [DONE_EARTH]
    cmp rax, r10
    jne .wait_earth
.wait_wind:
    mov rax, [DONE_WIND]
    cmp rax, r10
    jne .wait_wind
    jmp .commit

.serial:
    call water_compute
    call earth_compute
    call wind_compute

.commit:
    ; ── Wu-Wei strategy selection ──
    mov rax, [ORACLE]
    call fire_select_strategy

    ; ── CLOSED-LOOP CONTROL ──────────────────────────────────────────────
    ; Strategy is now causal: the oracle decides what happens to FIRE.
    ; FLOWING   = commit the candidate normally.
    ; CONVERGE  = commit, but skip the expensive prime observer this tick.
    ; NONACTION = hold the canonical state for one cycle, then re-observe.
    ; REDIRECT  = rebase to the canonical (0,1) seed and restart dynamics.
    ; CRITICAL  = fire_select_strategy halts before returning.
    mov rax, [STRATEGY]
    cmp rax, STRATEGY_REDIRECT
    je  .control_redirect
    cmp rax, STRATEGY_NONACTION
    je  .control_hold

    ; Prime observer is subordinate to the controller. In convergence mode
    ; we spend this cycle observing the substrate rather than the prime stream.
    cmp rax, STRATEGY_CONVERGE
    je  .control_commit_no_prime

    ; FLOWING RIVER: normal throttled prime observation.
    mov rax, r10
    test rax, (PRIME_TEST_STRIDE - 1)
    jnz .control_commit
    call prime_test_step
    jmp .control_commit

.control_commit_no_prime:
    ; Convergence is itself an observation event; do not pay for the
    ; expensive prime gate on this tick. The mathematical state still flows.
    jmp .control_commit

.control_hold:
    ; NON-ACTION is a real hold, not merely a label. Keep (a,b,k) intact,
    ; reset the pattern history so the next observation starts a fresh pair.
    inc qword [CONTROL_HOLDS]
    mov qword [EARTH_PREV_DELTA], 0
    mov qword [ORACLE], 0
    ; Continue to the display/depth machinery without committing FIRE.
    jmp .control_post

.control_redirect:
    ; REDIRECT is a true rebase. Return to Ω=(0,1), the canonical seed.
    inc qword [CONTROL_REDIRECTS]
    mov qword [STATE_A], 0
    mov qword [STATE_B], 1
    mov qword [STATE_K], 0
    mov qword [EARTH_PREV_DELTA], 0
    mov qword [EARTH_N], 1
    mov qword [EARTH_N_FIRE], 0
    mov qword [EARTH_DELTA], 0
    mov qword [WIND_RES_A], 0
    mov qword [WIND_RES_B], 0
    mov qword [WIND_FIX], 0
    mov qword [ORACLE], 0
    mov qword [STRATEGY], STRATEGY_FLOWING
    mov qword [PHASE], 0
    mov qword [YIN], 2
    jmp .control_post

.control_commit:
    ; Commit FIRE's candidate state only when the controller permits flow.
    mov rax, [FIRE_A]
    mov rbx, [FIRE_B]
    mov [STATE_A], rax
    mov [STATE_B], rbx
    mov [STATE_K], r10

.control_post:
    ; ── YIN: s -> s² - 2 ──
    mov rax, [YIN]
    imul rax, rax
    sub  rax, 2
    mov  [YIN], rax

    ; ── Completion: 0->1->2->3->0 ──
    inc  qword [PHASE]
    and  qword [PHASE], 3

    ; ── Depth ──
    inc  qword [DEPTH]

    ; ── Display every PRINT_EVERY iterations ──
    mov rax, r10
    test rax, PRINT_MASK
    jnz fire_cycle

    call vga_update
    jmp fire_cycle

; ============================================================================
; FIRE: WU-WEI STRATEGY SELECTOR
; Input: rax = ORACLE bitfield
; ============================================================================

fire_select_strategy:
    ; CRITICAL: halt and display
    test al, ORACLE_CRITICAL
    jnz  .critical

    ; EARTH magnitude wrong: redirect (rebase)
    test al, ORACLE_EARTH_MAGNITUDE
    jnz  .redirect

    ; EARTH pattern broken: non-action
    test al, ORACLE_EARTH_PATTERN
    jnz  .nonaction

    ; WIND divergence: redirect/rebase rather than blindly continuing.
    test al, ORACLE_WIND_DIVERGE
    jnz  .redirect

    ; WIND convergence: log it and continue flowing.
    test al, ORACLE_WIND_FIXED
    jnz  .converge

    ; WATER broken: flag but continue
    test al, ORACLE_WATER_BROKEN
    jnz  .water_anom

    ; All clear
    mov qword [STRATEGY], STRATEGY_FLOWING
    ret

.critical:
    mov qword [STRATEGY], STRATEGY_CRITICAL
    call vga_update          ; force display
    cli
    hlt                      ; deliberate halt on critical
    jmp .critical

.redirect:
    mov qword [STRATEGY], STRATEGY_REDIRECT
    ; Rebase: reset STATE to (0,1) to restart from known phi seed
    ; In a more sophisticated version this would be a soft reset
    ret

.nonaction:
    mov qword [STRATEGY], STRATEGY_NONACTION
    ret

.converge:
    mov qword [STRATEGY], STRATEGY_CONVERGE
    ret

.water_anom:
    ; Water anomaly with no other flags: continue but log
    mov qword [STRATEGY], STRATEGY_FLOWING
    ret

; =============================================================================
; WATER — CPU 1 (Inverse Verification)
; =============================================================================
; WATER(a,b) = (b, a-b)
; Checks: WATER(FIRE(Ω)) == Ω
; WATER(a+b, a) = (a, (a+b)-a) = (a, b) = Ω  -- always true for exact arithmetic
; So ORACLE_WATER_BROKEN fires only on arithmetic error (impossible mod 2^64)

water_compute:
    mov r8, [STATE_A]
    mov r9, [STATE_B]

    ; Compute WATER of current state
    mov rax, r9
    mov rbx, r8
    sub rbx, r9
    mov [WATER_A], rax
    mov [WATER_B], rbx

    ; Verify WATER(FIRE(Ω)) == Ω
    ; FIRE = (FIRE_A, FIRE_B) = (a+b, a)
    ; WATER(a+b, a) = (a, b)  so check WATER_FIRE_A==STATE_A, WATER_FIRE_B==STATE_B
    mov rcx, [FIRE_A]
    mov rdx, [FIRE_B]
    ; WATER of FIRE: first = FIRE_B = a, second = FIRE_A - FIRE_B = b
    cmp rdx, r8         ; FIRE_B == STATE_A?
    jne .broken
    mov rsi, rcx
    sub rsi, rdx
    cmp rsi, r9         ; FIRE_A - FIRE_B == STATE_B?
    jne .broken

    ; Clear water bit in oracle
    mov rax, [ORACLE]
    and rax, ~ORACLE_WATER_BROKEN
    mov [ORACLE], rax
    ret

.broken:
    or qword [ORACLE], ORACLE_WATER_BROKEN
    or qword [ORACLE], ORACLE_CRITICAL      ; water failure is always critical
    ret

; =============================================================================
; WATER WORKER — CPU 1 (AP loop)
; =============================================================================

role_water:
    xor r15d, r15d
    lock or qword [READY_MASK], 2

.wait:
    mov rax, [REQUEST_K]
    cmp rax, r15
    je  .wait
    mov r15, rax
    call water_compute
    mov [DONE_WATER], r15
    jmp .wait

; =============================================================================
; EARTH — CPU 2 (N_phi Pattern Oracle)
; =============================================================================
; N_phi(a,b) = -a² + ab + b²
;
; WU-WEI: For Fibonacci pairs, N oscillates: N(k) = (-1)^k.
; Expected delta each step: -(EARTH_N)*2  (flips sign, magnitude 2)
; If delta != -2*N(prev): pattern broken -> ORACLE_EARTH_PATTERN
; If |delta| != 2:         magnitude wrong -> ORACLE_EARTH_MAGNITUDE

earth_compute:
    mov r8, [STATE_A]
    mov r9, [STATE_B]

    ; ── N(current) = -a² + ab + b² ──
    mov rax, r8
    imul rax, r8
    neg  rax                    ; -a²
    mov  rbx, r8
    imul rbx, r9
    add  rax, rbx               ; -a² + ab
    mov  rbx, r9
    imul rbx, r9
    add  rax, rbx               ; -a² + ab + b²
    mov  [EARTH_N], rax

    ; ── N(FIRE(current)): FIRE=(a+b, a) ──
    ; NOTE: was r10/r11 -- fire_cycle keeps the live iteration counter in
    ; r10 across the water/earth/wind calls (used for STATE_K commit,
    ; the PRIME_TEST_STRIDE gate, and the PRINT_MASK display cadence).
    ; earth_compute clobbered it every tick with no save/restore, silently
    ; replacing the counter with STATE_A+STATE_B from the second tick
    ; onward -- confirmed via QEMU serial trace (R10_BEFORE vs
    ; R10_AFTER_EARTH diverge every call; R10_AFTER_WATER does not).
    ; Moved to r12/r13, which nothing live across this call uses.
    mov r12, r8
    add r12, r9                 ; r12 = a+b = FIRE_A
    mov r13, r8                 ; r13 = a   = FIRE_B

    mov rax, r12
    imul rax, r12
    neg  rax
    mov  rbx, r12
    imul rbx, r13
    add  rax, rbx
    mov  rbx, r13
    imul rbx, r13
    add  rax, rbx
    mov  [EARTH_N_FIRE], rax

    ; ── Delta = N(FIRE) - N(current) ──
    mov rcx, [EARTH_N]
    mov rdx, [EARTH_N_FIRE]
    mov rax, rdx
    sub rax, rcx               ; delta = N_fire - N_curr
    mov [EARTH_DELTA], rax

    ; ── Pattern check: |delta| should be 2 ──
    mov rbx, rax
    ; abs(rax): if negative, negate
    test rax, rax
    jns  .pos
    neg  rbx
.pos:
    cmp rbx, 2
    jne .magnitude_wrong

    ; ── Sign check: delta should be opposite sign of N(current) ──
    ; N positive -> delta should be negative
    ; N negative -> delta should be positive
    ; i.e. N(current) * delta < 0  (opposite signs)
    ; Skip sign check on very first iteration (prev_delta == 0)
    cmp qword [EARTH_PREV_DELTA], 0
    je  .first_iter

    ; Check alternation: delta sign should be opposite of prev_delta sign
    mov r12, rax                ; current delta
    mov r13, [EARTH_PREV_DELTA]
    ; If both same sign -> pattern broken
    ; r12 and r13: test sign agreement via XOR of sign bits
    mov r14, r12
    xor r14, r13
    ; If bit 63 of XOR is 0, both same sign -> broken
    test r14, r14
    js   .signs_ok
    ; Same sign = pattern broken
    or   qword [ORACLE], ORACLE_EARTH_PATTERN
    jmp  .done

.signs_ok:
    ; Pattern good: clear earth bits
    mov rbx, [ORACLE]
    and rbx, ~(ORACLE_EARTH_PATTERN | ORACLE_EARTH_MAGNITUDE)
    mov [ORACLE], rbx
    jmp .done

.first_iter:
    ; First iteration: just clear earth error bits
    mov rbx, [ORACLE]
    and rbx, ~(ORACLE_EARTH_PATTERN | ORACLE_EARTH_MAGNITUDE)
    mov [ORACLE], rbx
    jmp .done

.magnitude_wrong:
    or  qword [ORACLE], ORACLE_EARTH_MAGNITUDE
    jmp .done

.done:
    ; Save delta for next iteration
    mov rax, [EARTH_DELTA]
    mov [EARTH_PREV_DELTA], rax

    ; ── Trinary projection: sign of N ──
    mov rax, [EARTH_N]
    test rax, rax
    jz   .tri_zero
    js   .tri_neg
    mov qword [TRINARY], 1
    ret
.tri_neg:
    mov qword [TRINARY], -1
    ret
.tri_zero:
    mov qword [TRINARY], 0
    ret

; =============================================================================
; EARTH WORKER — CPU 2 (AP loop)
; =============================================================================

role_earth:
    xor r15d, r15d
    lock or qword [READY_MASK], 4

.wait:
    mov rax, [REQUEST_K]
    cmp rax, r15
    je  .wait
    mov r15, rax
    call earth_compute
    mov [DONE_EARTH], r15
    jmp .wait

; =============================================================================
; WIND — CPU 3 (T(X) Fixed-Point Residual)
; =============================================================================
; T(X) = 1 + 1/X. Fixed point: X = phi.
; In Z[phi] with X = a*phi + b:
;   T(X) - X  residuals:
;     phi coeff:  a² + 2ab - a
;     const coeff: a² + b² - b - 1
; Both zero iff X = phi (the fixed point).
;
; WU-WEI: residuals grow as Fibonacci grows. 
; WIND_FIXED fires when both are zero (rare, meaningful event).
; WIND_DIVERGE fires when |res_a| + |res_b| exceeds threshold.

WIND_DIV_THRESH    equ 0x1000000000   ; ~68 billion: divergence threshold

wind_compute:
    mov r8, [STATE_A]
    mov r9, [STATE_B]

    ; ── phi-coeff residual: a² + 2ab - a ──
    mov rax, r8
    imul rax, r8                ; a²
    mov  rbx, r8
    imul rbx, r9                ; ab
    add  rbx, rbx               ; 2ab
    add  rax, rbx               ; a² + 2ab
    sub  rax, r8                ; a² + 2ab - a
    mov  [WIND_RES_A], rax

    ; ── const residual: a² + b² - b - 1 ──
    mov rcx, r8
    imul rcx, r8                ; a²
    mov  rdx, r9
    imul rdx, r9                ; b²
    add  rcx, rdx               ; a² + b²
    sub  rcx, r9                ; a² + b² - b
    dec  rcx                    ; a² + b² - b - 1
    mov  [WIND_RES_B], rcx

    ; ── Fixed point check ──
    test rax, rax
    jnz  .not_fixed
    test rcx, rcx
    jnz  .not_fixed
    mov  qword [WIND_FIX], 1
    or   qword [ORACLE], ORACLE_WIND_FIXED
    ret

.not_fixed:
    mov qword [WIND_FIX], 0

    ; ── Divergence check ──
    ; |res_a| + |res_b| > threshold?
    mov  rax, [WIND_RES_A]
    test rax, rax
    jns  .pos_a
    neg  rax
.pos_a:
    mov  rbx, [WIND_RES_B]
    test rbx, rbx
    jns  .pos_b
    neg  rbx
.pos_b:
    add  rax, rbx
    mov  r12, WIND_DIV_THRESH
    cmp  rax, r12
    jbe  .no_diverge
    or   qword [ORACLE], ORACLE_WIND_DIVERGE
    jmp  .wind_done

.no_diverge:
    ; Clear wind bits
    mov  rax, [ORACLE]
    and  rax, ~(ORACLE_WIND_FIXED | ORACLE_WIND_DIVERGE)
    mov  [ORACLE], rax

.wind_done:
    ret

; =============================================================================
; WIND WORKER — CPU 3 (AP loop)
; =============================================================================

role_wind:
    xor r15d, r15d
    lock or qword [READY_MASK], 8

.wait:
    mov rax, [REQUEST_K]
    cmp rax, r15
    je  .wait
    mov r15, rax
    call wind_compute
    mov [DONE_WIND], r15
    jmp .wait

; =============================================================================
; FIBONACCI–LEGENDRE PROBABLE-PRIME ORACLE
; =============================================================================
;
; modmul64: (RAX * RBX) mod RCX -> RAX
;   Uses MUL for the full 128-bit product then DIV for mod reduction.
;   Safe for any RCX != 0: since RAX,RBX < RCX on entry (both already
;   reduced), the product < RCX^2, so quotient < RCX < 2^64 -- always
;   fits, DIV can never fault here.
; =============================================================================

modmul64:
    push rdx
    mul  rbx            ; RDX:RAX = RAX*RBX
    div  rcx             ; RAX=quotient RDX=remainder
    mov  rax, rdx        ; return remainder
    pop  rdx
    ret

; ============================================================================
; legendre5: RAX = P  ->  returns RAX = 1, or RAX = 0xFFFFFFFFFFFFFFFF (-1),
; or RAX = 0 (only when P is a multiple of 5)
; ============================================================================

legendre5:
    push rdx
    push rcx
    mov  rcx, 5
    xor  rdx, rdx
    div  rcx             ; RAX=P/5, RDX = P mod 5
    mov  rax, rdx
    cmp  rax, 0
    je   .zero
    cmp  rax, 1
    je   .plus1
    cmp  rax, 4
    je   .plus1
    ; remainder is 2 or 3
    mov  rax, -1
    jmp  .done
.plus1:
    mov  rax, 1
    jmp  .done
.zero:
    xor  rax, rax
.done:
    pop  rcx
    pop  rdx
    ret

; ============================================================================
; modfib: computes F(N) mod M via iterative fast doubling.
;   Input:  RDI = N (index), RSI = M (modulus)
;   Output: RAX = F(N) mod M
;   Clobbers: RBX, RCX, RDX, R8, R9, R10, R11, R12, R13, R14
;
;   Recurrence (fast doubling):
;     F(2k)   = F(k) * (2*F(k+1) - F(k))
;     F(2k+1) = F(k+1)^2 + F(k)^2
;   Processed MSB-to-LSB over the bits of N.
; ============================================================================

modfib:
    push rbx
    push rcx
    push rdx
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14

    ; special case N=0 -> F(0)=0
    test rdi, rdi
    jnz  .have_bits
    xor  rax, rax
    jmp  .modfib_ret

.have_bits:
    ; R12 = M (modulus, kept resident)
    mov  r12, rsi

    ; Find highest set bit of N (BSR) -> R13 = bit index
    bsr  r13, rdi

    ; (R8,R9) = (a,b) = (F(0),F(1)) mod M = (0,1)
    xor  r8, r8
    mov  r9, 1

.bit_loop:
    ; c = a*(2b - a) mod M
    mov  rax, r9
    add  rax, rax        ; 2b
    cmp  rax, r12
    jb   .no_corr1
    sub  rax, r12
.no_corr1:
    ; rax = 2b mod M ; now compute (2b - a) mod M, non-negative
    cmp  rax, r8
    jae  .no_corr2
    add  rax, r12
.no_corr2:
    sub  rax, r8          ; rax = (2b-a) mod M, in [0,M)
    mov  rbx, rax         ; RBX = (2b-a) mod M
    mov  rax, r8
    mov  rcx, r12
    call modmul64          ; RAX = a*(2b-a) mod M = c
    mov  r10, rax          ; R10 = c

    ; d = a^2 + b^2 mod M
    mov  rax, r8
    mov  rbx, r8
    mov  rcx, r12
    call modmul64           ; RAX = a*a mod M
    mov  r11, rax           ; R11 = a^2 mod M
    mov  rax, r9
    mov  rbx, r9
    mov  rcx, r12
    call modmul64            ; RAX = b*b mod M
    add  rax, r11
    cmp  rax, r12
    jb   .no_corr3
    sub  rax, r12
.no_corr3:
    mov  r14, rax            ; R14 = d = a^2+b^2 mod M

    ; test bit R13 of N (RDI)
    mov  rcx, r13
    mov  rax, 1
    shl  rax, cl
    test rdi, rax
    jz   .bit_zero

    ; bit=1: (a,b) = (d, (c+d) mod M)
    mov  r8, r14
    mov  rax, r10
    add  rax, r14
    cmp  rax, r12
    jb   .no_corr4
    sub  rax, r12
.no_corr4:
    mov  r9, rax
    jmp  .bit_done

.bit_zero:
    ; bit=0: (a,b) = (c, d)
    mov  r8, r10
    mov  r9, r14

.bit_done:
    test r13, r13
    jz   .modfib_done
    dec  r13
    jmp  .bit_loop

.modfib_done:
    mov  rax, r8

.modfib_ret:
    pop  r14
    pop  r13
    pop  r12
    pop  r11
    pop  r10
    pop  r9
    pop  r8
    pop  rdx
    pop  rcx
    pop  rbx
    ret

; ============================================================================
; prime_test_step: tests the current PRIME_CANDIDATE for probable primality
; via the Fibonacci-Legendre test, advances the candidate by 1, and updates
; PRIME_FOUND_COUNT / PRIME_LAST_FOUND on a pass.
; ============================================================================

; ============================================================================
; zmul_mod: (R8,R9) = (a,b) * (c,d) mod M, in Z[phi], phi^2=phi+1
;   Input:  R8,R9 = a,b (first factor)   R10,R11 = c,d (second factor)
;           R13   = M (modulus)
;   Output: R8,R9 = result, reduced mod M
;   (a,b)*(c,d) = (ac+ad+bc, ac+bd)
; ============================================================================

zmul_mod:
    push rax
    push rbx
    push rcx
    push r12
    push r14
    push r15

    mov  rax, r8
    mov  rbx, r10
    mov  rcx, r13
    call modmul64
    mov  r12, rax             ; ac

    mov  rax, r8
    mov  rbx, r11
    mov  rcx, r13
    call modmul64
    mov  r14, rax             ; ad

    mov  rax, r9
    mov  rbx, r10
    mov  rcx, r13
    call modmul64
    mov  r15, rax             ; bc

    mov  rax, r9
    mov  rbx, r11
    mov  rcx, r13
    call modmul64              ; bd

    ; new_b = (ac+bd) mod M
    add  rax, r12
    cmp  rax, r13
    jb   .nb_ok
    sub  rax, r13
.nb_ok:
    mov  r9, rax               ; new_b

    ; new_a = (ac+ad+bc) mod M -- sum of THREE terms each already < M,
    ; so the sum can reach just under 3M. ONE conditional subtraction
    ; only fully reduces sums up to 2M; a sum in [2M,3M) needs a SECOND
    ; subtraction. (This was the bug: single subtraction left a residual
    ; +M in ~19% of cases, verified against an independent Python
    ; zmul_mod -- found_count mismatched 14611 vs 10992 until this fix.)
    mov  rax, r12
    add  rax, r14
    add  rax, r15
    cmp  rax, r13
    jb   .na_ok
    sub  rax, r13
    cmp  rax, r13
    jb   .na_ok
    sub  rax, r13
.na_ok:
    mov  r8, rax                ; new_a
    ; r9 already holds new_b from above

    pop  r15
    pop  r14
    pop  r12
    pop  rcx
    pop  rbx
    pop  rax
    ret

; ============================================================================
; zpow_mod: computes phi^N mod M via square-and-multiply in Z[phi]/(M).
;   Input:  RDI = N (exponent), RSI = M (modulus)
;   Output: R8,R9 = (a,b) such that phi^N == a*phi+b (mod M)
;   Clobbers: RAX,RBX,RCX,RDX,R10,R11,R12,R13,R14,R15
; ============================================================================

zpow_mod:
    push rax
    push rbx
    push rcx
    push rdx

    mov  r13, rsi              ; M resident
    mov  r12, rdi              ; exponent resident (consumed by shifting)

    mov  r8, 0                 ; result = phi^0 = (0,1)
    mov  r9, 1
    mov  r14, 1                ; base = phi = (1,0)
    xor  r15, r15

.zp_loop:
    test r12, r12
    jz   .zp_done

    test r12, 1
    jz   .zp_sq

    ; result *= base
    mov  r10, r14
    mov  r11, r15
    call zmul_mod

.zp_sq:
    ; base *= base
    push r8
    push r9
    mov  r8, r14
    mov  r9, r15
    mov  r10, r14
    mov  r11, r15
    call zmul_mod
    mov  r14, r8
    mov  r15, r9
    pop  r9
    pop  r8

    shr  r12, 1
    jmp  .zp_loop

.zp_done:
    pop  rdx
    pop  rcx
    pop  rbx
    pop  rax
    ret

; ============================================================================
; prime_test_step: full two-coefficient Frobenius probable-prime test.
;   Checks phi^P mod P against the expected split (phi=(1,0)) or inert
;   (psi=(P-1,1)) target EXACTLY -- both coefficients, not just one.
;   Verified in Python: reduces false positives from 59 to 1 (the single
;   documented exception, 4181=37*113) over P=2..4999, at ~1.8-2x the
;   modular-multiply cost of the single-coefficient test.
; ============================================================================

prime_test_step:
    push rax
    push rbx
    push rcx
    push rdx
    push rdi
    push rsi
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15

    mov  rax, [PRIME_CANDIDATE]
    cmp  rax, 2
    jae  .valid_candidate
    mov  qword [PRIME_CANDIDATE], 2
    mov  rax, 2

.valid_candidate:
    ; iris_base computes span = P-3 and assumes P is comfortably large
    ; enough for that to be meaningful (unsigned underflow for P<3, and
    ; nothing useful to probe for P<5 anyway) -- guard small candidates
    ; and let them fall straight through to the existing Frobenius gate,
    ; which already handles them correctly.
    cmp  qword [PRIME_CANDIDATE], 5
    jb   .skip_iris

    call iris_prp_step
    cmp  qword [IRIS_RESULT], 0
    je   .not_prime

.skip_iris:
    call legendre5              ; RAX = (5|P)
    mov  [PRIME_LEGENDRE], rax
    mov  rbx, rax                ; keep legendre in RBX across zpow_mod

    mov  rdi, [PRIME_CANDIDATE]  ; N = P
    mov  rsi, [PRIME_CANDIDATE]  ; M = P
    call zpow_mod                 ; R8,R9 = phi^P mod P

    mov  [PRIME_FIB_MOD], r8      ; repurposed: store phi^P's phi-coeff

    cmp  rbx, 0
    je   .ramified

    cmp  rbx, 1
    je   .check_split

    ; inert case (5|P) == -1: expect phi^P == psi == (P-1, 1)
    mov  rax, [PRIME_CANDIDATE]
    dec  rax
    cmp  r8, rax
    jne  .not_prime
    cmp  r9, 1
    jne  .not_prime
    jmp  .is_prime

.check_split:
    ; split case (5|P) == 1: expect phi^P == phi == (1, 0)
    cmp  r8, 1
    jne  .not_prime
    cmp  r9, 0
    jne  .not_prime
    jmp  .is_prime

.ramified:
    ; (5|P) == 0 only when P is a multiple of 5; only P=5 itself is prime
    mov  rax, [PRIME_CANDIDATE]
    cmp  rax, 5
    jne  .not_prime
    jmp  .is_prime

.is_prime:
    inc  qword [PRIME_FOUND_COUNT]
    mov  rax, [PRIME_CANDIDATE]
    mov  [PRIME_LAST_FOUND], rax

.not_prime:
    inc  qword [PRIME_CANDIDATE]

    pop  r15
    pop  r14
    pop  r13
    pop  r12
    pop  r11
    pop  r10
    pop  r9
    pop  r8
    pop  rsi
    pop  rdi
    pop  rdx
    pop  rcx
    pop  rbx
    pop  rax
    ret


; ============================================================================
; IRIS STUTTER-STEP ADAPTIVE STRONG-PRP GATE
; ============================================================================
;
; A fixed base list (2,3,5,7,...) for a strong-MR gate can always be
; defeated by an Arnault-style construction targeting exactly that list --
; that's what 3215031751 (smallest strong pseudoprime to bases 2,3,5,7) is.
; The iris probe's base scales with the candidate itself (phi-Weyl
; equidistribution, three-distance theorem -- same golden-angle property
; behind phyllotactic packing), so there is no small fixed target for a
; construction to aim at. Still a member of the strong-MR family though
; (shares the multiplicative-order failure surface) -- this hardens and
; cheapens the MR side of the oracle, it does not replace an independent-
; family closer.
;
; Cost profile: resolves the overwhelming majority of composites in ONE
; modpow_u64 call (measured avg 1.033 probes across 151 known-hard natural
; composites), so composites are usually rejected far more cheaply than
; the existing Z[phi] Frobenius gate (zpow_mod, 2 zmul_mod calls per bit
; of P) -- and never touch that gate at all once iris rejects them.
;
; ============================================================================

; ---------------------------------------------------------------------------
; modpow_u64: RDI^RSI mod RDX -> RAX   (plain scalar modexp, square-and-
; multiply). Distinct from zpow_mod: that one exponentiates in the Z[phi]
; RING (pairs, via zmul_mod). This is ordinary scalar modular
; exponentiation, needed because the strong-MR test operates on plain
; integers mod P, not Z[phi] elements. Reuses the existing modmul64.
; ---------------------------------------------------------------------------
modpow_u64:
    push r8
    push r9
    push r10
    push r11
    mov  r9, rdx           ; modulus
    mov  r8, rsi           ; exponent
    mov  r10, rdi          ; base (mod m, caller ensures < m)
    mov  r11, 1            ; result

.mp_loop:
    test r8, r8
    jz   .mp_done
    test r8, 1
    jz   .mp_sq

    mov  rax, r11
    mov  rbx, r10
    mov  rcx, r9
    call modmul64
    mov  r11, rax

.mp_sq:
    mov  rax, r10
    mov  rbx, r10
    mov  rcx, r9
    call modmul64
    mov  r10, rax

    shr  r8, 1
    jmp  .mp_loop

.mp_done:
    mov  rax, r11
    pop  r11
    pop  r10
    pop  r9
    pop  r8
    ret

; ---------------------------------------------------------------------------
; strong_sprp: strong probable-prime test, single base.
; in: RDI = n, RSI = a (2 <= a <= n-2)
; out: RAX = 1 (a is a witness for "probably prime" -- FOOLED, or n really
;             is prime), 0 (a proves n composite)
; ---------------------------------------------------------------------------
strong_sprp:
    push rbx
    push rcx
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13

    mov r12, rdi            ; n
    mov r13, rsi            ; a

    cmp r13, 1
    jbe .pass
    mov rax, r12
    dec rax
    cmp r13, rax
    je .pass

    mov rax, r12
    dec rax
    xor r8, r8               ; r
    mov r9, rax               ; d
.factor_loop:
    test r9, 1
    jnz .factor_done
    shr r9, 1
    inc r8
    jmp .factor_loop
.factor_done:

    mov rdi, r13
    mov rsi, r9
    mov rdx, r12
    call modpow_u64
    mov r10, rax             ; x = a^d mod n

    cmp r10, 1
    je .pass
    mov rax, r12
    dec rax
    cmp r10, rax
    je .pass

    mov r11, r8
    dec r11
    test r11, r11
    jz .fail

.sq_loop:
    mov rax, r10
    mov rbx, r10
    mov rcx, r12
    call modmul64
    mov r10, rax
    mov rax, r12
    dec rax
    cmp r10, rax
    je .pass
    dec r11
    jnz .sq_loop

.fail:
    xor rax, rax
    jmp .ssp_done
.pass:
    mov rax, 1
.ssp_done:
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rcx
    pop rbx
    ret

; ---------------------------------------------------------------------------
; iris_base: k-th phi-Weyl probe base for candidate n.
; in: RDI = n, RSI = k
; out: RAX = base, in [2, n-2]
; base_k(n) = 2 + high64( ((k*GOLDEN64) mod 2^64) * (n-3) )
; ---------------------------------------------------------------------------
iris_base:
    push rbx
    push rdx
    push r8

    mov rax, rsi
    mov rbx, GOLDEN64
    mul rbx
    mov r8, rax

    mov rax, rdi
    sub rax, 3
    mov rbx, rax
    mov rax, r8
    mul rbx
    mov rax, rdx
    add rax, 2

    pop r8
    pop rdx
    pop rbx
    ret

; ---------------------------------------------------------------------------
; iris_prp_step: stutter-step through phi-spaced bases for [PRIME_CANDIDATE].
; ---------------------------------------------------------------------------
iris_prp_step:
    push rdi
    push rsi
    push rax
    push r8
    push r9

    ; Even candidates > 2 are composite by construction (PRIME_CANDIDATE
    ; is guarded >=5 by the caller). Reject immediately, no probe needed --
    ; also sidesteps strong_sprp's r=0 case (n-1 odd), which this substrate
    ; never needs to handle since it's never asked to.
    mov  rax, [PRIME_CANDIDATE]
    test rax, 1
    jnz  .odd_candidate
    mov  qword [IRIS_RESULT], 0
    mov  qword [IRIS_PROBES_USED], 0
    jmp  .done

.odd_candidate:
    xor r8, r8

.probe_loop:
    inc r8

    mov rdi, [PRIME_CANDIDATE]
    mov rsi, r8
    call iris_base
    mov r9, rax
    mov [IRIS_BASE_USED], r9

    mov rdi, [PRIME_CANDIDATE]
    mov rsi, r9
    call strong_sprp
    test rax, rax
    jz .composite

    cmp r8, IRIS_MAX_PROBES
    jl .probe_loop

    mov qword [IRIS_RESULT], 1
    mov [IRIS_PROBES_USED], r8
    jmp .done

.composite:
    mov qword [IRIS_RESULT], 0
    mov [IRIS_PROBES_USED], r8

.done:
    pop r9
    pop r8
    pop rax
    pop rsi
    pop rdi
    ret


; ============================================================================
; SERIAL OUTPUT HELPERS (COM1, 115200 8N1)
; ============================================================================

; serial_wait: wait for TX empty
serial_wait:
    push rax
    push rdx
.w:
    mov  dx, 0x3FD
    in   al, dx
    and  al, 0x20
    jz   .w
    pop  rdx
    pop  rax
    ret

; serial_putchar: send AL via COM1
serial_putchar:
    push rdx
    push rax
    mov  ah, al
    call serial_wait
    mov  dx, 0x3F8
    mov  al, ah
    out  dx, al
    pop  rax
    pop  rdx
    ret

serial_putchar_L:
    mov  al, 'L'
    jmp  serial_putchar

serial_putchar_V:
    mov  al, 'V'
    jmp  serial_putchar

; serial_put_hex64: print RAX as 16 hex digits + newline to COM1
serial_put_hex64:
    push rcx
    push rax
    push rbx
    mov  rbx, rax
    mov  rcx, 16
.hex:
    mov  rax, rbx
    shr  rax, 60
    and  eax, 0x0F
    movzx eax, byte [hex_digits + PHYS_ADJ + rax]
    call serial_putchar
    shl  rbx, 4
    loop .hex
    ; newline
    mov  al, 0x0D
    call serial_putchar
    mov  al, 0x0A
    call serial_putchar
    pop  rbx
    pop  rax
    pop  rcx
    ret

; serial_puts: RSI = physical address of null-terminated string
serial_puts:
    push rsi
    push rax
.next:
    lodsb
    test al, al
    jz   .done
    call serial_putchar
    jmp  .next
.done:
    pop  rax
    pop  rsi
    ret

; =============================================================================
; AP TRAMPOLINE (16-bit, copied to 0x8000)
; =============================================================================

BITS 16

ap_trampoline:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00

    ; Use embedded GDT (don't rely on boot sector memory at 0x7C00)
    lgdt [cs:ap_gdt_ptr - ap_trampoline]

    mov eax, cr0
    or  eax, 1
    mov cr0, eax

    jmp dword 0x08:AP_PM_PHYS

; Embedded GDT for AP (at known offset from ap_trampoline start)
align 8
ap_gdt_base:
    dq 0x0000000000000000
    dq 0x00CF9A000000FFFF  ; 0x08: 32-bit code
    dq 0x00CF92000000FFFF  ; 0x10: data
    dq 0x00AF9A000000FFFF  ; 0x18: 64-bit code
ap_gdt_end:
ap_gdt_ptr:
    dw ap_gdt_end - ap_gdt_base - 1
    dd AP_TRAMP_PHYS + (ap_gdt_base - ap_trampoline)

; AP: 32-bit pmode
BITS 32

ap_pm_entry:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov esp, 0x00078000     ; temporary stack for AP

    ; PAE
    mov eax, cr4
    or  eax, (1 << 5)
    mov cr4, eax

    ; EFER.LME
    mov ecx, 0xC0000080
    rdmsr
    or  eax, (1 << 8)
    wrmsr

    ; Use BSP page tables
    mov eax, PML4_PHYS
    mov cr3, eax

    ; Paging on
    mov eax, cr0
    or  eax, (1 << 31)
    mov cr0, eax

    jmp dword 0x18:AP_LM_PHYS

; AP: 64-bit entry
BITS 64

ap_lm_entry:
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov ss, ax

    ; APIC ID -> stack assignment
    mov eax, 1
    cpuid
    shr ebx, 24
    and ebx, 0xFF

    ; Private stack: AP_STACK_BASE + apic_id * AP_STACK_STRIDE
    mov rcx, AP_STACK_BASE
    mov rdx, rbx
    imul rdx, AP_STACK_STRIDE
    add  rcx, rdx
    mov  rsp, rcx

    ; Dispatch by APIC ID
    cmp ebx, 1
    je  .water
    cmp ebx, 2
    je  .earth
    cmp ebx, 3
    je  .wind
    jmp .dead

.water: call role_water
        jmp .dead
.earth: call role_earth
        jmp .dead
.wind:  call role_wind

.dead:
    cli
.halt:
    hlt
    jmp .halt

align 2
ap_trampoline_end:

; =============================================================================
; VGA INITIALIZATION
; =============================================================================

BITS 64

vga_init:
    ; Clear screen (2000 cells, attribute 0x07 = white on black)
    mov  rdi, VGA_BASE
    mov  ax,  0x0720
    mov  rcx, 2000
    rep  stosw

    ; Row 0: title
    mov  rdi, VGA_BASE + VGA_ROW * 0
    mov  rsi, str_title + PHYS_ADJ
    mov  bl,  0x0F          ; bright white
    call vga_puts_color

    ; Row 1: topology
    mov  rdi, VGA_BASE + VGA_ROW * 1
    mov  rsi, str_topology + PHYS_ADJ
    mov  bl,  0x0B          ; cyan
    call vga_puts_color

    ; Row 2: STATE header
    mov  rdi, VGA_BASE + VGA_ROW * 2
    mov  rsi, str_state + PHYS_ADJ
    mov  bl,  0x07
    call vga_puts_color

    ; Row 3: FIRE header
    mov  rdi, VGA_BASE + VGA_ROW * 3
    mov  rsi, str_fire + PHYS_ADJ
    mov  bl,  0x0C          ; bright red
    call vga_puts_color

    ; Row 4: WATER header
    mov  rdi, VGA_BASE + VGA_ROW * 4
    mov  rsi, str_water + PHYS_ADJ
    mov  bl,  0x09          ; bright blue
    call vga_puts_color

    ; Row 5: EARTH header
    mov  rdi, VGA_BASE + VGA_ROW * 5
    mov  rsi, str_earth + PHYS_ADJ
    mov  bl,  0x0A          ; bright green
    call vga_puts_color

    ; Row 6: WIND header
    mov  rdi, VGA_BASE + VGA_ROW * 6
    mov  rsi, str_wind + PHYS_ADJ
    mov  bl,  0x0E          ; yellow
    call vga_puts_color

    ; Row 7: ORACLE header
    mov  rdi, VGA_BASE + VGA_ROW * 7
    mov  rsi, str_oracle + PHYS_ADJ
    mov  bl,  0x0D          ; bright magenta
    call vga_puts_color

    ; Row 8: YIN header
    mov  rdi, VGA_BASE + VGA_ROW * 8
    mov  rsi, str_yin + PHYS_ADJ
    mov  bl,  0x07
    call vga_puts_color

    ; Row 9: PRIME oracle header
    mov  rdi, VGA_BASE + VGA_ROW * 9
    mov  rsi, str_prime + PHYS_ADJ
    mov  bl,  0x0E          ; yellow
    call vga_puts_color

    ret

; =============================================================================
; VGA UPDATE (called every PRINT_EVERY iterations)
; =============================================================================

; Column positions for values (each hex64 = 16 chars + 1 space = 17 cols)
; Labels end around col 10, values start at col 10 (byte offset = col*2)

vga_update:
    ; ── Row 2: STATE K= A= B= ──
    mov rdi, VGA_BASE + VGA_ROW * 2 + 10*2
    mov rax, [STATE_K]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 2 + 28*2
    mov rax, [STATE_A]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 2 + 46*2
    mov rax, [STATE_B]
    call vga_hex64

    ; ── Row 3: FIRE A= B= ──
    mov rdi, VGA_BASE + VGA_ROW * 3 + 10*2
    mov rax, [FIRE_A]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 3 + 28*2
    mov rax, [FIRE_B]
    call vga_hex64

    ; ── Row 4: WATER A= B= ──
    mov rdi, VGA_BASE + VGA_ROW * 4 + 10*2
    mov rax, [WATER_A]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 4 + 28*2
    mov rax, [WATER_B]
    call vga_hex64

    ; ── Row 5: EARTH N= NF= DELTA= ──
    mov rdi, VGA_BASE + VGA_ROW * 5 + 10*2
    mov rax, [EARTH_N]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 5 + 28*2
    mov rax, [EARTH_N_FIRE]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 5 + 46*2
    mov rax, [EARTH_DELTA]
    call vga_hex64

    ; ── Row 6: WIND RA= RB= FIX= ──
    mov rdi, VGA_BASE + VGA_ROW * 6 + 10*2
    mov rax, [WIND_RES_A]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 6 + 28*2
    mov rax, [WIND_RES_B]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 6 + 46*2
    mov rax, [WIND_FIX]
    call vga_hex64

    ; ── Row 7: ORACLE= STRATEGY= ──
    mov rdi, VGA_BASE + VGA_ROW * 7 + 10*2
    mov rax, [ORACLE]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 7 + 28*2
    mov rax, [STRATEGY]
    call vga_hex64
    ; Strategy name
    mov rdi, VGA_BASE + VGA_ROW * 7 + 46*2
    mov rax, [STRATEGY]
    call vga_strategy_name

    ; ── Row 8: YIN PH DEPTH ──
    mov rdi, VGA_BASE + VGA_ROW * 8 + 10*2
    mov rax, [YIN]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 8 + 28*2
    mov rax, [PHASE]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 8 + 46*2
    mov rax, [DEPTH]
    call vga_hex64

    ; ── Row 9: PRIME candidate / found-count / last-found ──
    mov rdi, VGA_BASE + VGA_ROW * 9 + 10*2
    mov rax, [PRIME_CANDIDATE]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 9 + 28*2
    mov rax, [PRIME_FOUND_COUNT]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 9 + 46*2
    mov rax, [PRIME_LAST_FOUND]
    call vga_hex64

    ; ── Row 10: closed-loop control counters ──
    mov rdi, VGA_BASE + VGA_ROW * 10
    mov rsi, str_control + PHYS_ADJ
    mov bl, 0x0D
    call vga_puts_color
    mov rdi, VGA_BASE + VGA_ROW * 10 + 10*2
    mov rax, [CONTROL_HOLDS]
    call vga_hex64
    mov rdi, VGA_BASE + VGA_ROW * 10 + 28*2
    mov rax, [CONTROL_REDIRECTS]
    call vga_hex64

    ; ── COM1 serial: send terse status line ──
    ; Format: "D=xxxx O=xx S=x\r\n"
    call serial_putchar_V   ; 'V' = VGA update marker
    mov  rsi, str_serial_depth + PHYS_ADJ
    call serial_puts
    mov  rax, [DEPTH]
    call serial_put_hex64
    mov  rsi, str_serial_oracle + PHYS_ADJ
    call serial_puts
    mov  rax, [ORACLE]
    call serial_put_hex64

    ret

; ============================================================================
; vga_strategy_name: print strategy name at RDI
; Input: rax = STRATEGY index
; ============================================================================

vga_strategy_name:
    cmp rax, STRATEGY_FLOWING
    je  .flowing
    cmp rax, STRATEGY_NONACTION
    je  .nonaction
    cmp rax, STRATEGY_REDIRECT
    je  .redirect
    cmp rax, STRATEGY_CONVERGE
    je  .converge
    cmp rax, STRATEGY_CRITICAL
    je  .critical
    mov rsi, str_strat_unknown + PHYS_ADJ
    jmp .print
.flowing:
    mov rsi, str_strat_flowing + PHYS_ADJ
    jmp .print
.nonaction:
    mov rsi, str_strat_nonaction + PHYS_ADJ
    jmp .print
.redirect:
    mov rsi, str_strat_redirect + PHYS_ADJ
    jmp .print
.converge:
    mov rsi, str_strat_converge + PHYS_ADJ
    jmp .print
.critical:
    mov rsi, str_strat_critical + PHYS_ADJ
.print:
    mov bl, 0x0D
    jmp vga_puts_color     ; tail call

; =============================================================================
; VGA HELPERS
; =============================================================================

; vga_puts_color: RDI=dest, RSI=string, BL=attribute
vga_puts_color:
.next:
    lodsb
    test al, al
    jz   .done
    mov  [rdi], al
    mov  [rdi + 1], bl
    add  rdi, 2
    jmp  .next
.done:
    ret

; vga_hex64: RDI=dest, RAX=value, writes 16 hex digits
vga_hex64:
    push rbx
    push rcx
    push rdx
    push rdi
    push rax
    mov  rcx, 16
    mov  rbx, rdi

.hloop:
    mov  rdx, rax
    shr  rdx, 60
    and  edx, 0x0F
    movzx edx, byte [hex_digits + PHYS_ADJ + rdx]
    mov  [rbx], dl
    mov  byte [rbx + 1], 0x07
    add  rbx, 2
    shl  rax, 4
    loop .hloop

    pop  rax
    pop  rdi
    pop  rdx
    pop  rcx
    pop  rbx
    ret

; =============================================================================
; STRINGS
; =============================================================================

str_title:
    db "HDGL Z[phi] WU-WEI SUBSTRATE  FIRE/WATER/EARTH/WIND",0
str_topology:
    db "CPU0=FIRE  CPU1=WATER  CPU2=EARTH  CPU3=WIND",0
str_state:
    db "STATE  K=                  A=                  B=",0
str_fire:
    db "FIRE   A=                  B=",0
str_water:
    db "WATER  A=                  B=",0
str_earth:
    db "EARTH  N=                  NF=                 DELTA=",0
str_wind:
    db "WIND   RA=                 RB=                 FIX=",0
str_oracle:
    db "ORACLE=                    STRATEGY=",0
str_yin:
    db "YIN    S=                  PH=                 DEPTH=",0

str_prime:
    db "PRIME  P=                  FOUND=              LAST=",0
str_control:
    db "CTRL   HOLDS=               REDIRECTS=",0

str_strat_flowing:  db "FLOWING RIVER",0
str_strat_nonaction: db "NON-ACTION   ",0
str_strat_redirect: db "REDIRECT     ",0
str_strat_converge: db "CONVERGENCE  ",0
str_strat_critical: db "!! CRITICAL !!",0
str_strat_unknown:  db "UNKNOWN      ",0

hex_digits:
    db "0123456789ABCDEF"

str_serial_depth:  db "DEPTH=",0
str_serial_oracle: db "ORACLE=",0

; =============================================================================
; PHYSICAL ADDRESS CONSTANTS
; =============================================================================
;
; All labels are relative to ORG 0x7C00.
; Physical address of a label L in payload = 0x10000 + (L - boot_start) - 512
; because:
;   - payload loads at physical 0x10000
;   - boot_start = 0x7C00
;   - sector 1 (boot sector) = 512 bytes, payload starts at file offset 512
;   - So physical(L) = 0x10000 + (L - 0x7C00) - 512
;                    = 0x10000 + L - 0x7E00
;                    = L + (0x10000 - 0x7E00)
;                    = L + 0x8200
;
; Verify: protected_entry label value = 0x7C00 + 512 = 0x7E00
;         physical = 0x7E00 + 0x8200 = 0x10200. Correct!
;
; For AP trampoline (copied to 0x8000):
;   ap_trampoline label = 0x7C00 + (its file offset)
;   AP_PM_PHYS = 0x8000 + (ap_pm_entry - ap_trampoline)
;   AP_LM_PHYS = 0x8000 + (ap_lm_entry - ap_trampoline)

PROTECTED_ENTRY_PHYS equ protected_entry
LONG_MODE_ENTRY_PHYS equ long_mode_entry
AP_PM_PHYS           equ AP_TRAMP_PHYS   + (ap_pm_entry  - ap_trampoline)
AP_LM_PHYS           equ AP_TRAMP_PHYS   + (ap_lm_entry  - ap_trampoline)

; =============================================================================
; IMAGE PADDING TO EXACTLY 80 SECTORS
; =============================================================================

times (IMAGE_SECTORS * 512) - ($ - $$) db 0