clifford_codegen/discovery/entity.rs
1//! Discovered entity representation.
2
3/// Represents a discovered geometric entity.
4///
5/// An entity is a valid grade combination with associated metadata.
6/// Valid entities must satisfy two constraints:
7///
8/// 1. **Geometric Product Constraint**: `u * ũ = scalar`
9/// 2. **Antiproduct Constraint**: `u ⊟ ũ̃ = antiscalar`
10///
11/// Each constraint may require a field constraint expression that must equal zero.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct DiscoveredEntity {
14 /// Suggested name for this entity (e.g., "Entity_0_2").
15 pub name: String,
16
17 /// The grades present in this entity.
18 pub grades: Vec<usize>,
19
20 /// Field constraint for the geometric product constraint (`u * ũ = scalar`).
21 ///
22 /// If `None`, the entity automatically satisfies the geometric constraint.
23 /// If `Some(expr)`, the expression must equal zero for the constraint to hold.
24 /// Example: "e01*e23 + e02*e31 + e03*e12 = 0" for PGA bivectors.
25 pub geometric_constraint: Option<String>,
26
27 /// Field constraint for the antiproduct constraint (`u ⊟ ũ̃ = antiscalar`).
28 ///
29 /// If `None`, the entity automatically satisfies the antiproduct constraint.
30 /// If `Some(expr)`, the expression must equal zero for the constraint to hold.
31 pub antiproduct_constraint: Option<String>,
32}
33
34impl DiscoveredEntity {
35 /// Creates a new discovered entity with minimal information.
36 pub fn new(name: impl Into<String>, grades: Vec<usize>) -> Self {
37 Self {
38 name: name.into(),
39 grades,
40 geometric_constraint: None,
41 antiproduct_constraint: None,
42 }
43 }
44
45 /// Sets the geometric field constraint expression.
46 pub fn with_geometric_constraint(mut self, constraint: impl Into<String>) -> Self {
47 self.geometric_constraint = Some(constraint.into());
48 self
49 }
50
51 /// Sets the antiproduct field constraint expression.
52 pub fn with_antiproduct_constraint(mut self, constraint: impl Into<String>) -> Self {
53 self.antiproduct_constraint = Some(constraint.into());
54 self
55 }
56
57 /// Returns the total number of constraints this entity has.
58 pub fn constraint_count(&self) -> usize {
59 let mut count = 0;
60 if self.geometric_constraint.is_some() {
61 count += 1;
62 }
63 if self.antiproduct_constraint.is_some() {
64 count += 1;
65 }
66 count
67 }
68
69 /// Returns true if this entity has any field constraints.
70 pub fn has_constraints(&self) -> bool {
71 self.geometric_constraint.is_some() || self.antiproduct_constraint.is_some()
72 }
73}