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