1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Clamp a value to a packed unsigned range.
//
// The bounds operand packs lo (bits 0..15) and hi is derived as
// (bounds >> 16) | lo, guaranteeing hi >= lo. Returns value.clamp(lo, hi).
use crate::ir::{Expr, Program};
use crate::ops::primitive;
use crate::ops::{AlgebraicLaw, OpSpec, U32_OUTPUTS, U32_U32_INPUTS};
pub const LAWS: &[AlgebraicLaw] = &[AlgebraicLaw::Bounded {
lo: 0,
hi: u32::MAX,
}];
/// Clamp operation.
#[derive(Debug, Clone, Copy, Default)]
pub struct Clamp;
impl Clamp {
/// Declarative operation specification.
///
/// Laws are declared as explicit `AlgebraicLaw` values on `SPEC`.
pub const SPEC: OpSpec = OpSpec::composition_inlinable(
"primitive.math.clamp",
U32_U32_INPUTS,
U32_OUTPUTS,
LAWS,
Self::program,
);
/// Build the canonical IR program.
///
/// # Examples
///
/// ```
/// use vyre::ir::Expr;
/// use vyre::ops::primitive::clamp::Clamp;
///
/// let _expr = Expr::select(Expr::le(Expr::u32(5), Expr::u32(9)), Expr::u32(5), Expr::u32(9));
/// let program = Clamp::program();
/// assert!(!program.entry().is_empty());
/// ```
#[must_use]
pub fn program() -> Program {
primitive::binary_u32_program(|value, bounds| {
let lo = Expr::bitand(bounds.clone(), Expr::u32(0xFFFF));
let hi = Expr::bitor(Expr::shr(bounds, Expr::u32(16)), lo.clone());
// clamp = max(lo, min(hi, value))
let clamped_hi = Expr::select(Expr::le(value.clone(), hi.clone()), value, hi);
Expr::select(Expr::ge(clamped_hi.clone(), lo.clone()), clamped_hi, lo)
})
}
}