1include!(concat!(env!("OUT_DIR"), "/simd_lanes.rs"));
19
20use std::hash::Hash;
21#[cfg(feature = "simd")]
22use std::simd::num::SimdUint;
23use std::simd::{LaneCount, SupportedLaneCount};
24
25#[cfg(feature = "fast_hash")]
26use ahash::AHashMap;
27use minarrow::{
28 BooleanAVT, BooleanArray, CategoricalAVT, CategoricalArray, FloatAVT, FloatArray, Integer,
29 IntegerAVT, IntegerArray, StringAVT, StringArray, Vec64,
30};
31#[cfg(feature = "datetime")]
32use minarrow::{DatetimeAVT, DatetimeArray};
33use num_traits::{Float, Signed};
34#[cfg(not(feature = "fast_hash"))]
35use std::collections::HashMap;
36
37use crate::errors::KernelError;
38use crate::kernels::logical::not_bool;
39
40#[inline(always)]
43fn prealloc_vec<T: Copy>(len: usize) -> Vec64<T> {
44 let mut v = Vec64::<T>::with_capacity(len);
45 unsafe { v.set_len(len) };
47 v
48}
49
50#[cfg(feature = "simd")]
53mod simd_impl {
54 use core::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
55
56 use num_traits::Zero;
57
58 #[inline(always)]
60 pub fn negate_dense<T, const LANES: usize>(a: &[T], out: &mut [T])
61 where
62 T: SimdElement + core::ops::Neg<Output = T> + Copy + Zero,
63 LaneCount<LANES>: SupportedLaneCount,
64 Simd<T, LANES>: core::ops::Sub<Output = Simd<T, LANES>>,
65 {
66 let mut i = 0;
67 while i + LANES <= a.len() {
68 let v = Simd::<T, LANES>::from_slice(&a[i..i + LANES]);
69 let res = Simd::<T, LANES>::splat(T::zero()) - v;
70 res.copy_to_slice(&mut out[i..i + LANES]);
71 i += LANES;
72 }
73 for j in i..a.len() {
75 out[j] = -a[j];
76 }
77 }
78}
79
80macro_rules! impl_unary_neg_int {
84 ($fn_name:ident, $ty:ty, $lanes:expr) => {
85 #[inline(always)]
98 pub fn $fn_name(window: IntegerAVT<$ty>) -> IntegerArray<$ty> {
99 let (arr, offset, len) = window;
100 let src = &arr.data[offset..offset + len];
101 let mut data = prealloc_vec::<$ty>(len);
102
103 #[cfg(feature = "simd")]
104 simd_impl::negate_dense::<$ty, $lanes>(src, &mut data);
105
106 #[cfg(not(feature = "simd"))]
107 for i in 0..len {
108 data[i] = -src[i];
109 }
110
111 IntegerArray {
112 data: data.into(),
113 null_mask: arr.null_mask.as_ref().map(|m| m.slice_clone(offset, len)),
114 }
115 }
116 };
117}
118
119#[cfg(feature = "datetime")]
120macro_rules! impl_unary_neg_datetime {
122 ($fn_name:ident, $ty:ty, $lanes:expr) => {
123 #[inline(always)]
136 pub fn $fn_name(window: DatetimeAVT<$ty>) -> DatetimeArray<$ty> {
137 let (arr, offset, len) = window;
138 let src = &arr.data[offset..offset + len];
139 let mut data = prealloc_vec::<$ty>(len);
140
141 #[cfg(feature = "simd")]
142 simd_impl::negate_dense::<$ty, $lanes>(src, &mut data);
143
144 #[cfg(not(feature = "simd"))]
145 for i in 0..len {
146 data[i] = -src[i];
147 }
148
149 DatetimeArray {
150 data: data.into(),
151 null_mask: arr.null_mask.as_ref().map(|m| m.slice_clone(offset, len)),
152 time_unit: arr.time_unit.clone(),
153 }
154 }
155 };
156}
157
158#[cfg(feature = "datetime")]
159#[inline(always)]
176pub fn unary_negate_int_datetime<T>(window: DatetimeAVT<T>) -> DatetimeArray<T>
177where
178 T: Signed + Copy + 'static,
179{
180 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<i32>() {
181 return unsafe { std::mem::transmute(unary_neg_datetime_i32(std::mem::transmute(window))) };
182 }
183 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<i64>() {
184 return unsafe { std::mem::transmute(unary_neg_datetime_i64(std::mem::transmute(window))) };
185 }
186 unreachable!("unsupported datetime type for negation")
187}
188
189#[cfg(feature = "datetime")]
190impl_unary_neg_datetime!(unary_neg_datetime_i32, i32, W32);
191#[cfg(feature = "datetime")]
192impl_unary_neg_datetime!(unary_neg_datetime_i64, i64, W64);
193#[cfg(feature = "extended_numeric_types")]
194impl_unary_neg_int!(unary_neg_i8, i8, W8);
195#[cfg(feature = "extended_numeric_types")]
196impl_unary_neg_int!(unary_neg_i16, i16, W16);
197impl_unary_neg_int!(unary_neg_i32, i32, W32);
198impl_unary_neg_int!(unary_neg_i64, i64, W64);
199
200#[inline(always)]
219pub fn unary_negate_int<T>(window: IntegerAVT<T>) -> IntegerArray<T>
220where
221 T: Signed + Copy + 'static,
222{
223 macro_rules! dispatch {
224 ($t:ty, $f:ident) => {
225 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<$t>() {
226 return unsafe { std::mem::transmute($f(std::mem::transmute(window))) };
227 }
228 };
229 }
230 #[cfg(feature = "extended_numeric_types")]
231 dispatch!(i8, unary_neg_i8);
232 #[cfg(feature = "extended_numeric_types")]
233 dispatch!(i16, unary_neg_i16);
234 dispatch!(i32, unary_neg_i32);
235 dispatch!(i64, unary_neg_i64);
236
237 unreachable!("unsupported integer type")
238}
239
240pub fn unary_negate_u32_to_i32(window: IntegerAVT<u32>) -> IntegerArray<i32> {
253 let (arr, offset, len) = window;
254 let src = &arr.data[offset..offset + len];
255 let mut data = prealloc_vec::<i32>(len);
256
257 for (dst, &v) in data.iter_mut().zip(src) {
276 *dst = -(v as i32);
277 }
278
279 IntegerArray {
280 data: data.into(),
281 null_mask: arr.null_mask.as_ref().map(|m| m.slice_clone(offset, len)),
282 }
283}
284
285pub fn unary_negate_u64_to_i64(window: IntegerAVT<u64>) -> IntegerArray<i64> {
298 let (arr, offset, len) = window;
299 let src = &arr.data[offset..offset + len];
300 let mut data = prealloc_vec::<i64>(len);
301
302 #[cfg(feature = "simd")]
303 {
304 use core::simd::Simd;
305 const LANES: usize = W64;
306 let mut i = 0;
307 while i + LANES <= len {
308 let v = Simd::<u64, LANES>::from_slice(&src[i..i + LANES]).cast::<i64>();
309 (Simd::<i64, LANES>::splat(0) - v).copy_to_slice(&mut data[i..i + LANES]);
310 i += LANES;
311 }
312 for j in i..len {
313 data[j] = -(src[j] as i64);
314 }
315 }
316 #[cfg(not(feature = "simd"))]
317 for (dst, &v) in data.iter_mut().zip(src) {
318 *dst = -(v as i64);
319 }
320
321 IntegerArray {
322 data: data.into(),
323 null_mask: arr.null_mask.as_ref().map(|m| m.slice_clone(offset, len)),
324 }
325}
326
327macro_rules! impl_unary_neg_float {
331 ($fname:ident, $ty:ty, $lanes:expr) => {
332 #[inline(always)]
345 pub fn $fname(window: FloatAVT<$ty>) -> FloatArray<$ty> {
346 let (arr, offset, len) = window;
347 let src = &arr.data[offset..offset + len];
348 let mut data = prealloc_vec::<$ty>(len);
349
350 #[cfg(feature = "simd")]
351 simd_impl::negate_dense::<$ty, $lanes>(src, &mut data);
352
353 #[cfg(not(feature = "simd"))]
354 for i in 0..len {
355 data[i] = -src[i];
356 }
357
358 FloatArray {
359 data: data.into(),
360 null_mask: arr.null_mask.as_ref().map(|m| m.slice_clone(offset, len)),
361 }
362 }
363 };
364}
365
366impl_unary_neg_float!(unary_neg_f32, f32, W32);
367impl_unary_neg_float!(unary_neg_f64, f64, W64);
368
369#[inline(always)]
386pub fn unary_negate_float<T>(window: FloatAVT<T>) -> FloatArray<T>
387where
388 T: Float + Copy + 'static,
389{
390 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
391 return unsafe { std::mem::transmute(unary_neg_f32(std::mem::transmute(window))) };
392 }
393 if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f64>() {
394 return unsafe { std::mem::transmute(unary_neg_f64(std::mem::transmute(window))) };
395 }
396 unreachable!("unsupported float type")
397}
398
399pub fn unary_not_bool<const LANES: usize>(
401 arr_window: BooleanAVT<()>,
402) -> Result<BooleanArray<()>, KernelError>
403where
404 LaneCount<LANES>: SupportedLaneCount,
405{
406 not_bool::<LANES>(arr_window)
407}
408
409pub fn unary_reverse_str<T: Integer>(arr: StringAVT<T>) -> StringArray<T> {
426 let (array, offset, len) = arr;
427 let offsets = &array.offsets;
428 let data_buf = &array.data;
429 let mask = array.null_mask.as_ref();
430
431 let total_bytes = if len == 0 {
433 0
434 } else {
435 let start = offsets[offset].to_usize();
436 let end = offsets[offset + len].to_usize();
437 end - start
438 };
439
440 let mut out_offsets = Vec64::<T>::with_capacity(len + 1);
442 let mut out_data = Vec64::<u8>::with_capacity(total_bytes);
443 unsafe {
444 out_offsets.set_len(len + 1);
445 }
446 out_offsets[0] = T::zero();
447
448 for i in 0..len {
449 if mask.map_or(true, |m| m.get(offset + i)) {
451 let start = offsets[offset + i].to_usize();
452 let end = offsets[offset + i + 1].to_usize();
453 let s = unsafe { std::str::from_utf8_unchecked(&data_buf[start..end]) };
454 for ch in s.chars().rev() {
455 let mut buf = [0u8; 4];
456 let encoded = ch.encode_utf8(&mut buf);
457 out_data.extend_from_slice(encoded.as_bytes());
458 }
459 }
460 out_offsets[i + 1] = T::from_usize(out_data.len());
462 }
463
464 let out_null_mask = mask.map(|m| m.slice_clone(offset, len));
465
466 StringArray {
467 offsets: out_offsets.into(),
468 data: out_data.into(),
469 null_mask: out_null_mask,
470 }
471}
472
473pub fn unary_reverse_dict<T: Integer + Hash>(arr: CategoricalAVT<T>) -> CategoricalArray<T> {
490 let (array, offset, len) = arr;
491 let mask = array.null_mask.as_ref();
492
493 let windowed_codes = array.data[offset..offset + len].to_vec();
495
496 #[cfg(feature = "fast_hash")]
498 let mut remap: AHashMap<T, T> = AHashMap::new();
499 #[cfg(not(feature = "fast_hash"))]
500 let mut remap: HashMap<T, T> = HashMap::new();
501 let mut new_uniques = Vec64::<String>::new();
502 let mut new_codes = Vec64::<T>::with_capacity(len);
503
504 for &code in &windowed_codes {
505 if !remap.contains_key(&code) {
506 let reversed = array.unique_values[code.to_usize()]
507 .chars()
508 .rev()
509 .collect::<String>();
510 remap.insert(code, T::from_usize(new_uniques.len()));
511 new_uniques.push(reversed);
512 }
513 new_codes.push(remap[&code]);
514 }
515
516 let out_null_mask = mask.map(|m| m.slice_clone(offset, len));
517
518 CategoricalArray {
519 data: new_codes.into(),
520 unique_values: new_uniques,
521 null_mask: out_null_mask,
522 }
523}
524
525#[cfg(test)]
526mod tests {
527 use minarrow::structs::variants::categorical::CategoricalArray;
528 use minarrow::structs::variants::float::FloatArray;
529 use minarrow::structs::variants::integer::IntegerArray;
530 use minarrow::structs::variants::string::StringArray;
531 use minarrow::{Bitmask, BooleanArray, MaskedArray};
532
533 use super::*;
534
535 fn bm(bits: &[bool]) -> Bitmask {
538 let mut m = Bitmask::new_set_all(bits.len(), false);
539 for (i, &b) in bits.iter().enumerate() {
540 m.set(i, b);
541 }
542 m
543 }
544
545 fn expect_int<T: PartialEq + std::fmt::Debug>(
546 arr: &IntegerArray<T>,
547 values: &[T],
548 valid: &[bool],
549 ) {
550 assert_eq!(arr.data.as_slice(), values);
551 let mask = arr.null_mask.as_ref().expect("mask missing");
552 for (i, &v) in valid.iter().enumerate() {
553 assert_eq!(mask.get(i), v, "mask bit {}", i);
554 }
555 }
556
557 fn expect_float<T: num_traits::Float + std::fmt::Debug>(
558 arr: &FloatArray<T>,
559 values: &[T],
560 valid: &[bool],
561 eps: T,
562 ) {
563 assert_eq!(arr.data.len(), values.len());
564 for (a, b) in arr.data.iter().zip(values.iter()) {
565 assert!((*a - *b).abs() <= eps, "value mismatch {:?} vs {:?}", a, b);
566 }
567 let mask = arr.null_mask.as_ref().expect("mask missing");
568 for (i, &v) in valid.iter().enumerate() {
569 assert_eq!(mask.get(i), v, "mask bit {}", i);
570 }
571 }
572
573 #[cfg(feature = "extended_numeric_types")]
576 #[test]
577 fn neg_i8_dense() {
578 let arr = IntegerArray::<i8>::from_slice(&[1, -2, 127]);
579 let out = unary_neg_i8((&arr, 0, arr.len()));
580 assert_eq!(out.data.as_slice(), &[-1, 2, -127]);
581 assert!(out.null_mask.is_none());
582 }
583
584 #[cfg(feature = "extended_numeric_types")]
585 #[test]
586 fn neg_i16_masked() {
587 let mut arr = IntegerArray::<i16>::from_slice(&[-4, 12, 8, 0]);
588 arr.null_mask = Some(bm(&[true, false, true, true]));
589 let out = unary_neg_i16((&arr, 0, arr.len()));
590 expect_int(&out, &[4, -12, -8, 0], &[true, false, true, true]);
591 }
592
593 #[test]
594 fn neg_i32_empty() {
595 let arr = IntegerArray::<i32>::from_slice(&[]);
596 let out = unary_neg_i32((&arr, 0, arr.len()));
597 assert_eq!(out.data.len(), 0);
598 }
599
600 #[test]
601 fn neg_i64_all_nulls() {
602 let mut arr = IntegerArray::<i64>::from_slice(&[5, 10]);
603 arr.null_mask = Some(bm(&[false, false]));
604 let out = unary_neg_i64((&arr, 0, arr.len()));
605 expect_int(&out, &[-5, -10], &[false, false]);
606 }
607
608 #[cfg(feature = "extended_numeric_types")]
609 #[test]
610 fn neg_dispatch_i16() {
611 let mut arr = IntegerArray::<i16>::from_slice(&[-2, 4]);
612 arr.null_mask = Some(bm(&[true, true]));
613 let out = unary_negate_int((&arr, 0, arr.len()));
614 expect_int(&out, &[2, -4], &[true, true]);
615 }
616
617 #[test]
620 fn neg_u32_to_i32() {
621 let mut arr = IntegerArray::<u32>::from_slice(&[1, 2, 100]);
622 arr.null_mask = Some(bm(&[true, false, true]));
623 let out = unary_negate_u32_to_i32((&arr, 0, arr.len()));
624 expect_int(&out, &[-1, -2, -100], &[true, false, true]);
625 }
626
627 #[test]
628 fn neg_u64_to_i64() {
629 let arr = IntegerArray::<u64>::from_slice(&[3, 4, 0]);
630 let out = unary_negate_u64_to_i64((&arr, 0, arr.len()));
631 assert_eq!(out.data.as_slice(), &[-3, -4, 0]);
632 }
633
634 #[test]
637 fn neg_f32_dense() {
638 let arr = FloatArray::<f32>::from_slice(&[0.5, -1.5, 2.0]);
639 let out = unary_neg_f32((&arr, 0, arr.len()));
640 assert_eq!(out.data.as_slice(), &[-0.5, 1.5, -2.0]);
641 assert!(out.null_mask.is_none());
642 }
643
644 #[test]
645 fn neg_f64_masked() {
646 let mut arr = FloatArray::<f64>::from_slice(&[1.1, -2.2, 3.3]);
647 arr.null_mask = Some(bm(&[true, false, true]));
648 let out = unary_neg_f64((&arr, 0, arr.len()));
649 expect_float(&out, &[-1.1, 2.2, -3.3], &[true, false, true], 1e-12);
650 }
651
652 #[test]
653 fn neg_dispatch_f64() {
654 let arr = FloatArray::<f64>::from_slice(&[2.2, -4.4]);
655 let out = unary_negate_float((&arr, 0, arr.len()));
656 assert_eq!(out.data.as_slice(), &[-2.2, 4.4]);
657 }
658
659 #[test]
662 fn not_bool_basic() {
663 let arr = BooleanArray::from_slice(&[true, false, true, false]);
664 let out = unary_not_bool::<W64>((&arr, 0, arr.len())).unwrap();
665 assert_eq!(out.data.as_slice(), &[0b00001010]);
666 assert!(out.null_mask.is_none());
667 }
668
669 #[test]
670 fn not_bool_masked() {
671 let mut arr = BooleanArray::from_slice(&[false, false, true, true]);
672 arr.null_mask = Some(bm(&[true, false, true, true]));
673 let out = unary_not_bool::<W64>((&arr, 0, arr.len())).unwrap();
674 assert_eq!(out.data.as_slice(), &[0b00001100]);
675 assert_eq!(out.null_mask, arr.null_mask);
676 }
677
678 #[test]
681 fn reverse_str_basic() {
682 let arr = StringArray::<u32>::from_slice(&["ab", "xyz", ""]);
683 let out = unary_reverse_str((&arr, 0, arr.len()));
684 assert_eq!(out.get(0), Some("ba"));
685 assert_eq!(out.get(1), Some("zyx"));
686 assert_eq!(out.get(2), Some(""));
687 }
688
689 #[test]
690 fn reverse_str_basic_chunk() {
691 let arr = StringArray::<u32>::from_slice(&["xxx", "ab", "xyz", ""]);
692 let out = unary_reverse_str((&arr, 1, 3)); assert_eq!(out.get(0), Some("ba"));
694 assert_eq!(out.get(1), Some("zyx"));
695 assert_eq!(out.get(2), Some(""));
696 }
697
698 #[test]
699 fn reverse_str_with_nulls() {
700 let mut arr = StringArray::<u32>::from_slice(&["apple", "banana", "carrot"]);
701 arr.null_mask = Some(bm(&[true, false, true]));
702 let out = unary_reverse_str((&arr, 0, arr.len()));
703 assert_eq!(out.get(0), Some("elppa"));
704 assert_eq!(out.get(1), None);
705 assert_eq!(out.get(2), Some("torrac"));
706 assert_eq!(out.null_mask, arr.null_mask);
707 }
708
709 #[test]
710 fn reverse_str_with_nulls_chunk() {
711 let mut arr = StringArray::<u32>::from_slice(&["zero", "apple", "banana", "carrot"]);
712 arr.null_mask = Some(bm(&[true, true, false, true]));
713 let out = unary_reverse_str((&arr, 1, 3)); assert_eq!(out.get(0), Some("elppa"));
715 assert_eq!(out.get(1), None);
716 assert_eq!(out.get(2), Some("torrac"));
717 assert_eq!(
718 out.null_mask.as_ref().unwrap().as_slice(),
719 bm(&[true, false, true]).as_slice()
720 );
721 }
722
723 #[test]
726 fn reverse_dict_basic() {
727 let arr = CategoricalArray::<u32>::from_values(["cat", "dog", "bird", "cat"]);
728 let out = unary_reverse_dict((&arr, 0, arr.data.len()));
729 let uniq: Vec<String> = out.unique_values.iter().map(|s| s.clone()).collect();
730 assert!(uniq.contains(&"tac".to_string()));
731 assert!(uniq.contains(&"god".to_string()));
732 assert!(uniq.contains(&"drib".to_string()));
733 assert_eq!(out.data, arr.data);
734 }
735
736 #[test]
737 fn reverse_dict_basic_chunk() {
738 let arr = CategoricalArray::<u32>::from_values(["z", "cat", "dog", "bird"]);
739 let out = unary_reverse_dict((&arr, 1, 3));
740 let uniq: Vec<String> = out.unique_values.iter().map(|s| s.clone()).collect();
741 assert!(uniq.contains(&"tac".to_string()));
742 assert!(uniq.contains(&"god".to_string()));
743 assert!(uniq.contains(&"drib".to_string()));
744 assert_eq!(&out.data[..], &[0, 1, 2]);
745 }
746
747 #[test]
748 fn test_unary_reverse_str_empty_and_all_nulls() {
749 let arr0 = StringArray::<u32>::from_slice(&[]);
751 let out0 = unary_reverse_str((&arr0, 0, arr0.len()));
752 assert_eq!(out0.len(), 0);
753
754 let mut arr1 = StringArray::<u32>::from_slice(&["a", "b"]);
756 arr1.null_mask = Some(bm(&[false, false]));
757 let out1 = unary_reverse_str((&arr1, 0, arr1.len()));
758 assert_eq!(out1.get(0), None);
760 assert_eq!(out1.get(1), None);
761 assert_eq!(out1.null_mask, arr1.null_mask);
762 }
763
764 #[test]
765 fn test_unary_reverse_str_empty_and_all_nulls_chunk() {
766 let mut arr = StringArray::<u32>::from_slice(&["x", "a", "b", "y"]);
768 arr.null_mask = Some(bm(&[true, false, false, true]));
769 let out = unary_reverse_str((&arr, 1, 2)); assert_eq!(out.get(0), None);
771 assert_eq!(out.get(1), None);
772 assert_eq!(out.null_mask, Some(bm(&[false, false])));
773 }
774}