Skip to main content

cliffy_test/
lib.rs

1//! # Cliffy Test - Algebraic Testing Framework
2//!
3//! Tests are geometric invariants. Failures are geometric distances.
4//! Test composition uses geometric product.
5//!
6//! ## Philosophy
7//!
8//! Traditional testing asks: "Does this assertion pass?"
9//! Cliffy testing asks: "Does this state lie on the expected manifold?"
10//!
11//! By integrating with `amari-flynn`, we categorize test events into three types:
12//!
13//! | Category | Probability | Meaning |
14//! |----------|-------------|---------|
15//! | **Impossible** | P = 0 | Formally proven to never occur |
16//! | **Rare** | 0 < P << 1 | Statistically bounded failure rate |
17//! | **Emergent** | P > 0 | Valid but unpredicted behaviors |
18//!
19//! ## Quick Start
20//!
21//! ```rust
22//! use cliffy_test::prelude::*;
23//!
24//! // Define an impossible invariant - must NEVER fail
25//! let inv = invariant_impossible! {
26//!     name: "Magnitude is non-negative",
27//!     check: || {
28//!         let v = vector(1.0, 2.0, 3.0);
29//!         if v.magnitude() >= 0.0 {
30//!             TestResult::Pass
31//!         } else {
32//!             TestResult::fail_with_distance(v.magnitude(), "Negative magnitude")
33//!         }
34//!     }
35//! };
36//! ```
37
38#![warn(missing_docs)]
39
40pub mod error;
41pub mod generators;
42pub mod invariants;
43pub mod macros;
44pub mod manifold;
45pub mod result;
46
47// Re-export amari types
48pub use amari_core::{Bivector, Multivector, Vector};
49
50// Re-export amari-flynn for probabilistic contracts
51pub use amari_flynn;
52
53/// Type alias for 3D Euclidean geometric algebra
54pub type GA3 = Multivector<3, 0, 0>;
55
56/// Type alias for 3D Vector
57pub type Vec3 = Vector<3, 0, 0>;
58
59/// Type alias for 3D Bivector
60pub type Biv3 = Bivector<3, 0, 0>;
61
62/// Convenience function to create a GA3 vector from components
63pub fn vector(x: f64, y: f64, z: f64) -> GA3 {
64    GA3::from_vector(&Vec3::from_components(x, y, z))
65}
66
67/// Convenience function to create a GA3 bivector from components
68pub fn bivector(xy: f64, xz: f64, yz: f64) -> GA3 {
69    GA3::from_bivector(&Biv3::from_components(xy, xz, yz))
70}
71
72/// Convenience function to create a GA3 from coefficient array
73pub fn from_coeffs(coeffs: [f64; 8]) -> GA3 {
74    GA3::from_coefficients(coeffs.to_vec())
75}
76
77/// Sandwich product: r * v * r.reverse()
78///
79/// This is the fundamental operation for geometric transformations.
80/// When r is a rotor (unit versor), this preserves magnitude.
81pub fn sandwich(rotor: &GA3, value: &GA3) -> GA3 {
82    rotor
83        .geometric_product(value)
84        .geometric_product(&rotor.reverse())
85}
86
87/// Prelude module for common imports
88pub mod prelude {
89    // Core types
90    pub use crate::error::GeometricError;
91    pub use crate::result::TestResult;
92
93    // Invariant types
94    pub use crate::invariants::{EmergentBehavior, ImpossibleInvariant, Invariant, RareInvariant};
95
96    // Manifold testing
97    pub use crate::manifold::{Manifold, ManifoldConstraint};
98
99    // Generators for property testing
100    pub use crate::generators::{
101        arbitrary_ga3, arbitrary_rotor, arbitrary_unit_vector, arbitrary_vector,
102    };
103
104    // Convenience constructors
105    pub use crate::{bivector, from_coeffs, sandwich, vector, Biv3, Vec3, GA3};
106
107    // Macros
108    pub use crate::{emergent, invariant_impossible, invariant_rare};
109
110    // Re-export amari types
111    pub use amari_core::{Bivector, Multivector, Vector};
112
113    // Re-export amari-flynn types
114    pub use amari_flynn::prelude::*;
115
116    // SMT proof obligations for formal verification
117    pub use amari_flynn::backend::smt::SmtProofObligation;
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_vector_creation() {
126        let v = vector(1.0, 2.0, 3.0);
127        assert!((v.magnitude() - 14.0_f64.sqrt()).abs() < 1e-10);
128    }
129
130    #[test]
131    fn test_sandwich_identity() {
132        let v = vector(1.0, 2.0, 3.0);
133        let identity = GA3::scalar(1.0);
134        let result = sandwich(&identity, &v);
135
136        // Identity sandwich should return the same vector
137        assert!((v.magnitude() - result.magnitude()).abs() < 1e-10);
138    }
139
140    #[test]
141    fn test_bivector_creation() {
142        let b = bivector(1.0, 0.0, 0.0);
143        assert!(b.magnitude() > 0.0);
144    }
145}