1use runmat_builtins::{LogicalArray, Value};
2
3pub fn matlab_class_name(value: &Value) -> String {
5 match value {
6 Value::Num(_) | Value::ComplexTensor(_) | Value::Complex(_, _) => "double".to_string(),
7 Value::Tensor(tensor) => tensor.dtype.class_name().to_string(),
8 Value::Int(iv) => iv.class_name().to_string(),
9 Value::Bool(_) | Value::LogicalArray(_) => "logical".to_string(),
10 Value::String(_) | Value::StringArray(_) => "string".to_string(),
11 Value::CharArray(_) => "char".to_string(),
12 Value::Cell(_) => "cell".to_string(),
13 Value::Struct(_) => "struct".to_string(),
14 Value::GpuTensor(_) => "gpuArray".to_string(),
15 Value::FunctionHandle(_)
16 | Value::ExternalFunctionHandle(_)
17 | Value::MethodFunctionHandle(_)
18 | Value::BoundFunctionHandle { .. }
19 | Value::Closure(_) => "function_handle".to_string(),
20 Value::HandleObject(handle) => {
21 if handle.class_name.is_empty() {
22 "handle".to_string()
23 } else {
24 handle.class_name.clone()
25 }
26 }
27 Value::Listener(_) => "event.listener".to_string(),
28 Value::OutputList(_) => "OutputList".to_string(),
31 Value::Object(obj) => obj.class_name.clone(),
32 Value::ClassRef(_) => "meta.class".to_string(),
33 Value::MException(_) => "MException".to_string(),
34 }
35}
36
37pub fn value_shape(value: &Value) -> Option<Vec<usize>> {
39 match value {
40 Value::Num(_) | Value::Int(_) | Value::Bool(_) | Value::Complex(_, _) => Some(vec![1, 1]),
41 Value::LogicalArray(arr) => Some(arr.shape.clone()),
42 Value::StringArray(sa) => Some(sa.shape.clone()),
43 Value::String(s) => Some(vec![1, s.chars().count()]),
44 Value::CharArray(ca) => Some(vec![ca.rows, ca.cols]),
45 Value::Tensor(t) => Some(t.shape.clone()),
46 Value::ComplexTensor(t) => Some(t.shape.clone()),
47 Value::Cell(ca) => Some(ca.shape.clone()),
48 Value::GpuTensor(handle) => Some(handle.shape.clone()),
49 Value::Object(obj) if obj.is_class("datetime") => match obj.properties.get("__serial") {
50 Some(Value::Tensor(tensor)) => Some(tensor.shape.clone()),
51 Some(Value::Num(_)) => Some(vec![1, 1]),
52 _ => None,
53 },
54 _ => None,
55 }
56}
57
58pub fn numeric_dtype_label(value: &Value) -> Option<&'static str> {
60 match value {
61 Value::Num(_) | Value::Complex(_, _) => Some("double"),
62 Value::Tensor(t) => Some(t.dtype.class_name()),
63 Value::LogicalArray(_) => Some("logical"),
64 Value::Int(iv) => Some(iv.class_name()),
65 _ => None,
66 }
67}
68
69pub fn approximate_size_bytes(value: &Value) -> Option<u64> {
71 Some(match value {
72 Value::Num(_) | Value::Int(_) | Value::Complex(_, _) => 8,
73 Value::Bool(_) => 1,
74 Value::LogicalArray(arr) => arr.data.len() as u64,
75 Value::Tensor(t) => (t.data.len() * 8) as u64,
76 Value::ComplexTensor(t) => (t.data.len() * 16) as u64,
77 Value::String(s) => s.len() as u64,
78 Value::StringArray(sa) => sa.data.iter().map(|s| s.len() as u64).sum(),
79 Value::CharArray(ca) => (ca.rows * ca.cols) as u64,
80 _ => return None,
81 })
82}
83
84pub fn preview_numeric_values(value: &Value, limit: usize) -> Option<(Vec<f64>, bool)> {
86 match value {
87 Value::Num(n) => Some((vec![*n], false)),
88 Value::Int(iv) => Some((vec![iv.to_f64()], false)),
89 Value::Bool(flag) => Some((vec![if *flag { 1.0 } else { 0.0 }], false)),
90 Value::Tensor(t) => Some(preview_f64_slice(&t.data, limit)),
91 Value::LogicalArray(arr) => Some(preview_logical_slice(arr, limit)),
92 Value::StringArray(_) | Value::String(_) | Value::CharArray(_) => None,
93 Value::ComplexTensor(_) | Value::Complex(_, _) => None,
94 Value::Cell(_)
95 | Value::Struct(_)
96 | Value::Object(_)
97 | Value::HandleObject(_)
98 | Value::Listener(_)
99 | Value::OutputList(_)
100 | Value::FunctionHandle(_)
101 | Value::ExternalFunctionHandle(_)
102 | Value::MethodFunctionHandle(_)
103 | Value::BoundFunctionHandle { .. }
104 | Value::Closure(_)
105 | Value::ClassRef(_)
106 | Value::MException(_)
107 | Value::GpuTensor(_) => None,
108 }
109}
110
111fn preview_f64_slice(data: &[f64], limit: usize) -> (Vec<f64>, bool) {
112 if data.len() > limit {
113 (data[..limit].to_vec(), true)
114 } else {
115 (data.to_vec(), false)
116 }
117}
118
119fn preview_logical_slice(arr: &LogicalArray, limit: usize) -> (Vec<f64>, bool) {
120 let truncated = arr.data.len() > limit;
121 let mut preview = Vec::with_capacity(arr.data.len().min(limit));
122 for value in arr.data.iter().take(limit) {
123 preview.push(if *value == 0 { 0.0 } else { 1.0 });
124 }
125 (preview, truncated)
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use runmat_builtins::{NumericDType, ObjectInstance, Tensor};
132
133 #[test]
134 fn approximate_size_bytes_uses_f64_width_for_integer_dtypes() {
135 let u8_tensor = Tensor::new_with_dtype(vec![1.0, 2.0, 3.0], vec![3, 1], NumericDType::U8)
137 .expect("tensor");
138 let u16_tensor = Tensor::new_with_dtype(vec![1.0, 2.0, 3.0], vec![3, 1], NumericDType::U16)
139 .expect("tensor");
140 let f32_tensor = Tensor::new_with_dtype(vec![1.0, 2.0, 3.0], vec![3, 1], NumericDType::F32)
141 .expect("tensor");
142
143 assert_eq!(approximate_size_bytes(&Value::Tensor(u8_tensor)), Some(24));
144 assert_eq!(approximate_size_bytes(&Value::Tensor(u16_tensor)), Some(24));
145 assert_eq!(approximate_size_bytes(&Value::Tensor(f32_tensor)), Some(24));
146 }
147
148 #[test]
149 fn datetime_object_shape_comes_from_internal_serial_tensor() {
150 let mut object = ObjectInstance::new("datetime".to_string());
151 object.properties.insert(
152 "__serial".to_string(),
153 Value::Tensor(Tensor::new(vec![739351.0, 739352.0], vec![2, 1]).expect("tensor")),
154 );
155
156 assert_eq!(value_shape(&Value::Object(object)), Some(vec![2, 1]));
157 }
158}