runmat_runtime/
condition.rs1use runmat_value::Value;
7
8use crate::builtins::common::tensor::{is_scalar_tensor, tensor_element_len, tensor_value_f64};
9use crate::{gather_if_needed_async, runtime_error::semantic_error, RuntimeError};
10
11pub async fn logical_truth_from_value(value: &Value, label: &str) -> Result<bool, RuntimeError> {
13 match value {
14 Value::Bool(flag) => Ok(*flag),
15 Value::Int(integer) => Ok(!integer.is_zero()),
16 Value::Num(number) => Ok(*number != 0.0),
17 Value::LogicalArray(array) if array.data.len() == 1 => Ok(array.data[0] != 0),
18 Value::LogicalArray(array) => Err(invalid_condition(
19 label,
20 format!("logical array with {} elements", array.data.len()),
21 )),
22 Value::Tensor(tensor) if is_scalar_tensor(tensor) => Ok(tensor_value_f64(tensor, 0) != 0.0),
23 Value::Tensor(tensor) => Err(invalid_condition(
24 label,
25 format!("numeric array with {} elements", tensor_element_len(tensor)),
26 )),
27 Value::GpuTensor(_) => {
28 let gathered = gather_if_needed_async(value)
29 .await
30 .map_err(|error| RuntimeError::new(format!("{label}: {error}")))?;
34 Box::pin(logical_truth_from_value(&gathered, label)).await
35 }
36 other => Err(invalid_condition(label, format!("{other:?}"))),
37 }
38}
39
40fn invalid_condition(label: &str, actual: String) -> RuntimeError {
41 semantic_error(
42 "InvalidConditionType",
43 format!("{label}: expected scalar logical or numeric value, got {actual}"),
44 )
45}
46
47#[cfg(test)]
48mod tests {
49 use futures::executor::block_on;
50 use runmat_value::{IntValue, LogicalArray, Tensor};
51
52 use super::*;
53
54 #[test]
55 fn accepts_scalar_logical_and_numeric_values() {
56 assert!(!block_on(logical_truth_from_value(&Value::Bool(false), "condition")).unwrap());
57 assert!(block_on(logical_truth_from_value(
58 &Value::Int(IntValue::I32(-2)),
59 "condition"
60 ))
61 .unwrap());
62 assert!(!block_on(logical_truth_from_value(&Value::Num(0.0), "condition")).unwrap());
63 assert!(block_on(logical_truth_from_value(
64 &Value::LogicalArray(LogicalArray::new(vec![1], vec![1, 1]).unwrap()),
65 "condition"
66 ))
67 .unwrap());
68 assert!(block_on(logical_truth_from_value(
69 &Value::Tensor(Tensor::new_2d(vec![3.0], 1, 1).unwrap()),
70 "condition"
71 ))
72 .unwrap());
73 }
74
75 #[test]
76 fn rejects_nonscalar_and_non_numeric_values_with_semantic_identity() {
77 let error = block_on(logical_truth_from_value(
78 &Value::Tensor(Tensor::new_2d(vec![1.0, 2.0], 1, 2).unwrap()),
79 "if condition",
80 ))
81 .unwrap_err();
82 assert_eq!(error.identifier(), Some("RunMat:InvalidConditionType"));
83 assert!(error
84 .to_string()
85 .contains("if condition: expected scalar logical or numeric value"));
86 }
87}