1use crate::air::Air;
19use crate::air_expr::AirExpr;
20use crate::column::{Column, ColumnCount};
21use crate::error::Error;
22use crate::trace::Trace;
23use field_cat::Field;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub struct StepCount(usize);
37
38impl StepCount {
39 #[must_use]
41 pub fn new(n: usize) -> Self {
42 Self(n)
43 }
44
45 #[must_use]
47 pub fn count(self) -> usize {
48 self.0
49 }
50}
51
52#[derive(Debug, Clone)]
63pub struct FibonacciInput<F: Field> {
64 initial_a: F,
65 initial_b: F,
66 num_steps: StepCount,
67}
68
69impl<F: Field> FibonacciInput<F> {
70 #[must_use]
72 pub fn new(initial_a: F, initial_b: F, num_steps: StepCount) -> Self {
73 Self {
74 initial_a,
75 initial_b,
76 num_steps,
77 }
78 }
79
80 #[must_use]
82 pub fn initial_a(&self) -> &F {
83 &self.initial_a
84 }
85
86 #[must_use]
88 pub fn initial_b(&self) -> &F {
89 &self.initial_b
90 }
91
92 #[must_use]
94 pub fn num_steps(&self) -> StepCount {
95 self.num_steps
96 }
97}
98
99#[derive(Debug, Clone, Copy)]
122pub struct FibonacciAir;
123
124impl<F: Field> Air<F> for FibonacciAir {
125 type Input = FibonacciInput<F>;
126
127 fn column_count(&self) -> ColumnCount {
128 ColumnCount::new(2)
129 }
130
131 fn constraints(&self) -> Vec<AirExpr<F>> {
132 let current_a = AirExpr::current(Column::new(0));
133 let current_b = AirExpr::current(Column::new(1));
134 let next_a = AirExpr::next(Column::new(0));
135 let next_b = AirExpr::next(Column::new(1));
136
137 vec![
138 next_a - current_b.clone(),
140 next_b - current_a - current_b,
142 ]
143 }
144
145 fn generate_trace(&self, input: &FibonacciInput<F>) -> Result<Trace<F>, Error> {
146 let num_rows = input.num_steps.0 + 1;
147 let rows: Vec<Vec<F>> = std::iter::successors(
149 Some((input.initial_a.clone(), input.initial_b.clone())),
150 |prev| Some((prev.1.clone(), prev.0.clone() + prev.1.clone())),
151 )
152 .take(num_rows)
153 .map(|(a, b)| vec![a, b])
154 .collect();
155
156 Trace::from_rows(ColumnCount::new(2), &rows)
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::column::Column;
164 use field_cat::F101;
165
166 #[test]
167 fn fibonacci_trace_correctness() -> Result<(), Error> {
168 let air = FibonacciAir;
169 let input = FibonacciInput::new(F101::new(1), F101::new(1), StepCount::new(7));
170 let trace = air.generate_trace(&input)?;
171
172 assert_eq!(trace.row_count().count(), 8);
174 assert_eq!(trace.get(0, Column::new(0))?, F101::new(1));
175 assert_eq!(trace.get(0, Column::new(1))?, F101::new(1));
176 assert_eq!(trace.get(7, Column::new(0))?, F101::new(21));
177 assert_eq!(trace.get(7, Column::new(1))?, F101::new(34));
178 Ok(())
179 }
180
181 #[test]
182 fn fibonacci_constraints_satisfied() -> Result<(), Error> {
183 let air = FibonacciAir;
184 let input = FibonacciInput::new(F101::new(1), F101::new(1), StepCount::new(7));
185 let trace = air.generate_trace(&input)?;
186 let constraints = air.constraints();
187
188 (0..trace.row_count().count() - 1).try_for_each(|row| {
190 let assign = trace.row_pair_assignment(row)?;
191 constraints.iter().try_for_each(|c| {
192 let val = c.evaluate(&assign)?;
193 if val == F101::zero() {
194 Ok(())
195 } else {
196 Err(Error::UnsatisfiedAirConstraint { row })
197 }
198 })
199 })
200 }
201
202 #[test]
203 fn fibonacci_single_step() -> Result<(), Error> {
204 let air = FibonacciAir;
205 let input = FibonacciInput::new(F101::new(3), F101::new(5), StepCount::new(1));
206 let trace = air.generate_trace(&input)?;
207
208 assert_eq!(trace.row_count().count(), 2);
210 assert_eq!(trace.get(0, Column::new(0))?, F101::new(3));
211 assert_eq!(trace.get(0, Column::new(1))?, F101::new(5));
212 assert_eq!(trace.get(1, Column::new(0))?, F101::new(5));
213 assert_eq!(trace.get(1, Column::new(1))?, F101::new(8));
214 Ok(())
215 }
216}