Skip to content

Commit

Permalink
Merge pull request #792 from DaddyWesker/min_max_size
Browse files Browse the repository at this point in the history
Min max size functions added
  • Loading branch information
vsbogd authored Nov 8, 2024
2 parents f4e1366 + bfb15e0 commit 2112a78
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
18 changes: 18 additions & 0 deletions lib/src/metta/runner/stdlib.metta
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,24 @@
(@param "Tail of an expression")))
(@return "New expression consists of two input arguments"))

(@doc min-atom
(@desc "Returns atom with min value in the expression (first argument). Only numbers allowed")
(@params (
(@param "Expression which contains atoms of Number type")))
(@return "Min value in the expression. Error if expression contains non-numeric value or is empty"))

(@doc max-atom
(@desc "Returns atom with max value in the expression (first argument). Only numbers allowed")
(@params (
(@param "Expression which contains atoms of Number type")))
(@return "Max value in the expression. Error if expression contains non-numeric value or is empty"))

(@doc size-atom
(@desc "Returns size of an expression (first argument)")
(@params (
(@param "Expression")))
(@return "Size of an expression"))

(@doc index-atom
(@desc "Returns atom from an expression (first argument) using index (second argument) or error if index is out of bounds")
(@params (
Expand Down
124 changes: 124 additions & 0 deletions lib/src/metta/runner/stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,96 @@ impl CustomExecute for IntersectionAtomOp {
}
}

#[derive(Clone, Debug)]
pub struct MaxAtomOp {}

grounded_op!(MaxAtomOp, "max-atom");

impl Grounded for MaxAtomOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER])
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
Some(self)
}
}

impl CustomExecute for MaxAtomOp {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("max-atom expects one argument: expression");
let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
if children.is_empty() {
Err(ExecError::from("Empty expression"))
} else {
children.into_iter().fold(Ok(f64::NEG_INFINITY), |res, x| {
match (res, AsPrimitive::from_atom(x).as_number()) {
(res @ Err(_), _) => res,
(_, None) => Err(ExecError::from("Only numbers are allowed in expression")),
(Ok(max), Some(x)) => Ok(f64::max(max, x.into())),
}
})
}.map(|max| vec![Atom::gnd(Number::Float(max))])
}
}

#[derive(Clone, Debug)]
pub struct MinAtomOp {}

grounded_op!(MinAtomOp, "min-atom");

impl Grounded for MinAtomOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER])
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
Some(self)
}
}

impl CustomExecute for MinAtomOp {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("min-atom expects one argument: expression");
let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
if children.is_empty() {
Err(ExecError::from("Empty expression"))
} else {
children.into_iter().fold(Ok(f64::INFINITY), |res, x| {
match (res, AsPrimitive::from_atom(x).as_number()) {
(res @ Err(_), _) => res,
(_, None) => Err(ExecError::from("Only numbers are allowed in expression")),
(Ok(min), Some(x)) => Ok(f64::min(min, x.into())),
}
})
}.map(|min| vec![Atom::gnd(Number::Float(min))])
}
}

#[derive(Clone, Debug)]
pub struct SizeAtomOp {}

grounded_op!(SizeAtomOp, "size-atom");

impl Grounded for SizeAtomOp {
fn type_(&self) -> Atom {
Atom::expr([ARROW_SYMBOL, ATOM_TYPE_EXPRESSION, ATOM_TYPE_NUMBER])
}

fn as_execute(&self) -> Option<&dyn CustomExecute> {
Some(self)
}
}

impl CustomExecute for SizeAtomOp {
fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
let arg_error = || ExecError::from("size-atom expects one argument: expression");
let children = TryInto::<&ExpressionAtom>::try_into(args.get(0).ok_or_else(arg_error)?)?.children();
let size = children.len();
Ok(vec![Atom::gnd(Number::Integer(size as i64))])
}
}

#[derive(Clone, Debug)]
pub struct IndexAtomOp {}

Expand Down Expand Up @@ -1829,6 +1919,12 @@ mod non_minimal_only_stdlib {
tref.register_token(regex(r"cdr-atom"), move |_| { cdr_atom_op.clone() });
let cons_atom_op = Atom::gnd(ConsAtomOp{});
tref.register_token(regex(r"cons-atom"), move |_| { cons_atom_op.clone() });
let max_atom_op = Atom::gnd(MaxAtomOp{});
tref.register_token(regex(r"max-atom"), move |_| { max_atom_op.clone() });
let min_atom_op = Atom::gnd(MinAtomOp{});
tref.register_token(regex(r"min-atom"), move |_| { min_atom_op.clone() });
let size_atom_op = Atom::gnd(SizeAtomOp{});
tref.register_token(regex(r"size-atom"), move |_| { size_atom_op.clone() });
let index_atom_op = Atom::gnd(IndexAtomOp{});
tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() });
let random_int_op = Atom::gnd(RandomIntOp{});
Expand Down Expand Up @@ -2122,6 +2218,34 @@ mod tests {
assert_eq!(res, vec![expr!(("A" "F") ("B" "C") "D")]);
}

#[test]
fn size_atom_op() {
let res = SizeAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)})]).expect("No result returned");
assert_eq!(res, vec![expr!({Number::Integer(5)})]);
let res = SizeAtomOp{}.execute(&mut vec![expr!()]).expect("No result returned");
assert_eq!(res, vec![expr!({Number::Integer(0)})]);
}

