use numeric::Arith;
use numeric::precision::{Places, carried, product_places, quotient_places, sum_places};
pub mod dates;
pub mod datetime;
pub mod function;
pub mod math;
pub mod numval;
pub mod real;
pub mod text;
pub mod unicode;
pub const FIRST: &[&str] = &[
"CHAR", "ORD", "NATIONAL-OF", "LENGTH", "UPPER-CASE", "LOWER-CASE", "REVERSE", "CURRENT-DATE", "NUMVAL", "NUMVAL-C", "TRIM", "MOD", "REM",
"INTEGER", "INTEGER-PART", "ABS", "MIN", "MAX", "INTEGER-OF-DATE", "DATE-OF-INTEGER", "RANDOM",
];
pub const FUNCTIONS: &[&str] = &[
"ACOS", "ANNUITY", "ASIN", "ATAN", "BIT-OF", "BIT-TO-CHAR", "BYTE-LENGTH", "COS", "DATE-TO-YYYYMMDD", "DAY-OF-INTEGER",
"DAY-TO-YYYYDDD", "DISPLAY-OF", "E", "EXP", "EXP10", "FACTORIAL", "FORMATTED-CURRENT-DATE", "FORMATTED-DATE", "FORMATTED-DATETIME",
"FORMATTED-TIME", "HEX-OF", "HEX-TO-CHAR", "INTEGER-OF-DAY", "INTEGER-OF-FORMATTED-DATE", "LOG", "LOG10",
"MEAN", "MEDIAN", "MIDRANGE", "NUMVAL-F", "ORD-MAX", "ORD-MIN", "PI", "PRESENT-VALUE", "RANGE", "SECONDS-FROM-FORMATTED-TIME",
"SECONDS-PAST-MIDNIGHT", "SIGN", "SIN", "SQRT", "STANDARD-DEVIATION", "SUM", "TAN", "TEST-DATE-YYYYMMDD", "TEST-DAY-YYYYDDD",
"TEST-FORMATTED-DATETIME", "TEST-NUMVAL", "TEST-NUMVAL-C", "TEST-NUMVAL-F", "UUID4", "VARIANCE", "YEAR-TO-YYYY",
"ULENGTH", "UPOS", "USUBSTR", "USUPPLEMENTARY", "UVALID", "UWIDTH", "COMBINED-DATETIME", "CONTENT-OF",
"WHEN-COMPILED",
];
pub const CHARACTER_VALUED: &[&str] = &[
"BIT-OF", "BIT-TO-CHAR", "CHAR", "CONTENT-OF", "CURRENT-DATE", "DISPLAY-OF", "FORMATTED-CURRENT-DATE", "FORMATTED-DATE",
"FORMATTED-DATETIME", "FORMATTED-TIME", "HEX-OF", "HEX-TO-CHAR", "LOWER-CASE", "MAX", "MIN", "NATIONAL-OF", "REVERSE", "TRIM",
"UPPER-CASE", "USUBSTR", "UUID4", "WHEN-COMPILED",
];
pub const FLOATING_POINT: &[&str] = &[
"ACOS", "ANNUITY", "ASIN", "ATAN", "COMBINED-DATETIME", "COS", "E", "EXP", "EXP10", "LOG", "LOG10", "MEAN", "MEDIAN", "MIDRANGE", "NUMVAL", "NUMVAL-C", "NUMVAL-F", "PI",
"PRESENT-VALUE", "RANDOM", "SECONDS-FROM-FORMATTED-TIME", "SECONDS-PAST-MIDNIGHT", "SIN", "SQRT", "STANDARD-DEVIATION", "TAN", "VARIANCE",
];
pub const MIXED: &[&str] = &["ABS", "MAX", "MIN", "RANGE", "REM", "SUM"];
pub fn outer_dmax(name: &str, inner: u32) -> u32 {
if MIXED.contains(&name) { inner } else { 0 }
}
pub fn fixed_places(name: &str, args: &[Places], arith: Arith) -> Option<Places> {
let first = *args.first()?;
let widest = args.iter().fold(first, |p, a| Places::new(p.int.max(a.int), p.dec.max(a.dec)));
Some(match name {
"MAX" | "MIN" => widest,
"RANGE" => carried(sum_places(widest, widest), widest.dec, arith),
"ABS" => first,
"INTEGER" => carried(Places::new(first.total() + 1, 0), 0, arith),
"INTEGER-PART" => carried(Places::new(first.total(), 0), 0, arith),
"MOD" => {
let second = *args.get(1)?;
Places::new(first.int.min(second.int), widest.dec)
}
"REM" => {
let second = *args.get(1)?;
let quotient = carried(quotient_places(first, second, widest.dec), widest.dec, arith);
let product = carried(product_places(Places::new(quotient.int, 0), second), widest.dec, arith);
carried(sum_places(first, product), widest.dec, arith)
}
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_fixed_point_function_has_the_places_ibm_gives_it() {
let (n3, d2, s9, h3) = (Places::new(3, 0), Places::new(1, 2), Places::new(1, 0), Places::new(3, 0));
let of = |name: &str, args: &[Places]| fixed_places(name, args, Arith::Compat);
assert_eq!(of("MAX", &[n3, d2]), Some(Places::new(3, 2)));
assert_eq!(of("MIN", &[d2, Places::new(5, 0)]), Some(Places::new(5, 2)));
assert_eq!(of("RANGE", &[n3, d2]), Some(Places::new(4, 2)));
assert_eq!(of("INTEGER", &[d2]), Some(Places::new(4, 0)));
assert_eq!(of("INTEGER-PART", &[d2]), Some(Places::new(3, 0)));
assert_eq!(of("MOD", &[s9, h3]), Some(Places::new(1, 0)));
assert_eq!(of("REM", &[n3, d2]), Some(Places::new(7, 2)));
assert_eq!(of("ABS", &[d2]), Some(d2));
assert_eq!(of("INTEGER", &[Places::new(30, 0)]), Some(Places::new(30, 0)));
assert_eq!(of("SUM", &[n3]), None);
assert_eq!((outer_dmax("MAX", 2), outer_dmax("REM", 2), outer_dmax("INTEGER", 2), outer_dmax("MEAN", 2)), (2, 2, 0, 0));
}
}