use std::cmp::Ordering;
use std::fmt;
use super::apint::APInt;
#[derive(Debug, Clone)]
pub struct APSInt {
value: APInt,
is_signed: bool,
bit_width: u32,
}
impl APSInt {
pub fn new(bits: u32, is_signed: bool) -> Self {
Self {
value: APInt::new(bits, 0, is_signed),
is_signed,
bit_width: bits,
}
}
pub fn from_u64(val: u64, bits: u32, is_signed: bool) -> Self {
Self {
value: APInt::new(bits, val, is_signed),
is_signed,
bit_width: bits,
}
}
pub fn from_i64(val: i64, bits: u32, is_signed: bool) -> Self {
let abs = val.unsigned_abs();
let mut ap = APInt::new(bits, abs, is_signed);
if val < 0 {
ap = ap.negate();
}
Self {
value: ap,
is_signed,
bit_width: bits,
}
}
pub fn zero(bits: u32) -> Self {
Self::new(bits, true)
}
pub fn one(bits: u32) -> Self {
Self::from_u64(1, bits, true)
}
pub fn apint(&self) -> &APInt {
&self.value
}
pub fn is_signed(&self) -> bool {
self.is_signed
}
pub fn set_signed(&mut self, s: bool) {
self.is_signed = s;
}
pub fn bit_width(&self) -> u32 {
self.bit_width
}
pub fn is_zero(&self) -> bool {
self.value.is_zero()
}
pub fn is_one(&self) -> bool {
self.value.is_one()
}
pub fn is_negative(&self) -> bool {
self.is_signed && self.value.is_negative()
}
pub fn is_strictly_positive(&self) -> bool {
!self.is_zero() && !self.is_negative()
}
pub fn is_non_negative(&self) -> bool {
!self.is_negative()
}
pub fn cmp(&self, other: &APSInt) -> Ordering {
if self.is_signed && other.is_signed {
if self.value.slt(&other.value) {
Ordering::Less
} else if self.value.sgt(&other.value) {
Ordering::Greater
} else {
Ordering::Equal
}
} else {
if self.value.ult(&other.value) {
Ordering::Less
} else if self.value.ugt(&other.value) {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
pub fn ext_or_trunc(&self, bits: u32) -> Self {
let v = if bits > self.bit_width {
if self.is_signed {
self.value.sext(bits)
} else {
self.value.zext(bits)
}
} else {
self.value.trunc(bits)
};
Self {
value: v,
is_signed: self.is_signed,
bit_width: bits,
}
}
pub fn sext(&self, bits: u32) -> Self {
Self {
value: self.value.sext(bits),
is_signed: self.is_signed,
bit_width: bits,
}
}
pub fn zext(&self, bits: u32) -> Self {
Self {
value: self.value.zext(bits),
is_signed: self.is_signed,
bit_width: bits,
}
}
pub fn trunc(&self, bits: u32) -> Self {
Self {
value: self.value.trunc(bits),
is_signed: self.is_signed,
bit_width: bits,
}
}
pub fn neg(&self) -> Self {
Self {
value: self.value.negate(),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn get_min_value(bits: u32) -> Self {
Self {
value: APInt::get_signed_min_value(bits),
is_signed: true,
bit_width: bits,
}
}
pub fn get_max_value(bits: u32) -> Self {
Self {
value: APInt::get_signed_max_value(bits),
is_signed: true,
bit_width: bits,
}
}
pub fn get_max_unsigned(bits: u32) -> Self {
Self {
value: APInt::get_all_ones(bits),
is_signed: false,
bit_width: bits,
}
}
pub fn try_to_i64(&self) -> Option<i64> {
self.value.to_u64().map(|v| {
if self.is_signed && self.value.is_negative() {
let shift = 64 - self.bit_width;
(((v << shift) as i64) >> shift)
} else {
v as i64
}
})
}
pub fn try_to_u64(&self) -> Option<u64> {
self.value.to_u64()
}
pub fn fits_in(&self, bits: u32) -> bool {
if bits >= self.bit_width {
return true;
}
let truncated = self.trunc(bits);
let extended = if self.is_signed {
truncated.sext(self.bit_width)
} else {
truncated.zext(self.bit_width)
};
self.value.eq(&extended.value)
}
pub fn add(&self, other: &APSInt) -> Self {
Self {
value: self.value.add(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn sub(&self, other: &APSInt) -> Self {
Self {
value: self.value.sub(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn mul(&self, other: &APSInt) -> Self {
Self {
value: self.value.mul(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn sdiv(&self, other: &APSInt) -> Self {
Self {
value: self.value.sdiv(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn udiv(&self, other: &APSInt) -> Self {
Self {
value: self.value.udiv(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn srem(&self, other: &APSInt) -> Self {
Self {
value: self.value.srem(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn urem(&self, other: &APSInt) -> Self {
Self {
value: self.value.urem(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn abs(&self) -> Self {
if self.is_negative() {
self.neg()
} else {
self.clone()
}
}
pub fn bit_and(&self, other: &APSInt) -> Self {
Self {
value: self.value.and(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn bit_or(&self, other: &APSInt) -> Self {
Self {
value: self.value.or(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn bit_xor(&self, other: &APSInt) -> Self {
Self {
value: self.value.xor(&other.value),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn shl(&self, amt: u32) -> Self {
Self {
value: self.value.shl(amt),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn lshr(&self, amt: u32) -> Self {
Self {
value: self.value.lshr(amt),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn ashr(&self, amt: u32) -> Self {
Self {
value: self.value.ashr(amt),
is_signed: self.is_signed,
bit_width: self.bit_width,
}
}
pub fn eq(&self, other: &APSInt) -> bool {
self.value.eq(&other.value)
}
pub fn ne(&self, other: &APSInt) -> bool {
!self.value.eq(&other.value)
}
pub fn slt(&self, other: &APSInt) -> bool {
self.value.slt(&other.value)
}
pub fn sgt(&self, other: &APSInt) -> bool {
self.value.sgt(&other.value)
}
pub fn sle(&self, other: &APSInt) -> bool {
self.value.sle(&other.value)
}
pub fn sge(&self, other: &APSInt) -> bool {
self.value.sge(&other.value)
}
pub fn ult(&self, other: &APSInt) -> bool {
self.value.ult(&other.value)
}
pub fn ugt(&self, other: &APSInt) -> bool {
self.value.ugt(&other.value)
}
pub fn ule(&self, other: &APSInt) -> bool {
self.value.ule(&other.value)
}
pub fn uge(&self, other: &APSInt) -> bool {
self.value.uge(&other.value)
}
pub fn to_string(&self, radix: u8) -> String {
self.value.to_string(radix, self.is_signed)
}
pub fn to_binary_string(&self) -> String {
self.to_string(2)
}
pub fn to_octal_string(&self) -> String {
self.to_string(8)
}
pub fn to_decimal_string(&self) -> String {
self.to_string(10)
}
pub fn to_hex_string(&self) -> String {
self.to_string(16)
}
pub fn from_string(s: &str, radix: u8, bits: u32, is_signed: bool) -> Option<Self> {
let ap = APInt::from_string(s, radix).ok()?;
let mut result = Self {
value: ap,
is_signed,
bit_width: bits,
};
if bits < result.value.bit_width() {
result.value = result.value.trunc(bits);
} else if bits > result.value.bit_width() {
result.value = result.value.zext(bits);
}
Some(result)
}
pub fn get_bit_width(&self) -> u32 {
self.bit_width
}
pub fn get_min_signed_bits(&self) -> u32 {
if self.is_negative() {
let abs_val = self.abs();
abs_val.value.get_active_bits() as u32 + 1
} else {
self.value.get_active_bits() as u32 + 1
}
}
pub fn is_int_n(&self, n: u32) -> bool {
if n >= self.bit_width {
return true;
}
let truncated = self.trunc(n);
let extended = if self.is_signed {
truncated.sext(self.bit_width)
} else {
truncated.zext(self.bit_width)
};
self.value.eq(&extended.value)
}
pub fn is_uint_n(&self, n: u32) -> bool {
if self.is_negative() {
return false;
}
if n >= self.bit_width {
return true;
}
let shifted = self.value.lshr(n);
shifted.is_zero()
}
pub fn is_null_value(&self) -> bool {
self.is_zero()
}
pub fn is_one_value(&self) -> bool {
self.is_one()
}
pub fn is_all_ones_value(&self) -> bool {
self.value.is_all_ones()
}
pub fn get_min_unsigned(bits: u32) -> Self {
Self::zero(bits)
}
pub fn set_bit(&mut self, pos: u32) {
self.value.set_bit(pos);
}
pub fn clear_bit(&mut self, pos: u32) {
self.value.clear_bit(pos);
}
pub fn count_leading_zeros(&self) -> u32 {
self.value.count_leading_zeros()
}
pub fn count_trailing_zeros(&self) -> u32 {
self.value.count_trailing_zeros()
}
pub fn count_population(&self) -> u32 {
self.value.count_population()
}
pub fn get_active_bits(&self) -> u32 {
self.value.get_active_bits() as u32
}
pub fn get_min_unsigned_bits(&self) -> u32 {
if self.is_negative() {
return self.bit_width;
}
self.value.get_active_bits() as u32
}
pub fn sext_or_self(&self, bits: u32) -> Self {
if bits > self.bit_width {
self.sext(bits)
} else {
self.clone()
}
}
pub fn zext_or_self(&self, bits: u32) -> Self {
if bits > self.bit_width {
self.zext(bits)
} else {
self.clone()
}
}
pub fn compare_signed(&self, other: &APSInt) -> Ordering {
if self.value.slt(&other.value) {
Ordering::Less
} else if self.value.sgt(&other.value) {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub fn compare_unsigned(&self, other: &APSInt) -> Ordering {
if self.value.ult(&other.value) {
Ordering::Less
} else if self.value.ugt(&other.value) {
Ordering::Greater
} else {
Ordering::Equal
}
}
pub fn from_raw(val: u64, bits: u32, is_signed: bool) -> Self {
Self::from_u64(val, bits, is_signed)
}
pub fn get_sext_value(&self) -> Option<i64> {
self.try_to_i64()
}
pub fn get_zext_value(&self) -> Option<u64> {
self.try_to_u64()
}
}
impl PartialEq for APSInt {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for APSInt {}
impl PartialOrd for APSInt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
fn lt(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Less
}
fn le(&self, other: &Self) -> bool {
matches!(self.cmp(other), Ordering::Less | Ordering::Equal)
}
fn gt(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Greater
}
fn ge(&self, other: &Self) -> bool {
matches!(self.cmp(other), Ordering::Greater | Ordering::Equal)
}
}
impl Ord for APSInt {
fn cmp(&self, other: &Self) -> Ordering {
self.cmp(other)
}
}
impl fmt::Display for APSInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(v) = self.try_to_i64() {
write!(f, "{}", v)
} else {
write!(f, "<APSInt {} bits>", self.bit_width)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create() {
let a = APSInt::new(32, true);
assert!(a.is_zero());
assert_eq!(a.bit_width(), 32);
}
#[test]
fn test_from_i64() {
let a = APSInt::from_i64(42, 32, true);
assert_eq!(a.try_to_i64(), Some(42));
}
#[test]
fn test_negative() {
let a = APSInt::from_i64(-5, 32, true);
assert!(a.is_negative());
}
#[test]
fn test_cmp() {
let a = APSInt::from_i64(10, 32, true);
let b = APSInt::from_i64(20, 32, true);
assert!(a < b);
}
#[test]
fn test_neg() {
let a = APSInt::from_i64(7, 32, true);
let neg = a.neg();
assert_eq!(neg.try_to_i64(), Some(-7));
}
#[test]
fn test_sext() {
let a = APSInt::from_i64(-1, 8, true);
let b = a.sext(32);
assert_eq!(b.try_to_i64(), Some(-1));
}
#[test]
fn test_zext() {
let a = APSInt::from_u64(255, 8, false);
let b = a.zext(32);
assert_eq!(b.try_to_u64(), Some(255));
}
#[test]
fn test_min_max() {
let min = APSInt::get_min_value(8);
assert_eq!(min.try_to_i64(), Some(-128));
let max = APSInt::get_max_value(8);
assert_eq!(max.try_to_i64(), Some(127));
}
#[test]
fn test_fits_in() {
let a = APSInt::from_i64(100, 32, true);
assert!(!a.fits_in(7));
let b = APSInt::from_i64(50, 32, true);
assert!(b.fits_in(7));
}
#[test]
fn test_display() {
let a = APSInt::from_i64(-42, 32, true);
assert_eq!(format!("{}", a), "-42");
}
}