#[test]
fn min_atom_op() {
let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned");
assert_eq!(res, vec![expr!({Number::Integer(4)})]);
let res = MinAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]);
assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression")));
let res = MinAtomOp{}.execute(&mut vec![expr!()]);
assert_eq!(res, Err(ExecError::from("Empty expression")));
}

#[test]
fn max_atom_op() {
let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Float(5.5)})]).expect("No result returned");
assert_eq!(res, vec![expr!({Number::Float(5.5)})]);
let res = MaxAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} "A")]);
assert_eq!(res, Err(ExecError::from("Only numbers are allowed in expression")));
let res = MaxAtomOp{}.execute(&mut vec![expr!()]);
assert_eq!(res, Err(ExecError::from("Empty expression")));
}

#[test]
fn index_atom_op() {
let res = IndexAtomOp{}.execute(&mut vec![expr!({Number::Integer(5)} {Number::Integer(4)} {Number::Integer(3)} {Number::Integer(2)} {Number::Integer(1)}), expr!({Number::Integer(2)})]).expect("No result returned");
Expand Down
18 changes: 18 additions & 0 deletions lib/src/metta/runner/stdlib_minimal.metta
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@
(@return "Deconsed expression"))
(: decons-atom (-> Expression Expression))

(@doc min-atom
(@desc "Returns atom with min value in the expression (first argument). Only numbers allowed")
(@params (
(@param "Expression which contains atoms of Number type")))
(@return "Min value in the expression. Error if expression contains non-numeric value or is empty"))

(@doc max-atom
(@desc "Returns atom with max value in the expression (first argument). Only numbers allowed")
(@params (
(@param "Expression which contains atoms of Number type")))
(@return "Max value in the expression. Error if expression contains non-numeric value or is empty"))

(@doc size-atom
(@desc "Returns size of an expression (first argument)")
(@params (
(@param "Expression")))
(@return "Size of an expression"))

(@doc index-atom
(@desc "Returns atom from an expression (first argument) using index (second argument) or error if index is out of bounds")
(@params (
Expand Down
26 changes: 26 additions & 0 deletions lib/src/metta/runner/stdlib_minimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,12 @@ pub fn register_common_tokens(tref: &mut Tokenizer, _tokenizer: Shared<Tokenizer
tref.register_token(regex(r"nop"), move |_| { nop_op.clone() });
let match_op = Atom::gnd(stdlib::MatchOp{});
tref.register_token(regex(r"match"), move |_| { match_op.clone() });
let min_atom_op = Atom::gnd(stdlib::MinAtomOp{});
tref.register_token(regex(r"min-atom"), move |_| { min_atom_op.clone() });
let max_atom_op = Atom::gnd(stdlib::MaxAtomOp{});
tref.register_token(regex(r"max-atom"), move |_| { max_atom_op.clone() });
let size_atom_op = Atom::gnd(stdlib::SizeAtomOp{});
tref.register_token(regex(r"size-atom"), move |_| { size_atom_op.clone() });
let index_atom_op = Atom::gnd(stdlib::IndexAtomOp{});
tref.register_token(regex(r"index-atom"), move |_| { index_atom_op.clone() });
let random_int_op = Atom::gnd(stdlib::RandomIntOp{});
Expand Down Expand Up @@ -621,6 +627,26 @@ mod tests {
assert_eq!(run_program(&format!("!(cdr-atom $a)")), Ok(vec![vec![expr!("Error" ("cdr-atom" a) {Str::from_str("cdr-atom expects a non-empty expression as an argument")})]]));
}

#[test]
fn metta_size_atom() {
assert_eq!(run_program(&format!("!(size-atom (5 4 3 2 1))")), Ok(vec![vec![expr!({Number::Integer(5)})]]));
assert_eq!(run_program(&format!("!(size-atom ())")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
}

#[test]
fn metta_min_atom() {
assert_eq!(run_program(&format!("!(min-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Integer(4)})]]));
assert_eq!(run_program(&format!("!(min-atom ())")), Ok(vec![vec![expr!("Error" ({ stdlib::MinAtomOp{} } ()) "Empty expression")]]));
assert_eq!(run_program(&format!("!(min-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MinAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression")]]));
}

#[test]
fn metta_max_atom() {
assert_eq!(run_program(&format!("!(max-atom (5 4 5.5))")), Ok(vec![vec![expr!({Number::Float(5.5)})]]));
assert_eq!(run_program(&format!("!(max-atom ())")), Ok(vec![vec![expr!("Error" ({ stdlib::MaxAtomOp{} } ()) "Empty expression")]]));
assert_eq!(run_program(&format!("!(max-atom (3 A B 5))")), Ok(vec![vec![expr!("Error" ({ stdlib::MaxAtomOp{} } ({Number::Integer(3)} "A" "B" {Number::Integer(5)})) "Only numbers are allowed in expression")]]));
}

#[test]
fn metta_index_atom() {
assert_eq!(run_program(&format!("!(index-atom (5 4 3 2 1) 2)")), Ok(vec![vec![expr!({Number::Integer(3)})]]));
Expand Down

0 comments on commit 2112a78

Please sign in to comment.