1use std::cmp::Ordering;
9use std::collections::HashMap;
10
11use runmat_accelerate_api::{
12 GpuTensorHandle, GpuTensorStorage, HostTensorOwned, UniqueOccurrence, UniqueOptions,
13 UniqueOrder, UniqueResult,
14};
15use runmat_builtins::{
16 BuiltinCompletionPolicy, BuiltinDescriptor, BuiltinErrorDescriptor, BuiltinOutputMode,
17 BuiltinParamArity, BuiltinParamDescriptor, BuiltinParamType, BuiltinSignatureDescriptor,
18 CharArray, ComplexTensor, StringArray, Tensor, Value,
19};
20use runmat_macros::runtime_builtin;
21
22use super::type_resolvers::set_values_output_type;
23use crate::build_runtime_error;
24use crate::builtins::common::arg_tokens::tokens_from_values;
25use crate::builtins::common::gpu_helpers;
26use crate::builtins::common::random_args::complex_tensor_into_value;
27use crate::builtins::common::spec::{
28 BroadcastSemantics, BuiltinFusionSpec, BuiltinGpuSpec, ConstantStrategy, GpuOpKind,
29 ProviderHook, ReductionNaN, ResidencyPolicy, ScalarType, ShapeRequirements,
30};
31use crate::builtins::common::tensor;
32
33#[runmat_macros::register_gpu_spec(builtin_path = "crate::builtins::array::sorting_sets::unique")]
34pub const GPU_SPEC: BuiltinGpuSpec = BuiltinGpuSpec {
35 name: "unique",
36 op_kind: GpuOpKind::Custom("unique"),
37 supported_precisions: &[ScalarType::F32, ScalarType::F64],
38 broadcast: BroadcastSemantics::None,
39 provider_hooks: &[ProviderHook::Custom("unique")],
40 constant_strategy: ConstantStrategy::InlineLiteral,
41 residency: ResidencyPolicy::GatherImmediately,
42 nan_mode: ReductionNaN::Include,
43 two_pass_threshold: None,
44 workgroup_size: None,
45 accepts_nan_mode: true,
46 notes: "Providers may implement the `unique` hook; default providers download tensors and reuse the CPU implementation.",
47};
48
49#[runmat_macros::register_fusion_spec(
50 builtin_path = "crate::builtins::array::sorting_sets::unique"
51)]
52pub const FUSION_SPEC: BuiltinFusionSpec = BuiltinFusionSpec {
53 name: "unique",
54 shape: ShapeRequirements::Any,
55 constant_strategy: ConstantStrategy::InlineLiteral,
56 elementwise: None,
57 reduction: None,
58 emits_nan: true,
59 notes: "`unique` terminates fusion chains and materialises results on the host; upstream tensors are gathered when necessary.",
60};
61
62const BUILTIN_NAME: &str = "unique";
63
64const UNIQUE_OUTPUT_C: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
65 name: "C",
66 ty: BuiltinParamType::Any,
67 arity: BuiltinParamArity::Required,
68 default: None,
69 description: "Unique values or rows.",
70}];
71
72const UNIQUE_OUTPUT_C_IA: [BuiltinParamDescriptor; 2] = [
73 BuiltinParamDescriptor {
74 name: "C",
75 ty: BuiltinParamType::Any,
76 arity: BuiltinParamArity::Required,
77 default: None,
78 description: "Unique values or rows.",
79 },
80 BuiltinParamDescriptor {
81 name: "ia",
82 ty: BuiltinParamType::NumericArray,
83 arity: BuiltinParamArity::Required,
84 default: None,
85 description: "Indices selecting representatives in input A.",
86 },
87];
88
89const UNIQUE_OUTPUT_C_IA_IC: [BuiltinParamDescriptor; 3] = [
90 BuiltinParamDescriptor {
91 name: "C",
92 ty: BuiltinParamType::Any,
93 arity: BuiltinParamArity::Required,
94 default: None,
95 description: "Unique values or rows.",
96 },
97 BuiltinParamDescriptor {
98 name: "ia",
99 ty: BuiltinParamType::NumericArray,
100 arity: BuiltinParamArity::Required,
101 default: None,
102 description: "Indices selecting representatives in input A.",
103 },
104 BuiltinParamDescriptor {
105 name: "ic",
106 ty: BuiltinParamType::NumericArray,
107 arity: BuiltinParamArity::Required,
108 default: None,
109 description: "Indices mapping each input element/row to C.",
110 },
111];
112
113const UNIQUE_INPUTS_A: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
114 name: "A",
115 ty: BuiltinParamType::Any,
116 arity: BuiltinParamArity::Required,
117 default: None,
118 description: "Input array.",
119}];
120
121const UNIQUE_INPUTS_A_OPTIONS: [BuiltinParamDescriptor; 2] = [
122 BuiltinParamDescriptor {
123 name: "A",
124 ty: BuiltinParamType::Any,
125 arity: BuiltinParamArity::Required,
126 default: None,
127 description: "Input array.",
128 },
129 BuiltinParamDescriptor {
130 name: "option",
131 ty: BuiltinParamType::StringScalar,
132 arity: BuiltinParamArity::Variadic,
133 default: None,
134 description: "Option tokens: 'sorted'|'stable'|'rows'|'first'|'last'.",
135 },
136];
137
138const UNIQUE_SIGNATURES: [BuiltinSignatureDescriptor; 6] = [
139 BuiltinSignatureDescriptor {
140 label: "C = unique(A)",
141 inputs: &UNIQUE_INPUTS_A,
142 outputs: &UNIQUE_OUTPUT_C,
143 },
144 BuiltinSignatureDescriptor {
145 label: "C = unique(A, option...)",
146 inputs: &UNIQUE_INPUTS_A_OPTIONS,
147 outputs: &UNIQUE_OUTPUT_C,
148 },
149 BuiltinSignatureDescriptor {
150 label: "[C, ia] = unique(A)",
151 inputs: &UNIQUE_INPUTS_A,
152 outputs: &UNIQUE_OUTPUT_C_IA,
153 },
154 BuiltinSignatureDescriptor {
155 label: "[C, ia] = unique(A, option...)",
156 inputs: &UNIQUE_INPUTS_A_OPTIONS,
157 outputs: &UNIQUE_OUTPUT_C_IA,
158 },
159 BuiltinSignatureDescriptor {
160 label: "[C, ia, ic] = unique(A)",
161 inputs: &UNIQUE_INPUTS_A,
162 outputs: &UNIQUE_OUTPUT_C_IA_IC,
163 },
164 BuiltinSignatureDescriptor {
165 label: "[C, ia, ic] = unique(A, option...)",
166 inputs: &UNIQUE_INPUTS_A_OPTIONS,
167 outputs: &UNIQUE_OUTPUT_C_IA_IC,
168 },
169];
170
171const UNIQUE_ERROR_LEGACY_OPTION_UNSUPPORTED: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
172 code: "RM.UNIQUE.LEGACY_OPTION_UNSUPPORTED",
173 identifier: Some("RunMat:unique:LegacyOptionUnsupported"),
174 when: "Legacy compatibility options are requested.",
175 message: "unique: the 'legacy' behaviour is not supported",
176};
177
178const UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
179 code: "RM.UNIQUE.CONFLICTING_ORDER_OPTIONS",
180 identifier: Some("RunMat:unique:ConflictingOrderOptions"),
181 when: "Both 'sorted' and 'stable' options are provided.",
182 message: "unique: cannot combine 'sorted' with 'stable'",
183};
184
185const UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS: BuiltinErrorDescriptor =
186 BuiltinErrorDescriptor {
187 code: "RM.UNIQUE.CONFLICTING_OCCURRENCE_OPTIONS",
188 identifier: Some("RunMat:unique:ConflictingOccurrenceOptions"),
189 when: "Both 'first' and 'last' options are provided.",
190 message: "unique: cannot combine 'first' with 'last'",
191 };
192
193const UNIQUE_ERROR_UNKNOWN_OPTION: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
194 code: "RM.UNIQUE.UNKNOWN_OPTION",
195 identifier: Some("RunMat:unique:UnknownOption"),
196 when: "An unsupported option token is provided.",
197 message: "unique: unrecognised option",
198};
199
200const UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
201 code: "RM.UNIQUE.ROWS_REQUIRES_2D_MATRIX",
202 identifier: Some("RunMat:unique:RowsRequiresTwoDimensionalInput"),
203 when: "'rows' mode is used with non-2D data.",
204 message: "unique: 'rows' option requires a 2-D matrix input",
205};
206
207const UNIQUE_ERROR_UNSUPPORTED_INPUT_TYPE: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
208 code: "RM.UNIQUE.UNSUPPORTED_INPUT_TYPE",
209 identifier: Some("RunMat:unique:UnsupportedInputType"),
210 when: "Input cannot be converted into a supported unique domain.",
211 message: "unique: unsupported input type",
212};
213
214const UNIQUE_ERROR_INVALID_ARGUMENT: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
215 code: "RM.UNIQUE.INVALID_ARGUMENT",
216 identifier: Some("RunMat:unique:InvalidArgument"),
217 when: "Option arguments are not string-like where required.",
218 message: "unique: expected string option arguments",
219};
220
221const UNIQUE_ERROR_INTERNAL: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
222 code: "RM.UNIQUE.INTERNAL",
223 identifier: Some("RunMat:unique:Internal"),
224 when: "Internal conversion/allocation/provider decode fails.",
225 message: "unique: internal operation failed",
226};
227
228const UNIQUE_ERRORS: [BuiltinErrorDescriptor; 8] = [
229 UNIQUE_ERROR_LEGACY_OPTION_UNSUPPORTED,
230 UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS,
231 UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS,
232 UNIQUE_ERROR_UNKNOWN_OPTION,
233 UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX,
234 UNIQUE_ERROR_UNSUPPORTED_INPUT_TYPE,
235 UNIQUE_ERROR_INVALID_ARGUMENT,
236 UNIQUE_ERROR_INTERNAL,
237];
238
239pub const UNIQUE_DESCRIPTOR: BuiltinDescriptor = BuiltinDescriptor {
240 signatures: &UNIQUE_SIGNATURES,
241 output_mode: BuiltinOutputMode::ByRequestedOutputCount,
242 completion_policy: BuiltinCompletionPolicy::Public,
243 errors: &UNIQUE_ERRORS,
244};
245
246fn unique_error_with(
247 error: &'static BuiltinErrorDescriptor,
248 message: impl Into<String>,
249) -> crate::RuntimeError {
250 let mut builder = build_runtime_error(message).with_builtin(BUILTIN_NAME);
251 if let Some(identifier) = error.identifier {
252 builder = builder.with_identifier(identifier);
253 }
254 builder.build()
255}
256
257fn unique_error(error: &'static BuiltinErrorDescriptor) -> crate::RuntimeError {
258 unique_error_with(error, error.message)
259}
260
261fn unique_internal_error(message: impl Into<String>) -> crate::RuntimeError {
262 unique_error_with(&UNIQUE_ERROR_INTERNAL, message)
263}
264
265#[runtime_builtin(
266 name = "unique",
267 category = "array/sorting_sets",
268 summary = "Return unique elements or rows with optional index mappings.",
269 keywords = "unique,set,distinct,stable,rows,indices,gpu",
270 accel = "array_construct",
271 sink = true,
272 type_resolver(set_values_output_type),
273 descriptor(crate::builtins::array::sorting_sets::unique::UNIQUE_DESCRIPTOR),
274 builtin_path = "crate::builtins::array::sorting_sets::unique"
275)]
276async fn unique_builtin(value: Value, rest: Vec<Value>) -> crate::BuiltinResult<Value> {
277 let eval = evaluate(value, &rest).await?;
278 if let Some(out_count) = crate::output_count::current_output_count() {
279 if out_count == 0 {
280 return Ok(Value::OutputList(Vec::new()));
281 }
282 if out_count == 1 {
283 return Ok(Value::OutputList(vec![eval.into_values_value()]));
284 }
285 if out_count == 2 {
286 let (values, ia) = eval.into_pair();
287 return Ok(Value::OutputList(vec![values, ia]));
288 }
289 let (values, ia, ic) = eval.into_triple();
290 return Ok(crate::output_count::output_list_with_padding(
291 out_count,
292 vec![values, ia, ic],
293 ));
294 }
295 Ok(eval.into_values_value())
296}
297
298pub async fn evaluate(value: Value, rest: &[Value]) -> crate::BuiltinResult<UniqueEvaluation> {
300 let opts = parse_options(rest)?;
301 match value {
302 Value::GpuTensor(handle) => unique_gpu(handle, &opts).await,
303 other => unique_host(other, &opts),
304 }
305}
306
307fn parse_options(rest: &[Value]) -> crate::BuiltinResult<UniqueOptions> {
308 let mut opts = UniqueOptions {
309 rows: false,
310 order: UniqueOrder::Sorted,
311 occurrence: UniqueOccurrence::First,
312 };
313 let mut seen_order: Option<UniqueOrder> = None;
314 let mut seen_occurrence: Option<UniqueOccurrence> = None;
315
316 let tokens = tokens_from_values(rest);
317 for (arg, token) in rest.iter().zip(tokens.iter()) {
318 let text = match token {
319 crate::builtins::common::arg_tokens::ArgToken::String(text) => text.as_str(),
320 _ => {
321 let text = tensor::value_to_string(arg)
322 .ok_or_else(|| unique_error(&UNIQUE_ERROR_INVALID_ARGUMENT))?;
323 let lowered = text.trim().to_ascii_lowercase();
324 parse_unique_option(&mut opts, &mut seen_order, &mut seen_occurrence, &lowered)?;
325 continue;
326 }
327 };
328 parse_unique_option(&mut opts, &mut seen_order, &mut seen_occurrence, text)?;
329 }
330
331 Ok(opts)
332}
333
334fn parse_unique_option(
335 opts: &mut UniqueOptions,
336 seen_order: &mut Option<UniqueOrder>,
337 seen_occurrence: &mut Option<UniqueOccurrence>,
338 lowered: &str,
339) -> crate::BuiltinResult<()> {
340 match lowered {
341 "sorted" => {
342 if let Some(prev) = seen_order {
343 if *prev != UniqueOrder::Sorted {
344 return Err(unique_error_with(
345 &UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS,
346 UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS.message,
347 ));
348 }
349 }
350 *seen_order = Some(UniqueOrder::Sorted);
351 opts.order = UniqueOrder::Sorted;
352 }
353 "stable" => {
354 if let Some(prev) = seen_order {
355 if *prev != UniqueOrder::Stable {
356 return Err(unique_error_with(
357 &UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS,
358 UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS.message,
359 ));
360 }
361 }
362 *seen_order = Some(UniqueOrder::Stable);
363 opts.order = UniqueOrder::Stable;
364 }
365 "rows" => {
366 opts.rows = true;
367 }
368 "first" => {
369 if let Some(prev) = seen_occurrence {
370 if *prev != UniqueOccurrence::First {
371 return Err(unique_error_with(
372 &UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS,
373 UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS.message,
374 ));
375 }
376 }
377 *seen_occurrence = Some(UniqueOccurrence::First);
378 opts.occurrence = UniqueOccurrence::First;
379 }
380 "last" => {
381 if let Some(prev) = seen_occurrence {
382 if *prev != UniqueOccurrence::Last {
383 return Err(unique_error_with(
384 &UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS,
385 UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS.message,
386 ));
387 }
388 }
389 *seen_occurrence = Some(UniqueOccurrence::Last);
390 opts.occurrence = UniqueOccurrence::Last;
391 }
392 "legacy" | "r2012a" => {
393 return Err(unique_error(&UNIQUE_ERROR_LEGACY_OPTION_UNSUPPORTED));
394 }
395 other => {
396 return Err(unique_error_with(
397 &UNIQUE_ERROR_UNKNOWN_OPTION,
398 format!("unique: unrecognised option '{other}'"),
399 ));
400 }
401 }
402 Ok(())
403}
404
405async fn unique_gpu(
406 handle: GpuTensorHandle,
407 opts: &UniqueOptions,
408) -> crate::BuiltinResult<UniqueEvaluation> {
409 if let Some(provider) = runmat_accelerate_api::provider() {
410 if let Ok(result) = provider.unique(&handle, opts).await {
411 return UniqueEvaluation::from_unique_result(result);
412 }
413 }
414 let tensor = gpu_helpers::gather_tensor_async(&handle).await?;
415 unique_numeric_from_tensor(tensor, opts)
416}
417
418fn unique_host(value: Value, opts: &UniqueOptions) -> crate::BuiltinResult<UniqueEvaluation> {
419 match value {
420 Value::Tensor(tensor) => unique_numeric_from_tensor(tensor, opts),
421 Value::Num(n) => {
422 let tensor = Tensor::new(vec![n], vec![1, 1]).map_err(|e| unique_internal_error(format!("unique: {e}")))?;
423 unique_numeric_from_tensor(tensor, opts)
424 }
425 Value::Int(i) => {
426 let tensor = Tensor::new(vec![i.to_f64()], vec![1, 1])
427 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
428 unique_numeric_from_tensor(tensor, opts)
429 }
430 Value::Bool(b) => {
431 let tensor = Tensor::new(vec![if b { 1.0 } else { 0.0 }], vec![1, 1])
432 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
433 unique_numeric_from_tensor(tensor, opts)
434 }
435 Value::LogicalArray(logical) => {
436 let tensor = tensor::logical_to_tensor(&logical)
437 .map_err(|e| unique_internal_error(e))?;
438 unique_numeric_from_tensor(tensor, opts)
439 }
440 Value::ComplexTensor(tensor) => unique_complex_from_tensor(tensor, opts),
441 Value::Complex(re, im) => {
442 let tensor = ComplexTensor::new(vec![(re, im)], vec![1, 1])
443 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
444 unique_complex_from_tensor(tensor, opts)
445 }
446 Value::CharArray(array) => unique_char_array(array, opts),
447 Value::StringArray(array) => unique_string_array(array, opts),
448 Value::String(s) => {
449 let array = StringArray::new(vec![s], vec![1, 1]).map_err(|e| unique_internal_error(format!("unique: {e}")))?;
450 unique_string_array(array, opts)
451 }
452 other => Err(unique_error_with(
453 &UNIQUE_ERROR_UNSUPPORTED_INPUT_TYPE,
454 format!(
455 "unique: unsupported input type {:?}; expected numeric, logical, char, string, or complex values",
456 other
457 ),
458 )),
459 }
460}
461
462pub fn unique_numeric_from_tensor(
463 tensor: Tensor,
464 opts: &UniqueOptions,
465) -> crate::BuiltinResult<UniqueEvaluation> {
466 if opts.rows {
467 unique_numeric_rows(tensor, opts)
468 } else {
469 unique_numeric_elements(tensor, opts)
470 }
471}
472
473fn unique_numeric_elements(
474 tensor: Tensor,
475 opts: &UniqueOptions,
476) -> crate::BuiltinResult<UniqueEvaluation> {
477 let len = tensor.data.len();
478 if len == 0 {
479 let values = Tensor::new(Vec::new(), vec![0, 1])
480 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
481 let ia = Tensor::new(Vec::new(), vec![0, 1])
482 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
483 let ic = Tensor::new(Vec::new(), vec![0, 1])
484 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
485 return Ok(UniqueEvaluation::new(
486 tensor::tensor_into_value(values),
487 ia,
488 ic,
489 ));
490 }
491
492 let mut entries = Vec::<NumericElementEntry>::new();
493 let mut map: HashMap<u64, usize> = HashMap::new();
494 let mut element_entry_index = Vec::with_capacity(len);
495
496 for (idx, &value) in tensor.data.iter().enumerate() {
497 let key = canonicalize_f64(value);
498 match map.get(&key) {
499 Some(&entry_idx) => {
500 entries[entry_idx].last = idx;
501 element_entry_index.push(entry_idx);
502 }
503 None => {
504 let entry_idx = entries.len();
505 entries.push(NumericElementEntry {
506 value,
507 first: idx,
508 last: idx,
509 });
510 map.insert(key, entry_idx);
511 element_entry_index.push(entry_idx);
512 }
513 }
514 }
515
516 let mut order: Vec<usize> = (0..entries.len()).collect();
517 if opts.order == UniqueOrder::Sorted {
518 order.sort_by(|&a, &b| compare_f64(entries[a].value, entries[b].value));
519 }
520
521 let mut entry_to_position = vec![0usize; entries.len()];
522 for (pos, &entry_idx) in order.iter().enumerate() {
523 entry_to_position[entry_idx] = pos;
524 }
525
526 let mut values = Vec::with_capacity(order.len());
527 let mut ia = Vec::with_capacity(order.len());
528 for &entry_idx in &order {
529 let entry = &entries[entry_idx];
530 values.push(entry.value);
531 let occurrence = match opts.occurrence {
532 UniqueOccurrence::First => entry.first,
533 UniqueOccurrence::Last => entry.last,
534 };
535 ia.push((occurrence + 1) as f64);
536 }
537
538 let mut ic = Vec::with_capacity(len);
539 for entry_idx in element_entry_index {
540 let pos = entry_to_position[entry_idx];
541 ic.push((pos + 1) as f64);
542 }
543
544 let value_tensor = Tensor::new(values, vec![order.len(), 1])
545 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
546 let ia_tensor = Tensor::new(ia, vec![order.len(), 1])
547 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
548 let ic_tensor =
549 Tensor::new(ic, vec![len, 1]).map_err(|e| unique_internal_error(format!("unique: {e}")))?;
550
551 Ok(UniqueEvaluation::new(
552 tensor::tensor_into_value(value_tensor),
553 ia_tensor,
554 ic_tensor,
555 ))
556}
557
558fn unique_numeric_rows(
559 tensor: Tensor,
560 opts: &UniqueOptions,
561) -> crate::BuiltinResult<UniqueEvaluation> {
562 if tensor.shape.len() != 2 {
563 return Err(unique_error_with(
564 &UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX,
565 UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX.message,
566 ));
567 }
568 let rows = tensor.shape[0];
569 let cols = tensor.shape[1];
570
571 if rows == 0 || cols == 0 {
572 let values = Tensor::new(Vec::new(), vec![0, cols])
573 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
574 let ia = Tensor::new(Vec::new(), vec![0, 1])
575 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
576 let ic = Tensor::new(Vec::new(), vec![rows, 1])
577 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
578 return Ok(UniqueEvaluation::new(
579 tensor::tensor_into_value(values),
580 ia,
581 ic,
582 ));
583 }
584
585 let mut entries = Vec::<NumericRowEntry>::new();
586 let mut map: HashMap<NumericRowKey, usize> = HashMap::new();
587 let mut row_entry_index = Vec::with_capacity(rows);
588
589 for r in 0..rows {
590 let mut row_values = Vec::with_capacity(cols);
591 for c in 0..cols {
592 let idx = r + c * rows;
593 row_values.push(tensor.data[idx]);
594 }
595 let key = NumericRowKey::from_slice(&row_values);
596 match map.get(&key) {
597 Some(&entry_idx) => {
598 entries[entry_idx].last = r;
599 row_entry_index.push(entry_idx);
600 }
601 None => {
602 let entry_idx = entries.len();
603 entries.push(NumericRowEntry {
604 row_data: row_values.clone(),
605 first: r,
606 last: r,
607 });
608 map.insert(key, entry_idx);
609 row_entry_index.push(entry_idx);
610 }
611 }
612 }
613
614 let mut order: Vec<usize> = (0..entries.len()).collect();
615 if opts.order == UniqueOrder::Sorted {
616 order.sort_by(|&a, &b| compare_numeric_rows(&entries[a].row_data, &entries[b].row_data));
617 }
618
619 let mut entry_to_position = vec![0usize; entries.len()];
620 for (pos, &entry_idx) in order.iter().enumerate() {
621 entry_to_position[entry_idx] = pos;
622 }
623
624 let unique_rows_count = order.len();
625 let mut values = vec![0.0f64; unique_rows_count * cols];
626 for (row_pos, &entry_idx) in order.iter().enumerate() {
627 let row = &entries[entry_idx].row_data;
628 for (col, value) in row.iter().enumerate().take(cols) {
629 let dest = row_pos + col * unique_rows_count;
630 values[dest] = *value;
631 }
632 }
633
634 let mut ia = Vec::with_capacity(unique_rows_count);
635 for &entry_idx in &order {
636 let entry = &entries[entry_idx];
637 let occurrence = match opts.occurrence {
638 UniqueOccurrence::First => entry.first,
639 UniqueOccurrence::Last => entry.last,
640 };
641 ia.push((occurrence + 1) as f64);
642 }
643
644 let mut ic = Vec::with_capacity(rows);
645 for entry_idx in row_entry_index {
646 let pos = entry_to_position[entry_idx];
647 ic.push((pos + 1) as f64);
648 }
649
650 let value_tensor = Tensor::new(values, vec![unique_rows_count, cols])
651 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
652 let ia_tensor = Tensor::new(ia, vec![unique_rows_count, 1])
653 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
654 let ic_tensor = Tensor::new(ic, vec![rows, 1])
655 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
656
657 Ok(UniqueEvaluation::new(
658 tensor::tensor_into_value(value_tensor),
659 ia_tensor,
660 ic_tensor,
661 ))
662}
663
664fn unique_complex_from_tensor(
665 tensor: ComplexTensor,
666 opts: &UniqueOptions,
667) -> crate::BuiltinResult<UniqueEvaluation> {
668 if opts.rows {
669 unique_complex_rows(tensor, opts)
670 } else {
671 unique_complex_elements(tensor, opts)
672 }
673}
674
675fn unique_complex_elements(
676 tensor: ComplexTensor,
677 opts: &UniqueOptions,
678) -> crate::BuiltinResult<UniqueEvaluation> {
679 let len = tensor.data.len();
680 if len == 0 {
681 let values = ComplexTensor::new(Vec::new(), vec![0, 1])
682 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
683 let ia = Tensor::new(Vec::new(), vec![0, 1])
684 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
685 let ic = Tensor::new(Vec::new(), vec![0, 1])
686 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
687 return Ok(UniqueEvaluation::new(
688 complex_tensor_into_value(values),
689 ia,
690 ic,
691 ));
692 }
693
694 let mut entries = Vec::<ComplexElementEntry>::new();
695 let mut map: HashMap<ComplexKey, usize> = HashMap::new();
696 let mut element_entry_index = Vec::with_capacity(len);
697
698 for (idx, &value) in tensor.data.iter().enumerate() {
699 let key = ComplexKey::new(value);
700 match map.get(&key) {
701 Some(&entry_idx) => {
702 entries[entry_idx].last = idx;
703 element_entry_index.push(entry_idx);
704 }
705 None => {
706 let entry_idx = entries.len();
707 entries.push(ComplexElementEntry {
708 value,
709 first: idx,
710 last: idx,
711 });
712 map.insert(key, entry_idx);
713 element_entry_index.push(entry_idx);
714 }
715 }
716 }
717
718 let mut order: Vec<usize> = (0..entries.len()).collect();
719 if opts.order == UniqueOrder::Sorted {
720 order.sort_by(|&a, &b| compare_complex(entries[a].value, entries[b].value));
721 }
722
723 let mut entry_to_position = vec![0usize; entries.len()];
724 for (pos, &entry_idx) in order.iter().enumerate() {
725 entry_to_position[entry_idx] = pos;
726 }
727
728 let mut values = Vec::with_capacity(order.len());
729 let mut ia = Vec::with_capacity(order.len());
730 for &entry_idx in &order {
731 let entry = &entries[entry_idx];
732 values.push(entry.value);
733 let occurrence = match opts.occurrence {
734 UniqueOccurrence::First => entry.first,
735 UniqueOccurrence::Last => entry.last,
736 };
737 ia.push((occurrence + 1) as f64);
738 }
739
740 let mut ic = Vec::with_capacity(len);
741 for entry_idx in element_entry_index {
742 let pos = entry_to_position[entry_idx];
743 ic.push((pos + 1) as f64);
744 }
745
746 let value_tensor = ComplexTensor::new(values, vec![order.len(), 1])
747 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
748 let ia_tensor = Tensor::new(ia, vec![order.len(), 1])
749 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
750 let ic_tensor =
751 Tensor::new(ic, vec![len, 1]).map_err(|e| unique_internal_error(format!("unique: {e}")))?;
752
753 Ok(UniqueEvaluation::new(
754 complex_tensor_into_value(value_tensor),
755 ia_tensor,
756 ic_tensor,
757 ))
758}
759
760fn unique_complex_rows(
761 tensor: ComplexTensor,
762 opts: &UniqueOptions,
763) -> crate::BuiltinResult<UniqueEvaluation> {
764 if tensor.shape.len() != 2 {
765 return Err(unique_error_with(
766 &UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX,
767 UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX.message,
768 ));
769 }
770 let rows = tensor.shape[0];
771 let cols = tensor.shape[1];
772
773 if rows == 0 || cols == 0 {
774 let values = ComplexTensor::new(Vec::new(), vec![rows, cols])
775 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
776 let ia = Tensor::new(Vec::new(), vec![0, 1])
777 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
778 let ic = Tensor::new(Vec::new(), vec![rows, 1])
779 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
780 return Ok(UniqueEvaluation::new(
781 complex_tensor_into_value(values),
782 ia,
783 ic,
784 ));
785 }
786
787 let mut entries = Vec::<ComplexRowEntry>::new();
788 let mut map: HashMap<Vec<ComplexKey>, usize> = HashMap::new();
789 let mut row_entry_index = Vec::with_capacity(rows);
790
791 for r in 0..rows {
792 let mut row_values = Vec::with_capacity(cols);
793 let mut key_row = Vec::with_capacity(cols);
794 for c in 0..cols {
795 let idx = r + c * rows;
796 let value = tensor.data[idx];
797 row_values.push(value);
798 key_row.push(ComplexKey::new(value));
799 }
800 match map.get(&key_row) {
801 Some(&entry_idx) => {
802 entries[entry_idx].last = r;
803 row_entry_index.push(entry_idx);
804 }
805 None => {
806 let entry_idx = entries.len();
807 entries.push(ComplexRowEntry {
808 row_data: row_values.clone(),
809 first: r,
810 last: r,
811 });
812 map.insert(key_row, entry_idx);
813 row_entry_index.push(entry_idx);
814 }
815 }
816 }
817
818 let mut order: Vec<usize> = (0..entries.len()).collect();
819 if opts.order == UniqueOrder::Sorted {
820 order.sort_by(|&a, &b| compare_complex_rows(&entries[a].row_data, &entries[b].row_data));
821 }
822
823 let mut entry_to_position = vec![0usize; entries.len()];
824 for (pos, &entry_idx) in order.iter().enumerate() {
825 entry_to_position[entry_idx] = pos;
826 }
827
828 let unique_rows_count = order.len();
829 let mut values = vec![(0.0, 0.0); unique_rows_count * cols];
830 for (row_pos, &entry_idx) in order.iter().enumerate() {
831 let row = &entries[entry_idx].row_data;
832 for (col, value) in row.iter().enumerate().take(cols) {
833 let dest = row_pos + col * unique_rows_count;
834 values[dest] = *value;
835 }
836 }
837
838 let mut ia = Vec::with_capacity(unique_rows_count);
839 for &entry_idx in &order {
840 let entry = &entries[entry_idx];
841 let occurrence = match opts.occurrence {
842 UniqueOccurrence::First => entry.first,
843 UniqueOccurrence::Last => entry.last,
844 };
845 ia.push((occurrence + 1) as f64);
846 }
847
848 let mut ic = Vec::with_capacity(rows);
849 for entry_idx in row_entry_index {
850 let pos = entry_to_position[entry_idx];
851 ic.push((pos + 1) as f64);
852 }
853
854 let value_tensor = ComplexTensor::new(values, vec![unique_rows_count, cols])
855 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
856 let ia_tensor = Tensor::new(ia, vec![unique_rows_count, 1])
857 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
858 let ic_tensor = Tensor::new(ic, vec![rows, 1])
859 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
860
861 Ok(UniqueEvaluation::new(
862 complex_tensor_into_value(value_tensor),
863 ia_tensor,
864 ic_tensor,
865 ))
866}
867
868fn unique_char_array(
869 array: CharArray,
870 opts: &UniqueOptions,
871) -> crate::BuiltinResult<UniqueEvaluation> {
872 if opts.rows {
873 unique_char_rows(array, opts)
874 } else {
875 unique_char_elements(array, opts)
876 }
877}
878
879fn unique_char_elements(
880 array: CharArray,
881 opts: &UniqueOptions,
882) -> crate::BuiltinResult<UniqueEvaluation> {
883 let rows = array.rows;
884 let cols = array.cols;
885 let total = rows * cols;
886 if total == 0 {
887 let values = CharArray::new(Vec::new(), 0, 0)
888 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
889 let ia = Tensor::new(Vec::new(), vec![0, 1])
890 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
891 let ic = Tensor::new(Vec::new(), vec![0, 1])
892 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
893 return Ok(UniqueEvaluation::new(Value::CharArray(values), ia, ic));
894 }
895
896 let mut entries = Vec::<CharElementEntry>::new();
897 let mut map: HashMap<u32, usize> = HashMap::new();
898 let mut element_entry_index = Vec::with_capacity(total);
899
900 for col in 0..cols {
901 for row in 0..rows {
902 let linear_idx = row + col * rows;
903 let data_idx = row * cols + col;
904 let ch = array.data[data_idx];
905 let key = ch as u32;
906 match map.get(&key) {
907 Some(&entry_idx) => {
908 entries[entry_idx].last = linear_idx;
909 element_entry_index.push(entry_idx);
910 }
911 None => {
912 let entry_idx = entries.len();
913 entries.push(CharElementEntry {
914 ch,
915 first: linear_idx,
916 last: linear_idx,
917 });
918 map.insert(key, entry_idx);
919 element_entry_index.push(entry_idx);
920 }
921 }
922 }
923 }
924
925 let mut order: Vec<usize> = (0..entries.len()).collect();
926 if opts.order == UniqueOrder::Sorted {
927 order.sort_by(|&a, &b| entries[a].ch.cmp(&entries[b].ch));
928 }
929
930 let mut entry_to_position = vec![0usize; entries.len()];
931 for (pos, &entry_idx) in order.iter().enumerate() {
932 entry_to_position[entry_idx] = pos;
933 }
934
935 let mut values = Vec::with_capacity(order.len());
936 let mut ia = Vec::with_capacity(order.len());
937 for &entry_idx in &order {
938 let entry = &entries[entry_idx];
939 values.push(entry.ch);
940 let occurrence = match opts.occurrence {
941 UniqueOccurrence::First => entry.first,
942 UniqueOccurrence::Last => entry.last,
943 };
944 ia.push((occurrence + 1) as f64);
945 }
946
947 let mut ic = Vec::with_capacity(total);
948 for entry_idx in element_entry_index {
949 let pos = entry_to_position[entry_idx];
950 ic.push((pos + 1) as f64);
951 }
952
953 let value_array = CharArray::new(values, order.len(), 1)
954 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
955 let ia_tensor = Tensor::new(ia, vec![order.len(), 1])
956 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
957 let ic_tensor = Tensor::new(ic, vec![total, 1])
958 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
959
960 Ok(UniqueEvaluation::new(
961 Value::CharArray(value_array),
962 ia_tensor,
963 ic_tensor,
964 ))
965}
966
967fn unique_char_rows(
968 array: CharArray,
969 opts: &UniqueOptions,
970) -> crate::BuiltinResult<UniqueEvaluation> {
971 let rows = array.rows;
972 let cols = array.cols;
973 if rows == 0 {
974 let values = CharArray::new(Vec::new(), 0, cols)
975 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
976 let ia = Tensor::new(Vec::new(), vec![0, 1])
977 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
978 let ic = Tensor::new(Vec::new(), vec![0, 1])
979 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
980 return Ok(UniqueEvaluation::new(Value::CharArray(values), ia, ic));
981 }
982
983 let mut entries = Vec::<CharRowEntry>::new();
984 let mut map: HashMap<RowCharKey, usize> = HashMap::new();
985 let mut row_entry_index = Vec::with_capacity(rows);
986
987 for r in 0..rows {
988 let start = r * cols;
989 let end = start + cols;
990 let slice = &array.data[start..end];
991 let key = RowCharKey::from_slice(slice);
992 match map.get(&key) {
993 Some(&entry_idx) => {
994 entries[entry_idx].last = r;
995 row_entry_index.push(entry_idx);
996 }
997 None => {
998 let entry_idx = entries.len();
999 entries.push(CharRowEntry {
1000 row_data: slice.to_vec(),
1001 first: r,
1002 last: r,
1003 });
1004 map.insert(key, entry_idx);
1005 row_entry_index.push(entry_idx);
1006 }
1007 }
1008 }
1009
1010 let mut order: Vec<usize> = (0..entries.len()).collect();
1011 if opts.order == UniqueOrder::Sorted {
1012 order.sort_by(|&a, &b| compare_char_rows(&entries[a].row_data, &entries[b].row_data));
1013 }
1014
1015 let mut entry_to_position = vec![0usize; entries.len()];
1016 for (pos, &entry_idx) in order.iter().enumerate() {
1017 entry_to_position[entry_idx] = pos;
1018 }
1019
1020 let unique_rows_count = order.len();
1021 let mut values = vec!['\0'; unique_rows_count * cols];
1022 for (row_pos, &entry_idx) in order.iter().enumerate() {
1023 let row = &entries[entry_idx].row_data;
1024 for col in 0..cols {
1025 let dest = row_pos * cols + col;
1026 if col < row.len() {
1027 values[dest] = row[col];
1028 }
1029 }
1030 }
1031
1032 let mut ia = Vec::with_capacity(unique_rows_count);
1033 for &entry_idx in &order {
1034 let entry = &entries[entry_idx];
1035 let occurrence = match opts.occurrence {
1036 UniqueOccurrence::First => entry.first,
1037 UniqueOccurrence::Last => entry.last,
1038 };
1039 ia.push((occurrence + 1) as f64);
1040 }
1041
1042 let mut ic = Vec::with_capacity(rows);
1043 for entry_idx in row_entry_index {
1044 let pos = entry_to_position[entry_idx];
1045 ic.push((pos + 1) as f64);
1046 }
1047
1048 let value_array = CharArray::new(values, unique_rows_count, cols)
1049 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1050 let ia_tensor = Tensor::new(ia, vec![unique_rows_count, 1])
1051 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1052 let ic_tensor = Tensor::new(ic, vec![rows, 1])
1053 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1054
1055 Ok(UniqueEvaluation::new(
1056 Value::CharArray(value_array),
1057 ia_tensor,
1058 ic_tensor,
1059 ))
1060}
1061
1062fn unique_string_array(
1063 array: StringArray,
1064 opts: &UniqueOptions,
1065) -> crate::BuiltinResult<UniqueEvaluation> {
1066 if opts.rows {
1067 unique_string_rows(array, opts)
1068 } else {
1069 unique_string_elements(array, opts)
1070 }
1071}
1072
1073fn unique_string_elements(
1074 array: StringArray,
1075 opts: &UniqueOptions,
1076) -> crate::BuiltinResult<UniqueEvaluation> {
1077 let len = array.data.len();
1078 if len == 0 {
1079 let values = StringArray::new(Vec::new(), vec![0, 1])
1080 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1081 let ia = Tensor::new(Vec::new(), vec![0, 1])
1082 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1083 let ic = Tensor::new(Vec::new(), vec![0, 1])
1084 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1085 return Ok(UniqueEvaluation::new(Value::StringArray(values), ia, ic));
1086 }
1087
1088 let mut entries = Vec::<StringElementEntry>::new();
1089 let mut map: HashMap<String, usize> = HashMap::new();
1090 let mut element_entry_index = Vec::with_capacity(len);
1091
1092 for (idx, value) in array.data.iter().enumerate() {
1093 match map.get(value) {
1094 Some(&entry_idx) => {
1095 entries[entry_idx].last = idx;
1096 element_entry_index.push(entry_idx);
1097 }
1098 None => {
1099 let entry_idx = entries.len();
1100 entries.push(StringElementEntry {
1101 value: value.clone(),
1102 first: idx,
1103 last: idx,
1104 });
1105 map.insert(value.clone(), entry_idx);
1106 element_entry_index.push(entry_idx);
1107 }
1108 }
1109 }
1110
1111 let mut order: Vec<usize> = (0..entries.len()).collect();
1112 if opts.order == UniqueOrder::Sorted {
1113 order.sort_by(|&a, &b| entries[a].value.cmp(&entries[b].value));
1114 }
1115
1116 let mut entry_to_position = vec![0usize; entries.len()];
1117 for (pos, &entry_idx) in order.iter().enumerate() {
1118 entry_to_position[entry_idx] = pos;
1119 }
1120
1121 let mut values = Vec::with_capacity(order.len());
1122 let mut ia = Vec::with_capacity(order.len());
1123 for &entry_idx in &order {
1124 let entry = &entries[entry_idx];
1125 values.push(entry.value.clone());
1126 let occurrence = match opts.occurrence {
1127 UniqueOccurrence::First => entry.first,
1128 UniqueOccurrence::Last => entry.last,
1129 };
1130 ia.push((occurrence + 1) as f64);
1131 }
1132
1133 let mut ic = Vec::with_capacity(len);
1134 for entry_idx in element_entry_index {
1135 let pos = entry_to_position[entry_idx];
1136 ic.push((pos + 1) as f64);
1137 }
1138
1139 let value_array = StringArray::new(values, vec![order.len(), 1])
1140 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1141 let ia_tensor = Tensor::new(ia, vec![order.len(), 1])
1142 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1143 let ic_tensor =
1144 Tensor::new(ic, vec![len, 1]).map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1145
1146 Ok(UniqueEvaluation::new(
1147 Value::StringArray(value_array),
1148 ia_tensor,
1149 ic_tensor,
1150 ))
1151}
1152
1153fn unique_string_rows(
1154 array: StringArray,
1155 opts: &UniqueOptions,
1156) -> crate::BuiltinResult<UniqueEvaluation> {
1157 if array.shape.len() != 2 {
1158 return Err(unique_error_with(
1159 &UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX,
1160 UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX.message,
1161 ));
1162 }
1163 let rows = array.shape[0];
1164 let cols = array.shape[1];
1165
1166 if rows == 0 {
1167 let values = StringArray::new(Vec::new(), vec![0, cols])
1168 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1169 let ia = Tensor::new(Vec::new(), vec![0, 1])
1170 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1171 let ic = Tensor::new(Vec::new(), vec![0, 1])
1172 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1173 return Ok(UniqueEvaluation::new(Value::StringArray(values), ia, ic));
1174 }
1175
1176 let mut entries = Vec::<StringRowEntry>::new();
1177 let mut map: HashMap<RowStringKey, usize> = HashMap::new();
1178 let mut row_entry_index = Vec::with_capacity(rows);
1179
1180 for r in 0..rows {
1181 let mut row_values = Vec::with_capacity(cols);
1182 for c in 0..cols {
1183 let idx = r + c * rows;
1184 row_values.push(array.data[idx].clone());
1185 }
1186 let key = RowStringKey(row_values.clone());
1187 match map.get(&key) {
1188 Some(&entry_idx) => {
1189 entries[entry_idx].last = r;
1190 row_entry_index.push(entry_idx);
1191 }
1192 None => {
1193 let entry_idx = entries.len();
1194 entries.push(StringRowEntry {
1195 row_data: row_values.clone(),
1196 first: r,
1197 last: r,
1198 });
1199 map.insert(key, entry_idx);
1200 row_entry_index.push(entry_idx);
1201 }
1202 }
1203 }
1204
1205 let mut order: Vec<usize> = (0..entries.len()).collect();
1206 if opts.order == UniqueOrder::Sorted {
1207 order.sort_by(|&a, &b| compare_string_rows(&entries[a].row_data, &entries[b].row_data));
1208 }
1209
1210 let mut entry_to_position = vec![0usize; entries.len()];
1211 for (pos, &entry_idx) in order.iter().enumerate() {
1212 entry_to_position[entry_idx] = pos;
1213 }
1214
1215 let unique_rows_count = order.len();
1216 let mut values = vec![String::new(); unique_rows_count * cols];
1217 for (row_pos, &entry_idx) in order.iter().enumerate() {
1218 let row = &entries[entry_idx].row_data;
1219 for (col, value) in row.iter().enumerate().take(cols) {
1220 let dest = row_pos + col * unique_rows_count;
1221 values[dest] = value.clone();
1222 }
1223 }
1224
1225 let mut ia = Vec::with_capacity(unique_rows_count);
1226 for &entry_idx in &order {
1227 let entry = &entries[entry_idx];
1228 let occurrence = match opts.occurrence {
1229 UniqueOccurrence::First => entry.first,
1230 UniqueOccurrence::Last => entry.last,
1231 };
1232 ia.push((occurrence + 1) as f64);
1233 }
1234
1235 let mut ic = Vec::with_capacity(rows);
1236 for entry_idx in row_entry_index {
1237 let pos = entry_to_position[entry_idx];
1238 ic.push((pos + 1) as f64);
1239 }
1240
1241 let value_array = StringArray::new(values, vec![unique_rows_count, cols])
1242 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1243 let ia_tensor = Tensor::new(ia, vec![unique_rows_count, 1])
1244 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1245 let ic_tensor = Tensor::new(ic, vec![rows, 1])
1246 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1247
1248 Ok(UniqueEvaluation::new(
1249 Value::StringArray(value_array),
1250 ia_tensor,
1251 ic_tensor,
1252 ))
1253}
1254
1255#[derive(Debug)]
1256struct NumericElementEntry {
1257 value: f64,
1258 first: usize,
1259 last: usize,
1260}
1261
1262#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1263struct NumericRowKey(Vec<u64>);
1264
1265impl NumericRowKey {
1266 fn from_slice(values: &[f64]) -> Self {
1267 NumericRowKey(values.iter().map(|&v| canonicalize_f64(v)).collect())
1268 }
1269}
1270
1271#[derive(Debug, Clone)]
1272struct NumericRowEntry {
1273 row_data: Vec<f64>,
1274 first: usize,
1275 last: usize,
1276}
1277
1278#[derive(Debug)]
1279struct ComplexElementEntry {
1280 value: (f64, f64),
1281 first: usize,
1282 last: usize,
1283}
1284
1285#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1286struct ComplexKey {
1287 re: u64,
1288 im: u64,
1289}
1290
1291impl ComplexKey {
1292 fn new(value: (f64, f64)) -> Self {
1293 Self {
1294 re: canonicalize_f64(value.0),
1295 im: canonicalize_f64(value.1),
1296 }
1297 }
1298}
1299
1300#[derive(Debug, Clone)]
1301struct ComplexRowEntry {
1302 row_data: Vec<(f64, f64)>,
1303 first: usize,
1304 last: usize,
1305}
1306
1307#[derive(Debug)]
1308struct CharElementEntry {
1309 ch: char,
1310 first: usize,
1311 last: usize,
1312}
1313
1314#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1315struct RowCharKey(Vec<u32>);
1316
1317impl RowCharKey {
1318 fn from_slice(values: &[char]) -> Self {
1319 RowCharKey(values.iter().map(|&ch| ch as u32).collect())
1320 }
1321}
1322
1323#[derive(Debug, Clone)]
1324struct CharRowEntry {
1325 row_data: Vec<char>,
1326 first: usize,
1327 last: usize,
1328}
1329
1330#[derive(Debug, Clone)]
1331struct StringElementEntry {
1332 value: String,
1333 first: usize,
1334 last: usize,
1335}
1336
1337#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1338struct RowStringKey(Vec<String>);
1339
1340#[derive(Debug, Clone)]
1341struct StringRowEntry {
1342 row_data: Vec<String>,
1343 first: usize,
1344 last: usize,
1345}
1346
1347fn canonicalize_f64(value: f64) -> u64 {
1348 if value.is_nan() {
1349 0x7ff8_0000_0000_0000u64
1350 } else if value == 0.0 {
1351 0u64
1352 } else {
1353 value.to_bits()
1354 }
1355}
1356
1357fn compare_f64(a: f64, b: f64) -> Ordering {
1358 if a.is_nan() {
1359 if b.is_nan() {
1360 Ordering::Equal
1361 } else {
1362 Ordering::Greater
1363 }
1364 } else if b.is_nan() {
1365 Ordering::Less
1366 } else {
1367 a.partial_cmp(&b).unwrap_or(Ordering::Equal)
1368 }
1369}
1370
1371fn compare_numeric_rows(a: &[f64], b: &[f64]) -> Ordering {
1372 for (lhs, rhs) in a.iter().zip(b.iter()) {
1373 let ord = compare_f64(*lhs, *rhs);
1374 if ord != Ordering::Equal {
1375 return ord;
1376 }
1377 }
1378 Ordering::Equal
1379}
1380
1381fn complex_is_nan(value: (f64, f64)) -> bool {
1382 value.0.is_nan() || value.1.is_nan()
1383}
1384
1385fn compare_complex(a: (f64, f64), b: (f64, f64)) -> Ordering {
1386 match (complex_is_nan(a), complex_is_nan(b)) {
1387 (true, true) => Ordering::Equal,
1388 (true, false) => Ordering::Greater,
1389 (false, true) => Ordering::Less,
1390 (false, false) => {
1391 let mag_a = a.0.hypot(a.1);
1392 let mag_b = b.0.hypot(b.1);
1393 let mag_cmp = compare_f64(mag_a, mag_b);
1394 if mag_cmp != Ordering::Equal {
1395 return mag_cmp;
1396 }
1397 let re_cmp = compare_f64(a.0, b.0);
1398 if re_cmp != Ordering::Equal {
1399 return re_cmp;
1400 }
1401 compare_f64(a.1, b.1)
1402 }
1403 }
1404}
1405
1406fn compare_complex_rows(a: &[(f64, f64)], b: &[(f64, f64)]) -> Ordering {
1407 for (lhs, rhs) in a.iter().zip(b.iter()) {
1408 let ord = compare_complex(*lhs, *rhs);
1409 if ord != Ordering::Equal {
1410 return ord;
1411 }
1412 }
1413 Ordering::Equal
1414}
1415
1416fn compare_char_rows(a: &[char], b: &[char]) -> Ordering {
1417 for (lhs, rhs) in a.iter().zip(b.iter()) {
1418 let ord = lhs.cmp(rhs);
1419 if ord != Ordering::Equal {
1420 return ord;
1421 }
1422 }
1423 Ordering::Equal
1424}
1425
1426fn compare_string_rows(a: &[String], b: &[String]) -> Ordering {
1427 for (lhs, rhs) in a.iter().zip(b.iter()) {
1428 let ord = lhs.cmp(rhs);
1429 if ord != Ordering::Equal {
1430 return ord;
1431 }
1432 }
1433 Ordering::Equal
1434}
1435
1436#[derive(Debug)]
1437pub struct UniqueEvaluation {
1438 values: Value,
1439 ia: Tensor,
1440 ic: Tensor,
1441}
1442
1443impl UniqueEvaluation {
1444 fn new(values: Value, ia: Tensor, ic: Tensor) -> Self {
1445 Self { values, ia, ic }
1446 }
1447
1448 pub fn into_values_value(self) -> Value {
1449 self.values
1450 }
1451
1452 pub fn into_pair(self) -> (Value, Value) {
1453 let ia = tensor::tensor_into_value(self.ia);
1454 (self.values, ia)
1455 }
1456
1457 pub fn into_triple(self) -> (Value, Value, Value) {
1458 let ia = tensor::tensor_into_value(self.ia);
1459 let ic = tensor::tensor_into_value(self.ic);
1460 (self.values, ia, ic)
1461 }
1462
1463 pub fn from_unique_result(result: UniqueResult) -> crate::BuiltinResult<Self> {
1464 let UniqueResult { values, ia, ic } = result;
1465 let values_tensor = Tensor::new(values.data, values.shape)
1466 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1467 let ia_tensor = Tensor::new(ia.data, ia.shape)
1468 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1469 let ic_tensor = Tensor::new(ic.data, ic.shape)
1470 .map_err(|e| unique_internal_error(format!("unique: {e}")))?;
1471 Ok(UniqueEvaluation::new(
1472 tensor::tensor_into_value(values_tensor),
1473 ia_tensor,
1474 ic_tensor,
1475 ))
1476 }
1477
1478 pub fn into_numeric_unique_result(self) -> crate::BuiltinResult<UniqueResult> {
1479 let UniqueEvaluation { values, ia, ic } = self;
1480 let values_tensor = tensor::value_into_tensor_for("unique", values)
1481 .map_err(|e| unique_internal_error(e))?;
1482 Ok(UniqueResult {
1483 values: HostTensorOwned {
1484 data: values_tensor.data,
1485 shape: values_tensor.shape,
1486 storage: GpuTensorStorage::Real,
1487 },
1488 ia: HostTensorOwned {
1489 data: ia.data,
1490 shape: ia.shape,
1491 storage: GpuTensorStorage::Real,
1492 },
1493 ic: HostTensorOwned {
1494 data: ic.data,
1495 shape: ic.shape,
1496 storage: GpuTensorStorage::Real,
1497 },
1498 })
1499 }
1500
1501 pub fn ia_value(&self) -> Value {
1502 tensor::tensor_into_value(self.ia.clone())
1503 }
1504
1505 pub fn ic_value(&self) -> Value {
1506 tensor::tensor_into_value(self.ic.clone())
1507 }
1508}
1509
1510#[cfg(test)]
1511pub(crate) mod tests {
1512 use super::*;
1513 use crate::builtins::common::test_support;
1514 use runmat_builtins::{
1515 CharArray, IntValue, LogicalArray, ResolveContext, StringArray, Tensor, Type, Value,
1516 };
1517
1518 fn evaluate_sync(value: Value, rest: &[Value]) -> crate::BuiltinResult<UniqueEvaluation> {
1519 futures::executor::block_on(evaluate(value, rest))
1520 }
1521
1522 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1523 #[test]
1524 fn unique_sorted_default() {
1525 let tensor = Tensor::new(vec![3.0, 1.0, 3.0, 2.0], vec![4, 1]).unwrap();
1526 let eval = evaluate_sync(Value::Tensor(tensor), &[]).expect("unique");
1527 let (values, ia, ic) = eval.into_triple();
1528 match values {
1529 Value::Tensor(t) => {
1530 assert_eq!(t.data, vec![1.0, 2.0, 3.0]);
1531 assert_eq!(t.shape, vec![3, 1]);
1532 }
1533 Value::Num(_) => panic!("expected tensor result"),
1534 other => panic!("unexpected result {other:?}"),
1535 }
1536 match ia {
1537 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 4.0, 1.0]),
1538 other => panic!("unexpected IA {other:?}"),
1539 }
1540 match ic {
1541 Value::Tensor(t) => assert_eq!(t.data, vec![3.0, 1.0, 3.0, 2.0]),
1542 other => panic!("unexpected IC {other:?}"),
1543 }
1544 }
1545
1546 #[test]
1547 fn unique_type_resolver_numeric() {
1548 assert_eq!(
1549 set_values_output_type(&[Type::tensor()], &ResolveContext::new(Vec::new())),
1550 Type::tensor()
1551 );
1552 }
1553
1554 #[test]
1555 fn unique_type_resolver_string_array() {
1556 assert_eq!(
1557 set_values_output_type(
1558 &[Type::cell_of(Type::String)],
1559 &ResolveContext::new(Vec::new()),
1560 ),
1561 Type::cell_of(Type::String)
1562 );
1563 }
1564
1565 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1566 #[test]
1567 fn unique_sorted_handles_nan() {
1568 let tensor = Tensor::new(vec![f64::NAN, 2.0, f64::NAN, 1.0], vec![4, 1]).unwrap();
1569 let eval = evaluate_sync(Value::Tensor(tensor), &[]).expect("unique");
1570 let (values, ..) = eval.into_triple();
1571 match values {
1572 Value::Tensor(t) => {
1573 assert_eq!(t.data.len(), 3);
1574 assert_eq!(t.data[0], 1.0);
1575 assert_eq!(t.data[1], 2.0);
1576 assert!(t.data[2].is_nan());
1577 }
1578 other => panic!("unexpected values {other:?}"),
1579 }
1580 }
1581
1582 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1583 #[test]
1584 fn unique_stable_with_nan() {
1585 let tensor = Tensor::new(vec![f64::NAN, 2.0, f64::NAN, 1.0], vec![4, 1]).unwrap();
1586 let eval = evaluate_sync(Value::Tensor(tensor), &[Value::from("stable")]).expect("unique");
1587 let (values, ..) = eval.into_triple();
1588 match values {
1589 Value::Tensor(t) => {
1590 assert!(t.data[0].is_nan());
1591 assert_eq!(t.data[1], 2.0);
1592 assert_eq!(t.data[2], 1.0);
1593 }
1594 other => panic!("unexpected values {other:?}"),
1595 }
1596 }
1597
1598 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1599 #[test]
1600 fn unique_stable_preserves_order() {
1601 let tensor = Tensor::new(vec![4.0, 2.0, 4.0, 1.0, 2.0], vec![5, 1]).unwrap();
1602 let eval = evaluate_sync(Value::Tensor(tensor), &[Value::from("stable")]).expect("unique");
1603 let (values, ia) = eval.into_pair();
1604 match values {
1605 Value::Tensor(t) => assert_eq!(t.data, vec![4.0, 2.0, 1.0]),
1606 other => panic!("unexpected values {other:?}"),
1607 }
1608 match ia {
1609 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 2.0, 4.0]),
1610 other => panic!("unexpected IA {other:?}"),
1611 }
1612 }
1613
1614 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1615 #[test]
1616 fn unique_last_occurrence() {
1617 let tensor = Tensor::new(vec![9.0, 8.0, 9.0, 7.0, 8.0], vec![5, 1]).unwrap();
1618 let eval = evaluate_sync(Value::Tensor(tensor), &[Value::from("last")]).expect("unique");
1619 let (values, ia, ic) = eval.into_triple();
1620 match values {
1621 Value::Tensor(t) => assert_eq!(t.data, vec![7.0, 8.0, 9.0]),
1622 other => panic!("unexpected values {other:?}"),
1623 }
1624 match ia {
1625 Value::Tensor(t) => assert_eq!(t.data, vec![4.0, 5.0, 3.0]),
1626 other => panic!("unexpected IA {other:?}"),
1627 }
1628 match ic {
1629 Value::Tensor(t) => assert_eq!(t.data, vec![3.0, 2.0, 3.0, 1.0, 2.0]),
1630 other => panic!("unexpected IC {other:?}"),
1631 }
1632 }
1633
1634 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1635 #[test]
1636 fn unique_rows_sorted_default() {
1637 let tensor = Tensor::new(vec![1.0, 1.0, 2.0, 1.0, 3.0, 3.0, 4.0, 2.0], vec![4, 2]).unwrap();
1638 let eval = evaluate_sync(Value::Tensor(tensor), &[Value::from("rows")]).expect("unique");
1639 let (values, ia, ic) = eval.into_triple();
1640 match values {
1641 Value::Tensor(t) => {
1642 assert_eq!(t.shape, vec![3, 2]);
1643 assert_eq!(t.data, vec![1.0, 1.0, 2.0, 2.0, 3.0, 4.0]);
1644 }
1645 other => panic!("unexpected values {other:?}"),
1646 }
1647 match ia {
1648 Value::Tensor(t) => assert_eq!(t.data, vec![4.0, 1.0, 3.0]),
1649 other => panic!("unexpected IA {other:?}"),
1650 }
1651 match ic {
1652 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 2.0, 3.0, 1.0]),
1653 other => panic!("unexpected IC {other:?}"),
1654 }
1655 }
1656
1657 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1658 #[test]
1659 fn unique_rows_stable_last() {
1660 let tensor = Tensor::new(vec![1.0, 1.0, 2.0, 1.0, 1.0, 2.0], vec![3, 2]).unwrap();
1661 let eval = evaluate_sync(
1662 Value::Tensor(tensor),
1663 &[
1664 Value::from("rows"),
1665 Value::from("stable"),
1666 Value::from("last"),
1667 ],
1668 )
1669 .expect("unique");
1670 let (values, ia, ic) = eval.into_triple();
1671 match values {
1672 Value::Tensor(t) => {
1673 assert_eq!(t.shape, vec![2, 2]);
1674 assert_eq!(t.data, vec![1.0, 2.0, 1.0, 2.0]);
1675 }
1676 other => panic!("unexpected values {other:?}"),
1677 }
1678 match ia {
1679 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 3.0]),
1680 other => panic!("unexpected IA {other:?}"),
1681 }
1682 match ic {
1683 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 1.0, 2.0]),
1684 other => panic!("unexpected IC {other:?}"),
1685 }
1686 }
1687
1688 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1689 #[test]
1690 fn unique_char_elements_sorted() {
1691 let chars = CharArray::new(vec!['m', 'z', 'm', 'a'], 2, 2).unwrap();
1692 let eval = evaluate_sync(Value::CharArray(chars), &[]).expect("unique");
1693 let (values, ia, ic) = eval.into_triple();
1694 match values {
1695 Value::CharArray(arr) => {
1696 assert_eq!(arr.rows, 3);
1697 assert_eq!(arr.cols, 1);
1698 assert_eq!(arr.data, vec!['a', 'm', 'z']);
1699 }
1700 other => panic!("unexpected values {other:?}"),
1701 }
1702 match ia {
1703 Value::Tensor(t) => assert_eq!(t.data, vec![4.0, 1.0, 3.0]),
1704 other => panic!("unexpected IA {other:?}"),
1705 }
1706 match ic {
1707 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 2.0, 3.0, 1.0]),
1708 other => panic!("unexpected IC {other:?}"),
1709 }
1710 }
1711
1712 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1713 #[test]
1714 fn unique_char_rows_last() {
1715 let chars = CharArray::new(vec!['a', 'b', 'a', 'b', 'a', 'c'], 3, 2).unwrap();
1716 let eval = evaluate_sync(
1717 Value::CharArray(chars),
1718 &[Value::from("rows"), Value::from("last")],
1719 )
1720 .expect("unique");
1721 let (values, ia, ic) = eval.into_triple();
1722 match values {
1723 Value::CharArray(arr) => {
1724 assert_eq!(arr.rows, 2);
1725 assert_eq!(arr.cols, 2);
1726 assert_eq!(arr.data, vec!['a', 'b', 'a', 'c']);
1727 }
1728 other => panic!("unexpected values {other:?}"),
1729 }
1730 match ia {
1731 Value::Tensor(t) => assert_eq!(t.data, vec![2.0, 3.0]),
1732 other => panic!("unexpected IA {other:?}"),
1733 }
1734 match ic {
1735 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 1.0, 2.0]),
1736 other => panic!("unexpected IC {other:?}"),
1737 }
1738 }
1739
1740 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1741 #[test]
1742 fn unique_string_elements_stable() {
1743 let array = StringArray::new(
1744 vec!["beta".into(), "alpha".into(), "beta".into()],
1745 vec![3, 1],
1746 )
1747 .unwrap();
1748 let eval =
1749 evaluate_sync(Value::StringArray(array), &[Value::from("stable")]).expect("unique");
1750 let (values, ia, ic) = eval.into_triple();
1751 match values {
1752 Value::StringArray(sa) => {
1753 assert_eq!(sa.data, vec!["beta", "alpha"]);
1754 assert_eq!(sa.shape, vec![2, 1]);
1755 }
1756 other => panic!("unexpected values {other:?}"),
1757 }
1758 match ia {
1759 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 2.0]),
1760 other => panic!("unexpected IA {other:?}"),
1761 }
1762 match ic {
1763 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 2.0, 1.0]),
1764 other => panic!("unexpected IC {other:?}"),
1765 }
1766 }
1767
1768 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1769 #[test]
1770 fn unique_string_rows() {
1771 let array = StringArray::new(
1772 vec![
1773 "alpha".into(),
1774 "alpha".into(),
1775 "gamma".into(),
1776 "beta".into(),
1777 "beta".into(),
1778 "beta".into(),
1779 ],
1780 vec![3, 2],
1781 )
1782 .unwrap();
1783 let eval = evaluate_sync(
1784 Value::StringArray(array),
1785 &[Value::from("rows"), Value::from("stable")],
1786 )
1787 .expect("unique");
1788 let (values, ia, ic) = eval.into_triple();
1789 match values {
1790 Value::StringArray(sa) => {
1791 assert_eq!(sa.shape, vec![2, 2]);
1792 assert_eq!(sa.data, vec!["alpha", "gamma", "beta", "beta"]);
1793 }
1794 other => panic!("unexpected values {other:?}"),
1795 }
1796 match ia {
1797 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 3.0]),
1798 other => panic!("unexpected IA {other:?}"),
1799 }
1800 match ic {
1801 Value::Tensor(t) => assert_eq!(t.data, vec![1.0, 1.0, 2.0]),
1802 other => panic!("unexpected IC {other:?}"),
1803 }
1804 }
1805
1806 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1807 #[test]
1808 fn unique_complex_sorted() {
1809 let tensor = ComplexTensor::new(
1810 vec![(1.0, 1.0), (0.0, 2.0), (1.0, -1.0), (0.0, 2.0)],
1811 vec![4, 1],
1812 )
1813 .unwrap();
1814 let eval = evaluate_sync(Value::ComplexTensor(tensor), &[]).expect("unique");
1815 let (values, ..) = eval.into_triple();
1816 match values {
1817 Value::ComplexTensor(t) => {
1818 assert_eq!(t.data.len(), 3);
1819 assert_eq!(t.data[0], (1.0, -1.0));
1820 assert_eq!(t.data[1], (1.0, 1.0));
1821 assert_eq!(t.data[2], (0.0, 2.0));
1822 }
1823 other => panic!("unexpected values {other:?}"),
1824 }
1825 }
1826
1827 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1828 #[test]
1829 fn unique_handles_logical_arrays() {
1830 let logical = LogicalArray::new(vec![1, 0, 1, 1], vec![4, 1]).unwrap();
1831 let eval = evaluate_sync(Value::LogicalArray(logical), &[]).expect("unique");
1832 let values = eval.into_values_value();
1833 match values {
1834 Value::Tensor(t) => assert_eq!(t.data, vec![0.0, 1.0]),
1835 other => panic!("unexpected values {other:?}"),
1836 }
1837 }
1838
1839 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1840 #[test]
1841 fn unique_gpu_roundtrip() {
1842 test_support::with_test_provider(|provider| {
1843 let tensor = Tensor::new(vec![5.0, 3.0, 5.0, 1.0], vec![4, 1]).unwrap();
1844 let view = runmat_accelerate_api::HostTensorView {
1845 data: &tensor.data,
1846 shape: &tensor.shape,
1847 };
1848 let handle = provider.upload(&view).expect("upload");
1849 let eval =
1850 evaluate_sync(Value::GpuTensor(handle), &[Value::from("stable")]).expect("unique");
1851 let values = eval.into_values_value();
1852 match values {
1853 Value::Tensor(t) => assert_eq!(t.data, vec![5.0, 3.0, 1.0]),
1854 other => panic!("unexpected values {other:?}"),
1855 }
1856 });
1857 }
1858
1859 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1860 #[test]
1861 #[cfg(feature = "wgpu")]
1862 fn unique_wgpu_matches_cpu() {
1863 let _ = runmat_accelerate::backend::wgpu::provider::register_wgpu_provider(
1864 runmat_accelerate::backend::wgpu::provider::WgpuProviderOptions::default(),
1865 );
1866 let tensor = Tensor::new(vec![5.0, 3.0, 5.0, 1.0, 2.0], vec![5, 1]).unwrap();
1867 let host_eval = evaluate_sync(Value::Tensor(tensor.clone()), &[]).expect("host unique");
1868 let (host_values, host_ia, host_ic) = host_eval.into_triple();
1869
1870 let provider = runmat_accelerate_api::provider().expect("provider registered");
1871 let view = runmat_accelerate_api::HostTensorView {
1872 data: &tensor.data,
1873 shape: &tensor.shape,
1874 };
1875 let handle = provider.upload(&view).expect("upload");
1876 let gpu_eval = evaluate_sync(Value::GpuTensor(handle.clone()), &[]).expect("gpu unique");
1877 let (gpu_values, gpu_ia, gpu_ic) = gpu_eval.into_triple();
1878 let _ = provider.free(&handle);
1879
1880 let host_values = test_support::gather(host_values).expect("gather host values");
1881 let host_ia = test_support::gather(host_ia).expect("gather host ia");
1882 let host_ic = test_support::gather(host_ic).expect("gather host ic");
1883 let gpu_values = test_support::gather(gpu_values).expect("gather gpu values");
1884 let gpu_ia = test_support::gather(gpu_ia).expect("gather gpu ia");
1885 let gpu_ic = test_support::gather(gpu_ic).expect("gather gpu ic");
1886
1887 assert_eq!(gpu_values.shape, host_values.shape);
1888 assert_eq!(gpu_values.data, host_values.data);
1889 assert_eq!(gpu_ia.data, host_ia.data);
1890 assert_eq!(gpu_ic.data, host_ic.data);
1891 }
1892
1893 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1894 #[test]
1895 fn unique_rejects_legacy_option() {
1896 let tensor = Tensor::new(vec![1.0, 1.0], vec![2, 1]).unwrap();
1897 let err = evaluate_sync(Value::Tensor(tensor), &[Value::from("legacy")]).unwrap_err();
1898 assert_eq!(
1899 err.identifier(),
1900 UNIQUE_ERROR_LEGACY_OPTION_UNSUPPORTED.identifier
1901 );
1902 }
1903
1904 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1905 #[test]
1906 fn unique_conflicting_order_flags() {
1907 let tensor = Tensor::new(vec![1.0, 2.0], vec![2, 1]).unwrap();
1908 let err = evaluate_sync(
1909 Value::Tensor(tensor),
1910 &[Value::from("stable"), Value::from("sorted")],
1911 )
1912 .unwrap_err();
1913 assert_eq!(
1914 err.identifier(),
1915 UNIQUE_ERROR_CONFLICTING_ORDER_OPTIONS.identifier
1916 );
1917 }
1918
1919 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1920 #[test]
1921 fn unique_conflicting_occurrence_flags() {
1922 let tensor = Tensor::new(vec![1.0, 2.0], vec![2, 1]).unwrap();
1923 let err = evaluate_sync(
1924 Value::Tensor(tensor),
1925 &[Value::from("first"), Value::from("last")],
1926 )
1927 .unwrap_err();
1928 assert_eq!(
1929 err.identifier(),
1930 UNIQUE_ERROR_CONFLICTING_OCCURRENCE_OPTIONS.identifier
1931 );
1932 }
1933
1934 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1935 #[test]
1936 fn unique_rejects_unknown_option() {
1937 let tensor = Tensor::new(vec![1.0, 2.0], vec![2, 1]).unwrap();
1938 let err = evaluate_sync(Value::Tensor(tensor), &[Value::from("bogus")]).unwrap_err();
1939 assert_eq!(err.identifier(), UNIQUE_ERROR_UNKNOWN_OPTION.identifier);
1940 }
1941
1942 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1943 #[test]
1944 fn unique_rows_requires_two_dimensional_input() {
1945 let tensor = Tensor::new(vec![1.0, 2.0], vec![2, 1, 1]).unwrap();
1946 let err = evaluate_sync(Value::Tensor(tensor), &[Value::from("rows")]).unwrap_err();
1947 assert_eq!(
1948 err.identifier(),
1949 UNIQUE_ERROR_ROWS_REQUIRES_2D_MATRIX.identifier
1950 );
1951 }
1952
1953 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1954 #[test]
1955 fn unique_handles_empty_rows() {
1956 let tensor = Tensor::new(Vec::new(), vec![0, 3]).unwrap();
1957 let eval = evaluate_sync(Value::Tensor(tensor), &[Value::from("rows")]).expect("unique");
1958 let (values, ia, ic) = eval.into_triple();
1959 match values {
1960 Value::Tensor(t) => {
1961 assert_eq!(t.shape, vec![0, 3]);
1962 assert!(t.data.is_empty());
1963 }
1964 other => panic!("unexpected values {other:?}"),
1965 }
1966 match ia {
1967 Value::Tensor(t) => assert!(t.data.is_empty()),
1968 other => panic!("unexpected IA {other:?}"),
1969 }
1970 match ic {
1971 Value::Tensor(t) => assert!(t.data.is_empty()),
1972 other => panic!("unexpected IC {other:?}"),
1973 }
1974 }
1975
1976 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1977 #[test]
1978 fn unique_accepts_integer_scalars() {
1979 let eval = evaluate_sync(Value::Int(IntValue::I32(42)), &[]).expect("unique");
1980 let values = eval.into_values_value();
1981 match values {
1982 Value::Num(n) => assert_eq!(n, 42.0),
1983 other => panic!("unexpected values {other:?}"),
1984 }
1985 }
1986}