use crate::arena::{ContextStack, DataValue};
use crate::operators::array::{IterArgKind, ResolvedInput, resolve_iter_input};
use crate::{CompiledNode, Engine, Result};
use bumpalo::Bump;
#[inline]
fn min_max<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
init: f64,
pick_better: fn(f64, f64) -> bool,
) -> Result<&'a DataValue<'a>> {
if args.is_empty() {
return Err(crate::Error::invalid_args());
}
if args.len() > 1 {
return min_max_variadic(args, ctx, engine, arena, init, pick_better);
}
if matches!(&args[0], CompiledNode::Array { .. }) {
return Err(crate::Error::invalid_args());
}
if let CompiledNode::Value { value, .. } = &args[0] {
if matches!(value, datavalue::OwnedDataValue::Array(_)) {
return Err(crate::Error::invalid_args());
}
}
let src = match resolve_iter_input(&args[0], iter_arg_kind, ctx, engine, arena)? {
ResolvedInput::Iterable(s) => s,
ResolvedInput::Empty => return Err(crate::Error::invalid_args()),
ResolvedInput::Bridge(av) => {
if matches!(av, DataValue::Array(_)) {
return min_max_from_av(av, init, pick_better, arena);
}
if !matches!(av, DataValue::Number(_)) {
return Err(crate::Error::invalid_args());
}
return Ok(av);
}
};
if src.is_empty() {
return Err(crate::Error::invalid_args());
}
let mut best_f = init;
let mut best_idx: Option<usize> = None;
let len = src.len();
for i in 0..len {
match src.get(i) {
DataValue::Number(n) => {
let f = n.as_f64();
if pick_better(f, best_f) {
best_f = f;
best_idx = Some(i);
}
}
_ => return Err(crate::Error::invalid_args()),
}
}
match best_idx {
Some(i) => Ok(src.get(i)),
None => Ok(crate::arena::singletons::singleton_null()),
}
}
#[inline]
fn min_max_variadic<'a>(
args: &'a [CompiledNode],
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
init: f64,
pick_better: fn(f64, f64) -> bool,
) -> Result<&'a DataValue<'a>> {
let mut best_f = init;
let mut best_av: Option<&'a DataValue<'a>> = None;
for arg in args {
let av = engine.dispatch_node(arg, ctx, arena)?;
let f = match av {
DataValue::Number(n) => n.as_f64(),
_ => return Err(crate::Error::invalid_args()),
};
if pick_better(f, best_f) {
best_f = f;
best_av = Some(av);
}
}
match best_av {
Some(av) => Ok(av),
None => Ok(crate::arena::singletons::singleton_null()),
}
}
#[inline]
fn min_max_from_av<'a>(
av: &'a DataValue<'a>,
init: f64,
pick_better: fn(f64, f64) -> bool,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
let items: &[DataValue<'a>] = match av {
DataValue::Array(items) => items,
_ => return Err(crate::Error::invalid_args()),
};
if items.is_empty() {
return Err(crate::Error::invalid_args());
}
let mut best_f = init;
let mut best_idx: Option<usize> = None;
for (i, it) in items.iter().enumerate() {
let f = it.as_f64().ok_or_else(crate::Error::invalid_args)?;
if pick_better(f, best_f) {
best_f = f;
best_idx = Some(i);
}
}
match best_idx {
Some(i) => Ok(arena.alloc(items[i])),
None => Ok(crate::arena::singletons::singleton_null()),
}
}
#[inline]
pub(crate) fn evaluate_max<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
min_max(
args,
iter_arg_kind,
ctx,
engine,
arena,
f64::NEG_INFINITY,
|c, b| c > b,
)
}
#[inline]
pub(crate) fn evaluate_min<'a>(
args: &'a [CompiledNode],
iter_arg_kind: IterArgKind,
ctx: &mut ContextStack<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Result<&'a DataValue<'a>> {
min_max(
args,
iter_arg_kind,
ctx,
engine,
arena,
f64::INFINITY,
|c, b| c < b,
)
}