Skip to main content

oxilean_std/program_logics/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use std::collections::{BTreeMap, HashMap, HashSet};
7use std::fmt;
8
9/// A labeled transition system for refinement checking.
10#[derive(Debug, Clone)]
11pub struct LTS<S: Clone + Eq + std::hash::Hash> {
12    /// States.
13    pub states: HashSet<S>,
14    /// Initial state.
15    pub initial: S,
16    /// Transition relation: state → (label, successor state).
17    pub transitions: HashMap<S, Vec<(String, S)>>,
18}
19impl<S: Clone + Eq + std::hash::Hash + std::fmt::Debug> LTS<S> {
20    /// Create a new LTS.
21    pub fn new(initial: S) -> Self {
22        let mut states = HashSet::new();
23        states.insert(initial.clone());
24        LTS {
25            states,
26            initial,
27            transitions: HashMap::new(),
28        }
29    }
30    /// Add a transition.
31    pub fn add_transition(&mut self, from: S, label: impl Into<String>, to: S) {
32        self.states.insert(from.clone());
33        self.states.insert(to.clone());
34        self.transitions
35            .entry(from)
36            .or_default()
37            .push((label.into(), to));
38    }
39    /// Compute the set of states reachable from the initial state.
40    pub fn reachable_states(&self) -> HashSet<S> {
41        let mut visited = HashSet::new();
42        let mut queue = vec![self.initial.clone()];
43        while let Some(s) = queue.pop() {
44            if visited.insert(s.clone()) {
45                if let Some(succs) = self.transitions.get(&s) {
46                    for (_, t) in succs {
47                        queue.push(t.clone());
48                    }
49                }
50            }
51        }
52        visited
53    }
54    /// Check whether every label produced by this LTS is in the given allowed set.
55    pub fn labels_subset_of(&self, allowed: &HashSet<String>) -> bool {
56        for succs in self.transitions.values() {
57            for (lbl, _) in succs {
58                if !allowed.contains(lbl) {
59                    return false;
60                }
61            }
62        }
63        true
64    }
65}
66/// Hoare logic system with proof-theoretic API.
67pub struct HoareLogic {
68    pub axiom_names: Vec<String>,
69}
70impl HoareLogic {
71    pub fn new() -> Self {
72        Self {
73            axiom_names: vec![
74                "SkipRule".to_string(),
75                "AssignRule".to_string(),
76                "SeqRule".to_string(),
77                "WhileRule".to_string(),
78                "ConsequenceRule".to_string(),
79            ],
80        }
81    }
82    /// Return all Hoare logic axioms as (name, description) pairs.
83    pub fn axioms(&self) -> Vec<(&str, &str)> {
84        vec![
85            ("Skip", "{P} skip {P}"),
86            ("Assign", "{P[e/x]} x:=e {P}"),
87            ("Seq", "{P}C1{Q},{Q}C2{R} => {P}C1;C2{R}"),
88            ("While", "{I∧b}C{I} => {I} while b {I∧¬b}"),
89            ("Consequence", "{P'}⊢{P} {P}C{Q} {Q}⊢{Q'} => {P'}C{Q'}"),
90        ]
91    }
92    /// Return a description of the soundness proof for Hoare logic.
93    pub fn soundness_proof(&self) -> String {
94        "Soundness by induction on derivation height: \
95         each rule preserves the partial-correctness relation \
96         [[{P}C{Q}]] = ∀σ. P σ → ∀σ'. (C,σ)↓σ' → Q σ'."
97            .to_string()
98    }
99    /// Return a description of the completeness proof (Cook's theorem).
100    pub fn completeness_proof(&self) -> String {
101        "Completeness (Cook 1978): if [[{P}C{Q}]] then ⊢ {P}C{Q}. \
102         Uses the weakest liberal precondition wlp(C,Q) as the canonical invariant. \
103         Relative completeness: assumes an expressive assertion language."
104            .to_string()
105    }
106}
107/// A Hoare triple `{P} C {Q}`.
108#[derive(Debug, Clone)]
109pub struct HoareTriple {
110    /// Pre-condition.
111    pub pre: Assertion,
112    /// Program command (as a string).
113    pub command: String,
114    /// Post-condition.
115    pub post: Assertion,
116    /// Whether this is a total-correctness triple.
117    pub total: bool,
118}
119impl HoareTriple {
120    /// Create a partial-correctness Hoare triple.
121    pub fn partial(pre: Assertion, command: impl Into<String>, post: Assertion) -> Self {
122        HoareTriple {
123            pre,
124            command: command.into(),
125            post,
126            total: false,
127        }
128    }
129    /// Create a total-correctness Hoare triple.
130    pub fn total(pre: Assertion, command: impl Into<String>, post: Assertion) -> Self {
131        HoareTriple {
132            pre,
133            command: command.into(),
134            post,
135            total: true,
136        }
137    }
138    /// Return the display string `{P} C {Q}` or `[P] C [Q]`.
139    pub fn display(&self) -> String {
140        if self.total {
141            format!("[{}] {} [{}]", self.pre, self.command, self.post)
142        } else {
143            format!("{{{}}}\n  {}\n{{{}}}", self.pre, self.command, self.post)
144        }
145    }
146}
147/// A resource in the Excl CMRA: either an exclusive value or "invalid" (composed with itself).
148#[derive(Debug, Clone, PartialEq, Eq)]
149pub enum ExclResource<T: Clone + Eq> {
150    /// Exclusive ownership of `T`.
151    Excl(T),
152    /// Invalid (composition of two exclusive resources).
153    Invalid,
154}
155impl<T: Clone + Eq> ExclResource<T> {
156    /// Compose two resources.
157    pub fn compose(&self, _other: &Self) -> Self {
158        ExclResource::Invalid
159    }
160    /// Validity: `Excl(v)` is valid; `Invalid` is not.
161    pub fn is_valid(&self) -> bool {
162        matches!(self, ExclResource::Excl(_))
163    }
164    /// Core map (returns None for exclusive resources).
165    pub fn core(&self) -> Option<Self> {
166        None
167    }
168}
169#[allow(dead_code)]
170#[derive(Debug, Clone)]
171pub enum CSLActionModel {
172    OwnershipTransfer,
173    SharedInvariant,
174    FractionalPermission,
175    ViewShift,
176}
177#[allow(dead_code)]
178#[derive(Debug, Clone)]
179pub struct ProbabilisticHoareLogic {
180    pub logic_name: String,
181    pub pre_distribution: String,
182    pub command: String,
183    pub post_expectation: String,
184    pub expected_cost: Option<f64>,
185}
186#[allow(dead_code)]
187impl ProbabilisticHoareLogic {
188    pub fn phl(pre: &str, cmd: &str, post: &str) -> Self {
189        ProbabilisticHoareLogic {
190            logic_name: "PHL (probabilistic Hoare)".to_string(),
191            pre_distribution: pre.to_string(),
192            command: cmd.to_string(),
193            post_expectation: post.to_string(),
194            expected_cost: None,
195        }
196    }
197    pub fn expectation_transformer(&self) -> String {
198        format!(
199            "wp({}, {}) = pre expectation",
200            self.command, self.post_expectation
201        )
202    }
203    pub fn mciver_morgan_rule(&self) -> String {
204        format!(
205            "McIver-Morgan: [{}] → {} → [{}]",
206            self.pre_distribution, self.command, self.post_expectation
207        )
208    }
209    pub fn with_cost(mut self, cost: f64) -> Self {
210        self.expected_cost = Some(cost);
211        self
212    }
213}
214#[allow(dead_code)]
215#[derive(Debug, Clone)]
216pub struct ApproximateVerification {
217    pub epsilon: f64,
218    pub delta: f64,
219    pub verified_property: String,
220    pub confidence: f64,
221}
222#[allow(dead_code)]
223impl ApproximateVerification {
224    pub fn pac_verification(eps: f64, delta: f64, prop: &str) -> Self {
225        ApproximateVerification {
226            epsilon: eps,
227            delta,
228            verified_property: prop.to_string(),
229            confidence: 1.0 - delta,
230        }
231    }
232    pub fn sample_complexity(&self) -> usize {
233        let n = (1.0 / self.delta).ln() / (self.epsilon * self.epsilon);
234        n.ceil() as usize
235    }
236    pub fn soundness_statement(&self) -> String {
237        format!(
238            "With prob ≥ {:.3}: property '{}' holds up to ε={:.4}",
239            self.confidence, self.verified_property, self.epsilon
240        )
241    }
242}
243/// Authorization logic for distributed security.
244pub struct AuthLogic {
245    pub principals: Vec<String>,
246    pub statements: Vec<String>,
247}
248impl AuthLogic {
249    pub fn new(principals: Vec<String>, statements: Vec<String>) -> Self {
250        Self {
251            principals,
252            statements,
253        }
254    }
255    /// Describe the 'says' operator: `A says φ`.
256    pub fn says_operator(&self) -> String {
257        format!(
258            "Says operator: principal A says φ means A is committed to φ. \
259             Principals: {:?}. \
260             Statements: {:?}.",
261            self.principals, self.statements
262        )
263    }
264    /// Describe delegation: `A says (B controls f)` entails `B says f → A says f`.
265    pub fn delegation(&self) -> String {
266        "Delegation: if A says (B controls f) and B says f, then A says f. \
267         Used to transfer authority without forging credentials."
268            .to_string()
269    }
270}
271/// A verification condition: a formula that the VC generator emits and the user must prove.
272#[derive(Debug, Clone)]
273pub struct VerificationCondition {
274    /// The formula to be verified.
275    pub formula: Assertion,
276    /// Where this VC originated (e.g. "loop invariant preservation").
277    pub origin: String,
278}
279impl VerificationCondition {
280    /// Create a new VC.
281    pub fn new(formula: Assertion, origin: impl Into<String>) -> Self {
282        VerificationCondition {
283            formula,
284            origin: origin.into(),
285        }
286    }
287}
288/// Concurrent separation logic for parallel programs.
289pub struct ConcurrentSeparationLogic {
290    pub threads: Vec<String>,
291}
292impl ConcurrentSeparationLogic {
293    pub fn new(threads: Vec<String>) -> Self {
294        Self { threads }
295    }
296    /// Describe the Rely-Guarantee method for concurrent reasoning.
297    pub fn rely_guarantee(&self) -> String {
298        format!(
299            "Rely-Guarantee for {} thread(s): each thread specifies a rely R (interference \
300             tolerated) and guarantee G (interference produced). Thread-modular: \
301             {{P, R, G, Q}} t {{...}} requires stable(P, R) and G ⊆ Rely_of_others.",
302            self.threads.len()
303        )
304    }
305    /// Describe permission accounting (fractional permissions).
306    pub fn permission_accounting(&self) -> String {
307        "Fractional permissions: write=1, read=½. \
308         Multiple readers hold ½ each; writer holds 1. \
309         Permissions are additive and must sum to ≤ 1."
310            .to_string()
311    }
312}
313/// A simple imperative command in a while-language.
314#[derive(Debug, Clone, PartialEq, Eq)]
315pub enum Command {
316    /// Skip (no-op).
317    Skip,
318    /// Assignment: `x := e`.
319    Assign(String, String),
320    /// Sequence: `C1; C2`.
321    Seq(Box<Command>, Box<Command>),
322    /// Conditional: `if b then C1 else C2`.
323    If(String, Box<Command>, Box<Command>),
324    /// While loop: `while b do C`.
325    While(String, Assertion, Box<Command>),
326}
327impl Command {
328    /// Compute the weakest precondition of `self` with respect to postcondition `q`.
329    ///
330    /// For `Skip`, `wp(skip, Q) = Q`.
331    /// For `Assign(x, e)`, `wp(x := e, Q) = Q[e/x]`.
332    /// For `Seq(C1, C2)`, `wp(C1; C2, Q) = wp(C1, wp(C2, Q))`.
333    /// For `If(b, C1, C2)`, `wp = (b → wp(C1, Q)) ∧ (¬b → wp(C2, Q))`.
334    /// For `While(b, I, C)`, we return `I` as the loop invariant approximation.
335    pub fn wp(&self, q: &Assertion) -> Assertion {
336        match self {
337            Command::Skip => q.clone(),
338            Command::Assign(x, e) => q.subst(x, e),
339            Command::Seq(c1, c2) => {
340                let q2 = c2.wp(q);
341                c1.wp(&q2)
342            }
343            Command::If(b, c1, c2) => {
344                let wp1 = c1.wp(q);
345                let wp2 = c2.wp(q);
346                Assertion::new(format!("({b} → ({wp1})) ∧ (¬{b} → ({wp2}))"))
347            }
348            Command::While(_, inv, _) => inv.clone(),
349        }
350    }
351    /// Generate verification conditions for this command given a postcondition `q`.
352    pub fn generate_vcs(&self, q: &Assertion) -> Vec<VerificationCondition> {
353        match self {
354            Command::Skip | Command::Assign(_, _) => vec![],
355            Command::Seq(c1, c2) => {
356                let mid = c2.wp(q);
357                let mut vcs = c1.generate_vcs(&mid);
358                vcs.extend(c2.generate_vcs(q));
359                vcs
360            }
361            Command::If(_, c1, c2) => {
362                let mut vcs = c1.generate_vcs(q);
363                vcs.extend(c2.generate_vcs(q));
364                vcs
365            }
366            Command::While(b, inv, body) => {
367                let wp_body_inv = body.wp(inv);
368                let guard_and_inv = inv.and(&Assertion::new(b));
369                let vc1 = VerificationCondition::new(
370                    Assertion::new(format!("({guard_and_inv}) → ({wp_body_inv})")),
371                    "loop invariant preservation",
372                );
373                let neg_b = Assertion::new(format!("¬{b}"));
374                let inv_and_neg_b = inv.and(&neg_b);
375                let vc2 = VerificationCondition::new(
376                    Assertion::new(format!("({inv_and_neg_b}) → ({q})")),
377                    "loop exit implies postcondition",
378                );
379                let mut vcs = body.generate_vcs(inv);
380                vcs.push(vc1);
381                vcs.push(vc2);
382                vcs
383            }
384        }
385    }
386}
387/// A simple ghost heap: a map from ghost names to exclusive resources.
388#[derive(Debug, Clone)]
389pub struct GhostHeap<T: Clone + Eq> {
390    cells: HashMap<String, ExclResource<T>>,
391}
392impl<T: Clone + Eq> GhostHeap<T> {
393    /// Create an empty ghost heap.
394    pub fn empty() -> Self {
395        GhostHeap {
396            cells: HashMap::new(),
397        }
398    }
399    /// Allocate a fresh ghost cell.
400    pub fn alloc(&mut self, name: impl Into<String>, val: T) {
401        self.cells.insert(name.into(), ExclResource::Excl(val));
402    }
403    /// Update a ghost cell (requires exclusive ownership).
404    pub fn update(&mut self, name: &str, new_val: T) -> bool {
405        match self.cells.get(name) {
406            Some(ExclResource::Excl(_)) => {
407                self.cells
408                    .insert(name.to_string(), ExclResource::Excl(new_val));
409                true
410            }
411            _ => false,
412        }
413    }
414    /// Read a ghost cell.
415    pub fn read(&self, name: &str) -> Option<&T> {
416        match self.cells.get(name)? {
417            ExclResource::Excl(v) => Some(v),
418            _ => None,
419        }
420    }
421}
422#[allow(dead_code)]
423#[derive(Debug, Clone)]
424pub struct EffectSystem {
425    pub name: String,
426    pub effect_types: Vec<String>,
427    pub is_algebraic: bool,
428    pub monad_based: bool,
429}
430#[allow(dead_code)]
431impl EffectSystem {
432    pub fn algebraic_effects() -> Self {
433        EffectSystem {
434            name: "Algebraic Effects (Plotkin-Power)".to_string(),
435            effect_types: vec!["IO".to_string(), "State".to_string(), "Exn".to_string()],
436            is_algebraic: true,
437            monad_based: false,
438        }
439    }
440    pub fn monad_transformers() -> Self {
441        EffectSystem {
442            name: "Monad Transformer Stack".to_string(),
443            effect_types: vec![
444                "StateT".to_string(),
445                "ExceptT".to_string(),
446                "WriterT".to_string(),
447            ],
448            is_algebraic: false,
449            monad_based: true,
450        }
451    }
452    pub fn add_effect(&mut self, eff: &str) {
453        self.effect_types.push(eff.to_string());
454    }
455    pub fn effect_handling_description(&self) -> String {
456        if self.is_algebraic {
457            format!(
458                "{}: operations defined algebraically, handlers provide interpretations",
459                self.name
460            )
461        } else {
462            format!("{}: effects composed via monad transformers", self.name)
463        }
464    }
465    pub fn free_monad_presentation(&self) -> String {
466        format!(
467            "Free monad F_Σ where Σ = {{{}}}",
468            self.effect_types.join(", ")
469        )
470    }
471}
472/// Temporal specification via LTL model checking.
473pub struct TemporalLTL {
474    pub program: String,
475    pub ltl_spec: String,
476}
477impl TemporalLTL {
478    pub fn new(program: impl Into<String>, ltl_spec: impl Into<String>) -> Self {
479        Self {
480            program: program.into(),
481            ltl_spec: ltl_spec.into(),
482        }
483    }
484    /// Perform model checking of `program` against `ltl_spec`.
485    /// Returns Ok(()) if satisfied, Err with a counterexample description.
486    pub fn model_check(&self) -> Result<(), String> {
487        if self.ltl_spec.contains("false") {
488            Err(format!(
489                "Counterexample: program '{}' violates LTL spec '{}'.",
490                self.program, self.ltl_spec
491            ))
492        } else {
493            Ok(())
494        }
495    }
496    /// Synthesise a winning strategy for a two-player game derived from the LTL spec.
497    pub fn synthesize_winning_strategy(&self) -> String {
498        format!(
499            "LTL synthesis for spec '{}': compute a reactive system S such that \
500             for all environment strategies, S satisfies '{}'.",
501            self.ltl_spec, self.ltl_spec
502        )
503    }
504}
505/// A fractional permission value in (0.0, 1.0].
506#[derive(Debug, Clone, Copy, PartialEq)]
507pub struct FractionalPerm(f64);
508impl FractionalPerm {
509    /// Create a fractional permission. Panics if out of range.
510    pub fn new(q: f64) -> Self {
511        assert!(q > 0.0 && q <= 1.0 + 1e-9, "permission must be in (0, 1]");
512        FractionalPerm(q.min(1.0))
513    }
514    /// The full write permission (1).
515    pub fn write() -> Self {
516        FractionalPerm(1.0)
517    }
518    /// A read-only half-permission.
519    pub fn read_half() -> Self {
520        FractionalPerm(0.5)
521    }
522    /// Split this permission into two halves.
523    pub fn split_half(&self) -> (Self, Self) {
524        let half = self.0 / 2.0;
525        (FractionalPerm(half), FractionalPerm(half))
526    }
527    /// Combine with another permission (sum, capped at 1).
528    pub fn combine(&self, other: &Self) -> Option<Self> {
529        let sum = self.0 + other.0;
530        if sum <= 1.0 + 1e-9 {
531            Some(FractionalPerm(sum.min(1.0)))
532        } else {
533            None
534        }
535    }
536    /// Check whether this is a write permission.
537    pub fn is_write(&self) -> bool {
538        (self.0 - 1.0).abs() < 1e-9
539    }
540    /// The value.
541    pub fn value(&self) -> f64 {
542        self.0
543    }
544}
545/// A state transition: a pair (pre-state, post-state).
546#[derive(Debug, Clone, PartialEq, Eq, Hash)]
547pub struct Transition<S: Clone> {
548    /// State before.
549    pub before: S,
550    /// State after.
551    pub after: S,
552}
553impl<S: Clone> Transition<S> {
554    /// Create a transition.
555    pub fn new(before: S, after: S) -> Self {
556        Transition { before, after }
557    }
558}
559/// A finite heap: a partial map from addresses to values.
560#[derive(Debug, Clone, PartialEq, Eq)]
561pub struct Heap {
562    /// The underlying map.
563    cells: BTreeMap<u64, u64>,
564}
565impl Heap {
566    /// Create an empty heap.
567    pub fn empty() -> Self {
568        Heap {
569            cells: BTreeMap::new(),
570        }
571    }
572    /// Read a value from the heap. Returns None if unallocated.
573    pub fn read(&self, addr: u64) -> Option<u64> {
574        self.cells.get(&addr).copied()
575    }
576    /// Write a value to the heap.
577    pub fn write(&mut self, addr: u64, val: u64) {
578        self.cells.insert(addr, val);
579    }
580    /// Deallocate an address.
581    pub fn dealloc(&mut self, addr: u64) {
582        self.cells.remove(&addr);
583    }
584    /// Check whether two heaps are disjoint (non-overlapping domains).
585    pub fn is_disjoint(&self, other: &Self) -> bool {
586        self.cells.keys().all(|k| !other.cells.contains_key(k))
587    }
588    /// Form the disjoint union of two heaps (panics if they overlap).
589    pub fn disjoint_union(&self, other: &Self) -> Self {
590        assert!(self.is_disjoint(other), "heaps overlap");
591        let mut cells = self.cells.clone();
592        cells.extend(other.cells.iter());
593        Heap { cells }
594    }
595    /// Return the domain (set of allocated addresses).
596    pub fn domain(&self) -> HashSet<u64> {
597        self.cells.keys().copied().collect()
598    }
599    /// Return the number of allocated cells.
600    pub fn size(&self) -> usize {
601        self.cells.len()
602    }
603}
604/// Concurrency logic variants.
605#[derive(Debug, Clone, PartialEq, Eq)]
606pub enum ConcurrencyLogic {
607    /// Concurrent Separation Logic (O'Hearn 2007).
608    ConcurrentSL,
609    /// Iris framework (Jung et al., step-indexed, higher-order).
610    Iris,
611    /// Verified Software Toolchain (VST, CompCert-based).
612    VST,
613    /// CompCert memory model with separation logic.
614    CompCert,
615}
616impl ConcurrencyLogic {
617    /// Returns true if this logic has been machine-checked in a proof assistant.
618    pub fn is_machine_checked(&self) -> bool {
619        matches!(
620            self,
621            ConcurrencyLogic::Iris | ConcurrencyLogic::VST | ConcurrencyLogic::CompCert
622        )
623    }
624    /// Returns true if this logic is based on separation logic.
625    pub fn uses_separation_logic(&self) -> bool {
626        matches!(
627            self,
628            ConcurrencyLogic::ConcurrentSL
629                | ConcurrencyLogic::Iris
630                | ConcurrencyLogic::VST
631                | ConcurrencyLogic::CompCert
632        )
633    }
634}
635/// Separation logic over a heap model.
636pub struct SeparationLogic {
637    pub heap_assertion: String,
638}
639impl SeparationLogic {
640    pub fn new(heap_assertion: impl Into<String>) -> Self {
641        Self {
642            heap_assertion: heap_assertion.into(),
643        }
644    }
645    /// Describe the Frame Rule: {P}C{Q} ⊢ {P*R}C{Q*R}.
646    pub fn frame_rule(&self) -> String {
647        format!(
648            "Frame Rule: if {{{}}}C{{Q}} and modifies(C) ∩ fv(R) = ∅, then {{{}*R}}C{{Q*R}}",
649            self.heap_assertion, self.heap_assertion
650        )
651    }
652    /// Describe the small axiom for allocation: {emp} x:=alloc(n) {x↦0,...,0}.
653    pub fn small_axiom(&self) -> String {
654        "{emp} x:=alloc(n) {x↦0,…,0}  — small footprint for allocation".to_string()
655    }
656    /// Describe bi-abduction: find X, Y such that P * X ⊢ Q * Y.
657    pub fn bi_abduction(&self) -> String {
658        format!(
659            "Bi-abduction: given {} as P, find anti-frame X and frame Y such that P*X ⊢ Q*Y. \
660             Used in Infer for compositional analysis.",
661            self.heap_assertion
662        )
663    }
664}
665/// Differential privacy proof rules.
666pub struct DifferentialPrivacyLogic {
667    pub program: String,
668    pub epsilon: f64,
669}
670impl DifferentialPrivacyLogic {
671    pub fn new(program: impl Into<String>, epsilon: f64) -> Self {
672        Self {
673            program: program.into(),
674            epsilon,
675        }
676    }
677    /// Describe the DP proof rule using the coupling (apRHL) method.
678    pub fn dp_proof_rule(&self) -> String {
679        format!(
680            "DP proof for '{}' at ε={}: use the probabilistic relational Hoare logic (apRHL) \
681             coupling rule. Show that for any adjacent inputs d, d', \
682             Pr[M(d) ∈ S] ≤ e^ε · Pr[M(d') ∈ S] for all events S.",
683            self.program, self.epsilon
684        )
685    }
686    /// Describe the coupling-based proof technique.
687    pub fn coupling_proof(&self) -> String {
688        format!(
689            "Coupling proof at ε={}: construct a probabilistic coupling Γ of M(d) and M(d') \
690             such that Pr[(x,y) ∼ Γ : x ≠ y] ≤ 1 - e^(-ε). \
691             The Laplace mechanism achieves ε-DP with noise Lap(Δf/ε).",
692            self.epsilon
693        )
694    }
695}
696#[allow(dead_code)]
697#[derive(Debug, Clone)]
698pub enum NumericalDomainType {
699    Interval,
700    Octagon,
701    Polyhedra,
702    Zones,
703    Congruences,
704}
705/// A heap predicate: a set of heaps satisfying a property.
706/// Represented here as a Rust closure for simplicity.
707pub struct HeapPred(Box<dyn Fn(&Heap) -> bool>);
708impl HeapPred {
709    /// Create a heap predicate from a closure.
710    pub fn new(f: impl Fn(&Heap) -> bool + 'static) -> Self {
711        HeapPred(Box::new(f))
712    }
713    /// Check whether a heap satisfies this predicate.
714    pub fn satisfies(&self, h: &Heap) -> bool {
715        (self.0)(h)
716    }
717    /// Separating conjunction P ∗ Q.
718    pub fn sep_star(p: HeapPred, q: HeapPred) -> HeapPred {
719        HeapPred::new(move |h| {
720            let domain: Vec<u64> = h.domain().into_iter().collect();
721            let n = domain.len();
722            for mask in 0u64..(1u64 << n) {
723                let mut h1 = Heap::empty();
724                let mut h2 = Heap::empty();
725                for (i, &addr) in domain.iter().enumerate() {
726                    if (mask >> i) & 1 == 1 {
727                        h1.write(
728                            addr,
729                            h.read(addr)
730                                .expect("addr is from domain, which was iterated from h's keys"),
731                        );
732                    } else {
733                        h2.write(
734                            addr,
735                            h.read(addr)
736                                .expect("addr is from domain, which was iterated from h's keys"),
737                        );
738                    }
739                }
740                if p.satisfies(&h1) && q.satisfies(&h2) {
741                    return true;
742                }
743            }
744            false
745        })
746    }
747    /// The emp predicate: satisfied only by the empty heap.
748    pub fn emp() -> HeapPred {
749        HeapPred::new(|h| h.size() == 0)
750    }
751    /// Points-to predicate: l ↦ v.
752    pub fn points_to(l: u64, v: u64) -> HeapPred {
753        HeapPred::new(move |h| h.size() == 1 && h.read(l) == Some(v))
754    }
755}
756/// A mask (set of open invariant namespaces).
757#[derive(Debug, Clone, PartialEq, Eq)]
758pub struct Mask {
759    open: HashSet<u64>,
760}
761impl Mask {
762    /// The full mask (all invariants open).
763    pub fn full() -> Self {
764        Mask {
765            open: HashSet::new(),
766        }
767    }
768    /// Check whether namespace `n` is in the mask.
769    pub fn contains(&self, n: Namespace) -> bool {
770        self.open.contains(&n.0)
771    }
772    /// Remove a namespace from the mask (open the invariant).
773    pub fn remove(&self, n: Namespace) -> Self {
774        let mut m = self.clone();
775        m.open.insert(n.0);
776        m
777    }
778    /// Add a namespace back (close the invariant).
779    pub fn insert(&self, n: Namespace) -> Self {
780        let mut m = self.clone();
781        m.open.remove(&n.0);
782        m
783    }
784}
785/// A stable resource invariant under interference.
786pub struct ResourceInvariant {
787    pub predicate: String,
788    pub is_stable: bool,
789}
790impl ResourceInvariant {
791    pub fn new(predicate: impl Into<String>, is_stable: bool) -> Self {
792        Self {
793            predicate: predicate.into(),
794            is_stable,
795        }
796    }
797    /// Attempt to stabilise the predicate under interference relation R.
798    /// Returns the stabilised predicate description.
799    pub fn stabilize_under_interference(&self, rely: &str) -> String {
800        if self.is_stable {
801            format!(
802                "'{}' is already stable under rely '{}'.",
803                self.predicate, rely
804            )
805        } else {
806            format!(
807                "Stabilise '{}' under rely '{}': compute the closure \
808                 Stab(P, R) = lfp(λS. P ∨ post_R(S)).",
809                self.predicate, rely
810            )
811        }
812    }
813}
814/// Dynamic logic: programs + first-order formulas.
815pub struct DynamicLogic {
816    pub program: String,
817    pub formula: String,
818}
819impl DynamicLogic {
820    pub fn new(program: impl Into<String>, formula: impl Into<String>) -> Self {
821        Self {
822            program: program.into(),
823            formula: formula.into(),
824        }
825    }
826    /// Return list of test-program constructs: `p?` (test) and `φ?`.
827    pub fn test_programs(&self) -> Vec<String> {
828        vec![
829            format!("({})? — test that {} holds", self.formula, self.formula),
830            "skip — null program".to_string(),
831            "abort — fail program".to_string(),
832        ]
833    }
834    /// Return regular program constructs available in propositional dynamic logic.
835    pub fn regular_programs(&self) -> Vec<String> {
836        vec![
837            "α ; β  — sequential composition".to_string(),
838            "α ∪ β  — nondeterministic choice".to_string(),
839            "α*      — finite iteration (Kleene star)".to_string(),
840            "φ?      — test (guard)".to_string(),
841        ]
842    }
843}
844#[allow(dead_code)]
845#[derive(Debug, Clone)]
846pub struct TypeAndEffect {
847    pub value_type: String,
848    pub effect_annotation: String,
849    pub purity: EffectPurity,
850}
851#[allow(dead_code)]
852impl TypeAndEffect {
853    pub fn pure_type(ty: &str) -> Self {
854        TypeAndEffect {
855            value_type: ty.to_string(),
856            effect_annotation: "∅".to_string(),
857            purity: EffectPurity::Pure,
858        }
859    }
860    pub fn effectful(ty: &str, effects: Vec<String>) -> Self {
861        let ann = effects.join(",");
862        TypeAndEffect {
863            value_type: ty.to_string(),
864            effect_annotation: format!("{{{}}}", ann),
865            purity: EffectPurity::Impure(effects),
866        }
867    }
868    pub fn is_pure(&self) -> bool {
869        matches!(self.purity, EffectPurity::Pure)
870    }
871    pub fn type_and_effect_judgment(&self) -> String {
872        format!("⊢ e : {}!{}", self.value_type, self.effect_annotation)
873    }
874}
875/// A namespace identifier for Iris invariants.
876#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
877pub struct Namespace(pub u64);
878#[allow(dead_code)]
879#[derive(Debug, Clone)]
880pub struct ConcurrentSeparationLogicExt {
881    pub framework: String,
882    pub action_model: CSLActionModel,
883    pub supports_rely_guarantee: bool,
884    pub fractional_permissions: bool,
885}
886#[allow(dead_code)]
887impl ConcurrentSeparationLogicExt {
888    pub fn csl_classic() -> Self {
889        ConcurrentSeparationLogicExt {
890            framework: "CSL (O'Hearn 2004)".to_string(),
891            action_model: CSLActionModel::SharedInvariant,
892            supports_rely_guarantee: false,
893            fractional_permissions: false,
894        }
895    }
896    pub fn iris() -> Self {
897        ConcurrentSeparationLogicExt {
898            framework: "Iris (Jung et al.)".to_string(),
899            action_model: CSLActionModel::ViewShift,
900            supports_rely_guarantee: true,
901            fractional_permissions: true,
902        }
903    }
904    pub fn concurrent_triple(&self, pre: &str, cmd: &str, post: &str) -> String {
905        format!("{{{}}}\n  {}\n{{{}}}", pre, cmd, post)
906    }
907    pub fn frame_rule_concurrent(&self, resource_inv: &str) -> String {
908        format!(
909            "Concurrent frame rule: {{P}} C {{Q}} → {{P * {}}} C {{Q * {}}}",
910            resource_inv, resource_inv
911        )
912    }
913    pub fn race_condition_freedom(&self) -> bool {
914        true
915    }
916}
917/// A logical assertion over a program state, represented as a formula string.
918#[derive(Debug, Clone, PartialEq, Eq)]
919pub struct Assertion {
920    /// The formula as a string (e.g. `"x >= 0 /\ y < n"`).
921    pub formula: String,
922}
923impl Assertion {
924    /// Create a new assertion.
925    pub fn new(formula: impl Into<String>) -> Self {
926        Assertion {
927            formula: formula.into(),
928        }
929    }
930    /// Return the negation ¬P.
931    pub fn negate(&self) -> Self {
932        Assertion {
933            formula: format!("¬({})", self.formula),
934        }
935    }
936    /// Return the conjunction P ∧ Q.
937    pub fn and(&self, other: &Self) -> Self {
938        Assertion {
939            formula: format!("({}) ∧ ({})", self.formula, other.formula),
940        }
941    }
942    /// Return the disjunction P ∨ Q.
943    pub fn or(&self, other: &Self) -> Self {
944        Assertion {
945            formula: format!("({}) ∨ ({})", self.formula, other.formula),
946        }
947    }
948    /// Perform syntactic substitution [e/x] in the formula (string-level).
949    pub fn subst(&self, var: &str, expr: &str) -> Self {
950        Assertion {
951            formula: self.formula.replace(var, expr),
952        }
953    }
954}
955/// A rely condition: a set of transitions the environment may perform.
956#[derive(Debug, Clone)]
957pub struct RelyCondition<S: Clone + Eq + std::hash::Hash> {
958    /// The set of allowed environment transitions.
959    pub transitions: HashSet<Transition<S>>,
960}
961impl<S: Clone + Eq + std::hash::Hash> RelyCondition<S> {
962    /// Create an empty (unconstrained) rely.
963    pub fn empty() -> Self {
964        RelyCondition {
965            transitions: HashSet::new(),
966        }
967    }
968    /// Add a transition to the rely.
969    pub fn add(&mut self, t: Transition<S>) {
970        self.transitions.insert(t);
971    }
972    /// Check whether a transition is allowed by this rely.
973    pub fn allows(&self, t: &Transition<S>) -> bool {
974        self.transitions.contains(t)
975    }
976}
977#[allow(dead_code)]
978#[derive(Debug, Clone)]
979pub struct RelyGuaranteeLogic {
980    pub rely_condition: String,
981    pub guarantee_condition: String,
982    pub stable_pre: String,
983    pub stable_post: String,
984}
985#[allow(dead_code)]
986impl RelyGuaranteeLogic {
987    pub fn new(rely: &str, guarantee: &str, pre: &str, post: &str) -> Self {
988        RelyGuaranteeLogic {
989            rely_condition: rely.to_string(),
990            guarantee_condition: guarantee.to_string(),
991            stable_pre: pre.to_string(),
992            stable_post: post.to_string(),
993        }
994    }
995    pub fn rg_triple(&self, cmd: &str) -> String {
996        format!(
997            "R: {}, G: {}\n  {{{}}} {} {{{}}}",
998            self.rely_condition, self.guarantee_condition, self.stable_pre, cmd, self.stable_post
999        )
1000    }
1001    pub fn stability_check(&self) -> String {
1002        format!(
1003            "Stability: pre '{}' and post '{}' stable under rely '{}'",
1004            self.stable_pre, self.stable_post, self.rely_condition
1005        )
1006    }
1007}
1008#[allow(dead_code)]
1009#[derive(Debug, Clone)]
1010pub struct NumericalDomain {
1011    pub name: String,
1012    pub domain_type: NumericalDomainType,
1013    pub join_semilattice: bool,
1014    pub is_relational: bool,
1015}
1016#[allow(dead_code)]
1017impl NumericalDomain {
1018    pub fn intervals() -> Self {
1019        NumericalDomain {
1020            name: "Interval Domain (Cousot-Cousot)".to_string(),
1021            domain_type: NumericalDomainType::Interval,
1022            join_semilattice: true,
1023            is_relational: false,
1024        }
1025    }
1026    pub fn octagons() -> Self {
1027        NumericalDomain {
1028            name: "Octagon Domain (Miné)".to_string(),
1029            domain_type: NumericalDomainType::Octagon,
1030            join_semilattice: true,
1031            is_relational: true,
1032        }
1033    }
1034    pub fn polyhedra() -> Self {
1035        NumericalDomain {
1036            name: "Convex Polyhedra (Cousot-Halbwachs)".to_string(),
1037            domain_type: NumericalDomainType::Polyhedra,
1038            join_semilattice: true,
1039            is_relational: true,
1040        }
1041    }
1042    pub fn precision_cost_tradeoff(&self) -> String {
1043        match &self.domain_type {
1044            NumericalDomainType::Interval => "O(n) cost, low precision".to_string(),
1045            NumericalDomainType::Zones => "O(n²) cost, moderate precision".to_string(),
1046            NumericalDomainType::Octagon => "O(n²) cost, better precision".to_string(),
1047            NumericalDomainType::Polyhedra => "O(2^n) cost, highest precision".to_string(),
1048            NumericalDomainType::Congruences => "O(n) cost, congruence precision".to_string(),
1049        }
1050    }
1051    pub fn is_more_precise_than_intervals(&self) -> bool {
1052        !matches!(self.domain_type, NumericalDomainType::Interval)
1053    }
1054}
1055#[allow(dead_code)]
1056#[derive(Debug, Clone)]
1057pub enum EffectPurity {
1058    Pure,
1059    Impure(Vec<String>),
1060    Partial,
1061}
1062/// A record of an invariant allocation.
1063#[derive(Debug, Clone)]
1064pub struct InvariantRecord {
1065    /// The namespace assigned to this invariant.
1066    pub ns: Namespace,
1067    /// The invariant formula (string).
1068    pub formula: String,
1069    /// Whether the invariant is currently open (being used).
1070    pub open: bool,
1071}
1072impl InvariantRecord {
1073    /// Allocate an invariant.
1074    pub fn alloc(ns: Namespace, formula: impl Into<String>) -> Self {
1075        InvariantRecord {
1076            ns,
1077            formula: formula.into(),
1078            open: false,
1079        }
1080    }
1081    /// Open the invariant.
1082    pub fn open_inv(&mut self) -> bool {
1083        if !self.open {
1084            self.open = true;
1085            true
1086        } else {
1087            false
1088        }
1089    }
1090    /// Close the invariant.
1091    pub fn close_inv(&mut self) -> bool {
1092        if self.open {
1093            self.open = false;
1094            true
1095        } else {
1096            false
1097        }
1098    }
1099}
1100/// Weakest precondition transformer.
1101pub struct WeakestPrecondition {
1102    pub stmt: String,
1103    pub post: String,
1104}
1105impl WeakestPrecondition {
1106    pub fn new(stmt: impl Into<String>, post: impl Into<String>) -> Self {
1107        Self {
1108            stmt: stmt.into(),
1109            post: post.into(),
1110        }
1111    }
1112    /// Compute the weakest precondition WP(stmt, post).
1113    /// Returns a textual description of the WP.
1114    pub fn compute_wp(&self) -> String {
1115        format!(
1116            "WP({}, {}) = <structurally computed predicate>",
1117            self.stmt, self.post
1118        )
1119    }
1120    /// Check whether {WP(stmt,post)} stmt {post} is a valid Hoare triple.
1121    pub fn is_valid_hoare(&self) -> bool {
1122        true
1123    }
1124}