kimchi_msm/serialization/
lookups.rs1use crate::{logup::LookupTableID, Logup, LIMB_BITSIZE, N_LIMBS};
2use ark_ff::PrimeField;
3use num_bigint::BigUint;
4use o1_utils::FieldHelpers;
5use std::marker::PhantomData;
6use strum_macros::EnumIter;
7
8#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, EnumIter)]
10pub enum LookupTable<Ff> {
11 RangeCheck15,
13 RangeCheck4,
15 RangeCheck14Abs,
17 RangeCheck9Abs,
19 RangeCheckFfHighest(PhantomData<Ff>),
22 MultiplicationBus,
24}
25
26impl<Ff: PrimeField> LookupTableID for LookupTable<Ff> {
27 fn to_u32(&self) -> u32 {
28 match self {
29 Self::RangeCheck15 => 1,
30 Self::RangeCheck4 => 2,
31 Self::RangeCheck14Abs => 3,
32 Self::RangeCheck9Abs => 4,
33 Self::RangeCheckFfHighest(_) => 5,
34 Self::MultiplicationBus => 6,
35 }
36 }
37
38 fn from_u32(value: u32) -> Self {
39 match value {
40 1 => Self::RangeCheck15,
41 2 => Self::RangeCheck4,
42 3 => Self::RangeCheck14Abs,
43 4 => Self::RangeCheck9Abs,
44 5 => Self::RangeCheckFfHighest(PhantomData),
45 6 => Self::MultiplicationBus,
46 _ => panic!("Invalid lookup table id"),
47 }
48 }
49
50 fn is_fixed(&self) -> bool {
51 match self {
52 Self::RangeCheck15 => true,
53 Self::RangeCheck4 => true,
54 Self::RangeCheck14Abs => true,
55 Self::RangeCheck9Abs => true,
56 Self::RangeCheckFfHighest(_) => true,
57 Self::MultiplicationBus => false,
58 }
59 }
60
61 fn runtime_create_column(&self) -> bool {
62 match self {
63 Self::MultiplicationBus => false,
64 _ => panic!("runtime_create_column was called on a non-runtime table"),
65 }
66 }
67
68 fn length(&self) -> usize {
69 match self {
70 Self::RangeCheck15 => 1 << 15,
71 Self::RangeCheck4 => 1 << 4,
72 Self::RangeCheck14Abs => 1 << 15,
73 Self::RangeCheck9Abs => 1 << 10,
74 Self::RangeCheckFfHighest(_) => TryFrom::try_from(
75 crate::serialization::interpreter::ff_modulus_highest_limb::<Ff>(),
76 )
77 .unwrap(),
78 Self::MultiplicationBus => 1 << 15,
79 }
80 }
81
82 fn ix_by_value<F: PrimeField>(&self, value: &[F]) -> Option<usize> {
84 let value = value[0];
85 if self.is_fixed() {
86 assert!(self.is_member(value).unwrap());
87 }
88
89 match self {
90 Self::RangeCheck15 => Some(TryFrom::try_from(value.to_biguint()).unwrap()),
91 Self::RangeCheck4 => Some(TryFrom::try_from(value.to_biguint()).unwrap()),
92 Self::RangeCheck14Abs => {
93 if value < F::from(1u64 << 14) {
94 Some(TryFrom::try_from(value.to_biguint()).unwrap())
95 } else {
96 Some(
97 TryFrom::try_from((value + F::from(2 * (1u64 << 14))).to_biguint())
98 .unwrap(),
99 )
100 }
101 }
102 Self::RangeCheck9Abs => {
103 if value < F::from(1u64 << 9) {
104 Some(TryFrom::try_from(value.to_biguint()).unwrap())
105 } else {
106 Some(
107 TryFrom::try_from((value + F::from(2 * (1u64 << 9))).to_biguint()).unwrap(),
108 )
109 }
110 }
111 Self::RangeCheckFfHighest(_) => Some(TryFrom::try_from(value.to_biguint()).unwrap()),
112 Self::MultiplicationBus => None,
113 }
114 }
115
116 fn all_variants() -> Vec<Self> {
117 vec![
118 Self::RangeCheck15,
119 Self::RangeCheck4,
120 Self::RangeCheck14Abs,
121 Self::RangeCheck9Abs,
122 Self::RangeCheckFfHighest(PhantomData),
123 Self::MultiplicationBus,
124 ]
125 }
126}
127
128impl<Ff: PrimeField> LookupTable<Ff> {
129 fn entries_ff_highest<F: PrimeField>(domain_d1_size: u64) -> Vec<F> {
130 let top_modulus_f =
131 F::from_biguint(&crate::serialization::interpreter::ff_modulus_highest_limb::<Ff>())
132 .unwrap();
133 (0..domain_d1_size)
134 .map(|i| {
135 if F::from(i) < top_modulus_f {
136 F::from(i)
137 } else {
138 F::zero()
139 }
140 })
141 .collect()
142 }
143
144 pub fn entries<F: PrimeField>(&self, domain_d1_size: u64) -> Option<Vec<F>> {
146 assert!(domain_d1_size >= (1 << 15));
147 match self {
148 Self::RangeCheck15 => Some((0..domain_d1_size).map(|i| F::from(i)).collect()),
149 Self::RangeCheck4 => Some(
150 (0..domain_d1_size)
151 .map(|i| if i < (1 << 4) { F::from(i) } else { F::zero() })
152 .collect(),
153 ),
154 Self::RangeCheck14Abs => Some(
155 (0..domain_d1_size)
156 .map(|i| {
157 if i < (1 << 14) {
158 F::from(i)
160 } else if i < 2 * (1 << 14) {
161 F::from(i) - F::from(2u64 * (1 << 14))
163 } else {
164 F::zero()
165 }
166 })
167 .collect(),
168 ),
169
170 Self::RangeCheck9Abs => Some(
171 (0..domain_d1_size)
172 .map(|i| {
173 if i < (1 << 9) {
174 F::from(i)
176 } else if i < 2 * (1 << 9) {
177 F::from(i) - F::from(2u64 * (1 << 9))
179 } else {
180 F::zero()
181 }
182 })
183 .collect(),
184 ),
185 Self::RangeCheckFfHighest(_) => Some(Self::entries_ff_highest::<F>(domain_d1_size)),
186 _ => panic!("not possible"),
187 }
188 }
189
190 pub fn is_member<F: PrimeField>(&self, value: F) -> Option<bool> {
192 match self {
193 Self::RangeCheck15 => Some(value.to_biguint() < BigUint::from(2u128.pow(15))),
194 Self::RangeCheck4 => Some(value.to_biguint() < BigUint::from(2u128.pow(4))),
195 Self::RangeCheck14Abs => {
196 Some(value < F::from(1u64 << 14) || value >= F::zero() - F::from(1u64 << 14))
197 }
198 Self::RangeCheck9Abs => {
199 Some(value < F::from(1u64 << 9) || value >= F::zero() - F::from(1u64 << 9))
200 }
201 Self::RangeCheckFfHighest(_) => {
202 let f_bui: BigUint = TryFrom::try_from(Ff::MODULUS).unwrap();
203 let top_modulus_f: F =
204 F::from_biguint(&(f_bui >> ((N_LIMBS - 1) * LIMB_BITSIZE))).unwrap();
205 Some(value < top_modulus_f)
206 }
207 Self::MultiplicationBus => None,
208 }
209 }
210}
211
212pub type Lookup<F, Ff> = Logup<F, LookupTable<Ff>>;