use crate::allocator::{Allocator, NodePtr, SExp};
use crate::chia_dialect::ClvmFlags;
use crate::cost::Cost;
use crate::error::{EvalErr, Result};
use crate::op_utils::{first, get_args, nilp, rest};
use crate::reduction::{Reduction, Response};
const FIRST_COST: Cost = 30;
const IF_COST: Cost = 33;
const CONS_COST: Cost = 50;
const REST_COST: Cost = 30;
const LISTP_COST: Cost = 19;
const EQ_BASE_COST: Cost = 117;
const EQ_COST_PER_BYTE: Cost = 1;
pub fn op_if(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [cond, affirmative, negative] = get_args::<3>(a, input, "i")?;
let chosen_node = if nilp(a, cond) { negative } else { affirmative };
Ok(Reduction(IF_COST, chosen_node))
}
pub fn op_cons(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [n1, n2] = get_args::<2>(a, input, "c")?;
let r = a.new_pair(n1, n2)?;
Ok(Reduction(CONS_COST, r))
}
pub fn op_first(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [n] = get_args::<1>(a, input, "f")?;
Ok(Reduction(FIRST_COST, first(a, n)?))
}
pub fn op_rest(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [n] = get_args::<1>(a, input, "r")?;
Ok(Reduction(REST_COST, rest(a, n)?))
}
pub fn op_listp(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [n] = get_args::<1>(a, input, "l")?;
match a.sexp(n) {
SExp::Pair(_, _) => Ok(Reduction(LISTP_COST, a.one())),
_ => Ok(Reduction(LISTP_COST, a.nil())),
}
}
pub fn op_raise(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let throw_value = if let Ok([value]) = get_args::<1>(a, input, "") {
match a.sexp(value) {
SExp::Atom => value,
_ => input,
}
} else {
input
};
Err(EvalErr::Raise(throw_value))
}
fn ensure_atom(a: &Allocator, n: NodePtr, op: &str) -> Result<()> {
if let SExp::Atom = a.sexp(n) {
Ok(())
} else {
Err(EvalErr::InvalidOpArg(n, format!("{op} used on list")))?
}
}
pub fn op_eq(a: &mut Allocator, input: NodePtr, _max_cost: Cost, _flags: ClvmFlags) -> Response {
let [s0, s1] = get_args::<2>(a, input, "=")?;
ensure_atom(a, s0, "=")?;
ensure_atom(a, s1, "=")?;
let eq = a.atom_eq(s0, s1);
let cost = EQ_BASE_COST + (a.atom_len(s0) as Cost + a.atom_len(s1) as Cost) * EQ_COST_PER_BYTE;
Ok(Reduction(cost, if eq { a.one() } else { a.nil() }))
}