binius_core/constraint_system/
value_table.rs1use std::ops::Deref;
4
5use super::{ValueIndex, ValueSegment, ValueVec, ValueVecLayout, WordSource};
6use crate::word::Word;
7
8#[derive(Clone, Debug)]
48pub struct ValueTable<Data = Vec<Word>> {
49 layout: ValueVecLayout,
51 log_instances: usize,
53 n_hidden_words: usize,
59 data: Data,
65}
66
67impl<Data: Deref<Target = [Word]>> ValueTable<Data> {
68 pub fn from_hidden_words(layout: ValueVecLayout, log_instances: usize, data: Data) -> Self {
81 let n_hidden_words = layout.combined_len() - layout.offset_inout();
82 assert_eq!(
83 data.len(),
84 n_hidden_words << log_instances,
85 "the data must hold one row per hidden word, one column per instance"
86 );
87 Self {
88 layout,
89 log_instances,
90 n_hidden_words,
91 data,
92 }
93 }
94
95 pub const fn log_instances(&self) -> usize {
97 self.log_instances
98 }
99
100 pub const fn n_instances(&self) -> usize {
102 1usize << self.log_instances
103 }
104
105 pub const fn layout(&self) -> &ValueVecLayout {
107 &self.layout
108 }
109
110 pub const fn n_hidden_words(&self) -> usize {
112 self.n_hidden_words
113 }
114
115 pub fn as_words(&self) -> &[Word] {
120 &self.data
121 }
122
123 pub fn instance_value_vec(&self, instance: usize, constants: &[Word]) -> ValueVec {
134 assert!(instance < self.n_instances(), "instance index out of range");
135 assert_eq!(
136 constants.len(),
137 self.layout.n_const,
138 "constants length must match the layout's constant count"
139 );
140
141 let mut values = ValueVec::new(&self.layout);
143 for (i, &constant) in constants.iter().enumerate() {
144 values[ValueIndex::constant(i as u32)] = constant;
145 }
146
147 for row in 0..self.n_hidden_words {
150 *values.word_mut((self.layout.offset_inout() + row) as u32) =
151 self.data[(row << self.log_instances) + instance];
152 }
153
154 values
155 }
156
157 pub fn instance_words<'a>(
166 &'a self,
167 instance: usize,
168 constants: &'a [Word],
169 ) -> TableInstance<'a, Data> {
170 assert!(instance < self.n_instances(), "instance index out of range");
171 assert_eq!(
172 constants.len(),
173 self.layout.n_const,
174 "constants length must match the layout's constant count"
175 );
176 TableInstance {
177 table: self,
178 instance,
179 constants,
180 }
181 }
182}
183
184#[derive(Clone, Copy)]
187pub struct TableInstance<'a, Data> {
188 table: &'a ValueTable<Data>,
189 instance: usize,
190 constants: &'a [Word],
191}
192
193impl<Data: Deref<Target = [Word]>> WordSource for TableInstance<'_, Data> {
194 #[inline]
195 fn word(&self, index: ValueIndex) -> Word {
196 if index.segment() == ValueSegment::Constant {
198 return self.constants[index.index() as usize];
199 }
200
201 let row = self.table.layout.word_offset(index) - self.table.layout.offset_inout();
203 self.table.data[(row << self.table.log_instances) + self.instance]
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use proptest::{collection, prelude::any, prop_assert_eq, proptest};
210
211 use super::*;
212
213 fn layout() -> ValueVecLayout {
215 ValueVecLayout {
216 n_const: 1,
217 n_inout: 2,
218 n_witness: 1,
219 n_internal: 0,
220 n_scratch: 0,
221 }
222 }
223
224 fn table() -> ValueTable {
226 let n_hidden_words = layout().combined_len() - layout().offset_inout();
227 let data = (0..n_hidden_words)
228 .flat_map(|row| {
229 (0..2).map(move |instance| Word::from_u64(0x10 * row as u64 + instance))
230 })
231 .collect();
232 ValueTable::from_hidden_words(layout(), 1, data)
233 }
234
235 #[test]
236 fn the_row_count_follows_from_the_layout() {
237 let table = table();
238 assert_eq!(table.log_instances(), 1);
239 assert_eq!(table.n_instances(), 2);
240 assert_eq!(table.n_hidden_words(), 3);
242 assert_eq!(table.as_words().len(), 6);
243 }
244
245 #[test]
246 #[should_panic(expected = "one row per hidden word")]
247 fn a_buffer_of_the_wrong_length_is_rejected() {
248 ValueTable::from_hidden_words(layout(), 1, vec![Word::ZERO; 5]);
249 }
250
251 #[test]
253 fn an_instance_reads_back_as_its_own_column() {
254 let table = table();
255 let constants = [Word::from_u64(0xc0)];
256
257 for instance in 0..2 {
258 let values = table.instance_value_vec(instance, &constants);
259 assert_eq!(values[ValueIndex::constant(0)], constants[0]);
260 assert_eq!(values[ValueIndex::inout(0)], Word::from_u64(instance as u64));
261 assert_eq!(values[ValueIndex::inout(1)], Word::from_u64(0x10 + instance as u64));
262 assert_eq!(values[ValueIndex::private(0)], Word::from_u64(0x20 + instance as u64));
263 }
264 }
265
266 #[test]
267 #[should_panic(expected = "instance index out of range")]
268 fn reading_past_the_last_instance_panics() {
269 table().instance_value_vec(2, &[Word::from_u64(0xc0)]);
270 }
271
272 #[test]
273 #[should_panic(expected = "constants length must match")]
274 fn the_wrong_number_of_constants_is_rejected() {
275 table().instance_value_vec(0, &[]);
276 }
277
278 #[test]
279 fn instance_words_reads_back_as_its_own_column() {
280 let table = table();
281 let constants = [Word::from_u64(0xc0)];
282
283 for instance in 0..2 {
284 let words = table.instance_words(instance, &constants);
285 assert_eq!(words.word(ValueIndex::constant(0)), constants[0]);
286 assert_eq!(words.word(ValueIndex::inout(0)), Word::from_u64(instance as u64));
287 assert_eq!(words.word(ValueIndex::inout(1)), Word::from_u64(0x10 + instance as u64));
288 assert_eq!(words.word(ValueIndex::private(0)), Word::from_u64(0x20 + instance as u64));
289 }
290 }
291
292 #[test]
293 #[should_panic(expected = "instance index out of range")]
294 fn instance_words_past_the_last_instance_panics() {
295 table().instance_words(2, &[Word::from_u64(0xc0)]);
296 }
297
298 #[test]
299 #[should_panic(expected = "constants length must match")]
300 fn instance_words_rejects_the_wrong_number_of_constants() {
301 table().instance_words(0, &[]);
302 }
303
304 proptest! {
305 #[test]
308 fn instance_words_matches_instance_value_vec(
309 data in collection::vec(any::<u64>(), 6..=6),
310 constant in any::<u64>(),
311 ) {
312 let data: Vec<Word> = data.into_iter().map(Word::from_u64).collect();
313 let table = ValueTable::from_hidden_words(layout(), 1, data);
314 let constants = [Word::from_u64(constant)];
315 let indices = [
316 ValueIndex::constant(0),
317 ValueIndex::inout(0),
318 ValueIndex::inout(1),
319 ValueIndex::private(0),
320 ];
321
322 for instance in 0..table.n_instances() {
323 let reference = table.instance_value_vec(instance, &constants);
324 let words = table.instance_words(instance, &constants);
325 for &index in &indices {
326 prop_assert_eq!(words.word(index), reference[index]);
327 }
328 }
329 }
330 }
331}