use crate::ty::*;
use crate::value::*;
use num::{BigInt, BigRational};
use std;
use std::sync::Arc;
pub type Const = Arc<ConstKind>;
impl Into<ValueRef> for Const {
fn into(self) -> ValueRef {
ValueRef::Const(self)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstKind {
Int(ConstInt),
Time(ConstTime),
}
impl Value for ConstKind {
fn id(&self) -> ValueId {
INLINE_VALUE_ID
}
fn ty(&self) -> Type {
match *self {
ConstKind::Int(ref k) => int_ty(k.width()),
ConstKind::Time(_) => time_ty(),
}
}
fn name(&self) -> Option<&str> {
None
}
}
impl ConstKind {
fn desc(&self) -> &'static str {
match *self {
ConstKind::Int(_) => "ConstKind::Int",
ConstKind::Time(_) => "ConstKind::Time",
}
}
pub fn as_int(&self) -> &ConstInt {
match *self {
ConstKind::Int(ref k) => k,
_ => panic!("as_int called on {}", self.desc()),
}
}
pub fn as_time(&self) -> &ConstTime {
match *self {
ConstKind::Time(ref k) => k,
_ => panic!("as_time called on {}", self.desc()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConstInt {
width: usize,
value: BigInt,
}
impl ConstInt {
pub fn new(width: usize, value: BigInt) -> ConstInt {
ConstInt {
width: width,
value: value,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn value(&self) -> &BigInt {
&self.value
}
}
#[derive(Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ConstTime {
time: BigRational,
delta: BigInt,
epsilon: BigInt,
}
impl ConstTime {
pub fn new(time: BigRational, delta: BigInt, epsilon: BigInt) -> ConstTime {
ConstTime {
time: time,
delta: delta,
epsilon: epsilon,
}
}
pub fn time(&self) -> &BigRational {
&self.time
}
pub fn delta(&self) -> &BigInt {
&self.delta
}
pub fn epsilon(&self) -> &BigInt {
&self.epsilon
}
pub fn is_zero(&self) -> bool {
use num::Zero;
self.time.is_zero() && self.delta.is_zero() && self.epsilon.is_zero()
}
}
impl std::fmt::Debug for ConstTime {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl std::fmt::Display for ConstTime {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use num::Zero;
let mut any = false;
if !self.time.is_zero() || self.is_zero() {
write_ratio_as_si(&self.time, f)?;
any = true;
}
if !self.delta.is_zero() {
if any {
write!(f, " ")?
};
write!(f, "{}d", self.delta)?;
any = true;
}
if !self.epsilon.is_zero() {
if any {
write!(f, " ")?
};
write!(f, "{}e", self.epsilon)?;
}
Ok(())
}
}
fn write_ratio_as_si(ratio: &BigRational, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use num::{One, Zero};
if ratio.is_zero() {
return write!(f, "0s");
}
let prefices = ["", "m", "u", "n", "p", "f", "a"];
let mut scaled = ratio.clone();
let mut prefix = 0;
let mut shift = 0;
while prefix + 1 < prefices.len() && shift < 9 {
if scaled >= One::one() {
if scaled.is_integer() {
break;
} else {
shift += 3;
}
} else {
prefix += 1;
}
scaled = scaled * BigRational::from_integer(BigInt::from(1000));
}
let rounded = format!("{}", scaled.round());
if shift > 0 {
write!(
f,
"{}.{}{}s",
&rounded[0..3],
&rounded[3..],
prefices[prefix]
)?;
} else {
write!(f, "{}{}s", rounded, prefices[prefix])?;
}
Ok(())
}
pub fn const_int(width: usize, value: BigInt) -> Const {
Const::new(ConstKind::Int(ConstInt::new(width, value)))
}
pub fn const_time(time: BigRational, delta: BigInt, epsilon: BigInt) -> Const {
Const::new(ConstKind::Time(ConstTime::new(time, delta, epsilon)))
}
pub fn const_zero(ty: &Type) -> Const {
use num::Zero;
match **ty {
IntType(sz) => const_int(sz, BigInt::zero()),
TimeType => const_time(BigRational::zero(), BigInt::zero(), BigInt::zero()),
ref x => panic!("no const zero value for type {}", x),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn time_formatting() {
let make = |num: usize, denom: usize, delta: usize, epsilon: usize| {
format!(
"{}",
ConstTime::new(
BigRational::new(num.into(), denom.into()),
delta.into(),
epsilon.into()
)
)
};
assert_eq!(make(0, 1, 0, 0), "0s");
assert_eq!(make(0, 1, 0, 1), "1e");
assert_eq!(make(0, 1, 1, 0), "1d");
assert_eq!(make(0, 1, 1, 1), "1d 1e");
assert_eq!(make(1, 1, 0, 0), "1s");
assert_eq!(make(1, 1, 0, 1), "1s 1e");
assert_eq!(make(1, 1, 1, 0), "1s 1d");
assert_eq!(make(1, 1, 1, 1), "1s 1d 1e");
assert_eq!(make(1, 10, 0, 0), "100ms");
assert_eq!(make(1, 100, 0, 0), "10ms");
assert_eq!(make(1, 1000, 0, 0), "1ms");
assert_eq!(make(1, 10000, 0, 0), "100us");
assert_eq!(make(1, 100000, 0, 0), "10us");
assert_eq!(make(1, 1000000, 0, 0), "1us");
assert_eq!(make(1, 10000000, 0, 0), "100ns");
assert_eq!(make(1, 100000000, 0, 0), "10ns");
assert_eq!(make(1, 1000000000, 0, 0), "1ns");
assert_eq!(make(1, 10000000000, 0, 0), "100ps");
assert_eq!(make(1, 100000000000, 0, 0), "10ps");
assert_eq!(make(1, 1000000000000, 0, 0), "1ps");
assert_eq!(make(1, 10000000000000, 0, 0), "100fs");
assert_eq!(make(1, 100000000000000, 0, 0), "10fs");
assert_eq!(make(1, 1000000000000000, 0, 0), "1fs");
assert_eq!(make(1, 10000000000000000, 0, 0), "100as");
assert_eq!(make(1, 100000000000000000, 0, 0), "10as");
assert_eq!(make(1, 1000000000000000000, 0, 0), "1as");
assert_eq!(make(500, 1, 0, 0), "500s");
assert_eq!(make(50, 1, 0, 0), "50s");
assert_eq!(make(5, 1, 0, 0), "5s");
assert_eq!(make(5, 10, 0, 0), "500ms");
assert_eq!(make(5, 100, 0, 0), "50ms");
assert_eq!(make(5, 1000, 0, 0), "5ms");
assert_eq!(make(1, 3, 0, 0), "333.333333333ms");
}
}