use ergotree_ir::mir::coll_fold::Fold;
use ergotree_ir::mir::value::CollKind;
use ergotree_ir::mir::value::NativeColl;
use ergotree_ir::mir::value::Value;
use crate::eval::env::Env;
use crate::eval::EvalContext;
use crate::eval::EvalError;
use crate::eval::Evaluable;
impl Evaluable for Fold {
fn eval(&self, env: &Env, ctx: &mut EvalContext) -> Result<Value, EvalError> {
let input_v = self.input.eval(env, ctx)?;
let zero_v = self.zero.eval(env, ctx)?;
let fold_op_v = self.fold_op.eval(env, ctx)?;
let input_v_clone = input_v.clone();
let mut fold_op_call = |arg: Value| match &fold_op_v {
Value::Lambda(func_value) => {
let func_arg = func_value
.args
.first()
.ok_or_else(|| EvalError::NotFound("empty argument for fold op".to_string()))?;
let env1 = env.clone().extend(func_arg.idx, arg);
func_value.body.eval(&env1, ctx)
}
_ => Err(EvalError::UnexpectedValue(format!(
"expected fold_op to be Value::FuncValue got: {0:?}",
input_v_clone
))),
};
match input_v {
Value::Coll(coll) => match coll {
CollKind::NativeColl(NativeColl::CollByte(coll_byte)) => {
coll_byte.iter().try_fold(zero_v, |acc, byte| {
let tup_arg = Value::Tup([acc, Value::Byte(*byte)].into());
fold_op_call(tup_arg)
})
}
CollKind::WrappedColl {
elem_tpe: _,
items: v,
} => v.iter().try_fold(zero_v, |acc, item| {
let tup_arg = Value::Tup([acc, item.clone()].into());
fold_op_call(tup_arg)
}),
},
_ => Err(EvalError::UnexpectedValue(format!(
"expected Fold input to be Value::Coll, got: {0:?}",
input_v
))),
}
}
}
#[allow(clippy::panic)]
#[allow(clippy::unwrap_used)]
#[cfg(test)]
mod tests {
use std::convert::TryInto;
use std::rc::Rc;
use crate::eval::context::Context;
use crate::eval::tests::eval_out;
use ergotree_ir::mir::bin_op::ArithOp;
use ergotree_ir::mir::bin_op::BinOp;
use ergotree_ir::mir::expr::Expr;
use ergotree_ir::mir::extract_amount::ExtractAmount;
use ergotree_ir::mir::func_value::FuncArg;
use ergotree_ir::mir::func_value::FuncValue;
use ergotree_ir::mir::property_call::PropertyCall;
use ergotree_ir::mir::select_field::SelectField;
use ergotree_ir::mir::unary_op::OneArgOpTryBuild;
use ergotree_ir::mir::val_use::ValUse;
use ergotree_ir::types::scontext;
use ergotree_ir::types::stuple::STuple;
use ergotree_ir::types::stype::SType;
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(16))]
#[test]
fn eval_fold(ctx in any::<Context>()) {
let data_inputs: Expr = PropertyCall::new(Expr::Context, scontext::DATA_INPUTS_PROPERTY.clone()).unwrap()
.into();
let tuple: Expr = ValUse {
val_id: 1.into(),
tpe: SType::STuple(STuple {
items: [SType::SLong, SType::SBox].into(),
}),
}
.into();
let fold_op_body: Expr = BinOp {
kind: ArithOp::Plus.into(),
left: Box::new(Expr::SelectField(
SelectField::new(tuple.clone(), 1.try_into().unwrap()).unwrap(),
)),
right: Box::new(Expr::ExtractAmount(
ExtractAmount::try_build(Expr::SelectField(
SelectField::new(tuple, 2.try_into().unwrap()).unwrap(),
))
.unwrap(),
)),
}
.into();
let expr: Expr = Fold::new(
data_inputs,
Expr::Const(0i64.into()),
FuncValue::new(
vec![FuncArg {
idx: 1.into(),
tpe: SType::STuple(STuple {
items: [SType::SLong, SType::SBox].into(),
}),
}],
fold_op_body,
)
.into(),
)
.unwrap()
.into();
let ctx = Rc::new(ctx);
assert_eq!(
eval_out::<i64>(&expr, ctx.clone()),
ctx.data_inputs.clone()
.map_or(0i64, |d| d.iter().fold(0i64, |acc, b| acc + b.value.as_i64()))
);
}
}
}