Formal Methods

From Proofs to Code: Mathematical Transcription in C

How mathematical contracts become deterministic implementations

Published
January 12, 2026 23:25
Reading Time
11 min
Mathematical transcription process from proofs to C code

The Gap Between Mathematics and Implementation

Software engineers learn early that implementation differs from specification. Requirements written in natural language get interpreted, approximated, and sometimes misunderstood during coding. Even when requirements are precise, the translation to code introduces opportunities for divergence.

In safety-critical systems, this gap represents risk. When software controls flight surfaces, medical devices, or autonomous vehicles, the question “does the code match the specification?” isn’t academic—it’s a certification requirement. Standards like DO-178C, ISO 26262, and IEC 62304 all demand evidence that implementations correctly realize their requirements.

One approach to closing this gap is mathematical transcription: the practice of expressing requirements as formal proofs, then translating those proofs mechanically into code. When done properly, the code becomes a direct representation of proven mathematical properties rather than an interpretation of natural language requirements.

What Mathematical Transcription Is Not

Before describing the approach, it helps to clarify what we’re not discussing.

This is not automated code generation from formal specifications, though such tools exist. Systems like Coq’s extraction facility or SPARK’s proof-carrying code can generate implementations from high-level specifications. These are powerful approaches, particularly for purely functional code or systems where the target language matches the specification language well.

This is also not traditional “design by contract” in the sense Eiffel popularized, where preconditions and postconditions are runtime checks added to existing code. While contracts are central to our approach, we’re describing something more fundamental: using mathematical proofs to determine what the code should be, not just how to verify it afterward.

What we’re describing is a methodology where mathematical analysis precedes and directs implementation. The proof comes first, the struct design follows from the proof, and the code follows from the struct design. Each step is a mechanical translation of the previous one.

Stage 1: Mathematical Analysis

The process begins with a question: what are we trying to accomplish, stated mathematically?

Consider a simple monitoring function that should trigger an alert when a value exceeds a threshold. In natural language: “Monitor a value and alert when it crosses the threshold.” Simple enough, but this leaves critical questions unanswered:

What is the domain of valid values? What happens at boundary conditions? What if the value equals the threshold exactly? What are the timing requirements? Can the value change during evaluation?

Mathematical analysis makes these implicit assumptions explicit:

Let v ∈ ℕ be the monitored value, where 0 ≤ v < MAX
Let t ∈ ℕ be the threshold, where 0 ≤ t < MAX
Define alert condition: v ≥ t

Properties to maintain:
1. Values remain bounded: ∀ operations, 0 ≤ v < MAX
2. Threshold remains stable: t does not change during evaluation
3. Alert logic is total: defined for all valid (v,t) pairs
4. Evaluation is deterministic: same inputs produce same outputs

This formulation forces us to think about bounds, totality, and determinism before writing any code. It also reveals what we need to represent in our data structures.

Stage 2: Struct Design from Proofs

Once we have mathematical properties defined, we design data structures that can maintain those properties. Every field in the struct should trace directly to a requirement in the proof.

For our monitoring example, the proof referenced three mathematical objects: the value being monitored, the threshold, and the alert state. Our struct contains exactly these elements:

typedef struct {
    uint32_t value;      /* v: monitored value, [0, MAX) */
    uint32_t threshold;  /* t: alert threshold, [0, MAX) */
    uint8_t  triggered;  /* alert state: 0 or 1 */
} monitor_t;

The choice of uint32_t for value and threshold enforces the bounded nature from our proof—these cannot be negative or exceed the type’s maximum. The triggered field uses uint8_t rather than bool for deterministic representation, avoiding potential ambiguity about how boolean values are stored.

This struct design is not arbitrary. Each field exists because the mathematical proof requires it. There are no “convenience” fields added for implementation reasons, and no proof requirements left unrepresented.

Stage 3: Implementation as Transcription

With the mathematical properties formalized and the struct designed to represent them, implementation becomes mechanical transcription. The code directly expresses the logic from the proof:

void monitor_check(monitor_t* m) {
    /* Precondition: m is valid pointer to initialized monitor */
    assert(m != NULL);
    assert(m->value < MONITOR_MAX);
    assert(m->threshold < MONITOR_MAX);
    
    /* Core logic: direct transcription of alert condition */
    if (m->value >= m->threshold) {
        m->triggered = 1;
    }
    
    /* Postcondition: alert state correctly reflects comparison */
    assert((m->value >= m->threshold) == (m->triggered == 1));
}

The assertions are not defensive programming—they’re the mechanized form of our mathematical requirements. The preconditions ensure inputs satisfy our proof’s assumptions. The postcondition verifies the implementation matches the proven logic.

The core logic (if (m->value >= m->threshold)) is the direct C translation of our mathematical alert condition (v ≥ t). There’s no interpretation, no approximation—just transcription.

