@@ -151,3 +151,77 @@ fn test_broadcast_decomposition() {
151151 context. circuit . check_yields ( ) ;
152152 context. validate_circuit ( ) ;
153153}
154+
155+ #[ test]
156+ fn test_mixed_m31_and_qm31_constants_small ( ) {
157+ let mut context = TraceContext :: default ( ) ;
158+ // Add `u`.
159+ // TODO(Leo): remove this once `u` is added to the default constants.
160+ context. constant ( qm31_from_u32s ( 0 , 0 , 1 , 0 ) ) ;
161+ // General (non-broadcast, non-base-field) QM31 constant. All limbs (1, 2, 3, 4) live in the
162+ // chain, so no base decomposition is triggered.
163+ context. constant ( qm31_from_u32s ( 1 , 2 , 3 , 4 ) ) ;
164+ finalize_constants_with_min_base ( & mut context, 5 ) ;
165+
166+ // The plus-one chain populates [5]..=[8] for values 2..=5. The QM31 basis allocates [9] = u*u,
167+ // [10] = u² - 2 = i, [11] = i*u = iu. The ones vector is built as ([1] + [10]) + ([2] + [11])
168+ // yielding wires [12], [13], [14] (unused here, since (1, 2, 3, 4) isn't a broadcast). The
169+ // general QM31 constant is then assembled as a + b*i + c*u + d*iu:
170+ // [15] = [5] * [10] (2 * i), [16] = [1] + [15] (1 + 2*i)
171+ // [17] = [6] * [2] (3 * u), [18] = [7] * [11] (4 * iu), [19] = [17] + [18]
172+ // [3] = [16] + [19] (finally constrain the constant).
173+ expect ! [ [ r#"
174+ [0] = [0] + [0]
175+ [2] = [2] + [0]
176+ [1] = [1] + [0]
177+ [5] = [1] + [1]
178+ [6] = [5] + [1]
179+ [7] = [6] + [1]
180+ [8] = [7] + [1]
181+ [12] = [1] + [10]
182+ [13] = [2] + [11]
183+ [14] = [12] + [13]
184+ [16] = [1] + [15]
185+ [19] = [17] + [18]
186+ [3] = [16] + [19]
187+ [10] = [9] - [5]
188+ [4] = [2] * [1]
189+ [9] = [2] * [2]
190+ [11] = [10] * [2]
191+ [15] = [5] * [10]
192+ [17] = [6] * [2]
193+ [18] = [7] * [11]
194+ [4] = [2]
195+ output [2]
196+ "# ] ]
197+ . assert_eq ( & format ! ( "{:?}" , context. circuit) ) ;
198+
199+ // The reserved Var carries the assembled QM31 value; the partial sums hold the two halves.
200+ assert_eq ! ( context. get( Var { idx: 3 } ) , qm31_from_u32s( 1 , 2 , 3 , 4 ) ) ;
201+ assert_eq ! ( context. get( Var { idx: 16 } ) , qm31_from_u32s( 1 , 2 , 0 , 0 ) ) ;
202+ assert_eq ! ( context. get( Var { idx: 19 } ) , qm31_from_u32s( 0 , 0 , 3 , 4 ) ) ;
203+ context. circuit . check_yields ( ) ;
204+ context. validate_circuit ( ) ;
205+ }
206+
207+ #[ test]
208+ fn test_mixed_m31_and_qm31_constants_large ( ) {
209+ let mut context = TraceContext :: default ( ) ;
210+ // Add `u`.
211+ // TODO(Leo): remove this once `u` is added to the default constants.
212+ context. constant ( qm31_from_u32s ( 0 , 0 , 1 , 0 ) ) ;
213+ // Add constants of various types.
214+ context. constant ( qm31_from_u32s ( 1000 , 2000 , 3000 , 4000 ) ) ;
215+ context. constant ( qm31_from_u32s ( 1 , 1 , 1 , 1 ) ) ;
216+ context. constant ( qm31_from_u32s ( 2 , 2 , 2 , 2 ) ) ;
217+ context. constant ( qm31_from_u32s ( 666 , 666 , 666 , 666 ) ) ;
218+ context. constant ( qm31_from_u32s ( 3456 , 0 , 0 , 0 ) ) ;
219+ context. constant ( qm31_from_u32s ( 7890 , 0 , 0 , 0 ) ) ;
220+ context. constant ( qm31_from_u32s ( 1234 , 2 , 3 , 4 ) ) ;
221+ context. constant ( qm31_from_u32s ( 0 , 1234 , 0 , 0 ) ) ;
222+ context. constant ( qm31_from_u32s ( 0 , 0 , 1234 , 0 ) ) ;
223+ context. constant ( qm31_from_u32s ( 0 , 0 , 0 , 1234 ) ) ;
224+ finalize_constants ( & mut context) ;
225+ context. circuit . check_yields ( ) ;
226+ context. validate_circuit ( ) ;
227+ }
0 commit comments