use crate::language::*;
use crate::symbols::*;
use nar_dev_utils::matches_or;
use narsese::api::{GetCapacity, TermCapacity};
use std::{
fmt::{Display, Formatter},
ops::{Deref, DerefMut},
};
pub(in crate::language) mod vec_utils {
use crate::language::Term;
pub fn remove(vec: &mut Vec<Term>, term: &Term) -> bool {
let position = vec.iter().position(|t| t == term);
match position {
Some(i) => {
vec.remove(i);
true
}
None => false,
}
}
pub fn remove_all(vec: &mut Vec<Term>, terms: &[Term]) -> bool {
let mut removed = false;
for term in terms {
if remove(vec, term) {
removed = true;
}
}
removed
}
pub fn retain_all(vec: &mut Vec<Term>, terms: &[Term]) {
vec.retain(|t| terms.contains(t));
}
}
impl Term {
pub fn instanceof_compound_pure(&self) -> bool {
matches!(
self.identifier(),
SET_EXT_OPERATOR
| SET_INT_OPERATOR
| INTERSECTION_EXT_OPERATOR
| INTERSECTION_INT_OPERATOR
| DIFFERENCE_EXT_OPERATOR
| DIFFERENCE_INT_OPERATOR
| PRODUCT_OPERATOR
| IMAGE_EXT_OPERATOR
| IMAGE_INT_OPERATOR
| CONJUNCTION_OPERATOR
| DISJUNCTION_OPERATOR
| NEGATION_OPERATOR
)
}
#[must_use]
pub fn as_compound_type(&self, compound_class: impl AsRef<str>) -> Option<CompoundTermRef> {
matches_or! {
?self.as_compound(),
Some(compound)
if compound_class.as_ref() == self.identifier()
=> compound
}
}
#[must_use]
pub fn unwrap_compound_id_components(self) -> Option<(String, Box<[Term]>)> {
matches_or! {
?self.unwrap_id_comp(),
(
identifier,
TermComponents::Compound(terms)
)
=> (identifier, terms)
}
}
#[must_use]
pub fn unwrap_compound_components(self) -> Option<Box<[Term]>> {
matches_or! {
?self.unwrap_id_comp(),
(
_,
TermComponents::Compound(terms)
)
=> terms
}
}
#[must_use]
pub fn unwrap_compound_type_components(
self,
compound_class: impl AsRef<str>,
) -> Option<Box<[Term]>> {
matches_or! {
?self.unwrap_id_comp(),
(
identifier,
TermComponents::Compound(terms)
)
if identifier.as_str() == compound_class.as_ref()
=> terms
}
}
#[inline(always)]
pub fn instanceof_compound(&self) -> bool {
self.instanceof_compound_pure() || self.instanceof_statement()
}
#[inline(always)]
pub fn instanceof_set_ext(&self) -> bool {
self.identifier() == SET_EXT_OPERATOR
}
#[inline(always)]
pub fn instanceof_set_int(&self) -> bool {
self.identifier() == SET_INT_OPERATOR
}
#[inline(always)]
pub fn instanceof_set(&self) -> bool {
self.instanceof_set_ext() || self.instanceof_set_int()
}
#[inline(always)]
pub fn instanceof_intersection_ext(&self) -> bool {
self.identifier() == INTERSECTION_EXT_OPERATOR
}
#[inline(always)]
pub fn instanceof_intersection_int(&self) -> bool {
self.identifier() == INTERSECTION_INT_OPERATOR
}
#[inline(always)]
pub fn instanceof_intersection(&self) -> bool {
self.instanceof_intersection_ext() || self.instanceof_intersection_int()
}
#[inline(always)]
pub fn instanceof_difference_ext(&self) -> bool {
self.identifier() == DIFFERENCE_EXT_OPERATOR
}
#[inline(always)]
pub fn instanceof_difference_int(&self) -> bool {
self.identifier() == DIFFERENCE_INT_OPERATOR
}
#[inline(always)]
pub fn instanceof_difference(&self) -> bool {
self.instanceof_difference_ext() || self.instanceof_difference_int()
}
#[inline(always)]
pub fn instanceof_product(&self) -> bool {
self.identifier() == PRODUCT_OPERATOR
}
#[inline(always)]
pub fn instanceof_image_ext(&self) -> bool {
self.identifier() == IMAGE_EXT_OPERATOR
}
#[inline(always)]
pub fn instanceof_image_int(&self) -> bool {
self.identifier() == IMAGE_INT_OPERATOR
}
#[inline(always)]
pub fn instanceof_image(&self) -> bool {
self.instanceof_image_ext() || self.instanceof_image_int()
}
#[inline(always)]
pub fn instanceof_conjunction(&self) -> bool {
self.identifier() == CONJUNCTION_OPERATOR
}
#[inline(always)]
pub fn instanceof_disjunction(&self) -> bool {
self.identifier() == DISJUNCTION_OPERATOR
}
#[inline(always)]
pub fn instanceof_junction(&self) -> bool {
self.instanceof_conjunction() || self.instanceof_disjunction()
}
#[inline(always)]
pub fn instanceof_negation(&self) -> bool {
self.identifier() == NEGATION_OPERATOR
}
#[doc(alias = "is_symmetric")]
pub fn is_commutative(&self) -> bool {
matches!(
self.identifier(),
SET_EXT_OPERATOR
| SET_INT_OPERATOR
| INTERSECTION_EXT_OPERATOR
| INTERSECTION_INT_OPERATOR
| SIMILARITY_RELATION
| EQUIVALENCE_RELATION
| DISJUNCTION_OPERATOR
| CONJUNCTION_OPERATOR
)
}
#[inline(always)]
pub fn structural_match(&self, other: &Self) -> bool {
self.is_same_type(other)
&& self
.components()
.structural_match(other.components())
}
pub fn is_compound(&self) -> bool {
matches!(self.components(), TermComponents::Compound(..))
}
#[must_use]
pub fn as_compound(&self) -> Option<CompoundTermRef> {
matches_or!(
?self.components(),
TermComponents::Compound(ref c) => CompoundTermRef {
inner: self,
components: c
}
)
}
#[must_use]
pub fn as_compound_and(
&self,
predicate: impl FnOnce(&CompoundTermRef) -> bool,
) -> Option<CompoundTermRef> {
match self.as_compound() {
Some(compound) if predicate(&compound) => Some(compound),
_ => None,
}
}
#[must_use]
pub unsafe fn as_compound_unchecked(&self) -> CompoundTermRef {
debug_assert!(self.is_compound(), "转换前必须假定其为复合词项");
match self.components() {
TermComponents::Compound(ref c) => CompoundTermRef {
inner: self,
components: c,
},
_ => unsafe { core::hint::unreachable_unchecked() },
}
}
#[must_use]
pub fn as_compound_mut(&mut self) -> Option<CompoundTermRefMut> {
matches_or! {
?self.components_mut(),
TermComponents::Compound(components) => CompoundTermRefMut {
components: &mut **components as *mut [Term],
inner :self,
}
}
}
#[must_use]
pub unsafe fn as_compound_mut_unchecked(&mut self) -> CompoundTermRefMut {
debug_assert!(self.is_compound(), "转换前必须假定其为复合词项");
match self.components_mut() {
TermComponents::Compound(components) => CompoundTermRefMut {
components: &mut **components as *mut [Term],
inner: self,
},
_ => unsafe { core::hint::unreachable_unchecked() },
}
}
}
impl GetCapacity for Term {
fn get_capacity(&self) -> TermCapacity {
use TermCapacity::*;
match self.identifier() {
WORD | PLACEHOLDER | VAR_INDEPENDENT | VAR_DEPENDENT | VAR_QUERY => Atom,
NEGATION_OPERATOR => Unary,
DIFFERENCE_EXT_OPERATOR
| DIFFERENCE_INT_OPERATOR
| INHERITANCE_RELATION
| IMPLICATION_RELATION => BinaryVec,
SIMILARITY_RELATION | EQUIVALENCE_RELATION => BinarySet,
PRODUCT_OPERATOR | IMAGE_EXT_OPERATOR | IMAGE_INT_OPERATOR => Vec,
SET_EXT_OPERATOR
| SET_INT_OPERATOR
| INTERSECTION_EXT_OPERATOR
| INTERSECTION_INT_OPERATOR
| CONJUNCTION_OPERATOR
| DISJUNCTION_OPERATOR => Set,
id => panic!("Unexpected compound term identifier: {id}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CompoundTermRef<'a> {
pub inner: &'a Term,
pub components: &'a [Term],
}
impl<'s> CompoundTermRef<'s> {
#[inline]
pub fn size(&self) -> usize {
self.components.len()
}
#[inline]
pub fn component_at(self, index: usize) -> Option<&'s Term> {
self.components.get(index)
}
#[inline]
pub unsafe fn component_at_unchecked(&self, index: usize) -> &Term {
self.components.get_unchecked(index)
}
#[inline]
pub(super) fn get_components(&self) -> impl Iterator<Item = &Term> {
self.components.iter()
}
pub fn index_of_component(&self, t: &Term) -> Option<usize> {
self.components.iter().position(|term| term == t)
}
pub fn clone_components(&self) -> Vec<Term> {
self.components.to_vec()
}
pub fn clone_component_refs(&self) -> Vec<&Term> {
self.components.iter().collect()
}
pub fn contain_component(&self, component: &Term) -> bool {
self.get_components().any(|term| term == component)
}
pub fn contain_term(&self, term: &Term) -> bool {
self.get_components()
.any(|sub_term| match sub_term.as_compound() {
None => term == sub_term,
Some(sub_compound) => sub_compound.contain_term(term),
})
}
pub fn contain_all_components(&self, other: &Term) -> bool {
match self.inner.is_same_type(other) {
true => match other.as_compound() {
Some(other) => other
.get_components()
.all(|should_in| self.contain_component(should_in)),
_ => false,
},
false => self.contain_component(other),
}
}
pub fn as_conditional(self) -> Option<(StatementRef<'s>, CompoundTermRef<'s>)> {
self.as_statement()?.as_conditional()
}
}
impl Display for CompoundTermRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl Deref for CompoundTermRef<'_> {
type Target = Term;
fn deref(&self) -> &Self::Target {
self.inner
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct CompoundTermRefMut<'a> {
pub(super) inner: &'a mut Term,
pub(super) components: *mut [Term],
}
impl CompoundTermRefMut<'_> {
pub fn inner(&mut self) -> &mut Term {
self.inner
}
pub fn components(&mut self) -> &mut [Term] {
debug_assert!(self.inner.is_compound());
unsafe { &mut *self.components }
}
pub fn into_ref<'s>(self) -> CompoundTermRef<'s>
where
Self: 's,
{
debug_assert!(self.inner.is_compound());
CompoundTermRef {
inner: self.inner,
components: unsafe { &*self.components },
}
}
pub fn reorder_components(&mut self) {
let mut placeholder = TermComponents::Empty;
std::mem::swap(&mut placeholder, self.inner.components_mut());
let new_components = placeholder.sort_dedup();
*self.inner.components_mut() = new_components;
}
}
impl Display for CompoundTermRefMut<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl Deref for CompoundTermRefMut<'_> {
type Target = Term;
fn deref(&self) -> &Self::Target {
self.inner
}
}
impl DerefMut for CompoundTermRefMut<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner
}
}
impl<'s> From<CompoundTermRefMut<'s>> for CompoundTermRef<'s> {
fn from(r: CompoundTermRefMut<'s>) -> Self {
r.into_ref()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CompoundTerm {
term: Term,
}
impl CompoundTerm {
pub fn get_ref(&self) -> CompoundTermRef {
unsafe { self.term.as_compound_unchecked() }
}
pub fn mut_ref(&mut self) -> CompoundTermRefMut {
unsafe { self.term.as_compound_mut_unchecked() }
}
pub fn unwrap(self) -> (String, Box<[Term]>) {
self.term.unwrap_compound_id_components().unwrap()
}
}
impl TryFrom<Term> for CompoundTerm {
type Error = Term;
fn try_from(term: Term) -> Result<Self, Self::Error> {
match term.is_compound() {
true => Ok(Self { term }),
false => Err(term),
}
}
}
impl From<CompoundTerm> for Term {
fn from(value: CompoundTerm) -> Self {
value.term
}
}
impl Deref for CompoundTerm {
type Target = Term;
fn deref(&self) -> &Self::Target {
&self.term
}
}
impl DerefMut for CompoundTerm {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.term
}
}
impl Display for CompoundTerm {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.term.fmt(f)
}
}
impl CompoundTermRef<'_> {
#[must_use]
pub fn reduce_components(
self,
component_to_reduce: &Term,
) -> Option<Term> {
let mut components = self.clone_components();
let success = match (
self.is_same_type(component_to_reduce),
component_to_reduce.as_compound(),
) {
(
true,
Some(CompoundTermRef {
components: other_components,
..
}),
) => vec_utils::remove_all(&mut components, other_components),
_ => vec_utils::remove(&mut components, component_to_reduce),
};
if !success {
return None;
}
match components.len() {
2.. => Term::make_compound_term(self, components),
1 => match Self::can_extract_to_inner(&self) {
true => components.pop(),
false => None,
},
0 => None,
}
}
#[inline]
fn can_extract_to_inner(&self) -> bool {
matches!(
self.identifier(),
CONJUNCTION_OPERATOR
| DISJUNCTION_OPERATOR
| INTERSECTION_EXT_OPERATOR
| INTERSECTION_INT_OPERATOR
| DIFFERENCE_EXT_OPERATOR
| DIFFERENCE_INT_OPERATOR
)
}
#[must_use]
pub fn set_component(self, index: usize, term: Option<Term>) -> Option<Term> {
let mut list = self.clone_components();
list.remove(index);
if let Some(term) = term {
match (self.is_same_type(&term), term.as_compound()) {
(
true,
Some(CompoundTermRef {
components: list2, ..
}),
) => {
for (i, term) in list2.iter().enumerate() {
list.insert(index + i, term.clone());
}
}
_ => list.insert(index, term),
}
}
Term::make_compound_term(self, list)
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::{language::test_term::*, ok, option_term, test_term as term, util::AResult};
use nar_dev_utils::{asserts, macro_once, unwrap_or_return};
#[macro_export]
macro_rules! test_compound {
(box $($t:tt)*) => {
CompoundTerm::try_from(term!($($t)*)).unwrap()
};
(mut $($t:tt)*) => {
term!($($t)*).as_compound_mut().unwrap()
};
($($t:tt)*) => {
term!($($t)*).as_compound().unwrap()
};
}
macro_rules! compound {
($($t:tt)*) => {
test_compound!($($t)*)
};
}
mod term {
use super::*;
#[test]
fn instanceof_compound() -> AResult {
macro_once! {
macro instanceof_compound($( $s:literal => $expected:expr )*) {
asserts! {$(
term!($s).instanceof_compound() => $expected,
)*}
}
"_" => false
"A" => false
"$A" => false
"#A" => false
"?A" => false
"{A}" => true
"[A]" => true
"(&, A, B)" => true "(|, A, B)" => true
"(-, A, B)" => true
"(~, A, B)" => true
"(*, A)" => true
"(/, R, _)" => true
r"(\, R, _)" => true
"(&&, A, B)" => true
"(||, A, B)" => true
"(--, A)" => true
"<A --> B>" => true
"<A <-> B>" => true
"<A ==> B>" => true
"<A <=> B>" => true
}
ok!()
}
#[test]
fn is_commutative() -> AResult {
macro_once! {
macro is_commutative($( $s:literal => $expected:expr )*) {
asserts! {$(
term!($s).is_commutative() => $expected,
)*}
}
"_" => false
"A" => false
"$A" => false
"#A" => false
"?A" => false
"{A}" => true
"[A]" => true
"(&, A, B)" => true "(|, A, B)" => true
"(-, A, B)" => false
"(~, A, B)" => false
"(*, A)" => false
"(/, R, _)" => false
r"(\, R, _)" => false
"(&&, A, B)" => true
"(||, A, B)" => true
"(--, A)" => false
"<A --> B>" => false
"<A <-> B>" => true
"<A ==> B>" => false
"<A <=> B>" => true
}
ok!()
}
}
mod compound_term_ref {
use super::*;
#[test]
fn deref() -> AResult {
fn test(term: Term) {
assert!(term.is_compound());
let compound = unsafe { term.as_compound_unchecked() };
dbg!(compound.identifier(), compound.components());
let c1 = compound; let c2 = compound.as_compound().unwrap();
let c3 = term.as_compound().unwrap();
dbg!(c1, c2, c3);
asserts! {
compound.is_compound(),
compound.as_compound() => Some(compound),
*compound => term, compound.clone() => compound, (*compound).clone() => term, }
}
macro_once! {
macro test($( $term:literal )*) {$(
test(term!($term));
)*}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A, B, C)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
fn size() -> AResult {
macro_once! {
macro size($( $s:literal => $expected:expr )*) {
asserts! {$(
compound!($s).size() => $expected,
)*}
}
"{A}" => 1
"[A]" => 1
"(&, A, B)" => 2 "(|, A, B)" => 2
"(-, A, B)" => 2
"(~, A, B)" => 2
"(*, A, B, C)" => 3
"(/, R, _)" => 2 r"(\, R, _)" => 2
"(&&, A, B)" => 2
"(||, A, B)" => 2
"(--, A)" => 1
"<A --> B>" => 2
"<A <-> B>" => 2
"<A ==> B>" => 2
"<A <=> B>" => 2
}
ok!()
}
#[test]
fn component_at() -> AResult {
macro_once! {
macro component_at($( $s:literal [ $index:expr ] => $expected:expr )*) {
asserts! {$(
compound!($s).component_at($index) => Some(&term!($expected)),
)*}
}
"{A}"[0] => "A"
"[A]"[0] => "A"
"(&, A, B)"[0] => "A" "(|, A, B)"[0] => "A"
"(-, A, B)"[1] => "B"
"(~, A, B)"[1] => "B"
"(*, A, B, C)"[2] => "C"
"(/, R, _)"[0] => "R" r"(\, R, _)"[0] => "R"
"(/, R, _)"[1] => "_" r"(\, R, _)"[1] => "_"
"(&&, A, B)"[0] => "A"
"(||, A, B)"[0] => "A"
"(--, A)"[0] => "A"
"<A --> B>"[0] => "A"
"<A <-> B>"[0] => "A"
"<A ==> B>"[0] => "A"
"<A <=> B>"[0] => "A"
}
macro_once! {
macro component_at($( $s:literal [ $index:expr ] )*) {
asserts! {$(
compound!($s).component_at($index) => None,
)*}
}
"{A}"[1]
"[A]"[1]
"(&, A, B)"[2] "(|, A, B)"[2]
"(-, A, B)"[2]
"(~, A, B)"[2]
"(*, A, B, C)"[3]
"(/, R, _)"[2] r"(\, R, _)"[2]
"(&&, A, B)"[2]
"(||, A, B)"[2]
"(--, A)"[1]
"<A --> B>"[2]
"<A <-> B>"[2]
"<A ==> B>"[2]
"<A <=> B>"[2]
}
ok!()
}
#[test]
fn component_at_unchecked() -> AResult {
macro_once! {
macro component_at_unchecked($( $s:literal [ $index:expr ] => $expected:expr )*) {
unsafe {
asserts! {$(
compound!($s).component_at_unchecked($index) => &term!($expected),
)*}
}
}
"{A}"[0] => "A"
"[A]"[0] => "A"
"(&, A, B)"[0] => "A" "(|, A, B)"[0] => "A"
"(-, A, B)"[1] => "B"
"(~, A, B)"[1] => "B"
"(*, A, B, C)"[2] => "C"
"(/, R, _)"[0] => "R" r"(\, R, _)"[0] => "R"
"(&&, A, B)"[0] => "A"
"(||, A, B)"[0] => "A"
"(--, A)"[0] => "A"
"<A --> B>"[0] => "A"
"<A <-> B>"[0] => "A"
"<A ==> B>"[0] => "A"
"<A <=> B>"[0] => "A"
}
ok!()
}
#[test]
fn clone_components() -> AResult {
macro_once! {
macro clone_components($($s:literal)*) {
asserts! {$(
compound!($s).clone_components() => term!($s).components().iter().cloned().collect::<Vec<_>>(),
)*}
}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
fn contain_component() -> AResult {
macro_once! {
macro contain_component($($term:literal in $container:expr)*) {
asserts! {$(
compound!($container).contain_component(&term!($term))
)*}
}
"A" in "{A}"
"A" in "[A]"
"A" in "(&, A, B)" "A" in "(|, A, B)"
"A" in "(-, A, B)"
"A" in "(~, A, B)"
"B" in "(-, A, B)"
"B" in "(~, A, B)"
"A" in "(*, A)"
"R" in "(/, R, _)"
"R" in r"(\, R, _)"
"_" in "(/, R, _)" "_" in r"(\, R, _)" "A" in "(&&, A, B)"
"A" in "(||, A, B)"
"A" in "(--, A)"
"A" in "<A --> B>"
"A" in "<A <-> B>"
"A" in "<A ==> B>"
"A" in "<A <=> B>"
"B" in "<A --> B>"
"B" in "<A <-> B>"
"B" in "<A ==> B>"
"B" in "<A <=> B>"
}
macro_once! {
macro contain_component($($term:literal !in $container:expr)*) {
asserts! {$(
!compound!($container).contain_component(&term!($term))
)*}
}
"X" !in "{A}"
"X" !in "[A]"
"X" !in "(&, A, B)" "X" !in "(|, A, B)"
"X" !in "(-, A, B)"
"X" !in "(~, A, B)"
"X" !in "(*, A)"
"X" !in "(/, R, _)"
"X" !in r"(\, R, _)"
"X" !in "(&&, A, B)"
"X" !in "(||, A, B)"
"X" !in "(--, A)"
"C" !in "<A --> B>"
"C" !in "<A <-> B>"
"C" !in "<A ==> B>"
"C" !in "<A <=> B>"
}
ok!()
}
#[test]
fn contain_term() -> AResult {
macro_once! {
macro contain_term($($term:literal in $container:expr)*) {
asserts! {$(
compound!($container).contain_term(&term!($term))
)*}
}
"A" in "{{{{{{A}}}}}}"
"A" in "[[[[[[A]]]]]]"
"A" in "(&, (&, (&, (&, (&, A, B), B), B), B), B)"
"A" in "(|, (|, (|, (|, (|, A, B), B), B), B), B)"
"A" in "(-, (-, A, a), (-, B, b))"
"A" in "(~, (~, A, a), (~, B, b))"
"B" in "(-, (-, A, a), (-, B, b))"
"B" in "(~, (~, A, a), (~, B, b))"
"A" in "(*, (*, (*, (*, (*, A)))))"
"R" in "(/, (/, (/, (/, (/, R, _), _), _), _), _)"
"R" in r"(\, (\, (\, (\, (\, R, _), _), _), _), _)"
"A" in "(&&, (&&, (&&, (&&, (&&, A, B), B), B), B), B)"
"A" in "(||, (||, (||, (||, (||, A, B), B), B), B), B)"
"A" in "(--, (--, (--, (--, (--, A)))))"
"A" in "<<A --> a> --> <B ==> b>>"
"B" in "<<A --> a> --> <B ==> b>>"
"A" in "<<A <-> a> <-> <B <=> b>>"
"B" in "<<A <-> a> <-> <B <=> b>>"
"A" in "<<A --> a> ==> <B ==> b>>"
"B" in "<<A --> a> ==> <B ==> b>>"
"A" in "<<A <-> a> <=> <B <=> b>>"
"B" in "<<A <-> a> <=> <B <=> b>>"
}
macro_once! {
macro contain_term($($term:literal !in $container:expr)*) {
asserts! {$(
!compound!($container).contain_term(&term!($term))
)*}
}
"X" !in "{{{{{{A}}}}}}"
"X" !in "[[[[[[A]]]]]]"
"X" !in "(&, (&, (&, (&, (&, A, B), B), B), B), B)"
"X" !in "(|, (|, (|, (|, (|, A, B), B), B), B), B)"
"X" !in "(-, (-, A, a), (-, B, b))"
"X" !in "(~, (~, A, a), (~, B, b))"
"X" !in "(*, (*, (*, (*, (*, A)))))"
"X" !in "(/, (/, (/, (/, (/, R, _), _), _), _), _)"
"X" !in r"(\, (\, (\, (\, (\, R, _), _), _), _), _)"
"X" !in "(&&, (&&, (&&, (&&, (&&, A, B), B), B), B), B)"
"X" !in "(||, (||, (||, (||, (||, A, B), B), B), B), B)"
"X" !in "(--, (--, (--, (--, (--, A)))))"
"X" !in "<<A --> a> --> <B ==> b>>"
"X" !in "<<A --> a> --> <B ==> b>>"
"X" !in "<<A <-> a> <-> <B <=> b>>"
"X" !in "<<A <-> a> <-> <B <=> b>>"
}
ok!()
}
#[test]
fn contain_all_components() -> AResult {
macro_once! {
macro test($($term:literal in $container:expr)*) {
asserts! {$(
compound!($container).contain_all_components(&term!($term))
)*}
}
"A" in "{A}"
"{A}" in "{A}"
"{A}" in "{A, B}"
"{A}" in "{A, B, C}"
"{B}" in "{A, B, C}"
"{C}" in "{A, B, C}"
"{A, B}" in "{A, B, C}"
"{A, C}" in "{A, B, C}"
"{B, C}" in "{A, B, C}"
"{A, B, C}" in "{A, B, C}"
"A" in "(-, A, B)"
"B" in "(-, A, B)"
"(-, A, B)" in "(-, A, B)"
"A" in "(*, A, B, C, D, E)"
"(*, A)" in "(*, A, B, C, D, E)"
"(*, A, B)" in "(*, A, B, C, D, E)"
"(*, E, B)" in "(*, A, B, C, D, E)"
"(*, E, A)" in "(*, A, B, C, D, E)"
"R" in "(/, R, _)"
"_" in "(/, R, _)"
"R" in "(/, R, _, (*, A))"
"_" in "(/, R, _, (*, A))"
"(*, A)" in "(/, R, _, (*, A))"
"(/, R, _)" in "(/, R, _, (*, A))"
"R" in r"(\, R, _)"
"_" in r"(\, R, _)"
"R" in r"(\, R, _, (*, A))"
"_" in r"(\, R, _, (*, A))"
"(*, A)" in r"(\, R, _, (*, A))"
r"(\, R, _)" in r"(\, R, _, (*, A))"
"A" in "<A --> B>"
"B" in "<A --> B>"
"<A --> B>" in "<A --> B>"
"<B --> A>" in "<A --> B>"
"A" in "<A <-> B>"
"B" in "<A <-> B>"
"<A <-> B>" in "<A <-> B>"
"<B <-> A>" in "<A <-> B>"
"A" in "<A ==> B>"
"B" in "<A ==> B>"
"<A ==> B>" in "<A ==> B>"
"<B ==> A>" in "<A ==> B>"
"A" in "<A <=> B>"
"B" in "<A <=> B>"
"<A <=> B>" in "<A <=> B>"
"<B <=> A>" in "<A <=> B>"
}
ok!()
}
#[test]
fn can_extract() -> AResult {
fn test(term: Term, expected: bool) {
let compound = term.as_compound().unwrap();
assert_eq!(compound.can_extract_to_inner(), expected);
}
macro_once! {
macro test($($term:expr => $expected:expr)*) {
$( test(term!($term), $expected); )*
}
"(&&, A, B)" => true
"(||, A, B)" => true
"(&, A, B)" => true
"(|, A, B)" => true
"(-, A, B)" => true
"(~, A, B)" => true
"{A}" => false
"[A]" => false
}
ok!()
}
#[test]
fn reduce_components() -> AResult {
fn test(compound_str: &str, term_str: &str, expected: Option<Term>) {
let compound: Term = unwrap_or_return!(@compound_str.parse(), err => eprintln!("{compound_str:?}解析失败: {err}"));
let term: Term = unwrap_or_return!(@term_str.parse(), err => eprintln!("{term_str:?}解析失败: {err}"));
let compound_ref = compound.as_compound().expect("构造出来的不是复合词项");
let out = CompoundTermRef::reduce_components(compound_ref, &term);
assert_eq!(
out,
expected,
"{compound_str:?}, {term_str:?} => {} != {}",
format_option_term(&out),
format_option_term(&expected),
);
}
macro_once! {
macro test($($compound:tt, $term:tt => $expected:tt;)*) {
$( test($compound, $term, option_term!($expected)); )*
}
"(&&,<(&,bird,gull) --> bird>,<(&,bird,gull) --> [swimmer]>)", "<(&,bird,gull) --> [swimmer]>" => "<(&,bird,gull) --> bird>"; "(&&,<(&,bird,swan) --> [bird]>,<(&,bird,swan) --> [swimmer]>)", "<(&,bird,swan) --> [swimmer]>" => "<(&,bird,swan) --> [bird]>";
"(&&,<(&,bird,swimmer) --> (&,animal,swimmer)>,<(&,bird,swimmer) --> (|,swan,swimmer)>)", "<(&,bird,swimmer) --> (&,animal,swimmer)>" => "<(&,bird,swimmer) --> (|,swan,swimmer)>";
"(&&,<(&,chess,sport) --> chess>,<(&,chess,sport) --> competition>)", "<(&,chess,sport) --> competition>" => "<(&,chess,sport) --> chess>"; "(&&,<(&,key,(/,open,_,lock)) --> key>,<(&,key,(/,open,_,lock)) --> (/,open,_,{lock1})>)", "<(&,key,(/,open,_,lock)) --> (/,open,_,{lock1})>" => "<(&,key,(/,open,_,lock)) --> key>"; "(&&,<(*,0) --> (*,(/,num,_))>,<{0} --> (*,(/,num,_))>)", "<(*,0) --> (*,(/,num,_))>" => "<{0} --> (*,(/,num,_))>";
"(&&,<(*,0) --> (*,{0})>,<(*,(*,0)) --> (*,{0})>)", "<(*,(*,0)) --> (*,{0})>" => "<(*,0) --> (*,{0})>";
"(&&,<(*,0) --> (/,num,_)>,<(*,0) --> [num]>)", "<(*,0) --> (/,num,_)>" => "<(*,0) --> [num]>";
"(&&,<(*,0) --> num>,<(/,num,_) --> num>)", "<(/,num,_) --> num>" => "<(*,0) --> num>";
"(&&,<(*,0) --> num>,<{0} --> num>)", "<(*,0) --> num>" => "<{0} --> num>";
"(&&,<(*,0) --> num>,<{0} --> num>)", "<{0} --> num>" => "<(*,0) --> num>";
"(&&,<(*,a,b) --> like>,<(*,a,b) --> (*,a,b)>)", "<(*,a,b) --> like>" => "<(*,a,b) --> (*,a,b)>"; "(&&,<(*,b,a) --> [like]>,<(*,b,a) --> (*,b,(/,like,_,b))>)", "<(*,b,a) --> [like]>" => "<(*,b,a) --> (*,b,(/,like,_,b))>";
"(&&,<(*,b,a) --> like>,<(*,b,a) --> (*,(/,like,b,_),b)>)", "<(*,b,a) --> like>" => "<(*,b,a) --> (*,(/,like,b,_),b)>";
"(&&,<(/,(*,(/,num,_)),_) --> (/,num,_)>,<(/,(*,(/,num,_)),_) --> [num]>)", "<(/,(*,(/,num,_)),_) --> (/,num,_)>" => "<(/,(*,(/,num,_)),_) --> [num]>";
"(&&,<(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish) --> [cat]>,<(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish) --> (&,CAT,cat)>)", "<(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish) --> [cat]>" => "<(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish) --> (&,CAT,cat)>";
"(&&,<(/,neutralization,(/,reaction,_,base),_) --> base>,<(/,neutralization,(/,reaction,_,base),_) --> (/,reaction,(/,reaction,_,base),_)>)", "<(/,neutralization,(/,reaction,_,base),_) --> (/,reaction,(/,reaction,_,base),_)>" => "<(/,neutralization,(/,reaction,_,base),_) --> base>";
"(&&,<(/,open,_,lock) --> key>,<(/,open,_,lock) --> (/,open,_,{lock1})>)", "<(/,open,_,lock) --> (/,open,_,{lock1})>" => "<(/,open,_,lock) --> key>";
"(&&,<(/,open,{key1},_) --> lock>,<(/,open,{key1},_) --> (/,open,key,_)>)", "<(/,open,{key1},_) --> (/,open,key,_)>" => "<(/,open,{key1},_) --> lock>";
"(&&,<(|,bird,gull) --> [bird]>,<(|,bird,gull) --> [swimmer]>)", "<(|,bird,gull) --> [swimmer]>" => "<(|,bird,gull) --> [bird]>";
"(&&,<(|,robin,swan) --> (&,bird,swimmer)>,<(|,robin,swan) --> (|,bird,swimmer)>)", "<(|,robin,swan) --> (&,bird,swimmer)>" => "<(|,robin,swan) --> (|,bird,swimmer)>";
"(&&,<(~,boy,girl) --> [strong]>,<(~,boy,girl) --> [[strong]]>)", "<(~,boy,girl) --> [strong]>" => "<(~,boy,girl) --> [[strong]]>";
"(&&,<(~,swan,bird) --> [bird]>,<(~,swan,bird) --> [swimmer]>)", "<(~,swan,bird) --> [swimmer]>" => "<(~,swan,bird) --> [bird]>";
"(&&,<0 --> num>,<0 --> {0}>)", "<0 --> num>" => "<0 --> {0}>";
"(&&,<?1 --> animal>,<?1 --> [swimmer]>)", "<?1 --> [swimmer]>" => "<?1 --> animal>";
"(&&,<CAT --> CAT>,<cat --> CAT>)", "<cat --> CAT>" => "<CAT --> CAT>";
"(&&,<[[smart]] --> [bright]>,<[[smart]] --> [[bright]]>)", "<[[smart]] --> [[bright]]>" => "<[[smart]] --> [bright]>";
"(&&,<acid --> (/,reaction,_,base)>,<(/,neutralization,_,base) --> (/,reaction,_,base)>)", "<acid --> (/,reaction,_,base)>" => "<(/,neutralization,_,base) --> (/,reaction,_,base)>";
"(&&,<animal --> (&,bird,swimmer)>,<animal --> (|,bird,swimmer)>)", "<animal --> (|,bird,swimmer)>" => "<animal --> (&,bird,swimmer)>";
"(&&,<animal --> [bird]>,<animal --> (|,bird,swimmer)>)", "<animal --> (|,bird,swimmer)>" => "<animal --> [bird]>";
"(&&,<animal <-> robin>,<robin <-> [flying]>)", "<animal <-> robin>" => "<robin <-> [flying]>";
"(&&,<animal <-> robin>,<robin <-> [flying]>)", "<robin <-> [flying]>" => "<animal <-> robin>";
"(&&,<animal <-> robin>,<robin <-> [flying]>)", "[flying]" => None;
"(&&,<animal <-> robin>,<robin <-> [flying]>)", "animal" => None;
"(&&,<bird --> (|,robin,swimmer)>,<gull --> (|,robin,swimmer)>)", "<gull --> (|,robin,swimmer)>" => "<bird --> (|,robin,swimmer)>";
"(&&,<bird --> [bird]>,<{Tweety} --> [bird]>)", "<{Tweety} --> [bird]>" => "<bird --> [bird]>";
"(&&,<bird --> [with_wings]>,<bird --> [[with_wings]]>)", "<bird --> [with_wings]>" => "<bird --> [[with_wings]]>";
"(&&,<bird --> animal>,<bird --> [swimmer]>)", "<bird --> [swimmer]>" => "<bird --> animal>";
"(&&,<bird --> flyer>,<bird --> {Birdie}>)", "<bird --> {Birdie}>" => "<bird --> flyer>";
"(&&,<bird --> flyer>,<{Tweety} --> flyer>)", "<{Tweety} --> flyer>" => "<bird --> flyer>";
"(&&,<bird --> {Birdie}>,<{Tweety} --> {Birdie}>)", "<{Tweety} --> {Birdie}>" => "<bird --> {Birdie}>";
"(&&,<cat --> [CAT]>,<cat --> (|,CAT,(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish))>)", "<cat --> [CAT]>" => "<cat --> (|,CAT,(/,(/,REPRESENT,_,<{(*,CAT,FISH)} --> FOOD>),_,eat,fish))>";
"(&&,<cat --> cat>,<cat --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>)", "<cat --> (/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)>" => "<cat --> cat>";
"(&&,<chess --> [competition]>,<sport --> [competition]>)", "<sport --> [competition]>" => "<chess --> [competition]>";
"(&&,<flyer --> (|,bird,[yellow])>,<{Tweety} --> (|,bird,[yellow])>)", "<{Tweety} --> (|,bird,[yellow])>" => "<flyer --> (|,bird,[yellow])>";
"(&&,<gull --> [bird]>,<gull --> (&,bird,swimmer)>)", "<gull --> [bird]>" => "<gull --> (&,bird,swimmer)>";
"(&&,<key --> (/,open,_,lock1)>,<(/,open,_,lock) --> (/,open,_,lock1)>)", "<(/,open,_,lock) --> (/,open,_,lock1)>" => "<key --> (/,open,_,lock1)>";
"(&&,<key --> (/,open,_,{lock1})>,<{key1} --> (/,open,_,{lock1})>)", "<key --> (/,open,_,{lock1})>" => "<{key1} --> (/,open,_,{lock1})>";
"(&&,<key --> (/,open,_,{lock1})>,<{key1} --> (/,open,_,{lock1})>)", "<{key1} --> (/,open,_,{lock1})>" => "<key --> (/,open,_,{lock1})>";
"(&&,<key --> (|,key,(/,open,_,{lock1}))>,<{{key1}} --> (|,key,(/,open,_,{lock1}))>)", "<{{key1}} --> (|,key,(/,open,_,{lock1}))>" => "<key --> (|,key,(/,open,_,{lock1}))>";
"(&&,<key --> [key]>,<{{key1}} --> [key]>)", "<{{key1}} --> [key]>" => "<key --> [key]>";
"(&&,<key --> key>,<key --> (/,open,_,{lock1})>)", "<key --> (/,open,_,{lock1})>" => "<key --> key>";
"(&&,<key --> key>,<{{key1}} --> key>)", "<{{key1}} --> key>" => "<key --> key>";
"(&&,<key --> {key1}>,<{{key1}} --> {key1}>)", "<key --> {key1}>" => "<{{key1}} --> {key1}>";
"(&&,<lock --> lock>,<lock --> (/,open,{key1},_)>)", "<lock --> (/,open,{key1},_)>" => "<lock --> lock>";
"(&&,<lock1 --> (/,open,{key1},_)>,<{key1} --> key>)", "<lock1 --> (/,open,{key1},_)>" => "<{key1} --> key>";
"(&&,<lock1 --> (/,open,{key1},_)>,<{key1} --> key>)", "<{key1} --> key>" => "<lock1 --> (/,open,{key1},_)>";
"(&&,<lock1 --> (/,open,{key1},_)>,<{{key1}} --> key>)", "<lock1 --> (/,open,{key1},_)>" => "<{{key1}} --> key>";
"(&&,<lock1 --> [lock]>,<lock1 --> [(/,open,{key1},_)]>)", "<lock1 --> [(/,open,{key1},_)]>" => "<lock1 --> [lock]>";
"(&&,<lock1 --> [lock]>,<lock1 --> [(/,open,{key1},_)]>)", "<lock1 --> [lock]>" => "<lock1 --> [(/,open,{key1},_)]>";
"(&&,<neutralization --> (*,acid,(/,reaction,acid,_))>,<(*,(/,neutralization,_,base),base) --> (*,acid,(/,reaction,acid,_))>)", "<(*,(/,neutralization,_,base),base) --> (*,acid,(/,reaction,acid,_))>" => "<neutralization --> (*,acid,(/,reaction,acid,_))>";
"(&&,<neutralization --> neutralization>,<(*,acid,base) --> neutralization>)", "<(*,acid,base) --> neutralization>" => "<neutralization --> neutralization>";
"(&&,<neutralization --> reaction>,<neutralization --> (*,(/,reaction,_,base),base)>)", "<neutralization --> (*,(/,reaction,_,base),base)>" => "<neutralization --> reaction>";
"(&&,<neutralization --> reaction>,<neutralization --> (*,(/,reaction,_,base),base)>)", "<neutralization --> reaction>" => "<neutralization --> (*,(/,reaction,_,base),base)>";
"(&&,<neutralization --> reaction>,<neutralization --> (*,acid,base)>)", "<neutralization --> (*,acid,base)>" => "<neutralization --> reaction>";
"(&&,<robin --> (&,animal,(|,swimmer,(-,animal,swan)))>,<{bird} --> (&,animal,(|,swimmer,(-,animal,swan)))>)", "<{bird} --> (&,animal,(|,swimmer,(-,animal,swan)))>" => "<robin --> (&,animal,(|,swimmer,(-,animal,swan)))>";
"(&&,<robin --> (&,animal,swimmer)>,<robin --> (|,swan,swimmer)>)", "<robin --> (&,animal,swimmer)>" => "<robin --> (|,swan,swimmer)>";
"(&&,<robin --> (&,bird,[yellow])>,<{Tweety} --> (&,bird,[yellow])>)", "<{Tweety} --> (&,bird,[yellow])>" => "<robin --> (&,bird,[yellow])>";
"(&&,<robin --> (&,bird,swimmer)>,<robin --> (-,bird,swimmer)>)", "<robin --> (-,bird,swimmer)>" => "<robin --> (&,bird,swimmer)>";
"(&&,<robin --> (&,swimmer,(-,animal,swan))>,<{bird} --> (&,swimmer,(-,animal,swan))>)", "<{bird} --> (&,swimmer,(-,animal,swan))>" => "<robin --> (&,swimmer,(-,animal,swan))>";
"(&&,<robin --> (-,animal,swan)>,<{bird} --> (-,animal,swan)>)", "<{bird} --> (-,animal,swan)>" => "<robin --> (-,animal,swan)>";
"(&&,<robin --> (|,swan,swimmer)>,<{bird} --> (|,swan,swimmer)>)", "<{bird} --> (|,swan,swimmer)>" => "<robin --> (|,swan,swimmer)>";
"(&&,<robin --> (|,swimmer,(-,animal,swan))>,<{robin} --> (|,swimmer,(-,animal,swan))>)", "robin" => None;
"(&&,<robin --> [[chirping]]>,<robin --> [[flying]]>)", "robin" => None;
"(&&,<robin --> [[chirping]]>,<robin --> [[flying]]>,<robin --> [[living]]>)", "<robin --> [[flying]]>" => "(&&,<robin --> [[chirping]]>,<robin --> [[living]]>)";
"(&&,<robin --> [[chirping]]>,<robin --> [[flying]]>,<robin --> [[living]]>)", "robin" => None;
"(&&,<robin --> [[flying]]>,<robin --> [[with_wings]]>)", "<robin --> [[flying]]>" => "<robin --> [[with_wings]]>";
"(&&,<robin --> [[flying]]>,<robin --> [[with_wings]]>)", "<robin --> [bird]>" => None;
"(&&,<robin --> [[with_wings]]>,(||,<robin --> [bird]>,<robin --> [[flying]]>))", "robin" => None;
"(&&,<robin --> [animal]>,<robin --> [[flying]]>)", "<robin --> [[flying]]>" => "<robin --> [animal]>";
"(&&,<robin --> [animal]>,<robin --> [[flying]]>)", "robin" => None;
"(&&,<robin --> [animal]>,<robin --> [bird]>)", "robin" => None;
"(&&,<robin --> [bird]>,<robin --> (&,bird,swimmer)>)", "<robin --> (&,bird,swimmer)>" => "<robin --> [bird]>";
"(&&,<robin --> [bird]>,<robin --> [[flying]]>)", "<robin --> [[with_wings]]>" => None;
"(&&,<robin --> [chirping]>,(||,<robin --> bird>,<robin --> flyer>))", "(||,<robin --> bird>,<robin --> flyer>)" => "<robin --> [chirping]>";
"(&&,<robin --> [chirping]>,(||,<robin --> bird>,<robin --> flyer>))", "<robin --> [chirping]>" => "(||,<robin --> bird>,<robin --> flyer>)";
"(&&,<robin --> [chirping]>,(||,<robin --> bird>,<robin --> flyer>))", "<robin --> flyer>" => None;
"(&&,<robin --> [chirping]>,(||,<robin --> bird>,<robin --> flyer>))", "[chirping]" => None;
"(&&,<robin --> [chirping]>,(||,<robin --> bird>,<robin --> flyer>))", "robin" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>)", "[chirping]" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))", "(||,<robin --> bird>,<robin --> flyer>)" => "(&&,<robin --> [chirping]>,<robin --> [flying]>)";
"(&&,<robin --> [chirping]>,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))", "<robin --> [chirping]>" => "(&&,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))";
"(&&,<robin --> [chirping]>,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))", "<robin --> bird>" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))", "[chirping]" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>,(||,<robin --> bird>,<robin --> flyer>))", "robin" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [living]>)", "<robin --> [flying]>" => "(&&,<robin --> [chirping]>,<robin --> [living]>)";
"(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [living]>)", "[chirping]" => None;
"(&&,<robin --> [chirping]>,<robin --> [flying]>,<robin --> [living]>)", "robin" => None;
"(&&,<robin --> [chirping]>,<robin --> {Birdie}>)", "<robin --> {Birdie}>" => "<robin --> [chirping]>";
"(&&,<robin --> [chirping]>,<robin --> {Birdie}>)", "[chirping]" => None;
"(&&,<robin --> [chirping]>,<robin --> {Birdie}>)", "robin" => None;
"(&&,<robin --> [chirping]>,<robin --> {Birdie}>)", "{Birdie}" => None;
"(&&,<robin --> [flyer]>,<robin --> [[flying]]>)", "<robin --> [bird]>" => None;
"(&&,<robin --> animal>,<robin --> [flying]>)", "<robin --> animal>" => "<robin --> [flying]>";
"(&&,<robin --> animal>,<robin --> [flying]>)", "[flying]" => None;
"(&&,<robin --> animal>,<robin --> [flying]>)", "animal" => None;
"(&&,<robin --> flyer>,<(*,robin,worms) --> food>)", "flyer" => None;
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>)", "<robin <-> [chirping]>" => "<robin <-> [flying]>";
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>)", "[chirping]" => None;
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>)", "robin" => None;
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>,<robin <-> [with_wings]>)", "<robin <-> [with_wings]>" => "(&&,<robin <-> [chirping]>,<robin <-> [flying]>)";
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>,<robin <-> [with_wings]>)", "[chirping]" => None;
"(&&,<robin <-> [chirping]>,<robin <-> [flying]>,<robin <-> [with_wings]>)", "robin" => None;
"(&&,<robin <=> swimmer>,<robin <=> [flying]>)", "<robin <=> [flying]>" => "<robin <=> swimmer>";
"(&&,<robin <=> swimmer>,<robin <=> [flying]>)", "<robin <=> swimmer>" => "<robin <=> [flying]>";
"(&&,<robin <=> swimmer>,<robin <=> [flying]>)", "[flying]" => None;
"(&&,<robin <=> swimmer>,<robin <=> [flying]>)", "robin" => None;
"(&&,<robin ==> [flying]>,<robin ==> [with_wings]>)", "<robin ==> [flying]>" => "<robin ==> [with_wings]>";
"(&&,<robin ==> [flying]>,<robin ==> [with_wings]>)", "[flying]" => None;
"(&&,<robin ==> [flying]>,<robin ==> [with_wings]>)", "robin" => None;
"(&&,<robin ==> swimmer>,<robin ==> [flying]>)", "<robin ==> [flying]>" => "<robin ==> swimmer>";
"(&&,<robin ==> swimmer>,<robin ==> [flying]>)", "<robin ==> swimmer>" => "<robin ==> [flying]>";
"(&&,<robin ==> swimmer>,<robin ==> [flying]>)", "[flying]" => None;
"(&&,<robin ==> swimmer>,<robin ==> [flying]>)", "robin" => None;
"(&&,<soda --> [(/,reaction,acid,_)]>,<{base} --> [(/,reaction,acid,_)]>)", "<{base} --> [(/,reaction,acid,_)]>" => "<soda --> [(/,reaction,acid,_)]>";
"(&&,<sport --> competition>,<(&,chess,(|,chess,sport)) --> competition>)", "<(&,chess,(|,chess,sport)) --> competition>" => "<sport --> competition>";
"(&&,<swan --> [bird]>,<swan --> (|,bird,swimmer)>)", "<swan --> [bird]>" => "<swan --> (|,bird,swimmer)>";
"(&&,<swimmer --> animal>,<swimmer --> (|,swimmer,(-,animal,swan))>)", "<swimmer --> animal>" => "<swimmer --> (|,swimmer,(-,animal,swan))>";
"(&&,<worms --> (/,food,{Tweety},_)>,<{Tweety} --> [chirping]>)", "[chirping]" => None;
"(&&,<worms --> (/,food,{Tweety},_)>,<{Tweety} --> [chirping]>)", "{Tweety}" => None;
"(&&,<{(*,a,b)} --> [like]>,<{(*,a,b)} --> (*,b,(/,like,_,b))>)", "<{(*,a,b)} --> [like]>" => "<{(*,a,b)} --> (*,b,(/,like,_,b))>";
"(&&,<{(*,a,b)} --> like>,<{(*,b,a)} --> like>)", "<{(*,a,b)} --> like>" => "<{(*,b,a)} --> like>";
"(&&,<{(|,boy,girl)} --> [youth]>,<{(|,boy,girl)} --> (|,girl,[strong])>)", "<{(|,boy,girl)} --> [youth]>" => "<{(|,boy,girl)} --> (|,girl,[strong])>";
"(&&,<{Tweety} --> (&,[with_wings],(|,flyer,{Birdie}))>,<{{Tweety}} --> (&,[with_wings],(|,flyer,{Birdie}))>)", "<{{Tweety}} --> (&,[with_wings],(|,flyer,{Birdie}))>" => "<{Tweety} --> (&,[with_wings],(|,flyer,{Birdie}))>";
"(&&,<{Tweety} --> (&,[with_wings],{Birdie})>,<{{Tweety}} --> (&,[with_wings],{Birdie})>)", "<{{Tweety}} --> (&,[with_wings],{Birdie})>" => "<{Tweety} --> (&,[with_wings],{Birdie})>";
"(&&,<{Tweety} --> (&,flyer,[[with_wings]])>,<{{Tweety}} --> (&,flyer,[[with_wings]])>)", "<{{Tweety}} --> (&,flyer,[[with_wings]])>" => "<{Tweety} --> (&,flyer,[[with_wings]])>";
"(&&,<{Tweety} --> (|,[[with_wings]],(&,flyer,{Birdie}))>,<{{Tweety}} --> (|,[[with_wings]],(&,flyer,{Birdie}))>)", "<{{Tweety}} --> (|,[[with_wings]],(&,flyer,{Birdie}))>" => "<{Tweety} --> (|,[[with_wings]],(&,flyer,{Birdie}))>";
"(&&,<{Tweety} --> (|,bird,[yellow])>,<{{Tweety}} --> (|,bird,[yellow])>)", "<{Tweety} --> (|,bird,[yellow])>" => "<{{Tweety}} --> (|,bird,[yellow])>";
"(&&,<{Tweety} --> (|,flyer,[[with_wings]])>,<{{Tweety}} --> (|,flyer,[[with_wings]])>)", "<{{Tweety}} --> (|,flyer,[[with_wings]])>" => "<{Tweety} --> (|,flyer,[[with_wings]])>";
"(&&,<{Tweety} --> (|,flyer,[with_wings])>,<{{Tweety}} --> (|,flyer,[with_wings])>)", "<{{Tweety}} --> (|,flyer,[with_wings])>" => "<{Tweety} --> (|,flyer,[with_wings])>";
"(&&,<{Tweety} --> (|,flyer,{Birdie})>,<{{Tweety}} --> (|,flyer,{Birdie})>)", "<{{Tweety}} --> (|,flyer,{Birdie})>" => "<{Tweety} --> (|,flyer,{Birdie})>";
"(&&,<{Tweety} --> [chirping]>,<(*,{Tweety},worms) --> food>)", "[chirping]" => None;
"(&&,<{Tweety} --> [chirping]>,<(*,{Tweety},worms) --> food>)", "{Tweety}" => None;
"(&&,<{Tweety} --> [flyer]>,<{{Tweety}} --> [flyer]>)", "<{{Tweety}} --> [flyer]>" => "<{Tweety} --> [flyer]>";
"(&&,<{Tweety} --> [yellow]>,<{{Tweety}} --> [yellow]>)", "<{Tweety} --> [yellow]>" => "<{{Tweety}} --> [yellow]>";
"(&&,<{Tweety} --> [{Birdie}]>,<{Tweety} --> (&,flyer,[[with_wings]])>)", "<{Tweety} --> [{Birdie}]>" => "<{Tweety} --> (&,flyer,[[with_wings]])>";
"(&&,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "<{Tweety} --> [with_wings]>" => "<{Tweety} --> bird>";
"(&&,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "<{Tweety} --> bird>" => "<{Tweety} --> [with_wings]>";
"(&&,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "[with_wings]" => None;
"(&&,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "bird" => None;
"(&&,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "{Tweety}" => None;
"(&&,<{Tweety} --> flyer>,<(*,{Tweety},worms) --> food>)", "<(*,{Tweety},worms) --> food>" => "<{Tweety} --> flyer>";
"(&&,<{Tweety} --> flyer>,<(*,{Tweety},worms) --> food>)", "<{Tweety} --> flyer>" => "<(*,{Tweety},worms) --> food>";
"(&&,<{Tweety} --> flyer>,<(*,{Tweety},worms) --> food>)", "flyer" => None;
"(&&,<{Tweety} --> flyer>,<(*,{Tweety},worms) --> food>)", "{Tweety}" => None;
"(&&,<{Tweety} --> flyer>,<{Tweety} --> [{Birdie}]>)", "<{Tweety} --> [{Birdie}]>" => "<{Tweety} --> flyer>";
"(&&,<{Tweety} --> flyer>,<{{Tweety}} --> flyer>)", "<{{Tweety}} --> flyer>" => "<{Tweety} --> flyer>";
"(&&,<{[smart]} --> [bright]>,<{[smart]} --> [[bright]]>)", "<{[smart]} --> [[bright]]>" => "<{[smart]} --> [bright]>";
"(&&,<{bird} --> animal>,<(&,robin,swimmer) --> animal>)", "<{bird} --> animal>" => "<(&,robin,swimmer) --> animal>";
"(&&,<{key1} --> (/,open,_,{lock1})>,<{{key1}} --> (/,open,_,{lock1})>)", "<{key1} --> (/,open,_,{lock1})>" => "<{{key1}} --> (/,open,_,{lock1})>";
"(&&,<{key1} --> [key]>,<{lock1} --> [(/,open,key1,_)]>)", "<{key1} --> [key]>" => "<{lock1} --> [(/,open,key1,_)]>";
"(&&,<{key1} --> [key]>,<{lock1} --> [(/,open,{key1},_)]>)", "<{key1} --> [key]>" => "<{lock1} --> [(/,open,{key1},_)]>";
"(&&,<{key1} --> key>,<{key1} --> (/,open,_,{lock1})>)", "<{key1} --> key>" => "<{key1} --> (/,open,_,{lock1})>";
"(&&,<{lock1} --> [lock]>,<{lock1} --> [(/,open,{key1},_)]>)", "<{lock1} --> [(/,open,{key1},_)]>" => "<{lock1} --> [lock]>";
"(&&,<{lock1} --> lock>,<{lock1} --> (/,open,key,_)>)", "<{lock1} --> (/,open,key,_)>" => "<{lock1} --> lock>";
"(&&,<{robin} --> (&,bird,swimmer)>,<{robin} --> (-,bird,swimmer)>)", "<{robin} --> (-,bird,swimmer)>" => "<{robin} --> (&,bird,swimmer)>";
"(&&,<{robin} --> [[chirping]]>,<{robin} --> [[flying]]>)", "<{robin} --> [[chirping]]>" => "<{robin} --> [[flying]]>";
"(&&,<{robin} --> [[chirping]]>,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>)", "<{robin} --> [[chirping]]>" => "(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>)";
"(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>)", "<{robin} --> [bird]>" => None;
"(&&,<{robin} --> [animal]>,<{robin} --> [[flying]]>)", "<{robin} --> [[flying]]>" => "<{robin} --> [animal]>";
"(&&,<{robin} --> [animal]>,<{robin} --> [[flying]]>)", "<{robin} --> [animal]>" => "<{robin} --> [[flying]]>";
"(&&,<{robin} --> [chirping]>,<{robin} --> [flying]>)", "[chirping]" => None;
"(&&,<{robin} --> [chirping]>,<{robin} --> [flying]>,<{robin} --> [with_wings]>)", "[chirping]" => None;
"(&&,<{robin} --> [flying]>,<{robin} --> [with_wings]>)", "<{robin} --> [flying]>" => "<{robin} --> [with_wings]>";
"(&&,<{robin} --> bird>,<{robin} --> [flying]>)", "<{robin} --> [with_wings]>" => None;
"(&&,<{swan} --> [bird]>,<{swan} --> (&,bird,swimmer)>)", "<{swan} --> (&,bird,swimmer)>" => "<{swan} --> [bird]>";
"(&&,<{swan} --> [bird]>,<{swan} --> (|,bird,swimmer)>)", "<{swan} --> (|,bird,swimmer)>" => "<{swan} --> [bird]>";
"(&&,<{tim} --> [(/,uncle,_,tom)]>,<(/,(*,tim,tom),_,tom) --> [(/,uncle,_,tom)]>)", "<{tim} --> [(/,uncle,_,tom)]>" => "<(/,(*,tim,tom),_,tom) --> [(/,uncle,_,tom)]>";
"(&&,<{{key1}} --> key>,<{{key1}} --> [(/,open,_,{lock1})]>)", "<{{key1}} --> [(/,open,_,{lock1})]>" => "<{{key1}} --> key>";
"(&&,robin,(--,<robin ==> [flying]>))", "(--,<robin ==> [flying]>)" => "robin";
"(&&,robin,(--,<robin ==> [flying]>))", "<robin ==> [flying]>" => None;
"(&&,robin,(--,<robin ==> [flying]>))", "robin" => "(--,<robin ==> [flying]>)";
"(&&,robin,(--,<robin ==> bird>))", "(--,<robin ==> bird>)" => "robin";
"(&&,robin,(--,<robin ==> bird>))", "<robin ==> bird>" => None;
"(&&,robin,(--,<robin ==> bird>))", "robin" => "(--,<robin ==> bird>)";
"(&&,robin,<robin ==> [chirping]>)", "<robin ==> [chirping]>" => "robin";
"(&&,robin,<robin ==> [chirping]>)", "robin" => "<robin ==> [chirping]>";
"(&&,robin,<robin ==> [chirping]>,<robin ==> [flying]>)", "(&&,robin,<robin ==> [chirping]>)" => "<robin ==> [flying]>";
"(&&,robin,<robin ==> [chirping]>,<robin ==> [flying]>)", "[flying]" => None;
"(&&,robin,<robin ==> [chirping]>,<robin ==> [flying]>)", "robin" => "(&&,<robin ==> [chirping]>,<robin ==> [flying]>)";
"(&&,robin,<robin ==> [chirping]>,<robin ==> [flying]>,<robin ==> [with_wings]>)", "[flying]" => None;
"(&&,robin,<robin ==> [chirping]>,<robin ==> [flying]>,<robin ==> [with_wings]>)", "robin" => "(&&,<robin ==> [chirping]>,<robin ==> [flying]>,<robin ==> [with_wings]>)";
"(&&,robin,<robin ==> [chirping]>,<robin ==> [with_wings]>)", "<robin ==> [chirping]>" => "(&&,robin,<robin ==> [with_wings]>)";
"(&&,robin,<robin ==> [flying]>)", "[flying]" => None;
"(&&,robin,<robin ==> bird>)", "<robin ==> bird>" => "robin";
"(&&,robin,<robin ==> bird>)", "bird" => None;
"(&&,robin,<robin ==> bird>)", "robin" => "<robin ==> bird>";
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "(&&,robin,(--,<robin ==> bird>))" => "(&&,<robin ==> bird>,<robin ==> [flying]>)";
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "<robin ==> [flying]>" => "(&&,robin,<robin ==> bird>)";
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "<robin ==> bird>" => "(&&,robin,<robin ==> [flying]>)";
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "[flying]" => None;
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "bird" => None;
"(&&,robin,<robin ==> bird>,<robin ==> [flying]>)", "robin" => "(&&,<robin ==> bird>,<robin ==> [flying]>)";
"(&,(*,0),(*,(*,0)))", "(*,0)" => "(*,(*,0))";
"(&,(/,neutralization,_,base),(/,neutralization,_,soda),(/,reaction,_,(/,reaction,acid,_)))", "(/,reaction,_,(/,reaction,acid,_))" => "(&,(/,neutralization,_,base),(/,neutralization,_,soda))";
"(&,(|,bird,robin),(|,robin,swimmer))", "(|,robin,swimmer)" => "(|,bird,robin)";
"(&,CAT,(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish))", "CAT" => "(/,(/,REPRESENT,_,<(*,CAT,FISH) --> FOOD>),_,eat,fish)";
"(&,animal,swimmer)", "animal" => "swimmer";
"(&,bird,[yellow])", "bird" => "[yellow]";
"(&,bird,{Birdie})", "bird" => "{Birdie}";
"(&,chess,(|,chess,sport))", "chess" => "(|,chess,sport)";
"(&,flyer,[[with_wings]])", "flyer" => "[[with_wings]]";
"(&,gull,robin,swan)", "robin" => "(&,gull,swan)";
"(&,key,(/,open,_,{lock1}))", "key" => "(/,open,_,{lock1})";
"(&,tim,(|,{tim},(/,(*,tim,tom),_,tom)))", "tim" => "(|,{tim},(/,(*,tim,tom),_,tom))";
"(*,(/,num,_))", "(/,num,_)" => None;
"(*,0)", "0" => None;
"(*,a,b)", "(*,b,a)" => None;
"(-,bird,(-,mammal,swimmer))", "bird" => "(-,mammal,swimmer)";
"(-,bird,swimmer)", "bird" => "swimmer";
"(-,{Mars,Pluto,Venus},[{Pluto,Saturn}])", "[{Pluto,Saturn}]" => "{Mars,Pluto,Venus}";
"(|,(-,{Mars,Pluto,Venus},[{Pluto,Saturn}]),{Pluto,Saturn})", "(-,{Mars,Pluto,Venus},[{Pluto,Saturn}])" => "{Pluto,Saturn}";
"(|,[{Pluto,Saturn}],{Mars,Pluto,Venus})", "[{Pluto,Saturn}]" => "{Mars,Pluto,Venus}";
"(|,[{Pluto,Saturn}],{Mars,Venus})", "[{Pluto,Saturn}]" => "{Mars,Venus}";
"(|,animal,swimmer,(-,animal,swan))", "swimmer" => "(|,animal,(-,animal,swan))";
"(|,bird,(-,mammal,swimmer))", "bird" => "(-,mammal,swimmer)";
"(|,bird,[yellow])", "bird" => "[yellow]";
"(|,bird,robin)", "bird" => "robin";
"(|,boy,girl,youth,[strong])", "youth" => "(|,boy,girl,[strong])";
"(|,key,(/,open,_,lock))", "key" => "(/,open,_,lock)";
"(|,key,(/,open,_,{lock1}))", "key" => "(/,open,_,{lock1})";
"(|,like,{(*,a,b)})", "like" => "{(*,a,b)}";
"(|,lock,[(/,open,key1,_)])", "lock" => "[(/,open,key1,_)]";
"(|,tim,{tim},(/,(*,tim,tom),_,tom))", "tim" => "(|,{tim},(/,(*,tim,tom),_,tom))";
"(||,(&&,<robin --> [bird]>,<robin --> [[flying]]>),<robin --> [[with_wings]]>)", "(&&,<robin --> [bird]>,<robin --> [[flying]]>)" => "<robin --> [[with_wings]]>";
"(||,(&&,<robin --> [bird]>,<robin --> [[flying]]>),<robin --> [[with_wings]]>)", "<robin --> [[flying]]>" => None;
"(||,(&&,<robin --> [bird]>,<robin --> [[flying]]>),<robin --> [[with_wings]]>)", "robin" => None;
"(||,(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>),<{robin} --> [bird]>)", "(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>)" => "<{robin} --> [bird]>";
"(||,(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>),<{robin} --> [bird]>)", "<{robin} --> [[with_wings]]>" => None;
"(||,(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>),<{robin} --> [bird]>)", "<{robin} --> [bird]>" => "(&&,<{robin} --> [[flying]]>,<{robin} --> [[with_wings]]>)";
"(||,(&&,<{robin} --> bird>,<{robin} --> [flying]>),<{robin} --> [with_wings]>)", "<{robin} --> [flying]>" => None;
"(||,(&&,<{robin} --> bird>,<{robin} --> [flying]>),<{robin} --> [with_wings]>)", "[with_wings]" => None;
"(||,<robin --> [[flying]]>,<robin --> [[with_wings]]>)", "<robin --> [[flying]]>" => "<robin --> [[with_wings]]>";
"(||,<robin --> [[flying]]>,<robin --> [[with_wings]]>)", "robin" => None;
"(||,<robin --> [animal]>,<robin --> [bird]>)", "<robin --> [animal]>" => "<robin --> [bird]>";
"(||,<robin --> [animal]>,<robin --> [bird]>)", "robin" => None;
"(||,<robin --> [bird]>,<robin --> [[flying]]>)", "<robin --> [[flying]]>" => "<robin --> [bird]>";
"(||,<robin --> [bird]>,<robin --> [[flying]]>)", "<robin --> [bird]>" => "<robin --> [[flying]]>";
"(||,<robin --> [bird]>,<robin --> [[flying]]>)", "robin" => None;
"(||,<robin --> bird>,<robin --> [living]>)", "<robin --> [living]>" => "<robin --> bird>";
"(||,<robin --> bird>,<robin --> [living]>)", "<robin --> bird>" => "<robin --> [living]>";
"(||,<robin --> bird>,<robin --> [living]>)", "[living]" => None;
"(||,<robin --> bird>,<robin --> [living]>)", "bird" => None;
"(||,<robin --> bird>,<robin --> flyer>)", "<robin --> flyer>" => "<robin --> bird>";
"(||,<robin --> bird>,<robin --> flyer>)", "bird" => None;
"(||,<robin <-> swimmer>,<robin <-> [flying]>)", "<robin <-> [flying]>" => "<robin <-> swimmer>";
"(||,<robin <-> swimmer>,<robin <-> [flying]>)", "<robin <-> swimmer>" => "<robin <-> [flying]>";
"(||,<robin <-> swimmer>,<robin <-> [flying]>)", "[flying]" => None;
"(||,<robin <-> swimmer>,<robin <-> [flying]>)", "robin" => None;
"(||,<robin <=> swimmer>,<robin <=> [flying]>)", "<robin <=> [flying]>" => "<robin <=> swimmer>";
"(||,<robin <=> swimmer>,<robin <=> [flying]>)", "<robin <=> swimmer>" => "<robin <=> [flying]>";
"(||,<robin <=> swimmer>,<robin <=> [flying]>)", "[flying]" => None;
"(||,<robin <=> swimmer>,<robin <=> [flying]>)", "robin" => None;
"(||,<robin ==> swimmer>,<robin ==> [flying]>)", "<robin ==> [flying]>" => "<robin ==> swimmer>";
"(||,<robin ==> swimmer>,<robin ==> [flying]>)", "<robin ==> swimmer>" => "<robin ==> [flying]>";
"(||,<robin ==> swimmer>,<robin ==> [flying]>)", "[flying]" => None;
"(||,<robin ==> swimmer>,<robin ==> [flying]>)", "robin" => None;
"(||,<{Tweety} --> [with_wings]>,<{Tweety} --> [[with_wings]]>)", "<{Tweety} --> [[with_wings]]>" => "<{Tweety} --> [with_wings]>";
"(||,<{Tweety} --> [with_wings]>,<{Tweety} --> [[with_wings]]>)", "<{Tweety} --> [with_wings]>" => "<{Tweety} --> [[with_wings]]>";
"(||,<{Tweety} --> [with_wings]>,<{Tweety} --> [[with_wings]]>)", "[with_wings]" => None;
"(||,<{Tweety} --> [with_wings]>,<{Tweety} --> [[with_wings]]>)", "{Tweety}" => None;
"(||,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "<{Tweety} --> [with_wings]>" => "<{Tweety} --> bird>";
"(||,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "<{Tweety} --> bird>" => "<{Tweety} --> [with_wings]>";
"(||,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "[with_wings]" => None;
"(||,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "bird" => None;
"(||,<{Tweety} --> bird>,<{Tweety} --> [with_wings]>)", "{Tweety}" => None;
"(||,<{lock1} --> [(/,open,{key1},_)]>,<{{lock1}} --> [(/,open,key1,_)]>)", "<{lock1} --> [(/,open,{key1},_)]>" => "<{{lock1}} --> [(/,open,key1,_)]>";
"(||,<{lock1} --> [(/,open,{key1},_)]>,<{{lock1}} --> [(/,open,key1,_)]>)", "<{{lock1}} --> [(/,open,key1,_)]>" => "<{lock1} --> [(/,open,{key1},_)]>";
"(~,boy,girl)", "boy" => "girl";
"[(*,acid,base)]", "(*,acid,base)" => None;
"[(/,reaction,_,base)]", "(/,reaction,_,base)" => None;
"[acid]", "acid" => None;
"[{Mars,Pluto,Venus}]", "{Mars,Pluto,Venus}" => None;
"[{Pluto,Saturn}]", "{Pluto,Saturn}" => None;
"{(*,a,b)}", "(*,a,b)" => None;
"{(/,num,_)}", "(/,num,_)" => None;
"{(|,boy,girl)}", "(|,boy,girl)" => None;
"{(~,boy,girl)}", "(~,boy,girl)" => None;
"{0}", "0" => None;
"{Mars,Pluto,Saturn,Venus}", "{Mars,Pluto,Venus}" => None;
"{Mars,Pluto,Saturn,Venus}", "{Pluto,Saturn}" => "{Mars,Venus}";
"{Mars,Pluto,Venus}", "{Mars,Venus}" => None;
"{[bright]}", "[bright]" => None;
}
ok!()
}
#[test]
fn set_component() -> AResult {
fn test(compound: Term, index: usize, term: Option<Term>, expected: Option<Term>) {
let compound_ref = compound.as_compound().expect("构造出来的不是复合词项");
let compound_s = compound.to_string();
let term_s = format_option_term(&term);
let out = CompoundTermRef::set_component(compound_ref, index, term);
assert_eq!(
out,
expected,
"{compound_s:?}, {index:?}, {term_s:?} => {} != {}",
format_option_term(&out),
format_option_term(&expected),
);
}
macro_once! {
macro test($($compound:tt, $index:tt, $term:tt => $expected:tt;)*) {
$( test(term!($compound), $index, option_term!($term), option_term!($expected)); )*
}
"(*, <robin --> [chirping]>, <robin --> [flying]>, (||, <robin --> bird>, <robin --> flyer>))", 0, None => "(*, <robin --> [flying]>, (||, <robin --> bird>, <robin --> flyer>))";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [living]>)", 0, None => "(*, <robin --> [flying]>, <robin --> [living]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [living]>)", 2, None => "(*, <robin --> [chirping]>, <robin --> [flying]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 0, "<robin --> bird>" => "(*, <robin --> bird>, <robin --> [flying]>, <robin --> [with_wings]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 0, None => "(*, <robin --> [flying]>, <robin --> [with_wings]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 1, None => "(*, <robin --> [chirping]>, <robin --> [with_wings]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 2, "(||, <robin --> bird>, <robin --> flyer>)" => "(*, <robin --> [chirping]>, <robin --> [flying]>, (||, <robin --> bird>, <robin --> flyer>))";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 2, "<robin --> [living]>" => "(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [living]>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 2, "<robin --> bird>" => "(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> bird>)";
"(*, <robin --> [chirping]>, <robin --> [flying]>, <robin --> [with_wings]>)", 2, None => "(*, <robin --> [chirping]>, <robin --> [flying]>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>)", 0, "<robin --> bird>" => "(*, <robin --> bird>, <robin --> [with_wings]>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>)", 1, "(||, <robin --> bird>, <robin --> flyer>)" => "(*, <robin --> [chirping]>, (||, <robin --> bird>, <robin --> flyer>))";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>)", 1, "<robin --> [living]>" => "(*, <robin --> [chirping]>, <robin --> [living]>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>)", 1, "<robin --> bird>" => "(*, <robin --> [chirping]>, <robin --> bird>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>)", 1, "<robin --> flyer>" => "(*, <robin --> [chirping]>, <robin --> flyer>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>, <(*, robin, worms) --> food>)", 0, "<robin --> bird>" => "(*, <robin --> bird>, <robin --> [with_wings]>, <(*, robin, worms) --> food>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>, <(*, robin, worms) --> food>)", 0, None => "(*, <robin --> [with_wings]>, <(*, robin, worms) --> food>)";
"(*, <robin --> [chirping]>, <robin --> [with_wings]>, <worms --> (/, food, robin, _)>)", 0, None => "(*, <robin --> [with_wings]>, <worms --> (/, food, robin, _)>)";
"(*, <robin --> [flying]>, <robin --> [with_wings]>)", 1, "(||, <robin --> bird>, <robin --> flyer>)" => "(*, <robin --> [flying]>, (||, <robin --> bird>, <robin --> flyer>))";
"(*, <robin --> [flying]>, <robin --> [with_wings]>)", 1, "<robin --> bird>" => "(*, <robin --> [flying]>, <robin --> bird>)";
"(*, <robin --> flyer>, <(*, robin, worms) --> food>)", 0, "<robin --> bird>" => "(*, <robin --> bird>, <(*, robin, worms) --> food>)";
"(*, <robin --> flyer>, <robin --> [chirping]>, <(*, robin, worms) --> food>)", 1, "<robin --> bird>" => "(*, <robin --> flyer>, <robin --> bird>, <(*, robin, worms) --> food>)";
"(*, <robin --> flyer>, <robin --> [chirping]>, <(*, robin, worms) --> food>)", 1, None => "(*, <robin --> flyer>, <(*, robin, worms) --> food>)";
"(*, <robin --> flyer>, <robin --> [chirping]>, <worms --> (/, food, robin, _)>)", 0, None => "(*, <robin --> [chirping]>, <worms --> (/, food, robin, _)>)";
"(*, <robin --> flyer>, <robin --> [chirping]>, <worms --> (/, food, robin, _)>)", 1, "<robin --> bird>" => "(*, <robin --> flyer>, <robin --> bird>, <worms --> (/, food, robin, _)>)";
"(*, <robin <-> [chirping]>, <robin <-> [flying]>)", 0, "<bird <-> robin>" => "(*, <bird <-> robin>, <robin <-> [flying]>)";
"(*, <robin <-> [chirping]>, <robin <-> [flying]>, <robin <-> [with_wings]>)", 0, "<bird <-> robin>" => "(*, <bird <-> robin>, <robin <-> [flying]>, <robin <-> [with_wings]>)";
"(*, <robin <-> [chirping]>, <robin <-> [flying]>, <robin <-> [with_wings]>)", 0, None => "(*, <robin <-> [flying]>, <robin <-> [with_wings]>)";
"(*, <robin <-> [chirping]>, <robin <-> [flying]>, <robin <-> [with_wings]>)", 1, None => "(*, <robin <-> [chirping]>, <robin <-> [with_wings]>)";
"(*, <robin <-> [chirping]>, <robin <-> [flying]>, <robin <-> [with_wings]>)", 2, None => "(*, <robin <-> [chirping]>, <robin <-> [flying]>)";
"(*, <robin <-> [chirping]>, <robin <-> [with_wings]>)", 1, "<bird <-> robin>" => "(*, <robin <-> [chirping]>, <bird <-> robin>)";
"(*, <robin <-> [flying]>, <robin <-> [with_wings]>)", 1, "<bird <-> robin>" => "(*, <robin <-> [flying]>, <bird <-> robin>)";
"(*, <worms --> (/, food, {Tweety}, _)>, <{Tweety} --> flyer>, <{Tweety} --> [chirping]>)", 1, None => "(*, <worms --> (/, food, {Tweety}, _)>, <{Tweety} --> [chirping]>)";
"(*, <{Tweety} --> flyer>, <{Tweety} --> [chirping]>, <(*, {Tweety}, worms) --> food>)", 0, None => "(*, <{Tweety} --> [chirping]>, <(*, {Tweety}, worms) --> food>)";
"(*, <{Tweety} --> flyer>, <{Tweety} --> [chirping]>, <(*, {Tweety}, worms) --> food>)", 1, None => "(*, <{Tweety} --> flyer>, <(*, {Tweety}, worms) --> food>)";
"(*, <{Tweety} --> flyer>, <{Tweety} --> [chirping]>, <(*, {Tweety}, worms) --> food>)", 2, None => "(*, <{Tweety} --> flyer>, <{Tweety} --> [chirping]>)";
"(*, <{robin} --> [chirping]>, <{robin} --> [flying]>, <{robin} --> [with_wings]>)", 0, None => "(*, <{robin} --> [flying]>, <{robin} --> [with_wings]>)";
"(*, <{robin} --> [chirping]>, <{robin} --> [flying]>, <{robin} --> [with_wings]>)", 1, None => "(*, <{robin} --> [chirping]>, <{robin} --> [with_wings]>)";
"(*, <{robin} --> [chirping]>, <{robin} --> [flying]>, <{robin} --> [with_wings]>)", 2, None => "(*, <{robin} --> [chirping]>, <{robin} --> [flying]>)";
"(*, <{robin} --> [flying]>, <{robin} --> [with_wings]>)", 1, "<{robin} --> bird>" => "(*, <{robin} --> [flying]>, <{robin} --> bird>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>)", 0, None => "(*, <robin ==> [chirping]>, <robin ==> [flying]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>)", 1, None => "(*, robin, <robin ==> [flying]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>)", 2, None => "(*, robin, <robin ==> [chirping]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>, <robin ==> [with_wings]>)", 0, None => "(*, <robin ==> [chirping]>, <robin ==> [flying]>, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>, <robin ==> [with_wings]>)", 1, None => "(*, robin, <robin ==> [flying]>, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>, <robin ==> [with_wings]>)", 2, None => "(*, robin, <robin ==> [chirping]>, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>, <robin ==> [with_wings]>)", 3, None => "(*, robin, <robin ==> [chirping]>, <robin ==> [flying]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [with_wings]>)", 0, None => "(*, <robin ==> [chirping]>, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [with_wings]>)", 1, None => "(*, robin, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [chirping]>, <robin ==> [with_wings]>)", 2, None => "(*, robin, <robin ==> [chirping]>)";
"(*, robin, <robin ==> [flying]>, <robin ==> [with_wings]>)", 0, None => "(*, <robin ==> [flying]>, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [flying]>, <robin ==> [with_wings]>)", 1, None => "(*, robin, <robin ==> [with_wings]>)";
"(*, robin, <robin ==> [flying]>, <robin ==> [with_wings]>)", 2, None => "(*, robin, <robin ==> [flying]>)";
"(*, robin, <robin ==> bird>, <robin ==> [flying]>)", 0, None => "(*, <robin ==> bird>, <robin ==> [flying]>)";
"(*, robin, <robin ==> bird>, <robin ==> [flying]>)", 1, None => "(*, robin, <robin ==> [flying]>)";
"(*, robin, <robin ==> bird>, <robin ==> [flying]>)", 2, None => "(*, robin, <robin ==> bird>)";
"(*, robin, <robin ==> bird>, <robin ==> [living]>)", 0, None => "(*, <robin ==> bird>, <robin ==> [living]>)";
"(*, robin, <robin ==> bird>, <robin ==> [living]>)", 1, None => "(*, robin, <robin ==> [living]>)";
"(*, robin, <robin ==> bird>, <robin ==> [living]>)", 2, None => "(*, robin, <robin ==> bird>)";
"(*, robin, <robin ==> swimmer>, <robin ==> [flying]>)", 0, None => "(*, <robin ==> swimmer>, <robin ==> [flying]>)";
"(*, robin, <robin ==> swimmer>, <robin ==> [flying]>)", 1, None => "(*, robin, <robin ==> [flying]>)";
"(*, robin, <robin ==> swimmer>, <robin ==> [flying]>)", 2, None => "(*, robin, <robin ==> swimmer>)";
}
ok!()
}
}
mod compound_term_ref_mut {
use super::*;
#[test]
#[allow(unused_variables)]
pub fn assure_safe_interface() -> AResult {
fn use_inner(_: &mut Term) {}
fn use_components(_: &mut [Term]) {}
let mut term = term!("(*, A, B, C)");
let mut mut_compound = term.as_compound_mut().expect("无法转换为可变复合词项");
let components = mut_compound.components();
let inner = mut_compound.inner();
use_inner(inner);
use_components(mut_compound.components());
use_inner(mut_compound.inner());
let inner = mut_compound.inner();
let components = mut_compound.components();
use_components(components);
use_inner(mut_compound.inner());
use_components(mut_compound.components());
ok!()
}
#[test]
fn deref_and_mut() -> AResult {
#[allow(clippy::explicit_auto_deref)]
fn test(mut term: Term) {
assert!(term.is_compound());
let term2 = term.clone();
let mut compound = unsafe { term.as_compound_mut_unchecked() };
dbg!(compound.identifier());
dbg!(compound.components());
dbg!(compound.components_mut());
let original_id = compound.identifier().to_string();
let (id, _) = compound.id_comp_mut();
*id = "MUTATED".into(); assert_eq!(*id, "MUTATED");
*id = original_id;
let compound_ref = compound.as_compound().unwrap();
dbg!(compound_ref, compound_ref, compound_ref);
asserts! {
compound.is_compound(),
compound.as_compound().is_some(),
compound.as_compound_mut().is_some(),
*compound => term2, compound.clone() => term2, (*compound).clone() => term2, }
}
macro_once! {
macro test($( $term:literal )*) {$(
test(term!($term));
)*}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A, B, C)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
pub fn components() -> AResult {
macro_once! {
macro test($($term:literal => $container:expr)*) {
asserts! {$(
compound!(mut $term).components()
=> $container
)*}
}
"{A}" => [term!(A)]
"(--, A)" => [term!(A)]
"(-, A, B)" => term!(["A", "B"])
"(~, A, B)" => term!(["A", "B"])
"{A, B, C}" => term!(["A", "B", "C"])
"[A, B, C]" => term!(["A", "B", "C"])
"(*, A, B, C)" => term!(["A", "B", "C"])
"(/, A, B, C, _)" => term!(["A", "B", "C", "_"])
"<A --> B>" => term!(["A", "B"])
"<A <-> B>" => term!(["A", "B"])
"<A ==> B>" => term!(["A", "B"])
"<A <=> B>" => term!(["A", "B"])
"<A --> B>" => term!(["A", "B"])
"<A <-> B>" => term!(["A", "B"])
"<A ==> B>" => term!(["A", "B"])
"<A <=> B>" => term!(["A", "B"])
}
ok!()
}
#[test]
pub fn into_ref() -> AResult {
macro_once! {
macro test($($term:literal)*) {
asserts! {$(
compound!(mut $term).into_ref()
=> compound!($term)
)*}
}
"{A}"
"(--, A)"
"(-, A, B)"
"(~, A, B)"
"{A, B, C}"
"[A, B, C]"
"(*, A, B, C)"
"(/, A, B, C, _)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
pub fn set_term_when_dealing_variables() -> AResult {
fn test(mut term: Term, i: usize, new: Term, expected: Term) {
term.as_compound_mut().unwrap().components()[i] = new;
assert_eq!(term, expected);
}
macro_once! {
macro test($(
$term:literal [$i:expr] = $new:literal =>
$expected:literal
)*) {
$( test( term!($term), $i, term!($new), term!($expected)); )*
}
"{A}"[0] = "B" => "{B}"
"(--, A)"[0] = "B" => "(--, B)"
"(-, A, B)"[0] = "a" => "(-, a, B)"
"(~, A, B)"[0] = "a" => "(~, a, B)"
"{A, B, Z}"[1] = "X" => "{A, X, Z}" "[A, B, Z]"[1] = "X" => "[A, X, Z]" "(*, A, B, C)"[1] = "X" => "(*, A, X, C)"
"(/, A, _, B, C)"[2] = "X" => "(/, A, _, X, C)"
"<A --> B>"[0] = "a" => "<a --> B>"
"<A <-> B>"[1] = "X" => "<A <-> X>" "<A ==> B>"[0] = "a" => "<a ==> B>"
"<A <=> B>"[1] = "X" => "<A <=> X>" }
ok!()
}
#[test]
pub fn reorder_components() -> AResult {
fn test(mut term: Term, i: usize, new: Term, expected: Term) {
let mut ref_mut = term.as_compound_mut().unwrap();
ref_mut.components()[i] = new;
ref_mut.reorder_components();
assert_eq!(term, expected);
}
macro_once! {
macro test($(
$term:literal [$i:expr] = $new:literal =>
$expected:literal
)*) {
$( test( term!($term), $i, term!($new), term!($expected)); )*
}
"{A, B, C}"[1] = "X" => "{A, X, C}" "[A, B, C]"[1] = "X" => "[A, X, C]" "<A <-> B>"[0] = "a" => "<a <-> B>" "<A <=> B>"[0] = "a" => "<a <=> B>" }
ok!()
}
}
mod compound_term {
use super::*;
use std::str::FromStr;
#[test]
fn from_into() -> AResult {
fn test(compound: CompoundTerm) {
assert!(compound.is_compound());
let term: Term = (*compound).clone();
let _: CompoundTerm = term.try_into().expect("应该是复合词项!");
let term: Term = compound.into();
let _: CompoundTerm = term.try_into().expect("应该是复合词项!");
}
macro_once! {
macro test($( $term:literal )*) {$(
test(test_compound!(box $term));
)*}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A, B, C)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
fn get_ref() -> AResult {
fn test(compound: CompoundTerm) {
assert!(compound.is_compound());
let size = compound.get_ref().size();
println!("{compound}.size() => {size}");
compound
.get_ref()
.components()
.iter()
.enumerate()
.for_each(|(i, component)| println!(" [{i}] => {component}"))
}
macro_once! {
macro test($( $term:literal )*) {$(
test(test_compound!(box $term));
)*}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A, B, C)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
#[test]
fn mut_ref() -> AResult {
fn test(mut compound: CompoundTerm) -> AResult {
assert!(compound.is_compound());
let old_s = compound.to_string();
let mut mut_ref = compound.mut_ref();
let first = &mut mut_ref.components()[0];
let x = term!("X");
*first = x.clone();
println!("modification: {old_s:?} => \"{compound}\"");
assert_eq!(compound.get_ref().components[0], x);
compound
.mut_ref()
.components()
.iter_mut()
.enumerate()
.for_each(|(i, component)| {
*component = Term::from_str(&format!("T{i}")).unwrap()
});
print!(" => \"{compound}\"");
ok!()
}
macro_once! {
macro test($( $term:literal )*) {$(
test(test_compound!(box $term))?;
)*}
"{A}"
"[A]"
"(&, A, B)" "(|, A, B)"
"(-, A, B)"
"(~, A, B)"
"(*, A, B, C)"
"(/, R, _)"
r"(\, R, _)"
"(&&, A, B)"
"(||, A, B)"
"(--, A)"
"<A --> B>"
"<A <-> B>"
"<A ==> B>"
"<A <=> B>"
}
ok!()
}
}
}