Why This Works for Safety-Critical Systems

This approach addresses specific challenges in safety-critical software development.

Verification Tractability

When code is a direct transcription of proofs, verification becomes checking transcription accuracy rather than reasoning about complex implementation logic. Reviewers can compare the code against the proof term by term, confirming that each mathematical requirement has a corresponding code representation.

This makes manual review more effective and automated verification more tractable. Tools like Frama-C, SPARK, or TLA+ can verify that implementations satisfy their specifications because the gap between specification and implementation is minimal.

Certification Evidence

Safety standards require evidence that implementations correctly realize requirements. When the implementation process is “prove, then transcribe,” the mathematical proof itself serves as certification evidence.

DO-178C Level A, for instance, requires that safety requirements be traceable to design and implementation. When struct fields trace directly to proof elements, and code traces directly to proof logic, this traceability is inherent rather than documented after the fact.

Maintenance Clarity

Software changes over time. When modifications are needed, the mathematical proof provides the definitive specification of what the code is supposed to do. Engineers making changes can first update the proof to reflect new requirements, then update the struct and code to match.

This inverts the typical maintenance process where engineers read existing code, infer its intent, make modifications, and hope they haven’t broken invariants. With proof-first development, invariants are explicit and breaking them requires conscious proof modifications.

Properties Preserved Through Transcription

The transcription process is designed to preserve specific properties from proof to implementation:

Design Property: Bounds Preservation

Type choices in the struct design enforce the bounds defined in the mathematical proof, such that values cannot exist outside their proven domains.

By choosing uint32_t with explicit bounds checking, we guarantee that values cannot violate the mathematical assumptions. The type system participates in maintaining proof properties.

Totality

Mathematical proofs operate on total functions—functions defined for all inputs in their domain. Partial functions, which are undefined for some inputs, introduce complexity and risk.

The transcription process preserves totality by ensuring all code paths are defined. The if statement in our example handles both cases: value below threshold (no action) and value at or above threshold (set triggered flag). There are no missing cases, no undefined behavior.

Determinism

Proofs assume deterministic evaluation: given identical inputs, the function produces identical outputs. The transcription preserves this by avoiding sources of nondeterminism.

No dependency on system state beyond the monitored struct. No reliance on timing, no global variables, no hidden state. The function is pure: its behavior depends only on its inputs and affects only its outputs.

Practical Example: State Machine Transitions

The approach scales beyond simple monitors to more complex systems. Consider a state machine with proven transition conditions:

States: S = {IDLE, ACTIVE, ERROR}
Transitions proven valid:
  IDLE → ACTIVE    when condition C1 holds
  ACTIVE → IDLE    when condition C2 holds
  ACTIVE → ERROR   when condition C3 holds
  ERROR → IDLE     when reset() called

The struct represents this exactly:

typedef enum {
    STATE_IDLE,
    STATE_ACTIVE,
    STATE_ERROR
} state_t;

typedef struct {
    state_t  current_state;
    uint32_t tick_count;      /* For C1, C2, C3 evaluation */
    uint8_t  error_detected;  /* For C3 */
} fsm_t;

And transitions transcribe the proven logic:

void fsm_transition(fsm_t* fsm) {
    assert(fsm != NULL);
    
    switch (fsm->current_state) {
    case STATE_IDLE:
        if (condition_c1(fsm)) {
            fsm->current_state = STATE_ACTIVE;
        }
        break;
        
    case STATE_ACTIVE:
        if (condition_c3(fsm)) {
            fsm->current_state = STATE_ERROR;
        } else if (condition_c2(fsm)) {
            fsm->current_state = STATE_IDLE;
        }
        break;
        
    case STATE_ERROR:
        /* Only reset() can exit ERROR state */
        break;
    }
}

The code structure mirrors the mathematical transition rules. Each proven transition has exactly one corresponding code path. No transitions occur that weren’t proven valid.

Testing as Proof Validation

When code is transcribed from proofs, testing serves a different purpose than in conventional development. Traditional testing explores implementation behavior to find defects. Proof-based testing validates that transcription was performed correctly.

Test cases come directly from proof properties:

/* Test: boundary condition from proof */
void test_threshold_boundary(void) {
    monitor_t m = {
        .value = 100,
        .threshold = 100,
        .triggered = 0
    };
    
    monitor_check(&m);
    assert(m.triggered == 1);  /* v ≥ t should trigger */
}

/* Test: totality - below threshold case */
void test_below_threshold(void) {
    monitor_t m = {
        .value = 99,
        .threshold = 100,
        .triggered = 0
    };
    
    monitor_check(&m);
    assert(m.triggered == 0);  /* v < t should not trigger */
}

These aren’t arbitrary test cases chosen to exercise code paths. They’re mechanized checks of the mathematical properties we proved. If a test fails, either the transcription was incorrect or the proof was incomplete.

