Skip to content

Commit

Permalink
Merge pull request #814 from DaddyWesker/issue_744
Browse files Browse the repository at this point in the history
assertAlphaEqual added
  • Loading branch information
vsbogd authored Dec 18, 2024
2 parents b0f0a02 + 167e42b commit a7ca34f
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 55 deletions.
8 changes: 4 additions & 4 deletions lib/src/atom/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,10 +898,7 @@ pub struct BindingsSet(smallvec::SmallVec<[Bindings; 1]>);
// BindingsSets are conceptually unordered
impl PartialEq for BindingsSet {
fn eq(&self, other: &Self) -> bool {
match crate::common::assert::vec_eq_no_order(self.iter(), other.iter()) {
Ok(()) => true,
Err(_) => false
}
!crate::common::assert::compare_vec_no_order(self.iter(), other.iter(), crate::common::collections::DefaultEquality{}).has_diff()
}
}

Expand Down Expand Up @@ -1283,6 +1280,9 @@ mod test {

#[test]
fn test_atoms_are_equivalent() {
assert!(atoms_are_equivalent(&expr!(x "b" {"c"}), &expr!(x "b" {"c"})));
assert!(atoms_are_equivalent(&expr!(x "b" x), &expr!(x "b" x)));
assert!(atoms_are_equivalent(&expr!(a a "b" {"c"}), &expr!(x x "b" {"c"})));
assert!(atoms_are_equivalent(&expr!(a "b" {"c"}), &expr!(x "b" {"c"})));
assert!(atoms_are_equivalent(&expr!(a b), &expr!(c d)));
assert!(!atoms_are_equivalent(&expr!(a "b" {"c"}), &expr!(a "x" {"c"})));
Expand Down
105 changes: 80 additions & 25 deletions lib/src/common/assert.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,105 @@
use super::collections::ListMap;
use super::collections::{ListMap, Equality, DefaultEquality};

use std::cmp::Ordering;

pub fn vec_eq_no_order<'a, T: PartialEq + std::fmt::Debug + 'a, A: Iterator<Item=&'a T>, B: Iterator<Item=&'a T>>(left: A, right: B) -> Result<(), String> {
let mut left_count: ListMap<&T, usize> = ListMap::new();
let mut right_count: ListMap<&T, usize> = ListMap::new();
pub fn vec_eq_no_order<'a, T, A, B>(left: A, right: B) -> Option<String>
where
T: 'a + PartialEq + std::fmt::Debug,
A: Iterator<Item=&'a T>,
B: Iterator<Item=&'a T>,
{
compare_vec_no_order(left, right, DefaultEquality{}).as_string()
}

pub fn compare_vec_no_order<'a, T, A, B, E>(left: A, right: B, _cmp: E) -> VecDiff<'a, T, E>
where
A: Iterator<Item=&'a T>,
B: Iterator<Item=&'a T>,
E: Equality<&'a T>,
{
let mut left_count: ListMap<&T, usize, E> = ListMap::new();
let mut right_count: ListMap<&T, usize, E> = ListMap::new();
for i in left {
*left_count.entry(&i).or_insert(0) += 1;
}
for i in right {
*right_count.entry(&i).or_insert(0) += 1;
}
counter_eq_explanation(&left_count, &right_count)
VecDiff{ left_count, right_count }
}

fn counter_eq_explanation<T: PartialEq + std::fmt::Debug>(left: &ListMap<&T, usize>, right: &ListMap<&T, usize>) -> Result<(), String> {
for e in right.iter() {
if let Some(count) = left.get(e.0) {
match count.cmp(e.1) {
Ordering::Less => return Err(format!("Missed result: {:?}", e.0)),
Ordering::Greater => return Err(format!("Excessive result: {:?}", e.0)),
Ordering::Equal => {},
pub struct VecDiff<'a, T, E: Equality<&'a T>> {
left_count: ListMap<&'a T, usize, E>,
right_count: ListMap<&'a T, usize, E>,
}

trait DiffVisitor<'a, T> {
fn diff(&mut self, item: &'a T, left: usize, right: usize) -> bool;
}

impl<'a, T: std::fmt::Debug, E: Equality<&'a T>> VecDiff<'a, T, E> {
pub fn has_diff(&self) -> bool {
#[derive(Default)]
struct FindDiff {
diff: bool,
}
impl<T: std::fmt::Debug> DiffVisitor<'_, T> for FindDiff {
fn diff(&mut self, _item: &T, left: usize, right: usize) -> bool {
if left == right {
false
} else {
self.diff = true;
true
}
}
} else {
return Err(format!("Missed result: {:?}", e.0));
}
let mut f = FindDiff::default();
self.visit(&mut f);
f.diff
}
for e in left.iter() {
if let Some(count) = right.get(e.0) {
match e.1.cmp(count) {
Ordering::Less => return Err(format!("Missed result: {:?}", e.0)),
Ordering::Greater => return Err(format!("Excessive result: {:?}", e.0)),
Ordering::Equal => {},

pub fn as_string(&self) -> Option<String> {
#[derive(Default)]
struct StringDiff {
diff: Option<String>,
}
impl<'a, T: std::fmt::Debug> DiffVisitor<'a, T> for StringDiff {
fn diff(&mut self, item: &'a T, left: usize, right: usize) -> bool {
match left.cmp(&right) {
Ordering::Less => {
self.diff = Some(format!("Missed result: {:?}", item));
true
},
Ordering::Greater => {
self.diff = Some(format!("Excessive result: {:?}", item));
true
},
Ordering::Equal => false,
}
}
} else {
return Err(format!("Excessive result: {:?}", e.0));
}
let mut d = StringDiff{ diff: None };
self.visit(&mut d);
d.diff
}

fn visit<'b, V: DiffVisitor<'b, T>>(&'b self, visitor: &mut V) {
for e in self.right_count.iter() {
let count = self.left_count.get(e.0).unwrap_or(&0);
if visitor.diff(e.0, *count, *e.1) { return }
}
for e in self.left_count.iter() {
let count = self.right_count.get(e.0).unwrap_or(&0);
if visitor.diff(e.0, *e.1, *count) { return }
}
}
Ok(())
}

#[macro_export]
macro_rules! assert_eq_no_order {
($left:expr, $right:expr) => {
{
assert!($crate::common::assert::vec_eq_no_order($left.iter(), $right.iter()) == Ok(()),
assert!($crate::common::assert::vec_eq_no_order($left.iter(), $right.iter()) == None,
"(left == right some order)\n left: {:?}\n right: {:?}", $left, $right);
}
}
Expand All @@ -56,7 +111,7 @@ pub fn metta_results_eq<T: PartialEq + std::fmt::Debug>(
match (left, right) {
(Ok(left), Ok(right)) if left.len() == right.len() => {
for (left, right) in left.iter().zip(right.iter()) {
if let Err(_) = vec_eq_no_order(left.iter(), right.iter()) {
if vec_eq_no_order(left.iter(), right.iter()).is_some() {
return false;
}
}
Expand Down
33 changes: 24 additions & 9 deletions lib/src/common/collections.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
use std::fmt::Display;

pub trait Equality<T> {
fn eq(a: &T, b: &T) -> bool;
}

#[derive(Debug)]
pub struct DefaultEquality {}

impl<T: PartialEq> Equality<T> for DefaultEquality {
fn eq(a: &T, b: &T) -> bool {
a == b
}
}

#[derive(Clone, Debug)]
pub struct ListMap<K, V> {
pub struct ListMap<K, V, E: Equality<K> = DefaultEquality>
{
list: Vec<(K, V)>,
_phantom: std::marker::PhantomData<E>,
}

pub enum ListMapEntry<'a, K, V> {
Occupied(K, &'a mut ListMap<K, V>),
Vacant(K, &'a mut ListMap<K, V>),
pub enum ListMapEntry<'a, K, V, E: Equality<K>> {
Occupied(K, &'a mut ListMap<K, V, E>),
Vacant(K, &'a mut ListMap<K, V, E>),
}

impl<'a, K: PartialEq, V> ListMapEntry<'a, K, V> {
impl<'a, K, V, E: Equality<K>> ListMapEntry<'a, K, V, E> {
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
ListMapEntry::Occupied(key, map) => map.get_mut(&key).unwrap(),
Expand Down Expand Up @@ -44,7 +59,7 @@ macro_rules! list_map_get {
($get:ident, {$( $mut_:tt )?}) => {
pub fn $get(& $( $mut_ )? self, key: &K) -> Option<& $( $mut_ )? V> {
for (k, v) in & $( $mut_ )? self.list {
if *k == *key {
if E::eq(k, key) {
return Some(v)
}
}
Expand All @@ -53,12 +68,12 @@ macro_rules! list_map_get {
}
}

impl<K: PartialEq, V> ListMap<K, V> {
impl<K, V, E: Equality<K>> ListMap<K, V, E> {
pub fn new() -> Self {
Self{ list: vec![] }
Self{ list: vec![], _phantom: std::marker::PhantomData }
}

pub fn entry<'a>(&'a mut self, key: K) -> ListMapEntry<'a, K, V> {
pub fn entry<'a>(&'a mut self, key: K) -> ListMapEntry<'a, K, V, E> {
match self.get_mut(&key) {
Some(_) => ListMapEntry::Occupied(key, self),
None => ListMapEntry::Vacant(key, self)
Expand Down
Loading

0 comments on commit a7ca34f

Please sign in to comment.