1use std::panic::Location;
4
5use super::ops::integer_table;
6use super::{IntScalar, Kind, Scalar, Val, eq, gt, is_wide, known_bit, settle, wide, with_circuit};
7use crate::circuit::OutOfRange;
8use crate::emit::{Relation, emit_compare, emit_round};
9use crate::wire::{Lin, Wire};
10
11#[track_caller]
15pub fn unsupported(what: &str) -> ! {
16 panic!("{}: {what}", Location::caller())
17}
18
19pub trait Mux: Copy {
23 fn select(bit: Val<bool>, off: Self, on: Self) -> Self;
24
25 fn read_dynamic<I: IntScalar>(items: &[Self], index: Val<I>) -> Self {
28 let mut chosen = items[0];
29 for (position, &item) in items.iter().enumerate().skip(1) {
30 chosen = Self::select(eq_position(index, position), chosen, item);
31 }
32 chosen
33 }
34}
35
36fn eq_position<I: IntScalar>(index: Val<I>, position: usize) -> Val<bool> {
37 eq(index, I::from_f64(position as f64))
38}
39
40impl<T: Scalar> Mux for Val<T> {
41 fn select(bit: Val<bool>, off: Val<T>, on: Val<T>) -> Val<T> {
42 if let Some(value) = known_bit(bit) {
43 return if value { on } else { off };
44 }
45 if off.is_wide() || on.is_wide() {
46 return wide::select(bit, &wide::limbs_as::<T, T>(off), &wide::limbs_as::<T, T>(on));
47 }
48 if off.wire() == on.wire() {
49 return off;
50 }
51 with_circuit(|circuit| {
52 let (off_range, on_range) = (circuit.range(off.wire()), circuit.range(on.wire()));
53 let wire = circuit.select(bit.wire(), off.wire(), on.wire());
54 let (low, high) = (off_range.min.min(on_range.min), off_range.max.max(on_range.max));
57 match T::KIND {
58 Kind::Float => settle(circuit, wire, low, high),
59 _ => settle(circuit, wire, low.max(T::MIN), high.min(T::MAX)),
60 }
61 })
62 }
63
64 fn read_dynamic<I: IntScalar>(items: &[Val<T>], index: Val<I>) -> Val<T> {
65 if items.iter().any(Val::is_wide) {
66 let bytes: Vec<wide::Limbs> = items.iter().map(|&item| wide::limbs_as::<T, T>(item)).collect();
67 return wide::read(&bytes, index.wire());
68 }
69 let candidates: Vec<Wire> = items.iter().map(|item| item.wire()).collect();
70 with_circuit(|circuit| {
71 let wire = circuit.index_read(index.wire(), &candidates, OutOfRange::Clamp);
72 let range = circuit.range(wire);
73 settle(circuit, wire, range.min, range.max)
74 })
75 }
76}
77
78impl Mux for () {
79 fn select(_: Val<bool>, _: (), _: ()) {}
80}
81
82impl<M: Mux, const N: usize> Mux for [M; N] {
83 fn select(bit: Val<bool>, off: [M; N], on: [M; N]) -> [M; N] {
84 std::array::from_fn(|position| M::select(bit, off[position], on[position]))
85 }
86}
87
88pub trait Placeholder: Copy {
92 fn placeholder() -> Self;
93}
94impl<T: Scalar> Placeholder for Val<T> {
95 fn placeholder() -> Val<T> {
96 Val::constant(T::default_value())
97 }
98}
99impl Placeholder for () {
100 fn placeholder() {}
101}
102impl<P: Placeholder, const N: usize> Placeholder for [P; N] {
103 fn placeholder() -> [P; N] {
104 [P::placeholder(); N]
105 }
106}
107
108pub fn placeholder<P: Placeholder>() -> P {
109 P::placeholder()
110}
111
112pub trait Conv<T> {
117 fn conv(self) -> T;
118}
119
120impl<T: Scalar> Conv<Val<T>> for Val<T> {
121 fn conv(self) -> Val<T> {
122 self
123 }
124}
125impl Conv<()> for () {
126 fn conv(self) {}
127}
128impl<A: Conv<B>, B, const N: usize> Conv<[B; N]> for [A; N] {
129 fn conv(self) -> [B; N] {
130 self.map(Conv::conv)
131 }
132}
133
134pub trait Lift {
137 type Out;
138 fn lift(self) -> Self::Out;
139}
140
141impl<T: Scalar> Lift for Val<T> {
142 type Out = Val<T>;
143 fn lift(self) -> Val<T> {
144 self
145 }
146}
147impl Lift for () {
148 type Out = ();
149 fn lift(self) {}
150}
151impl<L: Lift, const N: usize> Lift for [L; N] {
152 type Out = [L::Out; N];
153 fn lift(self) -> [L::Out; N] {
154 self.map(Lift::lift)
155 }
156}
157
158pub trait Unify<B>: Sized {
161 type Out;
162 fn left(self) -> Self::Out;
163 fn right(other: B) -> Self::Out;
164}
165
166impl<T: Scalar> Unify<Val<T>> for Val<T> {
167 type Out = Val<T>;
168 fn left(self) -> Val<T> {
169 self
170 }
171 fn right(other: Val<T>) -> Val<T> {
172 other
173 }
174}
175impl<T: Scalar> Unify<T> for Val<T> {
176 type Out = Val<T>;
177 fn left(self) -> Val<T> {
178 self
179 }
180 fn right(other: T) -> Val<T> {
181 Val::constant(other)
182 }
183}
184impl Unify<()> for () {
185 type Out = ();
186 fn left(self) {}
187 fn right(_: ()) {}
188}
189impl<A: Unify<B>, B, const N: usize> Unify<[B; N]> for [A; N] {
190 type Out = [A::Out; N];
191 fn left(self) -> [A::Out; N] {
192 self.map(A::left)
193 }
194 fn right(other: [B; N]) -> [A::Out; N] {
195 other.map(A::right)
196 }
197}
198
199macro_rules! native_conversions {
200 ($($t:ident),*) => {$(
201 impl Conv<Val<$t>> for $t {
202 fn conv(self) -> Val<$t> {
203 Val::constant(self)
204 }
205 }
206 impl Lift for $t {
207 type Out = Val<$t>;
208 fn lift(self) -> Val<$t> {
209 Val::constant(self)
210 }
211 }
212 impl Unify<$t> for $t {
213 type Out = $t;
214 fn left(self) -> $t {
215 self
216 }
217 fn right(other: $t) -> $t {
218 other
219 }
220 }
221 impl Unify<Val<$t>> for $t {
222 type Out = Val<$t>;
223 fn left(self) -> Val<$t> {
224 Val::constant(self)
225 }
226 fn right(other: Val<$t>) -> Val<$t> {
227 other
228 }
229 }
230 )*};
231}
232native_conversions!(bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, f32);
233
234macro_rules! tuple_traits {
235 ($(($($name:ident $other:ident $index:tt),+)),*) => {$(
236 impl<$($name: Placeholder),+> Placeholder for ($($name,)+) {
237 fn placeholder() -> Self {
238 ($($name::placeholder(),)+)
239 }
240 }
241 impl<$($name: Mux),+> Mux for ($($name,)+) {
242 fn select(bit: Val<bool>, off: Self, on: Self) -> Self {
243 ($($name::select(bit, off.$index, on.$index),)+)
244 }
245 }
246 impl<$($name: Conv<$other>, $other),+> Conv<($($other,)+)> for ($($name,)+) {
247 fn conv(self) -> ($($other,)+) {
248 ($(self.$index.conv(),)+)
249 }
250 }
251 impl<$($name: Lift),+> Lift for ($($name,)+) {
252 type Out = ($($name::Out,)+);
253 fn lift(self) -> Self::Out {
254 ($(self.$index.lift(),)+)
255 }
256 }
257 impl<$($name: Unify<$other>, $other),+> Unify<($($other,)+)> for ($($name,)+) {
258 type Out = ($($name::Out,)+);
259 fn left(self) -> Self::Out {
260 ($(self.$index.left(),)+)
261 }
262 fn right(other: ($($other,)+)) -> Self::Out {
263 ($($name::right(other.$index),)+)
264 }
265 }
266 impl<$($name: Copy),+> Elem<usize> for ($($name,)+) {
267 type Out = Self;
268 fn index(items: &[Self], index: usize) -> Self {
269 items[index]
270 }
271 }
272 impl<$($name: Copy),+, I: IntScalar> Elem<Val<I>> for ($($name,)+)
273 where
274 Self: Lift,
275 <Self as Lift>::Out: Mux,
276 {
277 type Out = <Self as Lift>::Out;
278 fn index(items: &[Self], index: Val<I>) -> Self::Out {
279 let lifted: Vec<Self::Out> = items.iter().map(|&item| item.lift()).collect();
280 Mux::read_dynamic(&lifted, index)
281 }
282 }
283 )*};
284}
285tuple_traits!(
286 (A0 B0 0),
287 (A0 B0 0, A1 B1 1),
288 (A0 B0 0, A1 B1 1, A2 B2 2),
289 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3),
290 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4),
291 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5),
292 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6),
293 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6, A7 B7 7),
294 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6, A7 B7 7, A8 B8 8),
295 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6, A7 B7 7, A8 B8 8, A9 B9 9),
296 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6, A7 B7 7, A8 B8 8, A9 B9 9, A10 B10 10),
297 (A0 B0 0, A1 B1 1, A2 B2 2, A3 B3 3, A4 B4 4, A5 B5 5, A6 B6 6, A7 B7 7, A8 B8 8, A9 B9 9, A10 B10 10, A11 B11 11)
298);
299
300pub enum Branch {
305 Static(bool),
306 Dynamic(Val<bool>),
307}
308
309pub trait Cond: Copy {
310 fn branch(self) -> Branch;
311}
312impl Cond for bool {
313 fn branch(self) -> Branch {
314 Branch::Static(self)
315 }
316}
317impl Cond for Val<bool> {
318 fn branch(self) -> Branch {
319 Branch::Dynamic(self)
320 }
321}
322
323pub trait IfElse<A, B> {
325 type Out;
326 fn if_else(self, then: impl FnOnce() -> A, otherwise: impl FnOnce() -> B) -> Self::Out;
327}
328
329impl<A: Unify<B>, B> IfElse<A, B> for bool {
330 type Out = A::Out;
331 fn if_else(self, then: impl FnOnce() -> A, otherwise: impl FnOnce() -> B) -> A::Out {
332 if self { then().left() } else { A::right(otherwise()) }
333 }
334}
335
336impl<A: Unify<B>, B> IfElse<A, B> for Val<bool>
337where
338 A::Out: Lift,
339 <A::Out as Lift>::Out: Mux,
340{
341 type Out = <A::Out as Lift>::Out;
342 fn if_else(self, then: impl FnOnce() -> A, otherwise: impl FnOnce() -> B) -> Self::Out {
343 let on = then().left().lift();
344 let off = A::right(otherwise()).lift();
345 Mux::select(self, off, on)
346 }
347}
348
349pub fn if_else<C: IfElse<A, B>, A, B>(condition: C, then: impl FnOnce() -> A, otherwise: impl FnOnce() -> B) -> C::Out {
350 condition.if_else(then, otherwise)
351}
352
353pub trait Elem<I>: Sized {
357 type Out;
358 fn index(items: &[Self], index: I) -> Self::Out;
359}
360
361impl<T: Scalar> Elem<usize> for Val<T> {
362 type Out = Val<T>;
363 fn index(items: &[Val<T>], index: usize) -> Val<T> {
364 items[index]
365 }
366}
367impl<T: Scalar, I: IntScalar> Elem<Val<I>> for Val<T> {
368 type Out = Val<T>;
369 fn index(items: &[Val<T>], index: Val<I>) -> Val<T> {
370 Mux::read_dynamic(items, index)
371 }
372}
373impl<E: Copy, const N: usize> Elem<usize> for [E; N] {
374 type Out = [E; N];
375 fn index(items: &[[E; N]], index: usize) -> [E; N] {
376 items[index]
377 }
378}
379impl<E: Copy, const N: usize, I: IntScalar> Elem<Val<I>> for [E; N]
380where
381 [E; N]: Lift,
382 <[E; N] as Lift>::Out: Mux,
383{
384 type Out = <[E; N] as Lift>::Out;
385 fn index(items: &[[E; N]], index: Val<I>) -> Self::Out {
386 let lifted: Vec<Self::Out> = items.iter().map(|&item| item.lift()).collect();
387 Mux::read_dynamic(&lifted, index)
388 }
389}
390
391macro_rules! native_elements {
392 ($($t:ident),*) => {$(
393 impl Elem<usize> for $t {
394 type Out = $t;
395 fn index(items: &[$t], index: usize) -> $t {
396 items[index]
397 }
398 }
399 impl<I: IntScalar> Elem<Val<I>> for $t {
400 type Out = Val<$t>;
401 fn index(items: &[$t], index: Val<I>) -> Val<$t> {
402 let constants: Vec<Val<$t>> = items.iter().map(|&item| Val::constant(item)).collect();
403 Mux::read_dynamic(&constants, index)
404 }
405 }
406 )*};
407}
408native_elements!(bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, f32);
409
410pub fn index<E: Elem<I>, I, const N: usize>(items: &[E; N], index: I) -> E::Out {
411 E::index(items, index)
412}
413
414pub trait StoreElem<I>: Sized {
416 fn store(items: &mut [Self], index: I, value: Self);
417}
418impl<E> StoreElem<usize> for E {
419 fn store(items: &mut [E], index: usize, value: E) {
420 items[index] = value;
421 }
422}
423impl<E: Mux, I: IntScalar> StoreElem<Val<I>> for E {
424 fn store(items: &mut [E], index: Val<I>, value: E) {
425 for (position, item) in items.iter_mut().enumerate() {
426 *item = E::select(eq_position(index, position), *item, value);
427 }
428 }
429}
430
431pub fn store<E: StoreElem<I>, I, V: Conv<E>, const N: usize>(items: &mut [E; N], index: I, value: V) {
432 E::store(items, index, value.conv());
433}
434
435pub trait CastTo<T> {
439 type Out;
440 fn cast_to(self) -> Self::Out;
441}
442
443#[track_caller]
444pub fn cast<T, S: CastTo<T>>(value: S) -> S::Out {
445 value.cast_to()
446}
447
448impl<S: Scalar, T: Scalar> CastTo<T> for Val<S> {
449 type Out = Val<T>;
450 #[track_caller]
451 fn cast_to(self) -> Val<T> {
452 cast_value::<S, T>(self, Location::caller())
453 }
454}
455
456fn cast_value<S: Scalar, T: Scalar>(value: Val<S>, at: &'static Location<'static>) -> Val<T> {
457 if value.is_wide() {
458 return match T::KIND {
460 Kind::Int if is_wide::<T>() => Val::from_limbs(wide::limbs_as::<S, T>(value)),
461 Kind::Int => wide::narrow(&wide::limbs_as::<S, S>(value)),
462 _ => panic!("{at}: casting an integer beyond ±2^24 to {} isn't supported yet", T::NAME),
463 };
464 }
465 let same = Val::<T>::from_wire(value.wire());
466 match (S::KIND, T::KIND) {
467 (Kind::Bool, _) => same,
468 (_, Kind::Bool) => unsupported("only bool casts to bool"),
469 (Kind::Float, Kind::Float) => same,
470 (Kind::Int, Kind::Float) => {
471 let range = with_circuit(|circuit| circuit.range(value.wire()));
472 if range.min >= -16_777_216.0 && range.max <= 16_777_216.0 {
473 same
474 } else {
475 panic!("{at}: casting integers beyond ±2^24 to f32 rounds, which isn't supported yet");
476 }
477 }
478 (Kind::Int, Kind::Int) => {
479 if S::MIN >= T::MIN && S::MAX <= T::MAX {
480 return same;
481 }
482 let range = with_circuit(|circuit| circuit.range(value.wire()));
483 if range.min >= T::MIN && range.max <= T::MAX {
484 return same;
485 }
486 if is_wide::<T>() {
487 return Val::from_limbs(wide::limbs_as::<S, T>(value));
489 }
490 let span = T::MAX - T::MIN + 1.0;
492 integer_table(Val::<T>::from_wire(value.wire()), at, |wide| {
493 let wrapped = (wide as f64 - T::MIN).rem_euclid(span) + T::MIN;
494 wrapped as i64
495 })
496 }
497 (Kind::Float, Kind::Int) => truncate::<T>(value.wire(), at),
498 }
499}
500
501fn truncate<T: Scalar>(value: Wire, at: &'static Location<'static>) -> Val<T> {
503 let range = with_circuit(|circuit| circuit.range(value));
504 if !(range.min > -4_194_304.0 && range.max < 4_194_304.0) {
505 panic!("{at}: casting f32 to an integer needs a declared range within ±2^22, got {}..{}", range.min, range.max);
506 }
507 let truncated = with_circuit(|circuit| {
508 let rounded = emit_round(circuit, value);
509 let rounded_up = emit_compare(circuit, rounded, Relation::Greater, value);
511 let rounded_down = emit_compare(circuit, rounded, Relation::Less, value);
512 let zero = circuit.constant(0.0);
513 let positive = emit_compare(circuit, value, Relation::AtLeast, zero);
514 let too_high = circuit.multiply(rounded_up, positive);
515 let negative = circuit.affine(1.0 - positive);
516 let too_low = circuit.multiply(rounded_down, negative);
517 let wire = circuit.affine(Lin::new(vec![(rounded, 1.0), (too_high, -1.0), (too_low, 1.0)], 0.0));
518 circuit.assume_grid_allowing_readers(wire, 1.0);
519 Val::<T>::from_wire(wire)
520 });
521 if range.min >= T::MIN && range.max <= T::MAX {
522 return truncated;
523 }
524 let low = Val::<T>::constant(T::from_f64(T::MIN));
525 let high = Val::<T>::constant(T::from_f64(T::MAX));
526 let above = Mux::select(gt(truncated, high), truncated, high);
527 Mux::select(gt(low, above), above, low)
528}
529
530macro_rules! native_casts {
531 ($($source:ident),*) => {$(
532 native_casts!(@to $source: u8, u16, u32, u64, usize, i8, i16, i32, i64, f32);
533 )*};
534 (@to $source:ident: $($target:ident),*) => {$(
535 impl CastTo<$target> for $source {
536 type Out = $target;
537 fn cast_to(self) -> $target {
538 self as $target
539 }
540 }
541 )*};
542}
543native_casts!(u8, u16, u32, u64, usize, i8, i16, i32, i64, f32);
544
545macro_rules! bool_casts {
546 ($($target:ident),*) => {$(
547 impl CastTo<$target> for bool {
548 type Out = $target;
549 fn cast_to(self) -> $target {
550 self as $target
551 }
552 }
553 )*};
554}
555bool_casts!(u8, u16, u32, u64, usize, i8, i16, i32, i64);
556
557#[cfg(test)]
558mod tests {
559 use super::*;
560 use crate::circuit::Circuit;
561
562 #[test]
563 fn conversions_infer_from_context() {
564 super::super::CONTEXT.with(|context| *context.borrow_mut() = Some(Circuit::new(0)));
565 let value: Val<u8> = Conv::conv(5u8);
566 let picked = if_else(true, || value, || 7);
567 let _: Val<u8> = picked;
568 let native = if_else(false, || 1, || 2);
569 assert_eq!(native, 2);
570 super::super::CONTEXT.with(|context| *context.borrow_mut() = None);
571 }
572}