1#[cfg(feature = "fast_hash")]
30use ahash::{AHashMap, AHashSet};
31#[cfg(not(feature = "fast_hash"))]
32use std::collections::{HashMap, HashSet};
33
34use crate::{
35 Bitmask, BooleanArray, CategoricalArray, Integer, IntegerArray, MaskedArray, StringArray,
36 Vec64,
37 aliases::{CategoricalAVT, StringAVT},
38};
39#[cfg(feature = "views")]
40use crate::TextArrayV;
41#[cfg(feature = "regex")]
42use regex::Regex;
43
44use crate::enums::error::KernelError;
45use crate::utils::confirm_mask_capacity;
46use std::marker::PhantomData;
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum PadSide {
51 Left,
52 Right,
53 Both,
54}
55
56
57pub fn concat_str_str<T: Integer>(lhs: StringAVT<T>, rhs: StringAVT<T>) -> StringArray<T> {
81 let (larr, loff, llen) = lhs;
82 let (rarr, roff, rlen) = rhs;
83 let len = llen.min(rlen);
84
85 let lmask = larr.null_mask.as_ref();
86 let rmask = rarr.null_mask.as_ref();
87 let mut out_mask = Bitmask::new_set_all(len, false);
88 let _ = confirm_mask_capacity(larr.len(), lmask);
89 let _ = confirm_mask_capacity(rarr.len(), rmask);
90
91 let mut total_bytes = 0;
93 for i in 0..len {
94 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
95 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
96 if valid {
97 let l = unsafe { larr.get_str_unchecked(loff + i) };
98 let r = unsafe { rarr.get_str_unchecked(roff + i) };
99 total_bytes += l.len() + r.len();
100 }
101 }
102
103 let mut offsets = Vec64::<T>::with_capacity(len + 1);
105 unsafe {
106 offsets.set_len(len + 1);
107 }
108 let mut values = Vec64::<u8>::with_capacity(total_bytes);
109
110 offsets[0] = T::zero();
112 let mut cur = 0;
113
114 for i in 0..len {
115 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
116 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
117
118 if valid {
119 let l = unsafe { larr.get_str_unchecked(loff + i).as_bytes() };
120 let r = unsafe { rarr.get_str_unchecked(roff + i).as_bytes() };
121
122 values.extend_from_slice(l);
123 values.extend_from_slice(r);
124 cur += l.len() + r.len();
125
126 unsafe {
127 out_mask.set_unchecked(i, true);
128 }
129 } else {
130 unsafe {
131 out_mask.set_unchecked(i, false);
132 }
133 }
134
135 offsets[i + 1] = T::from_usize(cur);
136 }
137
138 StringArray {
139 offsets: offsets.into(),
140 data: values.into(),
141 null_mask: Some(out_mask),
142 }
143}
144
145pub fn concat_dict_dict<T: Integer>(
166 lhs: CategoricalAVT<T>,
167 rhs: CategoricalAVT<T>,
168) -> Result<CategoricalArray<T>, KernelError> {
169 let (larr, loff, llen) = lhs;
170 let (rarr, roff, rlen) = rhs;
171 let len = llen.min(rlen);
172
173 let lmask = larr.null_mask.as_ref();
174 let rmask = rarr.null_mask.as_ref();
175 let mut out_mask = Bitmask::new_set_all(len, false);
176 let _ = confirm_mask_capacity(larr.data.len(), lmask)?;
177 let _ = confirm_mask_capacity(rarr.data.len(), rmask)?;
178
179 let mut data = Vec64::<T>::with_capacity(len);
181 unsafe {
182 data.set_len(len);
183 }
184
185 let mut unique_values = Vec64::<String>::with_capacity(len);
186 #[cfg(feature = "fast_hash")]
187 let mut seen: AHashMap<String, T> = AHashMap::with_capacity(len);
188 #[cfg(not(feature = "fast_hash"))]
189 let mut seen: HashMap<String, T> = HashMap::with_capacity(len);
190 let mut unique_idx = 0;
191
192 for i in 0..len {
193 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
194 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
195
196 if valid {
197 let l = unsafe { larr.get_str_unchecked(loff + i) };
198 let r = unsafe { rarr.get_str_unchecked(roff + i) };
199 let cat = format!("{l}{r}");
200
201 let idx = match seen.get(&cat) {
202 Some(ix) => *ix,
203 None => {
204 let ix = T::from_usize(unique_idx);
205 unique_values.push(cat.clone());
206 seen.insert(cat, ix);
207 unique_idx += 1;
208 ix
209 }
210 };
211
212 unsafe {
213 *data.get_unchecked_mut(i) = idx;
214 out_mask.set_unchecked(i, true);
215 }
216 } else {
217 unsafe {
218 *data.get_unchecked_mut(i) = T::zero();
219 out_mask.set_unchecked(i, false);
220 }
221 }
222 }
223
224 unsafe {
225 unique_values.set_len(unique_idx);
226 }
227
228 Ok(CategoricalArray {
229 data: data.into(),
230 unique_values,
231 null_mask: Some(out_mask),
232 })
233}
234
235pub fn concat_str_dict<T: Integer, U: Integer>(
254 lhs: StringAVT<T>,
255 rhs: CategoricalAVT<U>,
256) -> Result<StringArray<T>, KernelError> {
257 let (larr, loff, llen) = lhs;
258 let (rarr, roff, rlen) = rhs;
259 let len = llen.min(rlen);
260
261 let lmask = larr.null_mask.as_ref();
262 let rmask = rarr.null_mask.as_ref();
263 let mut out_mask = Bitmask::new_set_all(len, false);
264 let _ = confirm_mask_capacity(larr.len(), lmask)?;
265 let _ = confirm_mask_capacity(rarr.data.len(), rmask)?;
266
267 let mut total_bytes = 0;
269 for i in 0..len {
270 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
271 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
272 if valid {
273 let a = unsafe { larr.get_str_unchecked(loff + i) };
274 let b = unsafe { rarr.get_str_unchecked(roff + i) };
275 total_bytes += a.len() + b.len();
276 }
277 }
278
279 let mut offsets = Vec64::<T>::with_capacity(len + 1);
281 unsafe {
282 offsets.set_len(len + 1);
283 }
284 let mut values = Vec64::<u8>::with_capacity(total_bytes);
285
286 offsets[0] = T::zero();
288 let mut cur = 0;
289
290 for i in 0..len {
291 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
292 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
293
294 if valid {
295 let a = unsafe { larr.get_str_unchecked(loff + i).as_bytes() };
296 let b = unsafe { rarr.get_str_unchecked(roff + i).as_bytes() };
297
298 values.extend_from_slice(a);
299 values.extend_from_slice(b);
300 cur += a.len() + b.len();
301
302 unsafe {
303 out_mask.set_unchecked(i, true);
304 }
305 } else {
306 unsafe {
307 out_mask.set_unchecked(i, false);
308 }
309 }
310
311 offsets[i + 1] = T::from_usize(cur);
312 }
313
314 Ok(StringArray {
315 offsets: offsets.into(),
316 data: values.into(),
317 null_mask: Some(out_mask),
318 })
319}
320
321pub fn concat_dict_str<T: Integer, U: Integer>(
340 lhs: CategoricalAVT<U>,
341 rhs: StringAVT<T>,
342) -> Result<StringArray<T>, KernelError> {
343 concat_str_dict(rhs, lhs)
344}
345
346macro_rules! binary_str_pred_loop {
347 ($len:expr, $lmask:expr, $rmask:expr, $out_mask:expr, $lhs:expr, $rhs:expr, $method:ident) => {{
348 let mut data = Bitmask::new_set_all($len, false);
349 let lhs_off = $lhs.1;
351 let rhs_off = $rhs.1;
352 let _ = confirm_mask_capacity(lhs_off + $len, $lmask)?;
353 let _ = confirm_mask_capacity(rhs_off + $len, $rmask)?;
354 for i in 0..$len {
355 let li = lhs_off + i;
356 let ri = rhs_off + i;
357 let valid = $lmask.map_or(true, |m| unsafe { m.get_unchecked(li) })
358 && $rmask.map_or(true, |m| unsafe { m.get_unchecked(ri) });
359 let result = valid && {
360 let s = unsafe { $lhs.0.get_str_unchecked(li) };
361 let pat = unsafe { $rhs.0.get_str_unchecked(ri) };
362 !pat.is_empty() && s.$method(pat)
363 };
364 unsafe {
365 data.set_unchecked(i, result);
366 $out_mask.set_unchecked(i, valid);
367 }
368 }
369 data
370 }};
371}
372
373macro_rules! str_predicate {
377 ($fn_name:ident, $method:ident) => {
378 pub fn $fn_name<T: Integer, U: Integer>(
397 lhs: StringAVT<T>,
398 rhs: StringAVT<U>,
399 ) -> BooleanArray<()> {
400 let (larr, loff, llen) = lhs;
401 let (rarr, roff, rlen) = rhs;
402 let len = llen.min(rlen);
403 let lmask = larr.null_mask.as_ref();
405 let rmask = rarr.null_mask.as_ref();
406 let mut out = Bitmask::new_set_all(len, false);
407
408 for i in 0..len {
409 unsafe {
410 let lv = lmask.map_or(true, |m| m.get_unchecked(loff + i));
412 let rv = rmask.map_or(true, |m| m.get_unchecked(roff + i));
413 if !lv || !rv {
414 continue;
416 }
417 let ls = larr.offsets[loff + i].to_usize();
419 let le = larr.offsets[loff + i + 1].to_usize();
420 let rs = rarr.offsets[roff + i].to_usize();
421 let re = rarr.offsets[roff + i + 1].to_usize();
422 let s = std::str::from_utf8_unchecked(&larr.data[ls..le]);
423 let p = std::str::from_utf8_unchecked(&rarr.data[rs..re]);
424 if !p.is_empty() && s.$method(p) {
426 out.set_unchecked(i, true);
427 }
428 }
429 }
430 BooleanArray {
432 data: out.into(),
433 null_mask: None,
434 len,
435 _phantom: PhantomData,
436 }
437 }
438 };
439}
440
441macro_rules! str_cat_predicate {
443 ($fn_name:ident, $method:ident) => {
444 pub fn $fn_name<T: Integer, U: Integer>(
463 lhs: StringAVT<T>,
464 rhs: CategoricalAVT<U>,
465 ) -> Result<BooleanArray<()>, KernelError> {
466 let (larr, loff, llen) = lhs;
467 let (rarr, roff, rlen) = rhs;
468 let len = llen.min(rlen);
469
470 let lmask = larr.null_mask.as_ref();
471 let rmask = rarr.null_mask.as_ref();
472 let mut out_mask = Bitmask::new_set_all(len, false);
473
474 let data = binary_str_pred_loop!(
475 len,
476 lmask,
477 rmask,
478 out_mask,
479 (larr, loff),
480 (rarr, roff),
481 $method
482 );
483
484 Ok(BooleanArray {
485 data: data.into(),
486 null_mask: Some(out_mask),
487 len,
488 _phantom: PhantomData,
489 })
490 }
491 };
492}
493
494macro_rules! cat_cat_predicate {
496 ($fn_name:ident, $method:ident) => {
497 pub fn $fn_name<T: Integer>(
515 lhs: CategoricalAVT<T>,
516 rhs: CategoricalAVT<T>,
517 ) -> Result<BooleanArray<()>, KernelError> {
518 let (larr, loff, llen) = lhs;
519 let (rarr, roff, rlen) = rhs;
520 let len = llen.min(rlen);
521
522 let lmask = larr.null_mask.as_ref();
523 let rmask = rarr.null_mask.as_ref();
524 let mut out_mask = Bitmask::new_set_all(len, false);
525
526 let data = binary_str_pred_loop!(
527 len,
528 lmask,
529 rmask,
530 out_mask,
531 (larr, loff),
532 (rarr, roff),
533 $method
534 );
535
536 Ok(BooleanArray {
537 data: data.into(),
538 null_mask: Some(out_mask),
539 len,
540 _phantom: PhantomData,
541 })
542 }
543 };
544}
545
546macro_rules! dict_str_predicate {
548 ($fn_name:ident, $method:ident) => {
549 pub fn $fn_name<T: Integer, U: Integer>(
568 lhs: CategoricalAVT<T>,
569 rhs: StringAVT<U>,
570 ) -> Result<BooleanArray<()>, KernelError> {
571 let (larr, loff, llen) = lhs;
572 let (rarr, roff, rlen) = rhs;
573 let len = llen.min(rlen);
574
575 let lmask = larr.null_mask.as_ref();
576 let rmask = rarr.null_mask.as_ref();
577 let mut out_mask = Bitmask::new_set_all(len, false);
578 let _ = confirm_mask_capacity(larr.data.len(), lmask)?;
579 let _ = confirm_mask_capacity(rarr.len(), rmask)?;
580
581 let mut data = Bitmask::new_set_all(len, false);
582 for i in 0..len {
583 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(loff + i) })
584 && rmask.map_or(true, |m| unsafe { m.get_unchecked(roff + i) });
585 let match_i = valid && {
586 let hay = unsafe { larr.get_str_unchecked(loff + i) };
587 let needle = unsafe { rarr.get_str_unchecked(roff + i) };
588 !needle.is_empty() && hay.$method(needle)
589 };
590 unsafe { data.set_unchecked(i, match_i) };
591 unsafe { out_mask.set_unchecked(i, valid) };
592 }
593
594 Ok(BooleanArray {
595 data: data.into(),
596 null_mask: Some(out_mask),
597 len,
598 _phantom: PhantomData,
599 })
600 }
601 };
602}
603
604str_predicate!(contains_str_str, contains);
605str_predicate!(starts_with_str_str, starts_with);
606str_predicate!(ends_with_str_str, ends_with);
607str_cat_predicate!(contains_str_dict, contains);
608cat_cat_predicate!(contains_dict_dict, contains);
609str_cat_predicate!(starts_with_str_dict, starts_with);
610cat_cat_predicate!(starts_with_dict_dict, starts_with);
611str_cat_predicate!(ends_with_str_dict, ends_with);
612cat_cat_predicate!(ends_with_dict_dict, ends_with);
613dict_str_predicate!(contains_dict_str, contains);
614dict_str_predicate!(starts_with_dict_str, starts_with);
615dict_str_predicate!(ends_with_dict_str, ends_with);
616
617#[cfg(feature = "regex")]
620macro_rules! regex_match_loop {
621 ($len:expr, $lmask:expr, $rmask:expr, $out_mask:expr, $lhs_arr:expr, $lhs_off:expr, $rhs_arr:expr, $rhs_off:expr) => {{
622 let mut data = Bitmask::new_set_all($len, false);
623 let _ = confirm_mask_capacity($len + $lhs_off, $lmask)?;
624 let _ = confirm_mask_capacity($len + $rhs_off, $rmask)?;
625 for i in 0..$len {
626 let valid = $lmask.map_or(true, |m| unsafe { m.get_unchecked($lhs_off + i) })
627 && $rmask.map_or(true, |m| unsafe { m.get_unchecked($rhs_off + i) });
628 let matched = if valid {
629 let s = unsafe { $lhs_arr.get_str_unchecked($lhs_off + i) };
630 let pat = unsafe { $rhs_arr.get_str_unchecked($rhs_off + i) };
631 if pat.is_empty() {
632 false
633 } else {
634 match Regex::new(pat) {
635 Ok(re) => re.is_match(s),
636 Err(_) => {
637 return Err(KernelError::InvalidArguments(
638 "Invalid regex string".to_string(),
639 ));
640 }
641 }
642 }
643 } else {
644 false
645 };
646 unsafe { data.set_unchecked(i, matched) };
647 unsafe { $out_mask.set_unchecked(i, valid) };
648 }
649 data
650 }};
651}
652
653#[cfg(feature = "regex")]
679pub fn regex_str_str<'a, T: Integer, U: Integer>(
680 lhs: StringAVT<'a, T>,
681 rhs: StringAVT<'a, U>,
682) -> Result<BooleanArray<()>, KernelError> {
683 let (larr, loff, llen) = lhs;
684 let (rarr, roff, rlen) = rhs;
685 let len = llen.min(rlen);
686 let lmask = larr.null_mask.as_ref();
687 let rmask = rarr.null_mask.as_ref();
688 let mut out_mask = Bitmask::new_set_all(len, false);
689
690 let data = regex_match_loop!(len, lmask, rmask, out_mask, larr, loff, rarr, roff);
691 Ok(BooleanArray {
692 data: data.into(),
693 null_mask: Some(out_mask),
694 len,
695 _phantom: PhantomData,
696 })
697}
698
699#[cfg(feature = "regex")]
721pub fn regex_dict_str<'a, U: Integer, T: Integer>(
722 lhs: CategoricalAVT<'a, U>,
723 rhs: StringAVT<'a, T>,
724) -> Result<BooleanArray<()>, KernelError> {
725 let (larr, loff, llen) = lhs;
726 let (rarr, roff, rlen) = rhs;
727 let len = llen.min(rlen);
728 let lmask = larr.null_mask.as_ref();
729 let rmask = rarr.null_mask.as_ref();
730 let mut out_mask = Bitmask::new_set_all(len, false);
731
732 let data = regex_match_loop!(len, lmask, rmask, out_mask, larr, loff, rarr, roff);
733 Ok(BooleanArray {
734 data: data.into(),
735 null_mask: Some(out_mask),
736 len,
737 _phantom: PhantomData,
738 })
739}
740
741#[cfg(feature = "regex")]
763pub fn regex_str_dict<'a, T: Integer, U: Integer>(
764 lhs: StringAVT<'a, T>,
765 rhs: CategoricalAVT<'a, U>,
766) -> Result<BooleanArray<()>, KernelError> {
767 let (larr, loff, llen) = lhs;
768 let (rarr, roff, rlen) = rhs;
769 let len = llen.min(rlen);
770 let lmask = larr.null_mask.as_ref();
771 let rmask = rarr.null_mask.as_ref();
772 let mut out_mask = Bitmask::new_set_all(len, false);
773
774 let data = regex_match_loop!(len, lmask, rmask, out_mask, larr, loff, rarr, roff);
775 Ok(BooleanArray {
776 data: data.into(),
777 null_mask: Some(out_mask),
778 len,
779 _phantom: PhantomData,
780 })
781}
782
783#[cfg(feature = "regex")]
808pub fn regex_dict_dict<'a, T: Integer>(
809 lhs: CategoricalAVT<'a, T>,
810 rhs: CategoricalAVT<'a, T>,
811) -> Result<BooleanArray<()>, KernelError> {
812 let (larr, loff, llen) = lhs;
813 let (rarr, roff, rlen) = rhs;
814 let len = llen.min(rlen);
815 let lmask = larr.null_mask.as_ref();
816 let rmask = rarr.null_mask.as_ref();
817 let mut out_mask = Bitmask::new_set_all(len, false);
818
819 let data = regex_match_loop!(len, lmask, rmask, out_mask, larr, loff, rarr, roff);
820 Ok(BooleanArray {
821 data: data.into(),
822 null_mask: Some(out_mask),
823 len,
824 _phantom: PhantomData,
825 })
826}
827
828pub fn len_str<'a, T: Integer + Copy>(
841 input: StringAVT<'a, T>,
842) -> Result<IntegerArray<T>, KernelError> {
843 let (array, offset, len) = input;
844 debug_assert!(offset + len <= array.offsets.len() - 1);
845
846 let mask_opt = array.null_mask.as_ref().map(|orig| {
847 let mut m = Bitmask::new_set_all(len, true);
848 for i in 0..len {
849 unsafe {
850 m.set_unchecked(i, orig.get_unchecked(offset + i));
851 }
852 }
853 m
854 });
855
856 let mut data = Vec64::<T>::with_capacity(len);
857 unsafe { data.set_len(len) };
858 for i in 0..len {
859 let valid = mask_opt
860 .as_ref()
861 .map_or(true, |m| unsafe { m.get_unchecked(i) });
862 if valid {
863 let start = array.offsets[offset + i].to_usize();
864 let end = array.offsets[offset + i + 1].to_usize();
865 let s = unsafe { std::str::from_utf8_unchecked(&array.data[start..end]) };
866 data[i] = T::from(s.chars().count()).unwrap();
867 } else {
868 data[i] = T::zero();
869 }
870 }
871
872 Ok(IntegerArray {
873 data: data.into(),
874 null_mask: mask_opt,
875 })
876}
877
878pub fn len_dict<'a, T: Integer>(
891 input: CategoricalAVT<'a, T>,
892) -> Result<IntegerArray<T>, KernelError> {
893 let (array, offset, len) = input;
894 debug_assert!(offset + len <= array.data.len());
895
896 let mask_opt = array.null_mask.as_ref().map(|orig| {
897 let mut m = Bitmask::new_set_all(len, true);
898 for i in 0..len {
899 unsafe {
900 m.set_unchecked(i, orig.get_unchecked(offset + i));
901 }
902 }
903 m
904 });
905
906 let mut data = Vec64::<T>::with_capacity(len);
907 unsafe { data.set_len(len) };
908 for i in 0..len {
909 let valid = mask_opt
910 .as_ref()
911 .map_or(true, |m| unsafe { m.get_unchecked(i) });
912 data[i] = if valid {
913 T::from(
914 unsafe { array.get_str_unchecked(offset + i) }
915 .chars()
916 .count(),
917 )
918 .unwrap()
919 } else {
920 T::zero()
921 };
922 }
923
924 Ok(IntegerArray {
925 data: data.into(),
926 null_mask: mask_opt,
927 })
928}
929
930#[inline]
941pub fn min_string_array<T: Integer>(window: StringAVT<T>) -> Option<String> {
942 let (arr, offset, len) = window;
943 let mut min_str: Option<&str> = None;
944 for i in offset..offset + len {
945 if arr
946 .null_mask
947 .as_ref()
948 .map_or(true, |b| unsafe { b.get_unchecked(i) })
949 {
950 let s = unsafe { arr.get_str_unchecked(i) };
951 if min_str.map_or(true, |min| s < min) {
952 min_str = Some(s);
953 }
954 }
955 }
956 min_str.map(str::to_owned)
957}
958
959#[inline]
970pub fn max_string_array<T: Integer>(window: StringAVT<T>) -> Option<String> {
971 let (arr, offset, len) = window;
972 let mut max_str: Option<&str> = None;
973 for i in offset..offset + len {
974 if arr
975 .null_mask
976 .as_ref()
977 .map_or(true, |b| unsafe { b.get_unchecked(i) })
978 {
979 let s = unsafe { arr.get_str_unchecked(i) };
980 if max_str.map_or(true, |max| s > max) {
981 max_str = Some(s);
982 }
983 }
984 }
985 max_str.map(str::to_owned)
986}
987
988#[inline]
999pub fn min_categorical_array<T: Integer>(window: CategoricalAVT<T>) -> Option<String> {
1000 let (arr, offset, len) = window;
1001 let mut min_code: Option<T> = None;
1002 for i in offset..offset + len {
1003 if arr
1004 .null_mask
1005 .as_ref()
1006 .map_or(true, |b| unsafe { b.get_unchecked(i) })
1007 {
1008 let code = arr.data[i];
1009 if min_code.map_or(true, |min| {
1010 let sc = &arr.unique_values[code.to_usize()];
1011 let sm = &arr.unique_values[min.to_usize()];
1012 sc < sm
1013 }) {
1014 min_code = Some(code);
1015 }
1016 }
1017 }
1018 min_code.map(|code| arr.unique_values[code.to_usize()].clone())
1019}
1020
1021#[inline]
1032pub fn max_categorical_array<T: Integer>(window: CategoricalAVT<T>) -> Option<String> {
1033 let (arr, offset, len) = window;
1034 let mut max_code: Option<T> = None;
1035 for i in offset..offset + len {
1036 if arr
1037 .null_mask
1038 .as_ref()
1039 .map_or(true, |b| unsafe { b.get_unchecked(i) })
1040 {
1041 let code = arr.data[i];
1042 if max_code.map_or(true, |max| {
1043 let sc = &arr.unique_values[code.to_usize()];
1044 let sm = &arr.unique_values[max.to_usize()];
1045 sc > sm
1046 }) {
1047 max_code = Some(code);
1048 }
1049 }
1050 }
1051 max_code.map(|code| arr.unique_values[code.to_usize()].clone())
1052}
1053
1054#[inline(always)]
1068pub fn count_distinct_string<T: Integer>(window: StringAVT<T>) -> usize {
1069 let (arr, offset, len) = window;
1070 #[cfg(feature = "fast_hash")]
1071 let mut set = AHashSet::with_capacity(len);
1072 #[cfg(not(feature = "fast_hash"))]
1073 let mut set = HashSet::with_capacity(len);
1074 let null_mask = arr.null_mask.as_ref();
1075
1076 for i in offset..offset + len {
1077 let valid = null_mask.map_or(true, |b| unsafe { b.get_unchecked(i) });
1078 if valid {
1079 let s = unsafe { arr.get_str_unchecked(i) };
1080 set.insert(s);
1081 if set.len() == len {
1082 break;
1083 }
1084 }
1085 }
1086 set.len()
1087}
1088
1089macro_rules! unary_str_transform {
1094 ($fn_name:ident, $doc:expr, $transform:expr) => {
1095 #[doc = $doc]
1096 pub fn $fn_name<T: Integer>(input: StringAVT<T>) -> Result<StringArray<T>, KernelError> {
1097 let (arr, offset, len) = input;
1098
1099 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1100 let mut m = Bitmask::new_set_all(len, true);
1101 for i in 0..len {
1102 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1103 }
1104 m
1105 });
1106
1107 let input_bytes = arr.offsets[offset + len].to_usize()
1108 - arr.offsets[offset].to_usize();
1109 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1110 unsafe { offsets.set_len(len + 1); }
1111 let mut data = Vec64::<u8>::with_capacity(input_bytes);
1112
1113 offsets[0] = T::zero();
1114 let mut cur = 0usize;
1115
1116 for i in 0..len {
1117 let valid = mask_opt.as_ref()
1118 .map_or(true, |m| unsafe { m.get_unchecked(i) });
1119
1120 if valid {
1121 let s = unsafe { arr.get_str_unchecked(offset + i) };
1122 let t: String = ($transform)(s);
1123 data.extend_from_slice(t.as_bytes());
1124 cur += t.len();
1125 }
1126 offsets[i + 1] = T::from_usize(cur);
1127 }
1128
1129 Ok(StringArray {
1130 offsets: offsets.into(),
1131 data: data.into(),
1132 null_mask: mask_opt,
1133 })
1134 }
1135 };
1136}
1137
1138macro_rules! unary_dict_transform {
1141 ($fn_name:ident, $doc:expr, $transform:expr) => {
1142 #[doc = $doc]
1143 pub fn $fn_name<T: Integer>(input: CategoricalAVT<T>) -> Result<CategoricalArray<T>, KernelError> {
1144 let (arr, offset, len) = input;
1145
1146 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1147 let mut m = Bitmask::new_set_all(len, true);
1148 for i in 0..len {
1149 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1150 }
1151 m
1152 });
1153
1154 #[cfg(feature = "fast_hash")]
1156 let mut seen: AHashMap<String, T> = AHashMap::with_capacity(arr.unique_values.len());
1157 #[cfg(not(feature = "fast_hash"))]
1158 let mut seen: std::collections::HashMap<String, T> =
1159 std::collections::HashMap::with_capacity(arr.unique_values.len());
1160
1161 let mut new_unique = Vec64::<String>::new();
1162 let mut idx_map = Vec64::<T>::with_capacity(arr.unique_values.len());
1163
1164 for old_val in arr.unique_values.iter() {
1165 let t: String = ($transform)(old_val.as_str());
1166 let new_idx = match seen.get(&t) {
1167 Some(&ix) => ix,
1168 None => {
1169 let ix = T::from_usize(new_unique.len());
1170 new_unique.push(t.clone());
1171 seen.insert(t, ix);
1172 ix
1173 }
1174 };
1175 idx_map.push(new_idx);
1176 }
1177
1178 let mut data = Vec64::<T>::with_capacity(len);
1180 unsafe { data.set_len(len); }
1181 for i in 0..len {
1182 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1183 data[i] = if valid {
1184 idx_map[arr.data[offset + i].to_usize()]
1185 } else {
1186 T::zero()
1187 };
1188 }
1189
1190 Ok(CategoricalArray {
1191 data: data.into(),
1192 unique_values: new_unique,
1193 null_mask: mask_opt,
1194 })
1195 }
1196 };
1197}
1198
1199unary_str_transform!(to_uppercase_str,
1200 "Converts each string element to uppercase.",
1201 |s: &str| s.to_uppercase()
1202);
1203unary_dict_transform!(to_uppercase_dict,
1204 "Converts each categorical string element to uppercase.",
1205 |s: &str| s.to_uppercase()
1206);
1207
1208unary_str_transform!(to_lowercase_str,
1209 "Converts each string element to lowercase.",
1210 |s: &str| s.to_lowercase()
1211);
1212unary_dict_transform!(to_lowercase_dict,
1213 "Converts each categorical string element to lowercase.",
1214 |s: &str| s.to_lowercase()
1215);
1216
1217unary_str_transform!(trim_str,
1218 "Trims leading and trailing whitespace from each string element.",
1219 |s: &str| s.trim().to_owned()
1220);
1221unary_dict_transform!(trim_dict,
1222 "Trims leading and trailing whitespace from each categorical string element.",
1223 |s: &str| s.trim().to_owned()
1224);
1225
1226unary_str_transform!(ltrim_str,
1227 "Trims leading whitespace from each string element.",
1228 |s: &str| s.trim_start().to_owned()
1229);
1230unary_dict_transform!(ltrim_dict,
1231 "Trims leading whitespace from each categorical string element.",
1232 |s: &str| s.trim_start().to_owned()
1233);
1234
1235unary_str_transform!(rtrim_str,
1236 "Trims trailing whitespace from each string element.",
1237 |s: &str| s.trim_end().to_owned()
1238);
1239unary_dict_transform!(rtrim_dict,
1240 "Trims trailing whitespace from each categorical string element.",
1241 |s: &str| s.trim_end().to_owned()
1242);
1243
1244unary_str_transform!(reverse_str,
1245 "Reverses each string element by Unicode characters.",
1246 |s: &str| s.chars().rev().collect::<String>()
1247);
1248unary_dict_transform!(reverse_dict,
1249 "Reverses each categorical string element by Unicode characters.",
1250 |s: &str| s.chars().rev().collect::<String>()
1251);
1252
1253pub fn byte_length_str<T: Integer + Copy>(
1257 input: StringAVT<T>,
1258) -> Result<IntegerArray<T>, KernelError> {
1259 let (array, offset, len) = input;
1260
1261 let mask_opt = array.null_mask.as_ref().map(|orig| {
1262 let mut m = Bitmask::new_set_all(len, true);
1263 for i in 0..len {
1264 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1265 }
1266 m
1267 });
1268
1269 let mut data = Vec64::<T>::with_capacity(len);
1270 unsafe { data.set_len(len); }
1271 for i in 0..len {
1272 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1273 if valid {
1274 let start = array.offsets[offset + i].to_usize();
1275 let end = array.offsets[offset + i + 1].to_usize();
1276 data[i] = T::from_usize(end - start);
1277 } else {
1278 data[i] = T::zero();
1279 }
1280 }
1281
1282 Ok(IntegerArray {
1283 data: data.into(),
1284 null_mask: mask_opt,
1285 })
1286}
1287
1288pub fn byte_length_dict<T: Integer>(
1290 input: CategoricalAVT<T>,
1291) -> Result<IntegerArray<T>, KernelError> {
1292 let (array, offset, len) = input;
1293
1294 let mask_opt = array.null_mask.as_ref().map(|orig| {
1295 let mut m = Bitmask::new_set_all(len, true);
1296 for i in 0..len {
1297 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1298 }
1299 m
1300 });
1301
1302 let mut data = Vec64::<T>::with_capacity(len);
1303 unsafe { data.set_len(len); }
1304 for i in 0..len {
1305 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1306 data[i] = if valid {
1307 T::from_usize(unsafe { array.get_str_unchecked(offset + i) }.len())
1308 } else {
1309 T::zero()
1310 };
1311 }
1312
1313 Ok(IntegerArray {
1314 data: data.into(),
1315 null_mask: mask_opt,
1316 })
1317}
1318
1319pub fn find_str<T: Integer>(
1323 input: StringAVT<T>,
1324 needle: &str,
1325) -> Result<IntegerArray<i32>, KernelError> {
1326 let (arr, offset, len) = input;
1327
1328 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1329 let mut m = Bitmask::new_set_all(len, true);
1330 for i in 0..len {
1331 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1332 }
1333 m
1334 });
1335
1336 let mut data = Vec64::<i32>::with_capacity(len);
1337 unsafe { data.set_len(len); }
1338 for i in 0..len {
1339 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1340 data[i] = if valid {
1341 let s = unsafe { arr.get_str_unchecked(offset + i) };
1342 s.find(needle).map_or(-1, |pos| pos as i32)
1343 } else {
1344 0
1345 };
1346 }
1347
1348 Ok(IntegerArray {
1349 data: data.into(),
1350 null_mask: mask_opt,
1351 })
1352}
1353
1354pub fn find_dict<T: Integer>(
1356 input: CategoricalAVT<T>,
1357 needle: &str,
1358) -> Result<IntegerArray<i32>, KernelError> {
1359 let (arr, offset, len) = input;
1360
1361 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1362 let mut m = Bitmask::new_set_all(len, true);
1363 for i in 0..len {
1364 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1365 }
1366 m
1367 });
1368
1369 let mut data = Vec64::<i32>::with_capacity(len);
1370 unsafe { data.set_len(len); }
1371 for i in 0..len {
1372 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1373 data[i] = if valid {
1374 let s = unsafe { arr.get_str_unchecked(offset + i) };
1375 s.find(needle).map_or(-1, |pos| pos as i32)
1376 } else {
1377 0
1378 };
1379 }
1380
1381 Ok(IntegerArray {
1382 data: data.into(),
1383 null_mask: mask_opt,
1384 })
1385}
1386
1387pub fn count_match_str<T: Integer>(
1389 input: StringAVT<T>,
1390 needle: &str,
1391) -> Result<IntegerArray<i32>, KernelError> {
1392 let (arr, offset, len) = input;
1393
1394 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1395 let mut m = Bitmask::new_set_all(len, true);
1396 for i in 0..len {
1397 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1398 }
1399 m
1400 });
1401
1402 let mut data = Vec64::<i32>::with_capacity(len);
1403 unsafe { data.set_len(len); }
1404 for i in 0..len {
1405 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1406 data[i] = if valid {
1407 let s = unsafe { arr.get_str_unchecked(offset + i) };
1408 s.matches(needle).count() as i32
1409 } else {
1410 0
1411 };
1412 }
1413
1414 Ok(IntegerArray {
1415 data: data.into(),
1416 null_mask: mask_opt,
1417 })
1418}
1419
1420pub fn count_match_dict<T: Integer>(
1422 input: CategoricalAVT<T>,
1423 needle: &str,
1424) -> Result<IntegerArray<i32>, KernelError> {
1425 let (arr, offset, len) = input;
1426
1427 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1428 let mut m = Bitmask::new_set_all(len, true);
1429 for i in 0..len {
1430 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1431 }
1432 m
1433 });
1434
1435 let mut data = Vec64::<i32>::with_capacity(len);
1436 unsafe { data.set_len(len); }
1437 for i in 0..len {
1438 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1439 data[i] = if valid {
1440 let s = unsafe { arr.get_str_unchecked(offset + i) };
1441 s.matches(needle).count() as i32
1442 } else {
1443 0
1444 };
1445 }
1446
1447 Ok(IntegerArray {
1448 data: data.into(),
1449 null_mask: mask_opt,
1450 })
1451}
1452
1453pub fn substring_str<T: Integer>(
1458 input: StringAVT<T>,
1459 start: usize,
1460 opt_len: Option<usize>,
1461) -> Result<StringArray<T>, KernelError> {
1462 let (arr, offset, len) = input;
1463 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1464 let mut m = Bitmask::new_set_all(len, true);
1465 for i in 0..len {
1466 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1467 }
1468 m
1469 });
1470
1471 let input_bytes = arr.offsets[offset + len].to_usize()
1472 - arr.offsets[offset].to_usize();
1473 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1474 unsafe { offsets.set_len(len + 1); }
1475 let mut data = Vec64::<u8>::with_capacity(input_bytes);
1476
1477 offsets[0] = T::zero();
1478 let mut cur = 0usize;
1479
1480 for i in 0..len {
1481 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1482 if valid {
1483 let s = unsafe { arr.get_str_unchecked(offset + i) };
1484 let chars = s.chars().skip(start);
1485 let t: String = match opt_len {
1486 Some(n) => chars.take(n).collect(),
1487 None => chars.collect(),
1488 };
1489 data.extend_from_slice(t.as_bytes());
1490 cur += t.len();
1491 }
1492 offsets[i + 1] = T::from_usize(cur);
1493 }
1494
1495 Ok(StringArray {
1496 offsets: offsets.into(),
1497 data: data.into(),
1498 null_mask: mask_opt,
1499 })
1500}
1501
1502pub fn substring_dict<T: Integer>(
1504 input: CategoricalAVT<T>,
1505 start: usize,
1506 opt_len: Option<usize>,
1507) -> Result<CategoricalArray<T>, KernelError> {
1508 let (arr, offset, len) = input;
1509 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1510 let mut m = Bitmask::new_set_all(len, true);
1511 for i in 0..len {
1512 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1513 }
1514 m
1515 });
1516
1517 #[cfg(feature = "fast_hash")]
1518 let mut seen: AHashMap<String, T> = AHashMap::with_capacity(arr.unique_values.len());
1519 #[cfg(not(feature = "fast_hash"))]
1520 let mut seen: std::collections::HashMap<String, T> =
1521 std::collections::HashMap::with_capacity(arr.unique_values.len());
1522
1523 let mut new_unique = Vec64::<String>::new();
1524 let mut idx_map = Vec64::<T>::with_capacity(arr.unique_values.len());
1525
1526 for old_val in arr.unique_values.iter() {
1527 let chars = old_val.chars().skip(start);
1528 let t: String = match opt_len {
1529 Some(n) => chars.take(n).collect(),
1530 None => chars.collect(),
1531 };
1532 let new_idx = match seen.get(&t) {
1533 Some(&ix) => ix,
1534 None => {
1535 let ix = T::from_usize(new_unique.len());
1536 new_unique.push(t.clone());
1537 seen.insert(t, ix);
1538 ix
1539 }
1540 };
1541 idx_map.push(new_idx);
1542 }
1543
1544 let mut data = Vec64::<T>::with_capacity(len);
1545 unsafe { data.set_len(len); }
1546 for i in 0..len {
1547 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1548 data[i] = if valid {
1549 idx_map[arr.data[offset + i].to_usize()]
1550 } else {
1551 T::zero()
1552 };
1553 }
1554
1555 Ok(CategoricalArray {
1556 data: data.into(),
1557 unique_values: new_unique,
1558 null_mask: mask_opt,
1559 })
1560}
1561
1562pub fn replace_str<T: Integer>(
1566 input: StringAVT<T>,
1567 from: &str,
1568 to: &str,
1569) -> Result<StringArray<T>, KernelError> {
1570 let (arr, offset, len) = input;
1571 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1572 let mut m = Bitmask::new_set_all(len, true);
1573 for i in 0..len {
1574 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1575 }
1576 m
1577 });
1578
1579 let input_bytes = arr.offsets[offset + len].to_usize()
1580 - arr.offsets[offset].to_usize();
1581 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1582 unsafe { offsets.set_len(len + 1); }
1583 let mut data = Vec64::<u8>::with_capacity(input_bytes);
1584
1585 offsets[0] = T::zero();
1586 let mut cur = 0usize;
1587
1588 for i in 0..len {
1589 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1590 if valid {
1591 let s = unsafe { arr.get_str_unchecked(offset + i) };
1592 let t = s.replace(from, to);
1593 data.extend_from_slice(t.as_bytes());
1594 cur += t.len();
1595 }
1596 offsets[i + 1] = T::from_usize(cur);
1597 }
1598
1599 Ok(StringArray {
1600 offsets: offsets.into(),
1601 data: data.into(),
1602 null_mask: mask_opt,
1603 })
1604}
1605
1606pub fn replace_dict<T: Integer>(
1608 input: CategoricalAVT<T>,
1609 from: &str,
1610 to: &str,
1611) -> Result<CategoricalArray<T>, KernelError> {
1612 let (arr, offset, len) = input;
1613 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1614 let mut m = Bitmask::new_set_all(len, true);
1615 for i in 0..len {
1616 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1617 }
1618 m
1619 });
1620
1621 #[cfg(feature = "fast_hash")]
1622 let mut seen: AHashMap<String, T> = AHashMap::with_capacity(arr.unique_values.len());
1623 #[cfg(not(feature = "fast_hash"))]
1624 let mut seen: std::collections::HashMap<String, T> =
1625 std::collections::HashMap::with_capacity(arr.unique_values.len());
1626
1627 let mut new_unique = Vec64::<String>::new();
1628 let mut idx_map = Vec64::<T>::with_capacity(arr.unique_values.len());
1629
1630 for old_val in arr.unique_values.iter() {
1631 let t = old_val.replace(from, to);
1632 let new_idx = match seen.get(&t) {
1633 Some(&ix) => ix,
1634 None => {
1635 let ix = T::from_usize(new_unique.len());
1636 new_unique.push(t.clone());
1637 seen.insert(t, ix);
1638 ix
1639 }
1640 };
1641 idx_map.push(new_idx);
1642 }
1643
1644 let mut data = Vec64::<T>::with_capacity(len);
1645 unsafe { data.set_len(len); }
1646 for i in 0..len {
1647 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1648 data[i] = if valid {
1649 idx_map[arr.data[offset + i].to_usize()]
1650 } else {
1651 T::zero()
1652 };
1653 }
1654
1655 Ok(CategoricalArray {
1656 data: data.into(),
1657 unique_values: new_unique,
1658 null_mask: mask_opt,
1659 })
1660}
1661
1662pub fn repeat_str<T: Integer>(
1666 input: StringAVT<T>,
1667 n: usize,
1668) -> Result<StringArray<T>, KernelError> {
1669 let (arr, offset, len) = input;
1670 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1671 let mut m = Bitmask::new_set_all(len, true);
1672 for i in 0..len {
1673 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1674 }
1675 m
1676 });
1677
1678 let input_bytes = arr.offsets[offset + len].to_usize()
1679 - arr.offsets[offset].to_usize();
1680 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1681 unsafe { offsets.set_len(len + 1); }
1682 let mut data = Vec64::<u8>::with_capacity(input_bytes * n);
1683
1684 offsets[0] = T::zero();
1685 let mut cur = 0usize;
1686
1687 for i in 0..len {
1688 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1689 if valid {
1690 let s = unsafe { arr.get_str_unchecked(offset + i) };
1691 let bytes = s.as_bytes();
1692 for _ in 0..n {
1693 data.extend_from_slice(bytes);
1694 }
1695 cur += bytes.len() * n;
1696 }
1697 offsets[i + 1] = T::from_usize(cur);
1698 }
1699
1700 Ok(StringArray {
1701 offsets: offsets.into(),
1702 data: data.into(),
1703 null_mask: mask_opt,
1704 })
1705}
1706
1707pub fn repeat_dict<T: Integer>(
1709 input: CategoricalAVT<T>,
1710 n: usize,
1711) -> Result<CategoricalArray<T>, KernelError> {
1712 let (arr, offset, len) = input;
1713 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1714 let mut m = Bitmask::new_set_all(len, true);
1715 for i in 0..len {
1716 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1717 }
1718 m
1719 });
1720
1721 let mut new_unique = Vec64::<String>::new();
1723 for old_val in arr.unique_values.iter() {
1724 new_unique.push(old_val.repeat(n));
1725 }
1726
1727 let mut data = Vec64::<T>::with_capacity(len);
1728 unsafe { data.set_len(len); }
1729 for i in 0..len {
1730 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1731 data[i] = if valid {
1732 arr.data[offset + i]
1733 } else {
1734 T::zero()
1735 };
1736 }
1737
1738 Ok(CategoricalArray {
1739 data: data.into(),
1740 unique_values: new_unique,
1741 null_mask: mask_opt,
1742 })
1743}
1744
1745pub fn pad_str<T: Integer>(
1749 input: StringAVT<T>,
1750 width: usize,
1751 fill_char: char,
1752 side: PadSide,
1753) -> Result<StringArray<T>, KernelError> {
1754 let (arr, offset, len) = input;
1755 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1756 let mut m = Bitmask::new_set_all(len, true);
1757 for i in 0..len {
1758 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1759 }
1760 m
1761 });
1762
1763 let max_padded = width * fill_char.len_utf8();
1764 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1765 unsafe { offsets.set_len(len + 1); }
1766 let mut data = Vec64::<u8>::with_capacity(len * max_padded);
1767
1768 offsets[0] = T::zero();
1769 let mut cur = 0usize;
1770
1771 for i in 0..len {
1772 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1773 if valid {
1774 let s = unsafe { arr.get_str_unchecked(offset + i) };
1775 let char_len = s.chars().count();
1776 if char_len >= width {
1777 data.extend_from_slice(s.as_bytes());
1778 cur += s.len();
1779 } else {
1780 let padding = width - char_len;
1781 let pad_bytes = fill_char.len_utf8();
1782 let mut pad_buf = [0u8; 4];
1783 let pad_slice = fill_char.encode_utf8(&mut pad_buf);
1784 match side {
1785 PadSide::Left => {
1786 for _ in 0..padding { data.extend_from_slice(pad_slice.as_bytes()); }
1787 data.extend_from_slice(s.as_bytes());
1788 }
1789 PadSide::Right => {
1790 data.extend_from_slice(s.as_bytes());
1791 for _ in 0..padding { data.extend_from_slice(pad_slice.as_bytes()); }
1792 }
1793 PadSide::Both => {
1794 let left = padding / 2;
1795 let right = padding - left;
1796 for _ in 0..left { data.extend_from_slice(pad_slice.as_bytes()); }
1797 data.extend_from_slice(s.as_bytes());
1798 for _ in 0..right { data.extend_from_slice(pad_slice.as_bytes()); }
1799 }
1800 }
1801 cur += s.len() + padding * pad_bytes;
1802 }
1803 }
1804 offsets[i + 1] = T::from_usize(cur);
1805 }
1806
1807 Ok(StringArray {
1808 offsets: offsets.into(),
1809 data: data.into(),
1810 null_mask: mask_opt,
1811 })
1812}
1813
1814pub fn pad_dict<T: Integer>(
1816 input: CategoricalAVT<T>,
1817 width: usize,
1818 fill_char: char,
1819 side: PadSide,
1820) -> Result<CategoricalArray<T>, KernelError> {
1821 let (arr, offset, len) = input;
1822 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1823 let mut m = Bitmask::new_set_all(len, true);
1824 for i in 0..len {
1825 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1826 }
1827 m
1828 });
1829
1830 let pad_one = |s: &str| -> String {
1831 let char_len = s.chars().count();
1832 if char_len >= width {
1833 return s.to_owned();
1834 }
1835 let padding = width - char_len;
1836 match side {
1837 PadSide::Left => {
1838 let mut out = String::with_capacity(s.len() + padding * fill_char.len_utf8());
1839 for _ in 0..padding { out.push(fill_char); }
1840 out.push_str(s);
1841 out
1842 }
1843 PadSide::Right => {
1844 let mut out = String::with_capacity(s.len() + padding * fill_char.len_utf8());
1845 out.push_str(s);
1846 for _ in 0..padding { out.push(fill_char); }
1847 out
1848 }
1849 PadSide::Both => {
1850 let left = padding / 2;
1851 let right = padding - left;
1852 let mut out = String::with_capacity(s.len() + padding * fill_char.len_utf8());
1853 for _ in 0..left { out.push(fill_char); }
1854 out.push_str(s);
1855 for _ in 0..right { out.push(fill_char); }
1856 out
1857 }
1858 }
1859 };
1860
1861 let mut new_unique = Vec64::<String>::new();
1863 for old_val in arr.unique_values.iter() {
1864 new_unique.push(pad_one(old_val));
1865 }
1866
1867 let mut data = Vec64::<T>::with_capacity(len);
1868 unsafe { data.set_len(len); }
1869 for i in 0..len {
1870 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1871 data[i] = if valid {
1872 arr.data[offset + i]
1873 } else {
1874 T::zero()
1875 };
1876 }
1877
1878 Ok(CategoricalArray {
1879 data: data.into(),
1880 unique_values: new_unique,
1881 null_mask: mask_opt,
1882 })
1883}
1884
1885pub fn join_str<T: Integer>(input: StringAVT<T>, delimiter: &str) -> Option<String> {
1890 let (arr, offset, len) = input;
1891 let mut parts: Vec<&str> = Vec::with_capacity(len);
1892 for i in offset..offset + len {
1893 let valid = arr.null_mask.as_ref().map_or(true, |b| unsafe { b.get_unchecked(i) });
1894 if valid {
1895 parts.push(unsafe { arr.get_str_unchecked(i) });
1896 }
1897 }
1898 if parts.is_empty() { None } else { Some(parts.join(delimiter)) }
1899}
1900
1901pub fn join_dict<T: Integer>(input: CategoricalAVT<T>, delimiter: &str) -> Option<String> {
1904 let (arr, offset, len) = input;
1905 let mut parts: Vec<&str> = Vec::with_capacity(len);
1906 for i in offset..offset + len {
1907 let valid = arr.null_mask.as_ref().map_or(true, |b| unsafe { b.get_unchecked(i) });
1908 if valid {
1909 parts.push(unsafe { arr.get_str_unchecked(i) });
1910 }
1911 }
1912 if parts.is_empty() { None } else { Some(parts.join(delimiter)) }
1913}
1914
1915#[cfg(feature = "regex")]
1919pub fn regex_replace_str<T: Integer>(
1920 input: StringAVT<T>,
1921 pattern: &str,
1922 replacement: &str,
1923) -> Result<StringArray<T>, KernelError> {
1924 let re = Regex::new(pattern).map_err(|_| {
1925 KernelError::InvalidArguments("Invalid regex pattern".to_string())
1926 })?;
1927 let (arr, offset, len) = input;
1928 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1929 let mut m = Bitmask::new_set_all(len, true);
1930 for i in 0..len {
1931 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1932 }
1933 m
1934 });
1935
1936 let input_bytes = arr.offsets[offset + len].to_usize()
1937 - arr.offsets[offset].to_usize();
1938 let mut offsets = Vec64::<T>::with_capacity(len + 1);
1939 unsafe { offsets.set_len(len + 1); }
1940 let mut data = Vec64::<u8>::with_capacity(input_bytes);
1941
1942 offsets[0] = T::zero();
1943 let mut cur = 0usize;
1944
1945 for i in 0..len {
1946 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
1947 if valid {
1948 let s = unsafe { arr.get_str_unchecked(offset + i) };
1949 let t = re.replace_all(s, replacement).into_owned();
1950 data.extend_from_slice(t.as_bytes());
1951 cur += t.len();
1952 }
1953 offsets[i + 1] = T::from_usize(cur);
1954 }
1955
1956 Ok(StringArray {
1957 offsets: offsets.into(),
1958 data: data.into(),
1959 null_mask: mask_opt,
1960 })
1961}
1962
1963#[cfg(feature = "regex")]
1965pub fn regex_replace_dict<T: Integer>(
1966 input: CategoricalAVT<T>,
1967 pattern: &str,
1968 replacement: &str,
1969) -> Result<CategoricalArray<T>, KernelError> {
1970 let re = Regex::new(pattern).map_err(|_| {
1971 KernelError::InvalidArguments("Invalid regex pattern".to_string())
1972 })?;
1973 let (arr, offset, len) = input;
1974 let mask_opt = arr.null_mask.as_ref().map(|orig| {
1975 let mut m = Bitmask::new_set_all(len, true);
1976 for i in 0..len {
1977 unsafe { m.set_unchecked(i, orig.get_unchecked(offset + i)); }
1978 }
1979 m
1980 });
1981
1982 #[cfg(feature = "fast_hash")]
1983 let mut seen: AHashMap<String, T> = AHashMap::with_capacity(arr.unique_values.len());
1984 #[cfg(not(feature = "fast_hash"))]
1985 let mut seen: std::collections::HashMap<String, T> =
1986 std::collections::HashMap::with_capacity(arr.unique_values.len());
1987
1988 let mut new_unique = Vec64::<String>::new();
1989 let mut idx_map = Vec64::<T>::with_capacity(arr.unique_values.len());
1990
1991 for old_val in arr.unique_values.iter() {
1992 let t = re.replace_all(old_val, replacement).into_owned();
1993 let new_idx = match seen.get(&t) {
1994 Some(&ix) => ix,
1995 None => {
1996 let ix = T::from_usize(new_unique.len());
1997 new_unique.push(t.clone());
1998 seen.insert(t, ix);
1999 ix
2000 }
2001 };
2002 idx_map.push(new_idx);
2003 }
2004
2005 let mut data = Vec64::<T>::with_capacity(len);
2006 unsafe { data.set_len(len); }
2007 for i in 0..len {
2008 let valid = mask_opt.as_ref().map_or(true, |m| unsafe { m.get_unchecked(i) });
2009 data[i] = if valid {
2010 idx_map[arr.data[offset + i].to_usize()]
2011 } else {
2012 T::zero()
2013 };
2014 }
2015
2016 Ok(CategoricalArray {
2017 data: data.into(),
2018 unique_values: new_unique,
2019 null_mask: mask_opt,
2020 })
2021}
2022
2023#[cfg(feature = "views")]
2034pub fn cross_tabulate(
2035 x: &TextArrayV,
2036 y: &TextArrayV,
2037) -> Result<(Vec64<Vec64<u64>>, Vec64<String>, Vec64<String>, usize), KernelError> {
2038 let n = x.len().min(y.len());
2039
2040 if x.len() != y.len() {
2041 return Err(KernelError::LengthMismatch(format!(
2042 "cross_tabulate: x has length {} but y has length {}",
2043 x.len(),
2044 y.len()
2045 )));
2046 }
2047
2048 #[cfg(feature = "fast_hash")]
2049 let mut row_map: AHashMap<String, usize> = AHashMap::with_capacity(n.min(256));
2050 #[cfg(not(feature = "fast_hash"))]
2051 let mut row_map: HashMap<String, usize> = HashMap::with_capacity(n.min(256));
2052
2053 #[cfg(feature = "fast_hash")]
2054 let mut col_map: AHashMap<String, usize> = AHashMap::with_capacity(n.min(256));
2055 #[cfg(not(feature = "fast_hash"))]
2056 let mut col_map: HashMap<String, usize> = HashMap::with_capacity(n.min(256));
2057
2058 let mut row_labels = Vec64::<String>::with_capacity(32);
2059 let mut col_labels = Vec64::<String>::with_capacity(32);
2060
2061 let mut counts = Vec64::<u64>::new();
2065 let mut n_cols_seen = 0_usize;
2066 let mut total = 0_usize;
2067
2068 for i in 0..n {
2069 let (Some(xv), Some(yv)) = (x.get_str(i), y.get_str(i)) else {
2070 continue;
2071 };
2072
2073 let row_len = row_labels.len();
2074 let ri = *row_map.entry(xv.to_string()).or_insert_with(|| {
2075 row_labels.push(xv.to_string());
2076 row_len
2077 });
2078
2079 let col_len = col_labels.len();
2080 let ci = *col_map.entry(yv.to_string()).or_insert_with(|| {
2081 col_labels.push(yv.to_string());
2082 col_len
2083 });
2084
2085 if col_labels.len() > n_cols_seen {
2087 let old_cols = n_cols_seen;
2088 n_cols_seen = col_labels.len();
2089 let nrows = row_labels.len();
2090 let mut new_counts = Vec64::<u64>::with_capacity(nrows * n_cols_seen);
2092 new_counts.resize(nrows * n_cols_seen, 0);
2093 for r in 0..nrows.min(ri + 1) {
2094 for c in 0..old_cols {
2095 if r * old_cols + c < counts.len() {
2096 new_counts[r * n_cols_seen + c] = counts[r * old_cols + c];
2097 }
2098 }
2099 }
2100 counts = new_counts;
2101 }
2102
2103 let needed = (ri + 1) * n_cols_seen;
2105 if needed > counts.len() {
2106 counts.resize(needed, 0);
2107 }
2108
2109 counts[ri * n_cols_seen + ci] += 1;
2110 total += 1;
2111 }
2112
2113 if total == 0 {
2114 return Err(KernelError::InvalidArguments(
2115 "cross_tabulate: no valid non-null pairs found".to_string(),
2116 ));
2117 }
2118
2119 let nrows = row_labels.len();
2121 let ncols = col_labels.len();
2122 let mut matrix = Vec64::<Vec64<u64>>::with_capacity(nrows);
2123 for r in 0..nrows {
2124 let start = r * ncols;
2125 let end = start + ncols;
2126 let row = Vec64::from_slice(&counts[start..end]);
2127 matrix.push(row);
2128 }
2129
2130 Ok((matrix, row_labels, col_labels, total))
2131}
2132
2133#[cfg(test)]
2134mod tests {
2135 use crate::{CategoricalArray, StringArray, vec64};
2136
2137 use super::*;
2138
2139 fn str_array<T: Integer>(vals: &[&str]) -> StringArray<T> {
2142 StringArray::<T>::from_slice(vals)
2143 }
2144
2145 fn dict_array<T: Integer>(vals: &[&str]) -> CategoricalArray<T> {
2146 let owned: Vec<&str> = vals.to_vec();
2147 CategoricalArray::<T>::from_values(owned)
2148 }
2149
2150 fn bm(bools: &[bool]) -> Bitmask {
2151 Bitmask::from_bools(bools)
2152 }
2153
2154 #[test]
2157 fn test_concat_str_str() {
2158 let a = str_array::<u32>(&["foo", "bar", ""]);
2159 let b = str_array::<u32>(&["baz", "qux", "quux"]);
2160 let out = concat_str_str((&a, 0, a.len()), (&b, 0, b.len()));
2161 assert_eq!(out.get(0), Some("foobaz"));
2162 assert_eq!(out.get(1), Some("barqux"));
2163 assert_eq!(out.get(2), Some("quux"));
2164 assert!(out.null_mask.as_ref().unwrap().all_set());
2165 }
2166
2167 #[test]
2168 fn test_concat_str_str_chunk() {
2169 let a = str_array::<u32>(&["XXX", "foo", "bar", ""]);
2170 let b = str_array::<u32>(&["YYY", "baz", "qux", "quux"]);
2171 let out = concat_str_str((&a, 1, 3), (&b, 1, 3));
2173 assert_eq!(out.get(0), Some("foobaz"));
2174 assert_eq!(out.get(1), Some("barqux"));
2175 assert_eq!(out.get(2), Some("quux"));
2176 assert!(out.null_mask.as_ref().unwrap().all_set());
2177 }
2178
2179 #[test]
2180 fn test_concat_dict_dict() {
2181 let a = dict_array::<u32>(&["x", "y"]);
2182 let b = dict_array::<u32>(&["1", "2"]);
2183 let out = concat_dict_dict((&a, 0, a.len()), (&b, 0, b.len())).unwrap();
2184 let s0 = out.get(0).unwrap();
2185 let s1 = out.get(1).unwrap();
2186 assert!(["x1", "y2"].contains(&s0));
2187 assert!(["x1", "y2"].contains(&s1));
2188 assert!(out.null_mask.as_ref().unwrap().all_set());
2189 }
2190
2191 #[test]
2192 fn test_concat_dict_dict_chunk() {
2193 let a = dict_array::<u32>(&["foo", "x", "y", "bar"]);
2194 let b = dict_array::<u32>(&["A", "1", "2", "B"]);
2195 let out = concat_dict_dict((&a, 1, 2), (&b, 1, 2)).unwrap();
2196 let s0 = out.get(0).unwrap();
2197 let s1 = out.get(1).unwrap();
2198 assert!(["x1", "y2"].contains(&s0));
2199 assert!(["x1", "y2"].contains(&s1));
2200 assert!(out.null_mask.as_ref().unwrap().all_set());
2201 }
2202
2203 #[test]
2204 fn test_concat_str_dict() {
2205 let a = str_array::<u32>(&["ab", "cd", ""]);
2206 let b = dict_array::<u32>(&["xy", "zq", ""]);
2207 let out = concat_str_dict((&a, 0, a.len()), (&b, 0, b.len())).unwrap();
2208 assert_eq!(out.get(0), Some("abxy"));
2209 assert_eq!(out.get(1), Some("cdzq"));
2210 assert_eq!(out.get(2), Some(""));
2211 assert!(out.null_mask.as_ref().unwrap().all_set());
2212 }
2213
2214 #[test]
2215 fn test_concat_str_dict_chunk() {
2216 let a = str_array::<u32>(&["dummy", "ab", "cd", ""]);
2217 let b = dict_array::<u32>(&["dummy", "xy", "zq", ""]);
2218 let out = concat_str_dict((&a, 1, 3), (&b, 1, 3)).unwrap();
2219 assert_eq!(out.get(0), Some("abxy"));
2220 assert_eq!(out.get(1), Some("cdzq"));
2221 assert_eq!(out.get(2), Some(""));
2222 assert!(out.null_mask.as_ref().unwrap().all_set());
2223 }
2224
2225 #[test]
2226 fn test_concat_dict_str() {
2227 let a = dict_array::<u32>(&["hi", "ho"]);
2228 let b = str_array::<u32>(&["yo", "no"]);
2229 let out = concat_dict_str((&a, 0, a.len()), (&b, 0, b.len())).unwrap();
2230 assert_eq!(out.get(0), Some("yohi"));
2231 assert_eq!(out.get(1), Some("noho"));
2232 assert!(out.null_mask.as_ref().unwrap().all_set());
2233 }
2234
2235 #[test]
2236 fn test_concat_dict_str_chunk() {
2237 let a = dict_array::<u32>(&["dummy", "hi", "ho", "zzz"]);
2238 let b = str_array::<u32>(&["dummy", "yo", "no", "xxx"]);
2239 let out = concat_dict_str((&a, 1, 2), (&b, 1, 2)).unwrap();
2240 assert_eq!(out.get(0), Some("yohi"));
2241 assert_eq!(out.get(1), Some("noho"));
2242 assert!(out.null_mask.as_ref().unwrap().all_set());
2243 }
2244
2245 #[test]
2248 fn test_contains_str_str() {
2249 let s = str_array::<u32>(&["abc", "def", "ghijk"]);
2250 let p = str_array::<u32>(&["b", "x", "jk"]);
2251 let out = contains_str_str((&s, 0, s.len()), (&p, 0, p.len()));
2252 assert_eq!(out.get(0), Some(true));
2253 assert_eq!(out.get(1), Some(false));
2254 assert_eq!(out.get(2), Some(true));
2255 }
2256
2257 #[test]
2258 fn test_contains_str_str_chunk() {
2259 let s = str_array::<u32>(&["dummy", "abc", "def", "ghijk"]);
2260 let p = str_array::<u32>(&["dummy", "b", "x", "jk"]);
2261 let out = contains_str_str((&s, 1, 3), (&p, 1, 3));
2262 assert_eq!(out.get(0), Some(true));
2263 assert_eq!(out.get(1), Some(false));
2264 assert_eq!(out.get(2), Some(true));
2265 }
2266
2267 #[test]
2268 fn test_starts_with_str_str() {
2269 let s = str_array::<u32>(&["apricot", "banana", "apple"]);
2270 let p = str_array::<u32>(&["ap", "ba", "a"]);
2271 let out = starts_with_str_str((&s, 0, s.len()), (&p, 0, p.len()));
2272 assert_eq!(out.get(0), Some(true));
2273 assert_eq!(out.get(1), Some(true));
2274 assert_eq!(out.get(2), Some(true));
2275 }
2276
2277 #[test]
2278 fn test_starts_with_str_str_chunk() {
2279 let s = str_array::<u32>(&["dummy", "apricot", "banana", "apple"]);
2280 let p = str_array::<u32>(&["dummy", "ap", "ba", "a"]);
2281 let out = starts_with_str_str((&s, 1, 3), (&p, 1, 3));
2282 assert_eq!(out.get(0), Some(true));
2283 assert_eq!(out.get(1), Some(true));
2284 assert_eq!(out.get(2), Some(true));
2285 }
2286
2287 #[test]
2288 fn test_ends_with_str_str() {
2289 let s = str_array::<u32>(&["robot", "fast", "last"]);
2290 let p = str_array::<u32>(&["ot", "st", "ast"]);
2291 let out = ends_with_str_str((&s, 0, s.len()), (&p, 0, p.len()));
2292 assert_eq!(out.get(0), Some(true));
2293 assert_eq!(out.get(1), Some(true));
2294 assert_eq!(out.get(2), Some(true));
2295 }
2296
2297 #[test]
2298 fn test_ends_with_str_str_chunk() {
2299 let s = str_array::<u32>(&["dummy", "robot", "fast", "last"]);
2300 let p = str_array::<u32>(&["dummy", "ot", "st", "ast"]);
2301 let out = ends_with_str_str((&s, 1, 3), (&p, 1, 3));
2302 assert_eq!(out.get(0), Some(true));
2303 assert_eq!(out.get(1), Some(true));
2304 assert_eq!(out.get(2), Some(true));
2305 }
2306
2307 #[test]
2308 fn test_contains_str_dict() {
2309 let s = str_array::<u32>(&["abcde", "xyz", "qrstuv"]);
2310 let p = dict_array::<u32>(&["c", "z", "tu"]);
2311 let out = contains_str_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2312 assert_eq!(out.get(0), Some(true));
2313 assert_eq!(out.get(1), Some(true));
2314 assert_eq!(out.get(2), Some(true));
2315 }
2316
2317 #[test]
2318 fn test_contains_str_dict_chunk() {
2319 let s = str_array::<u32>(&["dummy", "abcde", "xyz", "qrstuv"]);
2320 let p = dict_array::<u32>(&["dummy", "c", "z", "tu"]);
2321 let out = contains_str_dict((&s, 1, 3), (&p, 1, 3)).unwrap();
2322 assert_eq!(out.get(0), Some(true));
2323 assert_eq!(out.get(1), Some(true));
2324 assert_eq!(out.get(2), Some(true));
2325 }
2326
2327 #[test]
2328 fn test_contains_dict_dict() {
2329 let s = dict_array::<u32>(&["cdef", "foo", "bar"]);
2330 let p = dict_array::<u32>(&["cd", "oo", "baz"]);
2331 let out = contains_dict_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2332 assert_eq!(out.get(0), Some(true));
2333 assert_eq!(out.get(1), Some(true));
2334 assert_eq!(out.get(2), Some(false));
2335 }
2336
2337 #[test]
2338 fn test_contains_dict_dict_chunk() {
2339 let s = dict_array::<u32>(&["dummy", "cdef", "foo", "bar"]);
2340 let p = dict_array::<u32>(&["dummy", "cd", "oo", "baz"]);
2341 let out = contains_dict_dict((&s, 1, 3), (&p, 1, 3)).unwrap();
2342 assert_eq!(out.get(0), Some(true));
2343 assert_eq!(out.get(1), Some(true));
2344 assert_eq!(out.get(2), Some(false));
2345 }
2346
2347 #[test]
2348 fn test_contains_dict_str() {
2349 let s = dict_array::<u32>(&["hello", "world"]);
2350 let p = str_array::<u32>(&["he", "o"]);
2351 let out = contains_dict_str((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2352 assert_eq!(out.get(0), Some(true));
2353 assert_eq!(out.get(1), Some(true));
2354 }
2355
2356 #[test]
2357 fn test_contains_dict_str_chunk() {
2358 let s = dict_array::<u32>(&["dummy", "hello", "world"]);
2359 let p = str_array::<u32>(&["dummy", "he", "o"]);
2360 let out = contains_dict_str((&s, 1, 2), (&p, 1, 2)).unwrap();
2361 assert_eq!(out.get(0), Some(true));
2362 assert_eq!(out.get(1), Some(true));
2363 }
2364
2365 #[test]
2366 fn test_starts_with_str_dict() {
2367 let s = str_array::<u32>(&["abcdef", "foobar", "quux"]);
2368 let p = dict_array::<u32>(&["ab", "foo", "qu"]);
2369 let out = starts_with_str_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2370 assert_eq!(out.get(0), Some(true));
2371 assert_eq!(out.get(1), Some(true));
2372 assert_eq!(out.get(2), Some(true));
2373 }
2374
2375 #[test]
2376 fn test_starts_with_str_dict_chunk() {
2377 let s = str_array::<u32>(&["dummy", "abcdef", "foobar", "quux"]);
2378 let p = dict_array::<u32>(&["dummy", "ab", "foo", "qu"]);
2379 let out = starts_with_str_dict((&s, 1, 3), (&p, 1, 3)).unwrap();
2380 assert_eq!(out.get(0), Some(true));
2381 assert_eq!(out.get(1), Some(true));
2382 assert_eq!(out.get(2), Some(true));
2383 }
2384
2385 #[test]
2386 fn test_starts_with_dict_dict() {
2387 let s = dict_array::<u32>(&["qwerty", "banana"]);
2388 let p = dict_array::<u32>(&["qw", "ban"]);
2389 let out = starts_with_dict_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2390 assert_eq!(out.get(0), Some(true));
2391 assert_eq!(out.get(1), Some(true));
2392 }
2393
2394 #[test]
2395 fn test_starts_with_dict_dict_chunk() {
2396 let s = dict_array::<u32>(&["dummy", "qwerty", "banana"]);
2397 let p = dict_array::<u32>(&["dummy", "qw", "ban"]);
2398 let out = starts_with_dict_dict((&s, 1, 2), (&p, 1, 2)).unwrap();
2399 assert_eq!(out.get(0), Some(true));
2400 assert_eq!(out.get(1), Some(true));
2401 }
2402
2403 #[test]
2404 fn test_ends_with_str_dict() {
2405 let s = str_array::<u32>(&["poem", "dome", "gnome"]);
2406 let p = dict_array::<u32>(&["em", "me", "ome"]);
2407 let out = ends_with_str_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2408 assert_eq!(out.get(0), Some(true));
2409 assert_eq!(out.get(1), Some(true));
2410 assert_eq!(out.get(2), Some(true));
2411 }
2412
2413 #[test]
2414 fn test_ends_with_str_dict_chunk() {
2415 let s = str_array::<u32>(&["dummy", "poem", "dome", "gnome"]);
2416 let p = dict_array::<u32>(&["dummy", "em", "me", "ome"]);
2417 let out = ends_with_str_dict((&s, 1, 3), (&p, 1, 3)).unwrap();
2418 assert_eq!(out.get(0), Some(true));
2419 assert_eq!(out.get(1), Some(true));
2420 assert_eq!(out.get(2), Some(true));
2421 }
2422
2423 #[test]
2424 fn test_ends_with_dict_dict() {
2425 let s = dict_array::<u32>(&["tablet", "let", "bet"]);
2426 let p = dict_array::<u32>(&["let", "et", "xyz"]);
2427 let out = ends_with_dict_dict((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2428 assert_eq!(out.get(0), Some(true));
2429 assert_eq!(out.get(1), Some(true));
2430 assert_eq!(out.get(2), Some(false));
2431 }
2432
2433 #[test]
2434 fn test_ends_with_dict_dict_chunk() {
2435 let s = dict_array::<u32>(&["dummy", "tablet", "let", "bet"]);
2436 let p = dict_array::<u32>(&["dummy", "let", "et", "xyz"]);
2437 let out = ends_with_dict_dict((&s, 1, 3), (&p, 1, 3)).unwrap();
2438 assert_eq!(out.get(0), Some(true));
2439 assert_eq!(out.get(1), Some(true));
2440 assert_eq!(out.get(2), Some(false));
2441 }
2442
2443 #[test]
2446 fn test_len_str() {
2447 let arr = str_array::<u32>(&["", "a", "abc", "bar"]);
2448 let out = len_str((&arr, 0, arr.len())).unwrap();
2449 assert_eq!(&out.data[..], &[0, 1, 3, 3]);
2450 }
2451
2452 #[test]
2453 fn test_len_str_chunk() {
2454 let arr = str_array::<u32>(&["zzz", "", "a", "abc", "bar"]);
2455 let out = len_str((&arr, 1, 4)).unwrap(); assert_eq!(&out.data[..], &[0, 1, 3, 3]);
2457 }
2458
2459 #[test]
2460 fn test_len_dict() {
2461 let arr = dict_array::<u32>(&["", "one", "seven"]);
2462 let out = len_dict((&arr, 0, arr.len())).unwrap();
2463 assert_eq!(&out.data[..], &[0, 3, 5]);
2464 }
2465
2466 #[test]
2467 fn test_len_dict_chunk() {
2468 let arr = dict_array::<u32>(&["q", "", "one", "seven"]);
2469 let out = len_dict((&arr, 1, 3)).unwrap(); assert_eq!(&out.data[..], &[0, 3, 5]);
2471 }
2472
2473 #[test]
2474 fn test_contains_empty_pattern() {
2475 let s = str_array::<u32>(&["foo", "bar"]);
2476 let p = str_array::<u32>(&["", ""]);
2477 let out = contains_str_str((&s, 0, s.len()), (&p, 0, p.len()));
2478 assert_eq!(out.get(0), Some(false));
2480 assert_eq!(out.get(1), Some(false));
2481 assert!(out.null_mask.as_ref().is_none());
2482 }
2483
2484 #[test]
2485 fn test_contains_empty_pattern_chunk() {
2486 let s = str_array::<u32>(&["z", "foo", "bar"]);
2487 let p = str_array::<u32>(&["z", "", ""]);
2488 let out = contains_str_str((&s, 1, 2), (&p, 1, 2));
2489 assert_eq!(out.get(0), Some(false));
2490 assert_eq!(out.get(1), Some(false));
2491 assert!(out.null_mask.as_ref().is_none());
2492 }
2493
2494 #[test]
2495 fn test_contains_str_str_nulls_on_pattern() {
2496 let mut s = str_array::<u32>(&["abc", "def"]);
2497 s.null_mask = Some(bm(&[true, true]));
2498 let mut p = str_array::<u32>(&["b", "e"]);
2499 p.null_mask = Some(bm(&[true, false])); let out = contains_str_str((&s, 0, s.len()), (&p, 0, p.len()));
2501 assert_eq!(out.get(0), Some(true));
2502 assert_eq!(out.get(1), Some(false));
2503 }
2504
2505 #[test]
2506 fn test_contains_str_str_nulls_on_pattern_chunk() {
2507 let mut s = str_array::<u32>(&["X", "abc", "def"]);
2508 s.null_mask = Some(bm(&[true, true, true]));
2509 let mut p = str_array::<u32>(&["X", "b", "e"]);
2510 p.null_mask = Some(bm(&[true, true, false])); let out = contains_str_str((&s, 1, 2), (&p, 1, 2));
2512 assert_eq!(out.get(0), Some(true));
2513 assert_eq!(out.get(1), Some(false));
2514 }
2515
2516 #[cfg(feature = "regex")]
2517 #[test]
2518 fn test_regex_invalid_pattern_returns_err() {
2519 let s = str_array::<u32>(&["abc"]);
2520 let p = str_array::<u32>(&["["]);
2521 let err = regex_str_str((&s, 0, s.len()), (&p, 0, p.len())).unwrap_err();
2522 match err {
2523 KernelError::InvalidArguments(_) => {}
2524 _ => panic!("expected InvalidArguments"),
2525 }
2526 }
2527
2528 #[cfg(feature = "regex")]
2529 #[test]
2530 fn test_regex_invalid_pattern_returns_err_chunk() {
2531 let s = str_array::<u32>(&["foo", "abc"]);
2532 let p = str_array::<u32>(&["bar", "["]);
2533 let err = regex_str_str((&s, 1, 1), (&p, 1, 1)).unwrap_err();
2534 match err {
2535 KernelError::InvalidArguments(_) => {}
2536 _ => panic!("expected InvalidArguments"),
2537 }
2538 }
2539
2540 #[cfg(feature = "regex")]
2541 #[test]
2542 fn test_regex_empty_pattern_always_false() {
2543 let s = str_array::<u32>(&["abc", "def"]);
2544 let p = str_array::<u32>(&["", ""]);
2545 let out = regex_str_str((&s, 0, s.len()), (&p, 0, p.len())).unwrap();
2546 assert_eq!(out.get(0), Some(false));
2547 assert_eq!(out.get(1), Some(false));
2548 assert!(out.null_mask.unwrap().all_set());
2549 }
2550
2551 #[cfg(feature = "regex")]
2552 #[test]
2553 fn test_regex_empty_pattern_always_false_chunk() {
2554 let s = str_array::<u32>(&["z", "abc", "def"]);
2555 let p = str_array::<u32>(&["z", "", ""]);
2556 let out = regex_str_str((&s, 1, 2), (&p, 1, 2)).unwrap();
2557 assert_eq!(out.get(0), Some(false));
2558 assert_eq!(out.get(1), Some(false));
2559 assert!(out.null_mask.unwrap().all_set());
2560 }
2561
2562 #[test]
2563 fn test_len_str_with_nulls() {
2564 let mut arr = str_array::<u32>(&["foo", "", "bar"]);
2565 arr.null_mask = Some(bm(&[true, false, true]));
2566 let len_arr = len_str((&arr, 0, arr.len())).unwrap();
2567 assert_eq!(len_arr.data.as_slice(), &[3, 0, 3]);
2568 assert_eq!(
2569 len_arr.null_mask.unwrap().as_slice(),
2570 bm(&[true, false, true]).as_slice()
2571 );
2572 }
2573
2574 #[test]
2575 fn test_len_str_with_nulls_chunk() {
2576 let mut arr = str_array::<u32>(&["x", "foo", "", "bar"]);
2577 arr.null_mask = Some(bm(&[true, true, false, true]));
2578 let len_arr = len_str((&arr, 1, 3)).unwrap();
2579 assert_eq!(len_arr.data.as_slice(), &[3, 0, 3]);
2580 assert_eq!(
2581 len_arr.null_mask.unwrap().as_slice(),
2582 bm(&[true, false, true]).as_slice()
2583 );
2584 }
2585
2586 #[test]
2587 fn test_len_dict_with_nulls() {
2588 let mut arr = dict_array::<u32>(&["x", "yy", "zzz"]);
2589 arr.null_mask = Some(bm(&[false, true, true]));
2590 let len_arr = len_dict((&arr, 0, arr.len())).unwrap();
2591 assert_eq!(len_arr.data.as_slice(), &[0, 2, 3]);
2592 assert_eq!(
2593 len_arr.null_mask.unwrap().as_slice(),
2594 bm(&[false, true, true]).as_slice()
2595 );
2596 }
2597
2598 #[test]
2599 fn test_len_dict_with_nulls_chunk() {
2600 let mut arr = dict_array::<u32>(&["z", "x", "yy", "zzz"]);
2601 arr.null_mask = Some(bm(&[true, false, true, true]));
2602 let len_arr = len_dict((&arr, 1, 3)).unwrap();
2603 assert_eq!(len_arr.data.as_slice(), &[0, 2, 3]);
2604 assert_eq!(
2605 len_arr.null_mask.unwrap().as_slice(),
2606 bm(&[false, true, true]).as_slice()
2607 );
2608 }
2609
2610 fn bitmask_from_vec(v: &[bool]) -> Bitmask {
2611 let mut bm = Bitmask::with_capacity(v.len());
2612 for (i, &b) in v.iter().enumerate() {
2613 bm.set(i, b);
2614 }
2615 bm
2616 }
2617
2618 #[test]
2619 fn test_min_string_array_all_valid() {
2620 let arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2621 let view = (&arr, 0, arr.len());
2622 let result = min_string_array::<u32>(view);
2623 assert_eq!(result, Some("alpha".to_string()));
2624 }
2625
2626 #[test]
2627 fn test_min_string_array_with_nulls() {
2628 let mut arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2629 arr.null_mask = Some(bitmask_from_vec(&[false, true, true, true]));
2630 let view = (&arr, 0, arr.len());
2631 let result = min_string_array::<u32>(view);
2632 assert_eq!(result, Some("alpha".to_string()));
2633 }
2634
2635 #[test]
2636 fn test_min_string_array_all_null() {
2637 let arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2638 let mut null_mask = Bitmask::with_capacity(arr.len());
2639 for i in 0..arr.len() {
2640 null_mask.set(i, false);
2641 }
2642 let arr = StringArray::<u32> {
2643 null_mask: Some(null_mask),
2644 ..arr
2645 };
2646 let view = (&arr, 0, arr.len());
2647 let result = min_string_array::<u32>(view);
2648 assert_eq!(result, None);
2649 }
2650
2651 #[test]
2652 fn test_max_string_array_all_valid() {
2653 let arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2654 let view = (&arr, 0, arr.len());
2655 let result = max_string_array::<u32>(view);
2656 assert_eq!(result, Some("zulu".to_string()));
2657 }
2658
2659 #[test]
2660 fn test_max_string_array_with_nulls() {
2661 let mut arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2662 arr.null_mask = Some(bitmask_from_vec(&[true, false, true, false]));
2663 let view = (&arr, 0, arr.len());
2664 let result = max_string_array::<u32>(view);
2665 assert_eq!(result, Some("zulu".to_string()));
2666 }
2667
2668 #[test]
2669 fn test_max_string_array_all_null() {
2670 let arr = StringArray::<u32>::from_slice(&["zulu", "alpha", "echo", "bravo"]);
2671 let mut null_mask = Bitmask::with_capacity(arr.len());
2672 for i in 0..arr.len() {
2673 null_mask.set(i, false);
2674 }
2675 let arr = StringArray::<u32> {
2676 null_mask: Some(null_mask),
2677 ..arr
2678 };
2679 let view = (&arr, 0, arr.len());
2680 let result = max_string_array::<u32>(view);
2681 assert_eq!(result, None);
2682 }
2683
2684 #[test]
2685 fn test_min_categorical_array() {
2686 let uniques = vec64![
2687 "dog".to_string(),
2688 "zebra".to_string(),
2689 "ant".to_string(),
2690 "bee".to_string()
2691 ];
2692 let indices = vec64![1u32, 0, 3, 2]; let cat = CategoricalArray {
2694 data: indices.clone().into(),
2695 unique_values: uniques.clone().into(),
2696 null_mask: None,
2697 };
2698 let result = min_categorical_array((&cat, 0, indices.len()));
2699 assert_eq!(result, Some("ant".to_string()));
2700 }
2701
2702 #[test]
2703 fn test_max_categorical_array() {
2704 let uniques = vec64![
2705 "dog".to_string(),
2706 "zebra".to_string(),
2707 "ant".to_string(),
2708 "bee".to_string()
2709 ];
2710 let indices = vec64![2u32, 0, 1, 3]; let cat = CategoricalArray {
2712 data: indices.clone().into(),
2713 unique_values: uniques.clone().into(),
2714 null_mask: None,
2715 };
2716 let result = max_categorical_array((&cat, 0, indices.len()));
2717 assert_eq!(result, Some("zebra".to_string()));
2718 }
2719
2720 #[test]
2721 fn test_min_categorical_array_with_nulls() {
2722 let uniques = vec64!["dog".to_string(), "zebra".to_string(), "ant".to_string()];
2723 let indices = vec64![1u32, 2, 0];
2724 let mut null_mask = Bitmask::with_capacity(indices.len());
2725 null_mask.set(0, true);
2726 null_mask.set(1, false);
2727 null_mask.set(2, true);
2728 let cat = CategoricalArray {
2729 data: indices.clone().into(),
2730 unique_values: uniques.clone().into(),
2731 null_mask: Some(null_mask),
2732 };
2733 let result = min_categorical_array((&cat, 0, indices.len()));
2734 assert_eq!(result, Some("dog".to_string())); }
2736
2737 #[test]
2738 fn test_max_categorical_array_with_nulls() {
2739 let uniques = vec64!["dog".to_string(), "zebra".to_string(), "ant".to_string()];
2740 let indices = vec64![1u32, 2, 0];
2741 let mut null_mask = Bitmask::with_capacity(indices.len());
2742 null_mask.set(0, true);
2743 null_mask.set(1, false);
2744 null_mask.set(2, true);
2745 let cat = CategoricalArray {
2746 data: indices.clone().into(),
2747 unique_values: uniques.clone().into(),
2748 null_mask: Some(null_mask),
2749 };
2750 let result = max_categorical_array((&cat, 0, indices.len()));
2751 assert_eq!(result, Some("zebra".to_string())); }
2753
2754 #[test]
2757 fn test_to_uppercase_str() {
2758 let a = str_array::<u32>(&["hello", "World", "FOO"]);
2759 let out = to_uppercase_str((&a, 0, a.len())).unwrap();
2760 assert_eq!(out.get(0), Some("HELLO"));
2761 assert_eq!(out.get(1), Some("WORLD"));
2762 assert_eq!(out.get(2), Some("FOO"));
2763 }
2764
2765 #[test]
2766 fn test_to_uppercase_str_chunk() {
2767 let a = str_array::<u32>(&["skip", "hello", "world", "skip"]);
2768 let out = to_uppercase_str((&a, 1, 2)).unwrap();
2769 assert_eq!(out.get(0), Some("HELLO"));
2770 assert_eq!(out.get(1), Some("WORLD"));
2771 }
2772
2773 #[test]
2774 fn test_to_uppercase_dict() {
2775 let a = dict_array::<u32>(&["hello", "world"]);
2776 let out = to_uppercase_dict((&a, 0, a.data.len())).unwrap();
2777 assert_eq!(out.get_str(0), Some("HELLO"));
2778 assert_eq!(out.get_str(1), Some("WORLD"));
2779 }
2780
2781 #[test]
2782 fn test_to_lowercase_str() {
2783 let a = str_array::<u32>(&["HELLO", "World"]);
2784 let out = to_lowercase_str((&a, 0, a.len())).unwrap();
2785 assert_eq!(out.get(0), Some("hello"));
2786 assert_eq!(out.get(1), Some("world"));
2787 }
2788
2789 #[test]
2790 fn test_trim_str() {
2791 let a = str_array::<u32>(&[" hello ", "world ", " foo"]);
2792 let out = trim_str((&a, 0, a.len())).unwrap();
2793 assert_eq!(out.get(0), Some("hello"));
2794 assert_eq!(out.get(1), Some("world"));
2795 assert_eq!(out.get(2), Some("foo"));
2796 }
2797
2798 #[test]
2799 fn test_ltrim_str() {
2800 let a = str_array::<u32>(&[" hello", " world "]);
2801 let out = ltrim_str((&a, 0, a.len())).unwrap();
2802 assert_eq!(out.get(0), Some("hello"));
2803 assert_eq!(out.get(1), Some("world "));
2804 }
2805
2806 #[test]
2807 fn test_rtrim_str() {
2808 let a = str_array::<u32>(&["hello ", " world "]);
2809 let out = rtrim_str((&a, 0, a.len())).unwrap();
2810 assert_eq!(out.get(0), Some("hello"));
2811 assert_eq!(out.get(1), Some(" world"));
2812 }
2813
2814 #[test]
2815 fn test_reverse_str() {
2816 let a = str_array::<u32>(&["abc", "hello"]);
2817 let out = reverse_str((&a, 0, a.len())).unwrap();
2818 assert_eq!(out.get(0), Some("cba"));
2819 assert_eq!(out.get(1), Some("olleh"));
2820 }
2821
2822 #[test]
2823 fn test_reverse_dict() {
2824 let a = dict_array::<u32>(&["abc", "xy"]);
2825 let out = reverse_dict((&a, 0, a.data.len())).unwrap();
2826 assert_eq!(out.get_str(0), Some("cba"));
2827 assert_eq!(out.get_str(1), Some("yx"));
2828 }
2829
2830 #[test]
2833 fn test_byte_length_str() {
2834 let a = str_array::<u32>(&["hi", "café"]);
2835 let out = byte_length_str((&a, 0, a.len())).unwrap();
2836 assert_eq!(out.data[0], 2u32); assert_eq!(out.data[1], 5u32); }
2839
2840 #[test]
2841 fn test_byte_length_dict() {
2842 let a = dict_array::<u32>(&["hi", "café"]);
2843 let out = byte_length_dict((&a, 0, a.data.len())).unwrap();
2844 assert_eq!(out.data[0], 2u32);
2845 assert_eq!(out.data[1], 5u32);
2846 }
2847
2848 #[test]
2851 fn test_find_str() {
2852 let a = str_array::<u32>(&["hello world", "foo bar", "baz"]);
2853 let out = find_str((&a, 0, a.len()), "world").unwrap();
2854 assert_eq!(out.data[0], 6);
2855 assert_eq!(out.data[1], -1);
2856 assert_eq!(out.data[2], -1);
2857 }
2858
2859 #[test]
2860 fn test_find_dict() {
2861 let a = dict_array::<u32>(&["hello", "world"]);
2862 let out = find_dict((&a, 0, a.data.len()), "llo").unwrap();
2863 assert_eq!(out.data[0], 2);
2864 assert_eq!(out.data[1], -1);
2865 }
2866
2867 #[test]
2868 fn test_count_match_str() {
2869 let a = str_array::<u32>(&["abcabc", "abc", "xyz"]);
2870 let out = count_match_str((&a, 0, a.len()), "abc").unwrap();
2871 assert_eq!(out.data[0], 2);
2872 assert_eq!(out.data[1], 1);
2873 assert_eq!(out.data[2], 0);
2874 }
2875
2876 #[test]
2877 fn test_count_match_dict() {
2878 let a = dict_array::<u32>(&["aaa", "a"]);
2879 let out = count_match_dict((&a, 0, a.data.len()), "a").unwrap();
2880 assert_eq!(out.data[0], 3);
2881 assert_eq!(out.data[1], 1);
2882 }
2883
2884 #[test]
2887 fn test_substring_str() {
2888 let a = str_array::<u32>(&["hello world", "foo"]);
2889 let out = substring_str((&a, 0, a.len()), 6, None).unwrap();
2890 assert_eq!(out.get(0), Some("world"));
2891 assert_eq!(out.get(1), Some(""));
2892 }
2893
2894 #[test]
2895 fn test_substring_str_with_len() {
2896 let a = str_array::<u32>(&["hello world"]);
2897 let out = substring_str((&a, 0, a.len()), 0, Some(5)).unwrap();
2898 assert_eq!(out.get(0), Some("hello"));
2899 }
2900
2901 #[test]
2902 fn test_substring_dict() {
2903 let a = dict_array::<u32>(&["hello", "world"]);
2904 let out = substring_dict((&a, 0, a.data.len()), 1, Some(3)).unwrap();
2905 assert_eq!(out.get_str(0), Some("ell"));
2906 assert_eq!(out.get_str(1), Some("orl"));
2907 }
2908
2909 #[test]
2912 fn test_replace_str() {
2913 let a = str_array::<u32>(&["hello world", "foo bar foo"]);
2914 let out = replace_str((&a, 0, a.len()), "foo", "baz").unwrap();
2915 assert_eq!(out.get(0), Some("hello world"));
2916 assert_eq!(out.get(1), Some("baz bar baz"));
2917 }
2918
2919 #[test]
2920 fn test_replace_dict() {
2921 let a = dict_array::<u32>(&["aXb", "cXd"]);
2922 let out = replace_dict((&a, 0, a.data.len()), "X", "Y").unwrap();
2923 assert_eq!(out.get_str(0), Some("aYb"));
2924 assert_eq!(out.get_str(1), Some("cYd"));
2925 }
2926
2927 #[test]
2930 fn test_repeat_str() {
2931 let a = str_array::<u32>(&["ab", "x"]);
2932 let out = repeat_str((&a, 0, a.len()), 3).unwrap();
2933 assert_eq!(out.get(0), Some("ababab"));
2934 assert_eq!(out.get(1), Some("xxx"));
2935 }
2936
2937 #[test]
2938 fn test_repeat_dict() {
2939 let a = dict_array::<u32>(&["ha"]);
2940 let out = repeat_dict((&a, 0, a.data.len()), 2).unwrap();
2941 assert_eq!(out.get_str(0), Some("haha"));
2942 }
2943
2944 #[test]
2947 fn test_pad_str_left() {
2948 let a = str_array::<u32>(&["hi", "hello"]);
2949 let out = pad_str((&a, 0, a.len()), 5, ' ', PadSide::Left).unwrap();
2950 assert_eq!(out.get(0), Some(" hi"));
2951 assert_eq!(out.get(1), Some("hello")); }
2953
2954 #[test]
2955 fn test_pad_str_right() {
2956 let a = str_array::<u32>(&["hi"]);
2957 let out = pad_str((&a, 0, a.len()), 5, '-', PadSide::Right).unwrap();
2958 assert_eq!(out.get(0), Some("hi---"));
2959 }
2960
2961 #[test]
2962 fn test_pad_str_both() {
2963 let a = str_array::<u32>(&["hi"]);
2964 let out = pad_str((&a, 0, a.len()), 6, '*', PadSide::Both).unwrap();
2965 assert_eq!(out.get(0), Some("**hi**"));
2966 }
2967
2968 #[test]
2969 fn test_pad_dict() {
2970 let a = dict_array::<u32>(&["x"]);
2971 let out = pad_dict((&a, 0, a.data.len()), 3, '0', PadSide::Left).unwrap();
2972 assert_eq!(out.get_str(0), Some("00x"));
2973 }
2974
2975 #[test]
2978 fn test_join_str() {
2979 let a = str_array::<u32>(&["a", "b", "c"]);
2980 assert_eq!(join_str((&a, 0, a.len()), ", "), Some("a, b, c".to_owned()));
2981 }
2982
2983 #[test]
2984 fn test_join_str_chunk() {
2985 let a = str_array::<u32>(&["skip", "a", "b", "skip"]);
2986 assert_eq!(join_str((&a, 1, 2), "-"), Some("a-b".to_owned()));
2987 }
2988
2989 #[test]
2990 fn test_join_dict() {
2991 let a = dict_array::<u32>(&["x", "y"]);
2992 assert_eq!(join_dict((&a, 0, a.data.len()), "+"), Some("x+y".to_owned()));
2993 }
2994
2995 #[cfg(feature = "regex")]
2998 #[test]
2999 fn test_regex_replace_str() {
3000 let a = str_array::<u32>(&["abc123def456", "no digits"]);
3001 let out = regex_replace_str((&a, 0, a.len()), r"\d+", "N").unwrap();
3002 assert_eq!(out.get(0), Some("abcNdefN"));
3003 assert_eq!(out.get(1), Some("no digits"));
3004 }
3005
3006 #[cfg(feature = "regex")]
3007 #[test]
3008 fn test_regex_replace_dict() {
3009 let a = dict_array::<u32>(&["foo123"]);
3010 let out = regex_replace_dict((&a, 0, a.data.len()), r"\d+", "").unwrap();
3011 assert_eq!(out.get_str(0), Some("foo"));
3012 }
3013
3014 #[test]
3017 fn test_to_uppercase_str_with_nulls() {
3018 let mut a = str_array::<u32>(&["hello", "skip", "world"]);
3019 a.null_mask = Some(bm(&[true, false, true]));
3020 let out = to_uppercase_str((&a, 0, a.len())).unwrap();
3021 assert_eq!(out.get(0), Some("HELLO"));
3022 assert!(!out.null_mask.as_ref().unwrap().get(1));
3023 assert_eq!(out.get(2), Some("WORLD"));
3024 }
3025
3026 #[test]
3027 fn test_find_str_with_nulls() {
3028 let mut a = str_array::<u32>(&["hello", "skip"]);
3029 a.null_mask = Some(bm(&[true, false]));
3030 let out = find_str((&a, 0, a.len()), "ello").unwrap();
3031 assert_eq!(out.data[0], 1);
3032 assert_eq!(out.data[1], 0);
3033 assert!(!out.null_mask.as_ref().unwrap().get(1));
3034 }
3035
3036 #[test]
3037 fn test_join_str_with_nulls() {
3038 let mut a = str_array::<u32>(&["a", "SKIP", "b"]);
3039 a.null_mask = Some(bm(&[true, false, true]));
3040 assert_eq!(join_str((&a, 0, a.len()), ","), Some("a,b".to_owned()));
3041 }
3042}