Skip to main content

runmat_runtime/builtins/array/sorting_sets/
issortedrows.rs

1//! MATLAB-compatible `issortedrows` builtin.
2
3use runmat_builtins::{
4    BuiltinCompletionPolicy, BuiltinDescriptor, BuiltinErrorDescriptor, BuiltinOutputMode,
5    BuiltinParamArity, BuiltinParamDescriptor, BuiltinParamType, BuiltinSignatureDescriptor, Value,
6};
7use runmat_macros::runtime_builtin;
8
9const OUTPUTS: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
10    name: "tf",
11    ty: BuiltinParamType::LogicalArray,
12    arity: BuiltinParamArity::Required,
13    default: None,
14    description: "True when rows are sorted according to the requested row order.",
15}];
16
17const INPUTS: [BuiltinParamDescriptor; 2] = [
18    BuiltinParamDescriptor {
19        name: "A",
20        ty: BuiltinParamType::Any,
21        arity: BuiltinParamArity::Required,
22        default: None,
23        description: "Input matrix, character matrix, complex matrix, or table.",
24    },
25    BuiltinParamDescriptor {
26        name: "args",
27        ty: BuiltinParamType::Any,
28        arity: BuiltinParamArity::Variadic,
29        default: None,
30        description: "Column, direction, comparison, and missing-placement options.",
31    },
32];
33
34const SIGNATURES: [BuiltinSignatureDescriptor; 2] = [
35    BuiltinSignatureDescriptor {
36        label: "tf = issortedrows(A)",
37        inputs: &INPUTS,
38        outputs: &OUTPUTS,
39    },
40    BuiltinSignatureDescriptor {
41        label: "tf = issortedrows(A, args...)",
42        inputs: &INPUTS,
43        outputs: &OUTPUTS,
44    },
45];
46
47const ERROR_INVALID_INPUT: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
48    code: "RM.ISSORTEDROWS.INVALID_INPUT",
49    identifier: Some("RunMat:issortedrows:InvalidInput"),
50    when: "Input or row-sorting arguments are invalid.",
51    message: "issortedrows: invalid input",
52};
53
54const ERRORS: [BuiltinErrorDescriptor; 1] = [ERROR_INVALID_INPUT];
55
56pub const ISSORTEDROWS_DESCRIPTOR: BuiltinDescriptor = BuiltinDescriptor {
57    signatures: &SIGNATURES,
58    output_mode: BuiltinOutputMode::Fixed,
59    completion_policy: BuiltinCompletionPolicy::Public,
60    errors: &ERRORS,
61};
62
63#[runtime_builtin(
64    name = "issortedrows",
65    category = "array/sorting_sets",
66    summary = "Determine whether matrix or table rows are sorted.",
67    keywords = "issortedrows,sortrows,rows,sorted,table",
68    accel = "sink",
69    sink = true,
70    descriptor(crate::builtins::array::sorting_sets::issortedrows::ISSORTEDROWS_DESCRIPTOR),
71    builtin_path = "crate::builtins::array::sorting_sets::issortedrows"
72)]
73async fn issortedrows_builtin(value: Value, rest: Vec<Value>) -> crate::BuiltinResult<Value> {
74    let evaluation = crate::builtins::array::sorting_sets::sortrows::evaluate(value, &rest).await?;
75    let indices = evaluation.indices_value();
76    let sorted = match indices {
77        Value::Tensor(tensor) => tensor
78            .data
79            .iter()
80            .enumerate()
81            .all(|(idx, value)| *value == idx as f64 + 1.0),
82        Value::Num(value) => value == 1.0,
83        Value::Int(value) => value.to_i64() == 1,
84        _ => false,
85    };
86    Ok(Value::Bool(sorted))
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use futures::executor::block_on;
93    use runmat_builtins::Tensor;
94
95    #[test]
96    fn issortedrows_detects_sorted_and_unsorted_numeric_rows() {
97        let sorted = Value::Tensor(Tensor::new(vec![1.0, 2.0, 1.0, 3.0], vec![2, 2]).unwrap());
98        assert_eq!(
99            block_on(issortedrows_builtin(sorted, Vec::new())).unwrap(),
100            Value::Bool(true)
101        );
102        let unsorted = Value::Tensor(Tensor::new(vec![2.0, 1.0, 1.0, 3.0], vec![2, 2]).unwrap());
103        assert_eq!(
104            block_on(issortedrows_builtin(unsorted, Vec::new())).unwrap(),
105            Value::Bool(false)
106        );
107        assert_eq!(
108            block_on(issortedrows_builtin(Value::Num(1.0), Vec::new())).unwrap(),
109            Value::Bool(true)
110        );
111    }
112}