use std::{
fmt::{Debug, Formatter, Result as fmtResult},
ops::{Add, AddAssign, Mul},
ptr::NonNull,
};
const INLINE_CAP: usize = 4;
#[derive(Clone)]
pub struct TapeNode<T> {
childs: [NonNull<Self>; INLINE_CAP],
derivs: [T; INLINE_CAP],
spill: Vec<(NonNull<Self>, T)>,
len: u32,
pub adj: T,
pub(crate) idx: usize,
}
impl<T> TapeNode<T> {
#[inline]
pub fn push_child(&mut self, child: NonNull<Self>, deriv: T) {
let i = self.len as usize;
if i < INLINE_CAP {
self.childs[i] = child;
self.derivs[i] = deriv;
} else {
self.spill.push((child, deriv));
}
self.len += 1;
}
#[inline]
#[must_use]
pub const fn num_children(&self) -> usize {
self.len as usize
}
}
impl<T: Debug> Debug for TapeNode<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmtResult {
let n_inline = (self.len as usize).min(INLINE_CAP);
write!(f, "TapeNode {{ childs: [")?;
for i in 0..n_inline {
write!(f, "({:?}, {:?}), ", self.childs[i], self.derivs[i])?;
}
for (c, d) in &self.spill {
write!(f, "({c:?}, {d:?}), ")?;
}
write!(f, "], adj: {:?} }}", self.adj)
}
}
impl<T: Copy + Default> Default for TapeNode<T> {
fn default() -> Self {
Self {
childs: [NonNull::dangling(); INLINE_CAP],
derivs: [T::default(); INLINE_CAP],
spill: Vec::new(),
len: 0,
adj: T::default(),
idx: usize::MAX,
}
}
}
impl<T: Copy + Add<Output = T> + Mul<Output = T> + AddAssign> TapeNode<T> {
#[inline]
pub fn propagate_into(&self) {
let a = self.adj;
let n_inline = (self.len as usize).min(INLINE_CAP);
for i in 0..n_inline {
unsafe { (*self.childs[i].as_ptr()).adj += a * self.derivs[i] };
}
for &(child, d) in &self.spill {
unsafe { (*child.as_ptr()).adj += a * d };
}
}
}