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!(
81 agg("mean", nums(&[1, 2, 3, 6]), vec![]).unwrap(),
82 Value::Float(3.0)
83 );
84 assert_eq!(
86 agg("percentile", nums(&[1, 2, 3, 4]), vec![Value::Int(50)]).unwrap(),
87 Value::Float(2.5)
88 );
89 assert_eq!(
91 agg("linalg.norm", nums(&[3, 4]), vec![]).unwrap(),
92 Value::Float(5.0)
93 );
94 let t = agg(
98 "transpose",
99 Value::list(vec![nums(&[1, 2, 3]), nums(&[4, 5, 6])]),
100 vec![],
101 )
102 .unwrap();
103 let Value::Table(f) = &t else { panic!("{t:?}") };
104 assert!(
105 f.header.is_none() && f.kind().starts_with("an array 3×2"),
106 "{}",
107 f.kind()
108 );
109 assert_eq!(
110 f.rows_value(),
111 Value::list(vec![nums(&[1, 4]), nums(&[2, 5]), nums(&[3, 6])])
112 );
113 let syn = Numpy.resolve("percentile").unwrap();
115 assert_eq!(syn.operands[0].name, "a");
116 assert_eq!(syn.operands[1].name, "q");
117 assert!(
118 syn.options.iter().any(|o| o.long == "method"),
119 "{:?}",
120 syn.options
121 );
122 let e = Numpy.resolve("meen").unwrap_err();
123 assert!(e.contains("numpy") && e.contains("mean"), "{e}");
124 }
125}