use std::{fmt, num::NonZeroUsize, thread::panicking};
use awint::{
awint_dag::{dag, Lineage, Location, PState},
awint_internals::{forward_debug_fmt, BITS},
};
use crate::{
awi,
ensemble::{Ensemble, PExternal},
epoch::get_current_epoch,
Error,
};
pub struct EvalAwi {
p_external: PExternal,
nzbw: NonZeroUsize,
}
impl Drop for EvalAwi {
fn drop(&mut self) {
if !panicking() {
self.drop_internal();
}
}
}
macro_rules! from_impl {
($($fn:ident $t:ident);*;) => {
$(
#[track_caller]
pub fn $fn(x: dag::$t) -> Self {
Self::from_state(x.state())
}
)*
}
}
macro_rules! eval_primitives {
($($f:ident $x:ident $to_x:ident $w:expr);*;) => {
$(
pub fn $f(&self) -> Result<$x, Error> {
let awi = self.eval()?;
let awi_w = awi.bw();
if awi_w == $w {
Ok(awi.$to_x())
} else {
Err(Error::ConstBitwidthMismatch(awi_w, $w))
}
}
)*
};
}
impl EvalAwi {
from_impl!(
from_bool bool;
from_u8 u8;
from_i8 i8;
from_u16 u16;
from_i16 i16;
from_u32 u32;
from_i32 i32;
from_u64 u64;
from_i64 i64;
from_u128 u128;
from_i128 i128;
from_usize usize;
from_isize isize;
);
eval_primitives!(
eval_bool bool to_bool 1;
eval_u8 u8 to_u8 8;
eval_i8 i8 to_i8 8;
eval_u16 u16 to_u16 16;
eval_i16 i16 to_i16 16;
eval_u32 u32 to_u32 32;
eval_i32 i32 to_i32 32;
eval_u64 u64 to_u64 64;
eval_i64 i64 to_i64 64;
eval_u128 u128 to_u128 128;
eval_i128 i128 to_i128 128;
eval_usize usize to_usize BITS;
eval_isize isize to_isize BITS;
);
#[track_caller]
pub fn from_state(p_state: PState) -> Self {
let tmp = std::panic::Location::caller();
let location = Location {
file: tmp.file(),
line: tmp.line(),
col: tmp.column(),
};
let res = if let Ok(epoch) = get_current_epoch() {
let mut lock = epoch.epoch_data.borrow_mut();
match lock
.ensemble
.make_rnode_for_pstate(p_state, Some(location), true, true)
{
Ok(tmp) => Ok(tmp),
Err(e) => Err(Error::OtherString(format!(
"could not create or `future_*` an `EvalAwi` from the given mimicking state: \
{e}"
))),
}
} else {
Err(Error::OtherStr(
"attempted to create or `future_*` an `EvalAwi` when no active `starlight::Epoch` \
exists",
))
};
match res {
Ok((p_external, nzbw)) => Self { p_external, nzbw },
Err(e) => {
panic!("{e:?}")
}
}
}
pub fn p_external(&self) -> PExternal {
self.p_external
}
fn drop_internal(&self) {
if let Ok(epoch) = get_current_epoch() {
let mut lock = epoch.epoch_data.borrow_mut();
let _ = lock.ensemble.rnode_dec_rc(self.p_external());
}
}
pub fn nzbw(&self) -> NonZeroUsize {
self.nzbw
}
pub fn bw(&self) -> usize {
self.nzbw().get()
}
pub(crate) fn try_clone_from(p_external: PExternal) -> Result<Self, Error> {
let epoch = get_current_epoch()?;
let mut lock = epoch.epoch_data.borrow_mut();
let p_rnode = lock.ensemble.rnode_inc_rc(p_external)?;
let w = lock
.ensemble
.notary
.rnodes()
.get_val(p_rnode)
.unwrap()
.nzbw();
Ok(Self {
p_external,
nzbw: w,
})
}
pub fn try_clone(&self) -> Result<Self, Error> {
EvalAwi::try_clone_from(self.p_external())
}
#[track_caller]
pub fn from_bits(bits: &dag::Bits) -> Self {
Self::from_state(bits.state())
}
pub fn eval(&self) -> Result<awi::Awi, Error> {
let nzbw = self.nzbw();
let mut res = awi::Awi::zero(nzbw);
for bit_i in 0..res.bw() {
let val = Ensemble::request_thread_local_rnode_value(self.p_external, bit_i)?;
if let Some(val) = val.known_value() {
res.set(bit_i, val).unwrap();
} else {
return Err(Error::OtherString(format!(
"could not eval bit {bit_i} to known value, the node is {}",
self.p_external()
)))
}
}
Ok(res)
}
pub fn eval_is_all_unknown(&self) -> Result<bool, Error> {
let nzbw = self.nzbw();
let mut all_unknown = true;
for bit_i in 0..nzbw.get() {
let val = Ensemble::request_thread_local_rnode_value(self.p_external, bit_i)?;
if val.is_known() {
all_unknown = false;
}
}
Ok(all_unknown)
}
pub fn set_debug_name<S: AsRef<str>>(&self, debug_name: S) -> Result<(), Error> {
Ensemble::thread_local_rnode_set_debug_name(self.p_external, Some(debug_name.as_ref()))
}
pub fn opaque(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::opaque(w))
}
pub fn zero(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::zero(w))
}
pub fn umax(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::umax(w))
}
pub fn imax(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::imax(w))
}
pub fn imin(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::imin(w))
}
pub fn uone(w: NonZeroUsize) -> Self {
Self::from_bits(&dag::Awi::uone(w))
}
}
impl fmt::Debug for EvalAwi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
super::lazy_awi::format_auto_awi("EvalAwi", self.p_external(), self.nzbw(), f)
}
}
forward_debug_fmt!(EvalAwi);
impl<B: AsRef<dag::Bits>> From<B> for EvalAwi {
#[track_caller]
fn from(b: B) -> Self {
Self::from_bits(b.as_ref())
}
}