Skip to main content

machine_cat/
column.rs

1//! Column newtypes for AIR trace tables.
2//!
3//! - [`Column`]: a column index within a trace table
4//! - [`ColumnCount`]: the number of columns (the AIR's "shape")
5//! - [`ColumnRef`]: a row-relative column reference (current or next row)
6
7/// A column index within a trace table.
8///
9/// Analogous to [`Wire`](plonkish_cat::Wire) in plonkish-cat,
10/// but in the context of execution traces rather than flat witness vectors.
11///
12/// # Examples
13///
14/// ```
15/// use machine_cat::Column;
16///
17/// let col = Column::new(0);
18/// assert_eq!(col.index(), 0);
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct Column(usize);
22
23impl Column {
24    /// Create a new column index.
25    #[must_use]
26    pub fn new(index: usize) -> Self {
27        Self(index)
28    }
29
30    /// The underlying index.
31    #[must_use]
32    pub fn index(self) -> usize {
33        self.0
34    }
35}
36
37impl core::fmt::Display for Column {
38    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39        write!(f, "c{}", self.0)
40    }
41}
42
43/// The number of columns in a trace table.
44///
45/// This is the "shape" of an AIR: two AIRs with the same
46/// [`ColumnCount`] operate on trace tables of the same width.
47///
48/// # Examples
49///
50/// ```
51/// use machine_cat::ColumnCount;
52///
53/// let a = ColumnCount::new(2);
54/// let b = ColumnCount::new(3);
55/// assert_eq!((a + b).count(), 5);
56/// ```
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
58pub struct ColumnCount(usize);
59
60impl ColumnCount {
61    /// Create a new column count.
62    #[must_use]
63    pub fn new(n: usize) -> Self {
64        Self(n)
65    }
66
67    /// The underlying count.
68    #[must_use]
69    pub fn count(self) -> usize {
70        self.0
71    }
72
73    /// Zero columns (the unit object for tensor product).
74    #[must_use]
75    pub fn zero() -> Self {
76        Self(0)
77    }
78
79    /// Tensor product: parallel composition of column spaces.
80    #[must_use]
81    pub fn tensor(self, other: Self) -> Self {
82        Self(self.0 + other.0)
83    }
84}
85
86impl std::ops::Add for ColumnCount {
87    type Output = Self;
88    fn add(self, rhs: Self) -> Self {
89        self.tensor(rhs)
90    }
91}
92
93impl core::fmt::Display for ColumnCount {
94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95        write!(f, "{}", self.0)
96    }
97}
98
99/// A row-relative column reference.
100///
101/// AIR constraint expressions use [`ColumnRef`] to address
102/// values in the **current** row or the **next** row.
103/// This is the key distinction from plonkish-cat's absolute
104/// [`Wire`](plonkish_cat::Wire) references.
105///
106/// # Examples
107///
108/// ```
109/// use machine_cat::{Column, ColumnRef};
110///
111/// let curr = ColumnRef::Current(Column::new(0));
112/// let next = ColumnRef::Next(Column::new(1));
113/// assert_eq!(curr.column().index(), 0);
114/// assert_eq!(next.column().index(), 1);
115/// ```
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117pub enum ColumnRef {
118    /// The value of a column in the current row.
119    Current(Column),
120    /// The value of a column in the next row.
121    Next(Column),
122}
123
124impl ColumnRef {
125    /// The referenced column, regardless of row position.
126    #[must_use]
127    pub fn column(self) -> Column {
128        match self {
129            Self::Current(c) | Self::Next(c) => c,
130        }
131    }
132
133    /// Whether this references the current row.
134    #[must_use]
135    pub fn is_current(self) -> bool {
136        matches!(self, Self::Current(_))
137    }
138
139    /// Whether this references the next row.
140    #[must_use]
141    pub fn is_next(self) -> bool {
142        matches!(self, Self::Next(_))
143    }
144}
145
146impl core::fmt::Display for ColumnRef {
147    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148        match self {
149            Self::Current(c) => write!(f, "curr.{c}"),
150            Self::Next(c) => write!(f, "next.{c}"),
151        }
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn column_count_tensor_is_addition() {
161        let a = ColumnCount::new(3);
162        let b = ColumnCount::new(4);
163        assert_eq!(a.tensor(b), ColumnCount::new(7));
164        assert_eq!(a + b, ColumnCount::new(7));
165    }
166
167    #[test]
168    fn column_ref_accessors() {
169        let curr = ColumnRef::Current(Column::new(2));
170        assert!(curr.is_current());
171        assert!(!curr.is_next());
172        assert_eq!(curr.column(), Column::new(2));
173
174        let next = ColumnRef::Next(Column::new(5));
175        assert!(next.is_next());
176        assert!(!next.is_current());
177        assert_eq!(next.column(), Column::new(5));
178    }
179
180    #[test]
181    fn column_display() {
182        assert_eq!(format!("{}", Column::new(3)), "c3");
183    }
184
185    #[test]
186    fn column_ref_display() {
187        assert_eq!(format!("{}", ColumnRef::Current(Column::new(0))), "curr.c0");
188        assert_eq!(format!("{}", ColumnRef::Next(Column::new(1))), "next.c1");
189    }
190}