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