use crate::aig::{Aig, Lit, Word};
fn full_adder(aig: &mut Aig, a: Lit, b: Lit, cin: Lit) -> (Lit, Lit) {
let a_xor_b = aig.xor(a, b);
let sum = aig.xor(a_xor_b, cin);
let and_ab = aig.and(a, b);
let and_prop = aig.and(a_xor_b, cin);
let cout = aig.or(and_ab, and_prop);
(sum, cout)
}
fn sub_with_uge(aig: &mut Aig, a: &Word, b: &Word) -> (Word, Lit) {
debug_assert_eq!(a.len(), b.len(), "sub_with_uge: operand width mismatch");
let mut carry = Lit::TRUE;
let mut diff = Word::with_capacity(a.len());
for (&x, &y) in a.iter().zip(b) {
let (s, c) = full_adder(aig, x, y.not(), carry);
diff.push(s);
carry = c;
}
(diff, carry)
}
pub fn blast_mul(aig: &mut Aig, a: &Word, b: &Word) -> Word {
debug_assert_eq!(a.len(), b.len(), "blast_mul: operand width mismatch");
let w = a.len();
let mut acc: Word = vec![Lit::FALSE; w];
for (i, &bi) in b.iter().enumerate() {
let mut carry = Lit::FALSE;
for j in i..w {
let pp = aig.and(a[j - i], bi);
let (sum, cout) = full_adder(aig, acc[j], pp, carry);
acc[j] = sum;
carry = cout;
}
}
acc
}
pub fn blast_udivrem(aig: &mut Aig, a: &Word, b: &Word) -> (Word, Word) {
debug_assert_eq!(a.len(), b.len(), "blast_udivrem: operand width mismatch");
let w = a.len();
let mut rem: Word = vec![Lit::FALSE; w];
let mut quo: Word = vec![Lit::FALSE; w];
for i in (0..w).rev() {
let top = rem[w - 1];
for j in (1..w).rev() {
rem[j] = rem[j - 1];
}
rem[0] = a[i];
let (diff, low_ge) = sub_with_uge(aig, &rem, b);
let ge = aig.or(top, low_ge);
quo[i] = ge;
for j in 0..w {
rem[j] = aig.mux(ge, diff[j], rem[j]);
}
}
let b_zero = b
.iter()
.fold(Lit::FALSE, |acc, &bit| aig.or(acc, bit))
.not();
let quo = quo.iter().map(|&q| aig.mux(b_zero, Lit::TRUE, q)).collect();
let rem = rem
.iter()
.zip(a)
.map(|(&r, &ai)| aig.mux(b_zero, ai, r))
.collect();
(quo, rem)
}
pub fn blast_udiv(aig: &mut Aig, a: &Word, b: &Word) -> Word {
blast_udivrem(aig, a, b).0
}
pub fn blast_urem(aig: &mut Aig, a: &Word, b: &Word) -> Word {
let q = blast_udiv(aig, a, b);
let prod = blast_mul(aig, &q, b);
crate::blast::arith::blast_sub(aig, a, &prod)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::aig::{word_input, word_value};
use crate::eval::{Env, eval_bv};
use crate::term::{BvTerm, Sort};
fn c(value: u128, w: u32) -> Box<BvTerm> {
Box::new(BvTerm::Const {
value,
sort: Sort::new(w),
})
}
fn oracle(x: u128, y: u128, w: u32) -> (u128, u128, u128) {
let env = Env::new();
let e = |t: BvTerm| eval_bv(&t, &env).unwrap();
(
e(BvTerm::Mul(c(x, w), c(y, w))),
e(BvTerm::Udiv(c(x, w), c(y, w))),
e(BvTerm::Urem(c(x, w), c(y, w))),
)
}
struct Blasted {
aig: Aig,
width: u32,
mul: Word,
udiv: Word,
urem: Word,
}
impl Blasted {
fn new(width: u32) -> Self {
let mut aig = Aig::new();
let a = word_input(&mut aig, width);
let b = word_input(&mut aig, width);
let mul = blast_mul(&mut aig, &a, &b);
let udiv = blast_udiv(&mut aig, &a, &b);
let urem = blast_urem(&mut aig, &a, &b);
Blasted {
aig,
width,
mul,
udiv,
urem,
}
}
fn check(&self, x: u128, y: u128) {
let w = self.width;
let inputs: Vec<bool> = (0..w)
.map(|i| (x >> i) & 1 == 1)
.chain((0..w).map(|i| (y >> i) & 1 == 1))
.collect();
let vals = self.aig.simulate(&inputs);
let (mul, udiv, urem) = oracle(x, y, w);
let got = |word: &Word| word_value(&self.aig, &vals, word);
assert_eq!(got(&self.mul), mul, "bvmul {x:#x} {y:#x} width {w}");
assert_eq!(got(&self.udiv), udiv, "bvudiv {x:#x} {y:#x} width {w}");
assert_eq!(got(&self.urem), urem, "bvurem {x:#x} {y:#x} width {w}");
}
}
fn xorshift(s: &mut u64) -> u64 {
*s ^= *s << 13;
*s ^= *s >> 7;
*s ^= *s << 17;
*s
}
#[test]
fn exhaustive_width8_all_muldiv_ops_match_evaluator() {
let blasted = Blasted::new(8);
for x in 0..=0xFFu128 {
for y in 0..=0xFFu128 {
blasted.check(x, y);
}
}
}
#[test]
fn randomized_width16_matches_evaluator() {
let blasted = Blasted::new(16);
let mut s: u64 = 0xB1A5_7016_0000_0016;
for _ in 0..2000 {
let x = (xorshift(&mut s) & 0xFFFF) as u128;
let y = (xorshift(&mut s) & 0xFFFF) as u128;
blasted.check(x, y);
blasted.check(x, 0);
blasted.check(x, 1);
blasted.check(x, 0xFFFF); }
for x in [0u128, 1, 0x7FFF, 0x8000, 0xFFFF] {
for y in [0u128, 1, 0x7FFF, 0x8000, 0xFFFF] {
blasted.check(x, y);
}
}
}
#[test]
fn randomized_width32_matches_evaluator() {
let blasted = Blasted::new(32);
let mut s: u64 = 0xDEC0_5008_0000_0032;
for _ in 0..100 {
let x = (xorshift(&mut s) & 0xFFFF_FFFF) as u128;
let y = (xorshift(&mut s) & 0xFFFF_FFFF) as u128;
blasted.check(x, y);
blasted.check(x, 0);
blasted.check(x, 1);
blasted.check(x, x);
let (lo, hi) = (x.min(y), x.max(y));
if lo < hi {
blasted.check(lo, hi);
}
}
for x in [0u128, 1, 2, 0xFFFF_FFFF, 0x8000_0000, 0x7FFF_FFFF] {
for y in [0u128, 1, 2, 0xFFFF_FFFF, 0x8000_0000, 0x7FFF_FFFF] {
blasted.check(x, y);
}
}
}
#[test]
fn randomized_width64_matches_evaluator() {
let blasted = Blasted::new(64);
let mut s: u64 = 0xDEC0_5008_0000_0064;
for _ in 0..100 {
let x = xorshift(&mut s) as u128;
let y = xorshift(&mut s) as u128;
blasted.check(x, y);
blasted.check(x, 0);
blasted.check(x, 1);
blasted.check(x, x);
let (lo, hi) = (x.min(y), x.max(y));
if lo < hi {
blasted.check(lo, hi);
}
}
for x in [
0u128,
1,
2,
u64::MAX as u128,
1u128 << 63,
(1u128 << 63) - 1,
] {
for y in [
0u128,
1,
2,
u64::MAX as u128,
1u128 << 63,
(1u128 << 63) - 1,
] {
blasted.check(x, y);
}
}
}
}