use crate::eval::functions::check_arity;
use crate::types::{ErrorKind, Value};
use super::array_utils::{flatten_to_rows, values_equal, value_compare};
pub fn vlookup_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 3, 4) {
return err;
}
let search_key = &args[0];
let range = &args[1];
let col_index = match &args[2] {
Value::Number(n) => n.trunc() as usize,
_ => return Value::Error(ErrorKind::Value),
};
let is_sorted = if args.len() == 4 {
match &args[3] {
Value::Bool(b) => *b,
Value::Number(n) => *n != 0.0,
_ => true,
}
} else {
true
};
if col_index < 1 {
return Value::Error(ErrorKind::Value);
}
let rows = flatten_to_rows(range);
if rows.is_empty() {
return Value::Error(ErrorKind::NA);
}
if is_sorted {
let mut found_row: Option<&Vec<Value>> = None;
for row in &rows {
if row.is_empty() { continue; }
let first = &row[0];
match value_compare(first, search_key) {
Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => {
found_row = Some(row);
}
Some(std::cmp::Ordering::Greater) => break,
None => {}
}
}
match found_row {
None => Value::Error(ErrorKind::NA),
Some(row) => {
if col_index > row.len() {
Value::Error(ErrorKind::Ref)
} else {
row[col_index - 1].clone()
}
}
}
} else {
for row in &rows {
if row.is_empty() { continue; }
if values_equal(&row[0], search_key) {
if col_index > row.len() {
return Value::Error(ErrorKind::Ref);
}
return row[col_index - 1].clone();
}
}
Value::Error(ErrorKind::NA)
}
}
pub fn hlookup_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 3, 4) {
return err;
}
let search_key = &args[0];
let range = &args[1];
let row_index = match &args[2] {
Value::Number(n) => n.trunc() as usize,
_ => return Value::Error(ErrorKind::Value),
};
let is_sorted = if args.len() == 4 {
match &args[3] {
Value::Bool(b) => *b,
Value::Number(n) => *n != 0.0,
_ => true,
}
} else {
true
};
if row_index < 1 {
return Value::Error(ErrorKind::Value);
}
let rows = flatten_to_rows(range);
if rows.is_empty() {
return Value::Error(ErrorKind::NA);
}
let first_row = &rows[0];
if is_sorted {
let mut found_col: Option<usize> = None;
for (i, cell) in first_row.iter().enumerate() {
match value_compare(cell, search_key) {
Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => {
found_col = Some(i);
}
Some(std::cmp::Ordering::Greater) => break,
None => {}
}
}
match found_col {
None => Value::Error(ErrorKind::NA),
Some(col) => {
if row_index > rows.len() {
return Value::Error(ErrorKind::Ref);
}
let target_row = &rows[row_index - 1];
if col < target_row.len() {
target_row[col].clone()
} else {
Value::Error(ErrorKind::NA)
}
}
}
} else {
let mut found_col: Option<usize> = None;
for (i, cell) in first_row.iter().enumerate() {
if values_equal(cell, search_key) {
found_col = Some(i);
break;
}
}
match found_col {
None => Value::Error(ErrorKind::NA),
Some(col) => {
if row_index > rows.len() {
return Value::Error(ErrorKind::Ref);
}
let target_row = &rows[row_index - 1];
if col < target_row.len() {
target_row[col].clone()
} else {
Value::Error(ErrorKind::NA)
}
}
}
}
}