1use quarb::Value;
18use quarb::library::{CommandLibrary, LibCall, LibFailure, Synopsis};
19use quarb_pyvalue::Reflective;
20
21static REFLECT: Reflective = Reflective::new("numpy", &["numpy.ndarray"], "ndarray", false);
22
23pub struct Numpy;
25
26impl CommandLibrary for Numpy {
27 fn prefix(&self) -> &str {
28 "np"
29 }
30
31 fn commands(&self) -> &[Synopsis] {
32 &[]
33 }
34
35 fn requirements(&self) -> &[&str] {
36 &["numpy"]
37 }
38
39 fn available(&self) -> Result<(), String> {
40 quarb_pyvalue::importable(&["numpy"], "numpy")
41 }
42
43 fn resolve(&self, name: &str) -> Result<&Synopsis, String> {
44 REFLECT.resolve(name)
45 }
46
47 fn call(&self, name: &str, call: LibCall) -> Result<Value, LibFailure> {
48 let syn = REFLECT.resolve(name).map_err(LibFailure::Unknown)?;
49 REFLECT.call(syn, name, call)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 fn nums(v: &[i64]) -> Value {
58 Value::list(v.iter().map(|&n| Value::Int(n)).collect())
59 }
60
61 fn agg(name: &str, input: Value, operands: Vec<Value>) -> Result<Value, LibFailure> {
62 Numpy.call(
63 name,
64 LibCall {
65 agg: true,
66 input: Some(input),
67 operands,
68 options: vec![],
69 },
70 )
71 }
72
73 #[test]
74 fn functions_and_methods_by_reflection() {
75 if Numpy.available().is_err() {
76 eprintln!("numpy not importable; skipping");
77 return;
78 }
79 assert_eq!(agg("mean", nums(&[1, 2, 3, 6]), vec![]).unwrap(), Value::Float(3.0));
81 assert_eq!(
83 agg("percentile", nums(&[1, 2, 3, 4]), vec![Value::Int(50)]).unwrap(),
84 Value::Float(2.5)
85 );
86 assert_eq!(agg("linalg.norm", nums(&[3, 4]), vec![]).unwrap(), Value::Float(5.0));
88 let t = agg(
92 "transpose",
93 Value::list(vec![nums(&[1, 2, 3]), nums(&[4, 5, 6])]),
94 vec![],
95 )
96 .unwrap();
97 let Value::Table(f) = &t else { panic!("{t:?}") };
98 assert!(f.header.is_none() && f.kind().starts_with("an array 3×2"), "{}", f.kind());
99 assert_eq!(
100 f.rows_value(),
101 Value::list(vec![nums(&[1, 4]), nums(&[2, 5]), nums(&[3, 6])])
102 );
103 let syn = Numpy.resolve("percentile").unwrap();
105 assert_eq!(syn.operands[0].name, "a");
106 assert_eq!(syn.operands[1].name, "q");
107 assert!(syn.options.iter().any(|o| o.long == "method"), "{:?}", syn.options);
108 let e = Numpy.resolve("meen").unwrap_err();
109 assert!(e.contains("numpy") && e.contains("mean"), "{e}");
110 }
111}