use crate::error::Error;
use crate::vm::Vm;
use crate::vm::vcell::VCell;
use std::collections::HashSet;
const CYCLE_DETECT_THRESHOLD: usize = 10_000;
struct CycleState {
visited: Option<HashSet<(usize, usize)>>,
}
impl CycleState {
fn new() -> Self {
Self { visited: None }
}
fn upgrade(&mut self) {
if self.visited.is_none() {
self.visited = Some(HashSet::new());
}
}
fn check_or_insert(&mut self, l: usize, r: usize) -> bool {
match &mut self.visited {
None => true,
Some(set) => set.insert((l, r)),
}
}
}
impl Vm {
pub fn eqv(&self, left: &VCell, right: &VCell) -> Result<bool, Error> {
if left.is_ptr() && right.is_ptr() && (left == right) {
return Ok(true);
}
let left = match left {
VCell::Ptr(ptr) => self.heap.get_at_index(*ptr),
_ => left,
};
let right = match right {
VCell::Ptr(ptr) => self.heap.get_at_index(*ptr),
_ => right,
};
match (left, right) {
(VCell::Bool(left), VCell::Bool(right)) => Ok(left == right),
(VCell::Number(left), VCell::Number(right)) => Ok(left == right),
(VCell::Nil, VCell::Nil) => Ok(true),
(VCell::Eof, VCell::Eof) => Ok(true),
(VCell::Pair(_, _), VCell::Pair(_, _)) => Ok(left == right),
(VCell::Char(left), VCell::Char(right)) => Ok(left == right),
(VCell::String(left), VCell::String(right)) => Ok(left == right),
_ => Ok(false),
}
}
pub fn equal(&self, left: &VCell, right: &VCell) -> Result<bool, Error> {
let mut state = CycleState::new();
self.equal_inner(left, right, &mut state, 0)
}
fn equal_inner(
&self,
left: &VCell,
right: &VCell,
state: &mut CycleState,
depth: usize,
) -> Result<bool, Error> {
if depth > CYCLE_DETECT_THRESHOLD {
state.upgrade();
}
if self.eqv(left, right)? {
return Ok(true);
}
let lptr = left.as_ptr().ok();
let rptr = right.as_ptr().ok();
let left = match left {
VCell::Ptr(ptr) => self.heap.get_at_index(*ptr).clone(),
_ => left.clone(),
};
let right = match right {
VCell::Ptr(ptr) => self.heap.get_at_index(*ptr).clone(),
_ => right.clone(),
};
if left.is_pair() && right.is_pair() {
if let (Some(l), Some(r)) = (lptr, rptr)
&& !state.check_or_insert(l, r)
{
return Ok(true);
}
return self.compare_pair(left, right, state, depth + 1);
}
if left.is_vector() && right.is_vector() {
if let (Some(l), Some(r)) = (lptr, rptr)
&& !state.check_or_insert(l, r)
{
return Ok(true);
}
return self.compare_vector(left, right, state, depth + 1);
}
if left.is_string() && right.is_string() {
return Ok(left.as_string()?.borrow().as_str() == right.as_string()?.borrow().as_str());
}
self.eqv(&left, &right)
}
fn compare_pair(
&self,
mut left: VCell,
mut right: VCell,
state: &mut CycleState,
depth: usize,
) -> Result<bool, Error> {
let mut iters: usize = 0;
loop {
if !left.is_pair() || !right.is_pair() {
return self.eqv(&left, &right);
}
let lcar = left.as_car()?;
let rcar = right.as_car()?;
if !self.equal_inner(&lcar, &rcar, state, depth + 1)? {
return Ok(false);
}
let lcdr_ptr = left.as_cdr()?.as_ptr()?;
let rcdr_ptr = right.as_cdr()?.as_ptr()?;
let next_left = self.heap.get_at_index(lcdr_ptr).clone();
let next_right = self.heap.get_at_index(rcdr_ptr).clone();
iters += 1;
if iters > CYCLE_DETECT_THRESHOLD {
state.upgrade();
}
if next_left.is_pair()
&& next_right.is_pair()
&& !state.check_or_insert(lcdr_ptr, rcdr_ptr)
{
return Ok(true);
}
left = next_left;
right = next_right;
}
}
fn compare_vector(
&self,
left: VCell,
right: VCell,
state: &mut CycleState,
depth: usize,
) -> Result<bool, Error> {
let left = left.as_vector()?;
let right = right.as_vector()?;
if left.len() != right.len() {
return Ok(false);
}
for idx in 0..left.len() {
if !self.equal_inner(
&left.get(idx).unwrap(),
&right.get(idx).unwrap(),
state,
depth + 1,
)? {
return Ok(false);
}
}
Ok(true)
}
}
#[cfg(test)]
mod tests {}