Where Formal Verification Tools Enter

Mathematical transcription can proceed without automated tools—it’s fundamentally a human activity of careful translation. However, formal verification tools can validate that transcription was performed correctly.

Tools like Frama-C with its WP plugin allow expressing the mathematical proof as ACSL annotations, then proving the C implementation satisfies them. The process becomes:

  1. Write mathematical proof
  2. Design struct from proof
  3. Transcribe to C implementation
  4. Express proof as ACSL annotations
  5. Use WP to verify implementation matches annotations

The tool doesn’t replace the human insight of creating the proof or designing the struct. It validates that the mechanical transcription step was performed correctly.

Similarly, SPARK for Ada provides a framework where the language itself enforces many proof properties through its type system and runtime checks. The transcription process adapts to leverage language capabilities—Ada’s strong typing preserves more proof properties automatically than C requires us to enforce manually.

Real-World Application Patterns

Organizations using proof-based transcription typically follow certain patterns:

Safety Kernels

In safety-critical systems, a small “safety kernel” is often proven formally while surrounding functionality uses conventional development. The kernel handles critical functions like mode management, watchdog timing, or health monitoring. These components are small enough that complete mathematical treatment is tractable.

The c-from-scratch educational course demonstrates this pattern by building a heartbeat monitor using the prove-first methodology. The resulting code is approximately 200 lines, small enough for complete formal treatment.

Incremental Adoption

Rather than requiring all code to follow proof-based transcription, teams often start with the most critical components. A voting algorithm, a critical state machine, or a safety interlock gets the full mathematical treatment. Less critical code uses conventional practices.

This allows teams to gain experience with the methodology while limiting the investment. As the approach proves valuable, it expands to additional components.

Certification-Driven Development

For systems requiring DO-178C Level A or similar certification, proof-based transcription provides clear traceability from requirements through verification. Each certification deliverable maps to a specific artifact: requirements become mathematical proofs, design becomes struct definitions, implementation becomes transcribed code, and verification becomes proof validation.

Limitations and Practical Considerations

Mathematical transcription has clear benefits for safety-critical systems, but also limitations.

The approach works best for systems where behavior can be characterized mathematically. Control logic, state machines, monitoring functions, and algorithms map well to mathematical specification. Interactions with hardware, timing-dependent behavior, or systems with emergent properties are more challenging.

The methodology requires engineers comfortable with mathematical reasoning. Not every programming problem needs proof-first development, and not every engineer finds the approach natural. Organizations adopting this approach must invest in training and accept that some problems resist mathematical characterization.

Finally, proof-based transcription doesn’t eliminate all defects. The proof might be incorrect, the transcription might introduce errors, or the requirements themselves might be wrong. What it does provide is a systematic approach to reducing the gap between specification and implementation.

Integration with Deterministic Platforms

Proof-based transcription naturally aligns with deterministic execution platforms. When proofs assume deterministic behavior, implementing on platforms that guarantee determinism preserves proof properties through execution.

The Murray Deterministic Computing Platform (MDCP) provides execution semantics that match the assumptions common in mathematical proofs: bounded resources, deterministic scheduling, and reproducible behavior. Code transcribed from proofs runs on MDCP with the same properties the proofs assume.

This integration matters for certification. When an implementation is proven correct under certain execution assumptions, those assumptions must hold in the deployed system. Deterministic platforms make those assumptions explicit and verifiable.

Conclusion

Mathematical transcription offers a systematic approach to closing the gap between specification and implementation in safety-critical systems. By proving properties first, designing structs to represent those properties, and transcribing the proven logic to code, we create implementations that are traceable to their requirements by construction.

The approach is not suitable for all software. It requires mathematical characterization of requirements, engineers comfortable with formal reasoning, and acceptance that upfront proof effort exceeds initial coding time. For systems where certification matters, where errors have severe consequences, or where long-term maintainability justifies upfront investment, the benefits can justify the cost.

The key insight is treating implementation as a mechanical process rather than a creative one. Once the mathematics is worked out, the code practically writes itself. What remains is careful transcription and systematic validation that the transcription preserves the proven properties.

As with any architectural approach, suitability depends on system requirements, risk classification, and regulatory context. For organizations developing safety-critical software under standards like DO-178C or ISO 26262, proof-based transcription provides a path to the evidence and traceability those standards require.

About the Author

William Murray is a Regenerative Systems Architect with 30 years of UNIX infrastructure experience, specializing in deterministic computing for safety-critical systems. Based in the Scottish Highlands, he operates SpeyTech and maintains several open-source projects including C-Sentinel and c-from-scratch.

Discuss This Perspective

For technical discussions or acquisition inquiries, contact SpeyTech directly.

Get in touch
← Back to Insights