1include!(concat!(env!("OUT_DIR"), "/simd_lanes.rs"));
33
34#[cfg(feature = "fast_hash")]
35use ahash::AHashMap;
36#[cfg(feature = "str_arithmetic")]
37use core::ptr::copy_nonoverlapping;
38#[cfg(not(feature = "fast_hash"))]
39use std::collections::HashMap;
40
41use crate::enums::error::{KernelError, log_length_mismatch};
42use crate::kernels::bitmask::merge_bitmasks_to_new;
43use crate::structs::variants::categorical::CategoricalArray;
44#[cfg(feature = "str_arithmetic")]
45use memchr::memmem::Finder;
46
47use crate::structs::variants::string::StringArray;
48use crate::traits::type_unions::Integer;
49use crate::{Bitmask, Vec64};
50#[cfg(feature = "str_arithmetic")]
51use num_traits::ToPrimitive;
52
53use crate::enums::operators::ArithmeticOperator::{self};
54#[cfg(feature = "str_arithmetic")]
55use crate::utils::{
56 confirm_mask_capacity, estimate_categorical_cardinality, estimate_string_cardinality,
57};
58use crate::{CategoricalAVT, StringAVTExt};
59#[cfg(feature = "str_arithmetic")]
60use crate::{MaskedArray, StringAVT};
61
62#[cfg(feature = "str_arithmetic")]
63use ryu::Float;
64#[cfg(feature = "str_arithmetic")]
65use std::mem::MaybeUninit;
66
67pub const STRING_MULTIPLICATION_LIMIT: usize = 1_000_000;
72
73pub const MAX_DICT_CHECK: usize = 256;
79
80pub fn apply_str_num<T, N, O>(
89 lhs: StringAVTExt<T>,
90 rhs: &[N],
91 op: ArithmeticOperator,
92) -> Result<StringArray<O>, KernelError>
93where
94 T: Integer,
95 N: num_traits::ToPrimitive + Copy,
96 O: Integer + num_traits::NumCast,
97{
98 let (array, offset, logical_len, physical_bytes_len) = lhs;
99
100 if logical_len != rhs.len() {
101 return Err(KernelError::LengthMismatch(log_length_mismatch(
102 "apply_str_num".to_string(),
103 logical_len,
104 rhs.len(),
105 )));
106 }
107
108 let lhs_mask = array.null_mask.as_ref();
110 let mut out_mask = lhs_mask.map(|_| crate::Bitmask::new_set_all(logical_len, true));
111
112 let mut offsets = Vec64::<O>::with_capacity(logical_len + 1);
113 offsets.push(O::zero()); let estimated_bytes = physical_bytes_len.min(STRING_MULTIPLICATION_LIMIT * logical_len);
116 let mut data = Vec64::with_capacity(estimated_bytes);
117
118 for (out_idx, i) in (offset..offset + logical_len).enumerate() {
119 let valid = lhs_mask.map_or(true, |mask| unsafe { mask.get_unchecked(i) });
120
121 if let Some(mask) = &mut out_mask {
122 unsafe { mask.set_unchecked(out_idx, valid) };
123 }
124
125 if valid {
126 let s = unsafe { array.get_str_unchecked(i) };
127 let n = rhs[out_idx].to_usize().unwrap_or(0);
128
129 match op {
130 ArithmeticOperator::Multiply => {
131 let count = n.min(STRING_MULTIPLICATION_LIMIT);
132 for _ in 0..count {
133 data.extend_from_slice(s.as_bytes());
134 }
135 }
136 _ => {
137 data.extend_from_slice(s.as_bytes());
138 }
139 }
140 }
141
142 let new_offset = O::from(data.len()).expect("offset conversion overflow");
145 offsets.push(new_offset);
146 }
147
148 Ok(StringArray {
149 offsets: offsets.into(),
150 data: data.into(),
151 null_mask: out_mask,
152 })
153}
154
155#[cfg(feature = "str_arithmetic")]
191pub fn apply_str_float<T, F>(
192 lhs: StringAVT<T>,
193 rhs: &[F],
194 op: ArithmeticOperator,
195) -> Result<StringArray<T>, KernelError>
196where
197 T: Integer,
198 F: Into<f64> + Copy + ryu::Float,
199{
200 let (array, offset, logical_len) = lhs;
202
203 use std::mem::MaybeUninit;
206 if rhs.len() != logical_len {
207 return Err(KernelError::LengthMismatch(log_length_mismatch(
208 "apply_str_float".into(),
209 logical_len,
210 rhs.len(),
211 )));
212 }
213 let lhs_mask = &array.null_mask;
214 let _ = confirm_mask_capacity(array.len(), lhs_mask.as_ref())?;
215
216 let mut total_bytes = 0usize;
218 let mut fmt_buf: [MaybeUninit<u8>; 24] = unsafe { MaybeUninit::uninit().assume_init() };
219
220 for (out_idx, i) in (offset..offset + logical_len).enumerate() {
221 if !lhs_mask
222 .as_ref()
223 .map_or(true, |m| unsafe { m.get_unchecked(i) })
224 {
225 continue;
226 }
227
228 let src_len = {
230 let a = array.offsets[i].to_usize();
231 let b = array.offsets[i + 1].to_usize();
232 b - a
233 };
234 let n_s = format_finite(&mut fmt_buf, rhs[out_idx]);
235 total_bytes += match op {
236 ArithmeticOperator::Add => src_len + n_s.len(),
237 ArithmeticOperator::Subtract => src_len,
238 ArithmeticOperator::Multiply => {
239 let times =
240 rhs[out_idx].into().round().abs() as usize % (STRING_MULTIPLICATION_LIMIT + 1);
241 src_len * times
242 }
243 ArithmeticOperator::Divide => {
244 let pat_len = n_s.len();
245 let splits = (src_len + pat_len).saturating_sub(1) / pat_len;
246 src_len + splits
247 }
248 _ => {
249 return Err(KernelError::UnsupportedType(format!(
250 "Unsupported {:?}",
251 op
252 )));
253 }
254 };
255 }
256
257 let mut offsets = Vec64::<T>::with_capacity(logical_len + 1);
259
260 let mut data = Vec64::<u8>::with_capacity(total_bytes);
262 unsafe {
263 offsets.set_len(logical_len + 1);
264 data.set_len(total_bytes);
265 }
266
267 let mut out_mask = lhs_mask
268 .as_ref()
269 .map(|_| Bitmask::new_set_all(logical_len, false));
270
271 let mut cursor = 0usize;
272 offsets[0] = T::zero();
273
274 for (out_idx, i) in (offset..offset + logical_len).enumerate() {
275 let valid = lhs_mask
276 .as_ref()
277 .map_or(true, |m| unsafe { m.get_unchecked(i) });
278 if let Some(mask) = &mut out_mask {
279 unsafe { mask.set_unchecked(out_idx, valid) };
280 }
281
282 if !valid {
283 offsets[out_idx + 1] = T::from(cursor).unwrap();
284 continue;
285 }
286
287 let start = array.offsets[i].to_usize();
288 let end = array.offsets[i + 1].to_usize();
289 let src = &array.data[start..end];
290 let n_s = format_finite(&mut fmt_buf, rhs[out_idx]);
291 let pat = n_s.as_bytes();
292
293 let mut write = |bytes: &[u8]| unsafe {
294 copy_nonoverlapping(bytes.as_ptr(), data.as_mut_ptr().add(cursor), bytes.len());
295 cursor += bytes.len();
296 };
297
298 match op {
299 ArithmeticOperator::Add => {
300 write(src);
301 write(pat);
302 }
303 ArithmeticOperator::Subtract => {
304 if let Some(idx) = Finder::new(pat).find(src) {
305 write(&src[..idx]);
306 write(&src[(idx + pat.len())..]);
307 } else {
308 write(src);
309 }
310 }
311 ArithmeticOperator::Multiply => {
312 let times =
313 rhs[out_idx].into().round().abs() as usize % (STRING_MULTIPLICATION_LIMIT + 1);
314 for _ in 0..times {
315 write(src);
316 }
317 }
318 ArithmeticOperator::Divide => {
319 let finder = Finder::new(pat);
320 let mut start_pos = 0;
321 let mut first = true;
322 while let Some(idx) = finder.find(&src[start_pos..]) {
323 if !first {
324 data[cursor] = b'|';
325 cursor += 1;
326 }
327 let rel_idx = idx;
328 let segment = &src[start_pos..start_pos + rel_idx];
329 unsafe {
330 copy_nonoverlapping(
331 segment.as_ptr(),
332 data.as_mut_ptr().add(cursor),
333 segment.len(),
334 );
335 cursor += segment.len();
336 }
337 start_pos += rel_idx + pat.len();
338 first = false;
339 }
340 if !first {
341 data[cursor] = b'|';
342 cursor += 1;
343 }
344 let tail = &src[start_pos..];
345 unsafe {
346 copy_nonoverlapping(tail.as_ptr(), data.as_mut_ptr().add(cursor), tail.len());
347 cursor += tail.len();
348 }
349 }
350 _ => unreachable!(),
351 }
352 offsets[out_idx + 1] = T::from(cursor).unwrap();
353 }
354
355 Ok(StringArray {
358 offsets: offsets.into(),
359 data: data.into(),
360 null_mask: out_mask,
361 })
362}
363
364#[cfg(feature = "fast_hash")]
367#[inline(always)]
368fn intern<T: Integer>(s: &str, dict: &mut AHashMap<String, T>, uniq: &mut Vec64<String>) -> T {
369 if let Some(&code) = dict.get(s) {
370 code
371 } else {
372 let idx = T::from_usize(uniq.len());
373 uniq.push(s.to_owned());
374 dict.insert(s.to_owned(), idx);
375 idx
376 }
377}
378
379#[cfg(not(feature = "fast_hash"))]
382#[inline(always)]
383fn intern<T: Integer>(s: &str, dict: &mut HashMap<String, T>, uniq: &mut Vec64<String>) -> T {
384 if let Some(&code) = dict.get(s) {
385 code
386 } else {
387 let idx = T::from_usize(uniq.len());
388 uniq.push(s.to_owned());
389 dict.insert(s.to_owned(), idx);
390 idx
391 }
392}
393
394fn apply_dict_dict_impl<T: Integer>(
425 lhs: CategoricalAVT<T>,
426 rhs: CategoricalAVT<T>,
427 op: ArithmeticOperator,
428) -> Result<CategoricalArray<T>, KernelError> {
429 let (lhs_array, lhs_offset, lhs_logical_len) = lhs;
431 let (rhs_array, rhs_offset, rhs_logical_len) = rhs;
432
433 if lhs_logical_len != rhs_logical_len {
434 return Err(KernelError::LengthMismatch(log_length_mismatch(
435 "apply_dict_dict_impl".into(),
436 lhs_logical_len,
437 rhs_logical_len,
438 )));
439 }
440
441 let in_mask = merge_bitmasks_to_new(
443 lhs_array.null_mask.as_ref(),
444 rhs_array.null_mask.as_ref(),
445 lhs_logical_len,
446 );
447
448 let mut uniq: Vec64<String> = Vec64::with_capacity(
450 lhs_array.unique_values.len() + rhs_array.unique_values.len() + lhs_logical_len,
451 );
452
453 #[cfg(feature = "fast_hash")]
454 let mut dict: AHashMap<String, T> = AHashMap::with_capacity(uniq.capacity());
455
456 #[cfg(not(feature = "fast_hash"))]
457 let mut dict: HashMap<String, T> = HashMap::with_capacity(uniq.capacity());
458
459 for v in lhs_array
460 .unique_values
461 .iter()
462 .chain(rhs_array.unique_values.iter())
463 {
464 if !dict.contains_key(v) {
465 let idx = T::from_usize(uniq.len());
466 uniq.push(v.clone());
467 dict.insert(uniq.last().unwrap().clone(), idx);
468 }
469 }
470
471 let empty_code = *dict.entry("".to_owned()).or_insert_with(|| {
473 let idx = T::from_usize(uniq.len());
474 uniq.push("".to_owned());
475 idx
476 });
477
478 let mut total_out = 0usize;
480 for local_idx in 0..lhs_logical_len {
481 let i = lhs_offset + local_idx;
482 let j = rhs_offset + local_idx;
483 let valid = in_mask
484 .as_ref()
485 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
486 if !valid {
487 total_out += 1;
488 } else if let ArithmeticOperator::Divide = op {
489 let l = unsafe { lhs_array.get_str_unchecked(i) };
490 let r = unsafe { rhs_array.get_str_unchecked(j) };
491 if r.is_empty() {
492 total_out += 1;
493 } else {
494 let mut parts = 0;
495 let mut start = 0;
496 while let Some(pos) = l[start..].find(r) {
497 parts += 1;
498 start += pos + r.len();
499 }
500 total_out += parts + 1;
501 }
502 } else {
503 total_out += 1;
504 }
505 }
506
507 let mut out_data = Vec64::with_capacity(total_out);
509 unsafe {
510 out_data.set_len(total_out);
511 }
512 let mut out_mask = Bitmask::new_set_all(total_out, false);
513
514 let mut write_ptr = 0;
516 for local_idx in 0..lhs_logical_len {
517 let i = lhs_offset + local_idx;
518 let j = rhs_offset + local_idx;
519 let valid = in_mask
520 .as_ref()
521 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
522
523 if !valid {
524 out_data.push(empty_code);
525 unsafe { out_mask.set_unchecked(write_ptr, false) };
526 write_ptr += 1;
527 continue;
528 }
529
530 let l = unsafe { lhs_array.get_str_unchecked(i) };
531 let r = unsafe { rhs_array.get_str_unchecked(j) };
532
533 match op {
534 ArithmeticOperator::Add => {
535 let mut tmp = String::with_capacity(l.len() + r.len());
536 tmp.push_str(l);
537 tmp.push_str(r);
538 let code = intern(&tmp, &mut dict, &mut uniq);
539 unsafe {
540 *out_data.get_unchecked_mut(write_ptr) = code;
541 }
542 out_mask.set(write_ptr, true);
543 write_ptr += 1;
544 }
545 ArithmeticOperator::Subtract => {
546 let result = if r.is_empty() {
547 l.to_owned()
548 } else if let Some(pos) = l.find(r) {
549 let mut tmp = String::with_capacity(l.len() - r.len());
550 tmp.push_str(&l[..pos]);
551 tmp.push_str(&l[pos + r.len()..]);
552 tmp
553 } else {
554 l.to_owned()
555 };
556 let code = intern(&result, &mut dict, &mut uniq);
557 unsafe {
558 *out_data.get_unchecked_mut(write_ptr) = code;
559 }
560 out_mask.set(write_ptr, true);
561 write_ptr += 1;
562 }
563 ArithmeticOperator::Multiply => {
564 let code = intern(l, &mut dict, &mut uniq);
565 unsafe {
566 *out_data.get_unchecked_mut(write_ptr) = code;
567 }
568 out_mask.set(write_ptr, true);
569 write_ptr += 1;
570 }
571 ArithmeticOperator::Divide => {
572 if r.is_empty() {
573 let code = intern(l, &mut dict, &mut uniq);
574 unsafe {
575 *out_data.get_unchecked_mut(write_ptr) = code;
576 }
577 out_mask.set(write_ptr, true);
578 write_ptr += 1;
579 } else {
580 let mut start = 0;
581 while let Some(pos) = l[start..].find(r) {
582 let part = &l[start..start + pos];
583 let code = intern(part, &mut dict, &mut uniq);
584 unsafe {
585 *out_data.get_unchecked_mut(write_ptr) = code;
586 }
587 out_mask.set(write_ptr, true);
588 write_ptr += 1;
589 start += pos + r.len();
590 }
591 let tail = &l[start..];
592 let code = intern(tail, &mut dict, &mut uniq);
593 unsafe {
594 *out_data.get_unchecked_mut(write_ptr) = code;
595 }
596 out_mask.set(write_ptr, true);
597 write_ptr += 1;
598 }
599 }
600 _ => {
601 return Err(KernelError::UnsupportedType(format!(
602 "Unsupported apply_dict_dict_impl op={:?}",
603 op
604 )));
605 }
606 }
607 }
608
609 debug_assert_eq!(write_ptr, total_out);
610
611 Ok(CategoricalArray {
612 data: out_data.into(),
613 unique_values: uniq,
614 null_mask: Some(out_mask),
615 })
616}
617
618#[cfg(feature = "str_arithmetic")]
643pub fn apply_str_str<T, U>(
644 lhs: StringAVT<T>,
645 rhs: StringAVT<U>,
646 op: ArithmeticOperator,
647) -> Result<StringArray<T>, KernelError>
648where
649 T: Integer,
650 U: Integer,
651{
652 let (larr, loff, llen) = lhs;
653 let (rarr, roff, rlen) = rhs;
654
655 if llen != rlen {
656 return Err(KernelError::LengthMismatch(log_length_mismatch(
657 "apply_str_str".to_string(),
658 llen,
659 rlen,
660 )));
661 }
662
663 let lmask_slice = larr.null_mask.as_ref().map(|m| {
665 let mut m2 = Bitmask::new_set_all(llen, true);
666 for i in 0..llen {
667 unsafe {
668 m2.set_unchecked(i, m.get_unchecked(loff + i));
669 }
670 }
671 m2
672 });
673 let rmask_slice = rarr.null_mask.as_ref().map(|m| {
674 let mut m2 = Bitmask::new_set_all(llen, true);
675 for i in 0..llen {
676 unsafe {
677 m2.set_unchecked(i, m.get_unchecked(roff + i));
678 }
679 }
680 m2
681 });
682 let lmask_ref = lmask_slice.as_ref();
683 let rmask_ref = rmask_slice.as_ref();
684
685 let lmask = lmask_ref;
687 let rmask = rmask_ref;
688 let mut out_mask = Bitmask::new_set_all(llen, false);
689 let _ = confirm_mask_capacity(llen, lmask)?;
690 let _ = confirm_mask_capacity(llen, rmask)?;
691
692 let mut total_bytes = 0;
694 for idx in 0..llen {
695 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(idx) })
696 && rmask.map_or(true, |m| unsafe { m.get_unchecked(idx) });
697 if !valid {
698 continue;
699 }
700 let a = unsafe { larr.get_str_unchecked(loff + idx) };
701 let b = unsafe { rarr.get_str_unchecked(roff + idx) };
702 total_bytes += match op {
703 ArithmeticOperator::Add => a.len() + b.len(),
704 ArithmeticOperator::Subtract => a.len(),
705 ArithmeticOperator::Multiply => a.len() * b.len().min(STRING_MULTIPLICATION_LIMIT),
706 ArithmeticOperator::Divide => {
707 if b.is_empty() {
708 a.len()
709 } else {
710 a.len() + a.matches(b).count().saturating_sub(1)
711 }
712 }
713 _ => {
714 return Err(KernelError::UnsupportedType(format!(
715 "Unsupported {:?}",
716 op
717 )));
718 }
719 };
720 }
721
722 let mut offsets = Vec64::<T>::with_capacity(llen + 1);
724 let mut data = Vec64::<u8>::with_capacity(total_bytes);
725 offsets.push(T::zero());
726
727 for idx in 0..llen {
729 let valid = lmask.map_or(true, |m| unsafe { m.get_unchecked(idx) })
730 && rmask.map_or(true, |m| unsafe { m.get_unchecked(idx) });
731 if valid {
732 let a = unsafe { larr.get_str_unchecked(loff + idx) };
733 let b = unsafe { rarr.get_str_unchecked(roff + idx) };
734 match op {
735 ArithmeticOperator::Add => {
736 data.extend_from_slice(a.as_bytes());
737 data.extend_from_slice(b.as_bytes());
738 }
739 ArithmeticOperator::Subtract => {
740 if b.is_empty() {
741 data.extend_from_slice(a.as_bytes());
742 } else if let Some(p) =
743 memchr::memmem::Finder::new(b.as_bytes()).find(a.as_bytes())
744 {
745 data.extend_from_slice(&a.as_bytes()[..p]);
746 data.extend_from_slice(&a.as_bytes()[p + b.len()..]);
747 } else {
748 data.extend_from_slice(a.as_bytes());
749 }
750 }
751 ArithmeticOperator::Multiply => {
752 let times = b.len().min(STRING_MULTIPLICATION_LIMIT);
753 for _ in 0..times {
754 data.extend_from_slice(a.as_bytes());
755 }
756 }
757 ArithmeticOperator::Divide => {
758 if b.is_empty() {
759 data.extend_from_slice(a.as_bytes());
760 } else {
761 let finder = memchr::memmem::Finder::new(b.as_bytes());
762 let mut start = 0;
763 let mut first = true;
764 while let Some(p) = finder.find(&a.as_bytes()[start..]) {
765 if !first {
766 data.push(b'|');
767 }
768 let abs = start + p;
769 data.extend_from_slice(&a.as_bytes()[start..abs]);
770 start = abs + b.len();
771 first = false;
772 }
773 if !first {
774 data.push(b'|');
775 }
776 data.extend_from_slice(&a.as_bytes()[start..]);
777 }
778 }
779 _ => unreachable!(),
780 }
781 unsafe { out_mask.set_unchecked(idx, true) };
782 }
783 offsets.push(T::from_usize(data.len()));
784 }
785
786 Ok(StringArray {
787 offsets: offsets.into(),
788 data: data.into(),
789 null_mask: Some(out_mask),
790 })
791}
792
793macro_rules! impl_apply_dict_dict {
795 ($fn_name:ident, $idx:ty) => {
796 pub fn $fn_name(
797 lhs: CategoricalAVT<$idx>,
798 rhs: CategoricalAVT<$idx>,
799 op: ArithmeticOperator,
800 ) -> Result<CategoricalArray<$idx>, KernelError> {
801 apply_dict_dict_impl(lhs, rhs, op)
802 }
803 };
804}
805
806#[cfg(any(not(feature = "default_categorical_8"), feature = "extended_categorical"))]
807impl_apply_dict_dict!(apply_dict32_dict32, u32);
808
809#[cfg(feature = "default_categorical_8")]
810impl_apply_dict_dict!(apply_dict8_dict8, u8);
811
812#[cfg(feature = "str_arithmetic")]
814pub fn apply_dict32_str<T>(
815 lhs: CategoricalAVT<u32>,
816 rhs: StringAVT<T>,
817 op: ArithmeticOperator,
818) -> Result<CategoricalArray<u32>, KernelError>
819where
820 T: Integer,
821{
822 const SAMPLE_SIZE: usize = 256;
823 const CARDINALITY_THRESHOLD: f64 = 0.75;
824
825 let (larr, loff, llen) = lhs;
827 let (rarr, roff, rlen) = rhs;
828
829 if llen != rlen {
830 return Err(KernelError::LengthMismatch(log_length_mismatch(
831 "apply_dict32_str".to_string(),
832 llen,
833 rlen,
834 )));
835 }
836
837 let cat_ratio = estimate_categorical_cardinality(larr, SAMPLE_SIZE);
839 let str_ratio = estimate_string_cardinality(rarr, SAMPLE_SIZE);
840 let max_ratio = cat_ratio.max(str_ratio);
841
842 if max_ratio > CARDINALITY_THRESHOLD {
843 let lhs_str = larr.to_string_array();
845 let str_result = apply_str_str((&lhs_str, loff, llen), (rarr, roff, rlen), op)?;
846 return Ok(str_result.to_categorical_array());
847 }
848
849 let out_mask = merge_bitmasks_to_new(larr.null_mask.as_ref(), rarr.null_mask.as_ref(), llen);
851
852 let mut total_out = 0usize;
854 for local_idx in 0..llen {
855 let valid = out_mask
856 .as_ref()
857 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
858 if !valid {
859 total_out += 1;
860 } else if let ArithmeticOperator::Divide = op {
861 let i = loff + local_idx;
862 let j = roff + local_idx;
863 let l_val = unsafe { larr.get_str_unchecked(i) };
864 let r_val = unsafe { rarr.get_str_unchecked(j) };
865 if r_val.is_empty() {
866 total_out += 1;
867 } else {
868 let mut start = 0;
869 while let Some(pos) = l_val[start..].find(r_val) {
870 total_out += 1;
871 start += pos + r_val.len();
872 }
873 total_out += 1; }
875 } else {
876 total_out += 1;
877 }
878 }
879
880 let mut out_data = Vec64::<u32>::with_capacity(total_out);
882 unsafe {
883 out_data.set_len(total_out);
884 }
885 let mut out_null = Bitmask::new_set_all(total_out, false);
886
887 let mut uniq: Vec64<String> = Vec64::with_capacity(larr.unique_values.len() + llen);
889 uniq.extend(larr.unique_values.iter().cloned());
890
891 #[cfg(feature = "fast_hash")]
892 let mut dict: AHashMap<String, u32> = AHashMap::with_capacity(uniq.len());
893
894 #[cfg(not(feature = "fast_hash"))]
895 let mut dict: HashMap<String, u32> = HashMap::with_capacity(uniq.len());
896
897 for (i, s) in uniq.iter().enumerate() {
898 dict.insert(s.clone(), i as u32);
899 }
900 let empty_code = *dict.entry("".to_string()).or_insert_with(|| {
902 let idx = uniq.len() as u32;
903 uniq.push(String::new());
904 idx
905 });
906
907 let mut write_ptr = 0usize;
909 for local_idx in 0..llen {
910 let valid = out_mask
911 .as_ref()
912 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
913 if !valid {
914 out_data.push(empty_code);
915 out_null.set(write_ptr, false);
916 write_ptr += 1;
917 continue;
918 }
919 let i = loff + local_idx;
920 let j = roff + local_idx;
921 let l_val = unsafe { larr.get_str_unchecked(i) };
922 let r_val = unsafe { rarr.get_str_unchecked(j) };
923 match op {
924 ArithmeticOperator::Add => {
925 let mut s = String::with_capacity(l_val.len() + r_val.len());
926 s.push_str(l_val);
927 s.push_str(r_val);
928 let code = intern(&s, &mut dict, &mut uniq);
929 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
930 out_null.set(write_ptr, true);
931 write_ptr += 1;
932 }
933 ArithmeticOperator::Subtract => {
934 let result = if r_val.is_empty() {
935 l_val.to_string()
936 } else if let Some(pos) = l_val.find(r_val) {
937 let mut s = l_val[..pos].to_owned();
938 s.push_str(&l_val[pos + r_val.len()..]);
939 s
940 } else {
941 l_val.to_string()
942 };
943 let code = intern(&result, &mut dict, &mut uniq);
944 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
945 out_null.set(write_ptr, true);
946 write_ptr += 1;
947 }
948 ArithmeticOperator::Multiply => {
949 let code = intern(l_val, &mut dict, &mut uniq);
950 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
951 out_null.set(write_ptr, true);
952 write_ptr += 1;
953 }
954 ArithmeticOperator::Divide => {
955 if r_val.is_empty() {
956 let code = intern(l_val, &mut dict, &mut uniq);
957 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
958 out_null.set(write_ptr, true);
959 write_ptr += 1;
960 } else {
961 let mut start = 0;
962 loop {
963 match l_val[start..].find(r_val) {
964 Some(pos) => {
965 let part = &l_val[start..start + pos];
966 let code = intern(part, &mut dict, &mut uniq);
967 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
968 out_null.set(write_ptr, true);
969 write_ptr += 1;
970 start += pos + r_val.len();
971 }
972 None => {
973 let tail = &l_val[start..];
974 let code = intern(tail, &mut dict, &mut uniq);
975 *unsafe { out_data.get_unchecked_mut(write_ptr) } = code;
976 out_null.set(write_ptr, true);
977 write_ptr += 1;
978 break;
979 }
980 }
981 }
982 }
983 }
984 _ => {
985 return Err(KernelError::UnsupportedType(
986 "Unsupported Type Error.".to_string(),
987 ));
988 }
989 }
990 }
991
992 debug_assert_eq!(write_ptr, total_out);
993
994 Ok(CategoricalArray {
995 data: out_data.into(),
996 unique_values: uniq,
997 null_mask: Some(out_null),
998 })
999}
1000
1001#[cfg(feature = "str_arithmetic")]
1003pub fn apply_str_dict32<T>(
1004 lhs: StringAVT<T>,
1005 rhs: CategoricalAVT<u32>,
1006 op: ArithmeticOperator,
1007) -> Result<StringArray<T>, KernelError>
1008where
1009 T: Integer,
1010{
1011 let (larr, loff, llen) = lhs;
1013 let (rarr, roff, rlen) = rhs;
1014
1015 if llen != rlen {
1016 return Err(KernelError::LengthMismatch(log_length_mismatch(
1017 "apply_str_dict32".to_string(),
1018 llen,
1019 rlen,
1020 )));
1021 }
1022
1023 let out_mask = merge_bitmasks_to_new(larr.null_mask.as_ref(), rarr.null_mask.as_ref(), llen);
1024
1025 let mut total_rows = 0usize;
1027 let mut total_bytes = 0usize;
1028
1029 for local_idx in 0..llen {
1030 let valid = out_mask
1031 .as_ref()
1032 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
1033 if !valid {
1034 total_rows += 1;
1035 continue;
1036 }
1037
1038 let i = loff + local_idx;
1039 let j = roff + local_idx;
1040
1041 let l = unsafe { larr.get_str_unchecked(i) };
1042 let r = unsafe { rarr.get_str_unchecked(j) };
1043
1044 match op {
1045 ArithmeticOperator::Divide => {
1046 total_rows += l.split(r).count();
1047 total_bytes += l.len(); }
1049 ArithmeticOperator::Add => {
1050 total_rows += 1;
1051 total_bytes += l.len() + r.len();
1052 }
1053 ArithmeticOperator::Subtract => {
1054 total_rows += 1;
1055 total_bytes += l.len();
1056 }
1057 ArithmeticOperator::Multiply => {
1058 total_rows += 1;
1059 total_bytes += l.len();
1060 }
1061 _ => {
1062 return Err(KernelError::UnsupportedType(
1063 "Unsupported Type Error.".to_string(),
1064 ));
1065 }
1066 }
1067 }
1068
1069 let mut offsets = Vec64::<T>::with_capacity(total_rows + 1);
1071 let mut data = Vec64::<u8>::with_capacity(total_bytes);
1072
1073 unsafe {
1074 offsets.set_len(total_rows + 1);
1075 }
1076 offsets[0] = T::zero();
1077
1078 let mut cursor = 0;
1079 let mut offset_idx = 1;
1080
1081 for local_idx in 0..llen {
1082 let valid = out_mask
1083 .as_ref()
1084 .map_or(true, |m| unsafe { m.get_unchecked(local_idx) });
1085 if !valid {
1086 offsets[offset_idx] = T::from_usize(cursor);
1087 offset_idx += 1;
1088 continue;
1089 }
1090
1091 let i = loff + local_idx;
1092 let j = roff + local_idx;
1093
1094 let l = unsafe { larr.get_str_unchecked(i) };
1095 let r = unsafe { rarr.get_str_unchecked(j) };
1096
1097 match op {
1098 ArithmeticOperator::Divide => {
1099 for part in l.split(r) {
1100 data.extend_from_slice(part.as_bytes());
1101 cursor += part.len();
1102 offsets[offset_idx] = T::from_usize(cursor);
1103 offset_idx += 1;
1104 }
1105 }
1106 ArithmeticOperator::Add => {
1107 data.extend_from_slice(l.as_bytes());
1108 data.extend_from_slice(r.as_bytes());
1109 cursor += l.len() + r.len();
1110 offsets[offset_idx] = T::from_usize(cursor);
1111 offset_idx += 1;
1112 }
1113 ArithmeticOperator::Subtract => {
1114 if r.is_empty() {
1115 data.extend_from_slice(l.as_bytes());
1116 cursor += l.len();
1117 } else if let Some(pos) = l.find(r) {
1118 data.extend_from_slice(&l.as_bytes()[..pos]);
1119 data.extend_from_slice(&l.as_bytes()[pos + r.len()..]);
1120 cursor += l.len() - r.len();
1121 } else {
1122 data.extend_from_slice(l.as_bytes());
1123 cursor += l.len();
1124 }
1125 offsets[offset_idx] = T::from_usize(cursor);
1126 offset_idx += 1;
1127 }
1128 ArithmeticOperator::Multiply => {
1129 data.extend_from_slice(l.as_bytes());
1130 cursor += l.len();
1131 offsets[offset_idx] = T::from_usize(cursor);
1132 offset_idx += 1;
1133 }
1134 _ => unreachable!(),
1135 }
1136 }
1137
1138 debug_assert_eq!(offset_idx, total_rows + 1);
1139
1140 Ok(StringArray {
1141 offsets: offsets.into(),
1142 data: data.into(),
1143 null_mask: out_mask,
1144 })
1145}
1146
1147#[cfg(feature = "str_arithmetic")]
1150pub fn apply_dict32_num<T>(
1151 lhs: CategoricalAVT<u32>,
1152 rhs: &[T],
1153 op: ArithmeticOperator,
1154) -> Result<CategoricalArray<u32>, KernelError>
1155where
1156 T: ToPrimitive + Copy,
1157{
1158 #[cfg(feature = "fast_hash")]
1159 use ahash::{HashMap, HashMapExt};
1160
1161 #[cfg(not(feature = "fast_hash"))]
1162 use std::collections::HashMap;
1163
1164 let (larr, loff, llen) = lhs;
1165
1166 if llen != rhs.len() {
1167 return Err(KernelError::LengthMismatch(log_length_mismatch(
1168 "apply_dict32_num".to_string(),
1169 llen,
1170 rhs.len(),
1171 )));
1172 }
1173
1174 let has_mask = larr.null_mask.is_some();
1175 let mut out_mask = if has_mask {
1176 Some(Bitmask::new_set_all(llen, true))
1177 } else {
1178 None
1179 };
1180
1181 let mut data = Vec64::<u32>::with_capacity(llen);
1182 unsafe {
1183 data.set_len(llen);
1184 }
1185
1186 let mut unique_values = Vec64::<String>::with_capacity(llen);
1187 let mut seen: HashMap<String, u32> = HashMap::with_capacity(llen);
1188 let mut unique_idx = 0;
1189
1190 for local_idx in 0..llen {
1191 let valid = !has_mask
1192 || unsafe {
1193 larr.null_mask
1194 .as_ref()
1195 .unwrap()
1196 .get_unchecked(loff + local_idx)
1197 };
1198
1199 if valid {
1200 let i = loff + local_idx;
1201 let l_val = unsafe { larr.get_str_unchecked(i) };
1202 let n = rhs[local_idx].to_usize().unwrap_or(0);
1203
1204 let cat = match op {
1205 ArithmeticOperator::Multiply => {
1206 let count = n.min(1_000_000);
1207 l_val.repeat(count)
1208 }
1209 _ => l_val.to_owned(),
1210 };
1211
1212 let idx = if let Some(&ix) = seen.get(&cat) {
1213 ix
1214 } else {
1215 let ix = unique_idx as u32;
1216 seen.insert(cat.clone(), ix);
1217 unique_values.push(cat);
1218 unique_idx += 1;
1219 ix
1220 };
1221
1222 unsafe {
1223 *data.get_unchecked_mut(local_idx) = idx;
1224 if let Some(mask) = &mut out_mask {
1225 mask.set_unchecked(local_idx, true);
1226 }
1227 }
1228 } else {
1229 unsafe {
1230 *data.get_unchecked_mut(local_idx) = 0;
1231 if let Some(mask) = &mut out_mask {
1232 mask.set_unchecked(local_idx, false);
1233 }
1234 }
1235 }
1236 }
1237
1238 Ok(CategoricalArray {
1239 data: data.into(),
1240 unique_values,
1241 null_mask: out_mask,
1242 })
1243}
1244
1245#[inline]
1247#[cfg(feature = "str_arithmetic")]
1248pub fn format_finite<F: Float>(buf: &mut [MaybeUninit<u8>; 24], f: F) -> &str {
1249 unsafe {
1250 let ptr = buf.as_mut_ptr() as *mut u8;
1251 let n = f.write_to_ryu_buffer(ptr);
1252 debug_assert!(n <= buf.len());
1253
1254 let slice = core::slice::from_raw_parts(ptr, n);
1255 let s = core::str::from_utf8_unchecked(slice);
1256
1257 if s.ends_with(".0") {
1259 let trimmed_len = s.len() - 2;
1260 core::str::from_utf8_unchecked(&slice[..trimmed_len])
1261 } else {
1262 s
1263 }
1264 }
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269 use crate::MaskedArray;
1270 use crate::structs::variants::string::StringArray;
1271 #[cfg(feature = "str_arithmetic")]
1272 use crate::{Bitmask, CategoricalArray};
1273
1274 use super::*;
1275 use crate::enums::operators::ArithmeticOperator;
1276 #[cfg(feature = "str_arithmetic")]
1277 use crate::vec64;
1278
1279 fn assert_str<T>(arr: &StringArray<T>, expect: &[&str], valid: Option<&[bool]>)
1283 where
1284 T: Integer + std::fmt::Debug,
1285 {
1286 assert_eq!(arr.len(), expect.len());
1287 for (i, exp) in expect.iter().enumerate() {
1288 assert_eq!(unsafe { arr.get_str_unchecked(i) }, *exp);
1289 }
1290 match (valid, &arr.null_mask) {
1291 (None, None) => {}
1292 (Some(expected), Some(mask)) => {
1293 for (i, bit) in expected.iter().enumerate() {
1294 assert_eq!(unsafe { mask.get_unchecked(i) }, *bit);
1295 }
1296 }
1297 (None, Some(mask)) => {
1298 assert!(mask.all_true());
1299 }
1300 (Some(_), None) => panic!("expected mask missing"),
1301 }
1302 }
1303
1304 #[test]
1307 fn str_num_multiply() {
1308 let input = StringArray::<u32>::from_slice(&["hi", "bye", "x"]);
1309 let nums: &[i32] = &[3, 2, 0];
1310 let input_slice = (&input, 0, input.len(), input.data.len());
1311 let out =
1312 super::apply_str_num::<u32, i32, u32>(input_slice, nums, ArithmeticOperator::Multiply)
1313 .unwrap();
1314 assert_str(&out, &["hihihi", "byebye", ""], None);
1315 }
1316
1317 #[test]
1318 fn str_num_multiply_chunk() {
1319 let base = StringArray::<u32>::from_slice(&["pad", "hi", "bye", "x", "pad2"]);
1320 let nums: &[i32] = &[3, 2, 0];
1321 let input_slice = (&base, 1, 3, base.data.len());
1323 let out =
1324 super::apply_str_num::<u32, i32, u32>(input_slice, nums, ArithmeticOperator::Multiply)
1325 .unwrap();
1326 assert_str(&out, &["hihihi", "byebye", ""], None);
1327 }
1328
1329 #[test]
1330 fn str_num_len_mismatch() {
1331 let input = StringArray::<u32>::from_slice(&["a"]);
1332 let nums: &[i32] = &[1, 2];
1333 let input_slice = (&input, 0, input.len(), input.data.len());
1334 let err = super::apply_str_num::<u32, i32, u32>(input_slice, nums, ArithmeticOperator::Add)
1335 .unwrap_err();
1336 match err {
1337 KernelError::LengthMismatch(_) => {}
1338 _ => panic!("wrong error variant"),
1339 }
1340 }
1341
1342 #[test]
1343 fn str_num_len_mismatch_chunk() {
1344 let base = StringArray::<u32>::from_slice(&["pad", "a", "pad2"]);
1345 let nums: &[i32] = &[1, 2];
1346 let input_slice = (&base, 1, 1, base.data.len());
1348 let err = super::apply_str_num::<u32, i32, u32>(input_slice, nums, ArithmeticOperator::Add)
1349 .unwrap_err();
1350 match err {
1351 KernelError::LengthMismatch(_) => {}
1352 _ => panic!("wrong error variant"),
1353 }
1354 }
1355
1356 #[cfg(feature = "str_arithmetic")]
1357 #[test]
1358 fn str_float_all_ops() {
1359 let input = StringArray::<u32>::from_slice(&["foo", "bar1", "baz"]);
1360 let nums: &[f64] = &[1.0, 1.0, 2.0];
1361 let input_slice = (&input, 0, input.len());
1362 let add = super::apply_str_float(input_slice, nums, ArithmeticOperator::Add).unwrap();
1364 assert_str(&add, &["foo1", "bar11", "baz2"], None);
1365 let sub = super::apply_str_float(input_slice, nums, ArithmeticOperator::Subtract).unwrap();
1367 assert_str(&sub, &["foo", "bar", "baz"], None);
1368 let mul = super::apply_str_float(input_slice, nums, ArithmeticOperator::Multiply).unwrap();
1370 assert_str(&mul, &["foo", "bar1", "bazbaz"], None);
1371 let div = super::apply_str_float(input_slice, nums, ArithmeticOperator::Divide).unwrap();
1373 assert_str(&div, &["foo", "bar|", "baz"], None);
1374 }
1375
1376 #[cfg(feature = "str_arithmetic")]
1377 #[test]
1378 fn str_float_all_ops_chunk() {
1379 let base = StringArray::<u32>::from_slice(&["pad", "foo", "bar1", "baz", "pad2"]);
1380 let nums: &[f64] = &[1.0, 1.0, 2.0];
1381 let input_slice = (&base, 1, 3);
1383 let add = super::apply_str_float(input_slice, nums, ArithmeticOperator::Add).unwrap();
1385 assert_str(&add, &["foo1", "bar11", "baz2"], None);
1386 let sub = super::apply_str_float(input_slice, nums, ArithmeticOperator::Subtract).unwrap();
1388 assert_str(&sub, &["foo", "bar", "baz"], None);
1389 let mul = super::apply_str_float(input_slice, nums, ArithmeticOperator::Multiply).unwrap();
1391 assert_str(&mul, &["foo", "bar1", "bazbaz"], None);
1392 let div = super::apply_str_float(input_slice, nums, ArithmeticOperator::Divide).unwrap();
1394 assert_str(&div, &["foo", "bar|", "baz"], None);
1395 }
1396
1397 #[cfg(feature = "str_arithmetic")]
1400 fn cat(values: &[&str]) -> CategoricalArray<u32> {
1401 CategoricalArray::<u32>::from_values(values.iter().copied())
1402 }
1403
1404 #[cfg(feature = "str_arithmetic")]
1405 #[test]
1406 fn dict32_dict32_add() {
1407 let lhs = cat(&["A", "B", ""]);
1408 let rhs = cat(&["1", "2", "3"]);
1409 let lhs_slice = (&lhs, 0, lhs.data.len());
1410 let rhs_slice = (&rhs, 0, rhs.data.len());
1411 let out =
1412 super::apply_dict32_dict32(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1413 let expected = vec64!["A1", "B2", "3"];
1414 for (i, exp) in expected.iter().enumerate() {
1415 assert_eq!(out.get(i).unwrap_or(""), *exp);
1416 }
1417 }
1418
1419 #[cfg(feature = "str_arithmetic")]
1420 #[test]
1421 fn dict32_dict32_add_chunk() {
1422 let lhs = cat(&["pad", "A", "B", "", "pad2"]);
1423 let rhs = cat(&["padx", "1", "2", "3", "pady"]);
1424 let lhs_slice = (&lhs, 1, 3); let rhs_slice = (&rhs, 1, 3); let out =
1427 super::apply_dict32_dict32(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1428 let expected = vec64!["A1", "B2", "3"];
1429 for (i, exp) in expected.iter().enumerate() {
1430 assert_eq!(out.get(i).unwrap_or(""), *exp);
1431 }
1432 }
1433
1434 #[cfg(feature = "str_arithmetic")]
1435 #[test]
1436 fn dict32_str_subtract() {
1437 let lhs = cat(&["hello", "yellow"]);
1438 let rhs = StringArray::<u32>::from_slice(&["l", "el"]);
1439 let lhs_slice = (&lhs, 0, lhs.data.len());
1440 let rhs_slice = (&rhs, 0, rhs.len());
1441 let out =
1442 super::apply_dict32_str(lhs_slice, rhs_slice, ArithmeticOperator::Subtract).unwrap();
1443 assert_eq!(out.get(0).unwrap(), "helo");
1444 assert_eq!(out.get(1).unwrap(), "ylow");
1445 }
1446
1447 #[cfg(feature = "str_arithmetic")]
1448 #[test]
1449 fn dict32_str_subtract_chunk() {
1450 let lhs = cat(&["pad", "hello", "yellow", "pad2"]);
1451 let rhs = StringArray::<u32>::from_slice(&["pad", "l", "el", "pad2"]);
1452 let lhs_slice = (&lhs, 1, 2); let rhs_slice = (&rhs, 1, 2); let out =
1455 super::apply_dict32_str(lhs_slice, rhs_slice, ArithmeticOperator::Subtract).unwrap();
1456 assert_eq!(out.get(0).unwrap(), "helo");
1457 assert_eq!(out.get(1).unwrap(), "ylow");
1458 }
1459
1460 #[cfg(feature = "str_arithmetic")]
1461 #[test]
1462 fn str_dict32_divide() {
1463 let lhs = StringArray::<u32>::from_slice(&["a:b:c"]);
1464 let rhs = cat(&[":"]);
1465 let lhs_slice = (&lhs, 0, lhs.len());
1466 let rhs_slice = (&rhs, 0, rhs.data.len());
1467 let out =
1468 super::apply_str_dict32(lhs_slice, rhs_slice, ArithmeticOperator::Divide).unwrap();
1469 assert_str(&out, &["a", "b", "c"], None);
1470 }
1471
1472 #[cfg(feature = "str_arithmetic")]
1473 #[test]
1474 fn str_dict32_divide_chunk() {
1475 let lhs = StringArray::<u32>::from_slice(&["pad", "a:b:c", "pad2"]);
1477 let rhs = cat(&["pad", ":", "pad2"]);
1478 let lhs_slice = (&lhs, 1, 1); let rhs_slice = (&rhs, 1, 1); let out =
1481 super::apply_str_dict32(lhs_slice, rhs_slice, ArithmeticOperator::Divide).unwrap();
1482 assert_str(&out, &["a", "b", "c"], None);
1483 }
1484
1485 #[cfg(feature = "str_arithmetic")]
1486 #[test]
1487 fn dict32_num_multiply() {
1488 let lhs = cat(&["x", "y"]);
1489 let nums: &[u32] = &[3, 1];
1490 let lhs_slice = (&lhs, 0, lhs.data.len());
1491 let nums_window = &nums[0..lhs.data.len()];
1492 let out =
1493 super::apply_dict32_num(lhs_slice, nums_window, ArithmeticOperator::Multiply).unwrap();
1494 assert_eq!(out.get(0).unwrap(), "xxx");
1495 assert_eq!(out.get(1).unwrap(), "y");
1496 }
1497
1498 #[cfg(feature = "str_arithmetic")]
1499 #[test]
1500 fn dict32_num_multiply_chunk() {
1501 let lhs = cat(&["pad", "x", "y", "pad2"]);
1502 let nums: &[u32] = &[0, 3, 1, 0];
1503 let lhs_slice = (&lhs, 1, 2); let nums_window = &nums[1..3];
1505 let out =
1506 super::apply_dict32_num(lhs_slice, nums_window, ArithmeticOperator::Multiply).unwrap();
1507 assert_eq!(out.get(0).unwrap(), "xxx");
1508 assert_eq!(out.get(1).unwrap(), "y");
1509 }
1510
1511 #[cfg(feature = "str_arithmetic")]
1512 fn cat32_str_arr(strings: &[&str]) -> (CategoricalArray<u32>, StringArray<u32>) {
1513 let str_arr = StringArray::from_vec(strings.to_vec(), None);
1514 let cat_arr = str_arr.to_categorical_array();
1515 (cat_arr, str_arr)
1516 }
1517
1518 #[cfg(feature = "str_arithmetic")]
1519 #[test]
1520 fn test_apply_dict32_str_add_and_divide() {
1521 let (lhs_cat, rhs_str) = cat32_str_arr(&["foo", "bar|baz", ""]);
1522 let lhs_cat_slice = (&lhs_cat, 0, lhs_cat.data.len());
1524 let rhs_str_slice = (&rhs_str, 0, rhs_str.len());
1525 let added =
1526 apply_dict32_str(lhs_cat_slice, rhs_str_slice, ArithmeticOperator::Add).unwrap();
1527 let expected_cat = apply_str_str(
1528 (&lhs_cat.to_string_array(), 0, lhs_cat.len()),
1529 rhs_str_slice,
1530 ArithmeticOperator::Add,
1531 )
1532 .unwrap()
1533 .to_categorical_array();
1534 assert_eq!(added.unique_values, expected_cat.unique_values);
1535 assert_eq!(added.data, expected_cat.data);
1536
1537 let divided =
1539 apply_dict32_str(lhs_cat_slice, rhs_str_slice, ArithmeticOperator::Divide).unwrap();
1540 let expected_div = apply_str_str(
1541 (&lhs_cat.to_string_array(), 0, lhs_cat.len()),
1542 rhs_str_slice,
1543 ArithmeticOperator::Divide,
1544 )
1545 .unwrap()
1546 .to_categorical_array();
1547 assert_eq!(divided.unique_values, expected_div.unique_values);
1548 assert_eq!(divided.data, expected_div.data);
1549 }
1550
1551 #[cfg(feature = "str_arithmetic")]
1552 #[test]
1553 fn test_apply_dict32_str_add_and_divide_chunk() {
1554 let (lhs_cat, rhs_str) = cat32_str_arr(&["pad", "foo", "bar|baz", "", "pad2"]);
1555 let lhs_cat_slice = (&lhs_cat, 1, 3); let rhs_str_slice = (&rhs_str, 1, 3);
1557
1558 let added =
1560 apply_dict32_str(lhs_cat_slice, rhs_str_slice, ArithmeticOperator::Add).unwrap();
1561 let expected_cat = apply_str_str(
1562 (&lhs_cat.to_string_array(), 1, 3),
1563 rhs_str_slice,
1564 ArithmeticOperator::Add,
1565 )
1566 .unwrap()
1567 .to_categorical_array();
1568 assert_eq!(added.unique_values, expected_cat.unique_values);
1569 assert_eq!(added.data, expected_cat.data);
1570
1571 let divided =
1573 apply_dict32_str(lhs_cat_slice, rhs_str_slice, ArithmeticOperator::Divide).unwrap();
1574 let expected_div = apply_str_str(
1575 (&lhs_cat.to_string_array(), 1, 3),
1576 rhs_str_slice,
1577 ArithmeticOperator::Divide,
1578 )
1579 .unwrap()
1580 .to_categorical_array();
1581 assert_eq!(divided.unique_values, expected_div.unique_values);
1582 assert_eq!(divided.data, expected_div.data);
1583 }
1584
1585 #[cfg(feature = "str_arithmetic")]
1588 fn string_array<T: Integer>(data: &[&str], nulls: Option<&[bool]>) -> StringArray<T> {
1589 let array = StringArray::from_vec(data.to_vec(), nulls.map(Bitmask::from_bools));
1590 assert_eq!(array.len(), data.len());
1591 array
1592 }
1593
1594 #[cfg(feature = "str_arithmetic")]
1595 #[test]
1596 fn test_add_str() {
1597 let lhs = string_array::<u32>(&["a", "b", "c"], None);
1598 let rhs = string_array::<u32>(&["x", "y", "z"], None);
1599 let lhs_slice = (&lhs, 0, lhs.len());
1600 let rhs_slice = (&rhs, 0, rhs.len());
1601 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1602
1603 assert_eq!(result.get(0), Some("ax"));
1604 assert_eq!(result.get(1), Some("by"));
1605 assert_eq!(result.get(2), Some("cz"));
1606 }
1607
1608 #[cfg(feature = "str_arithmetic")]
1609 #[test]
1610 fn test_add_str_chunk() {
1611 let lhs = string_array::<u32>(&["pad", "a", "b", "c", "pad2"], None);
1612 let rhs = string_array::<u32>(&["pad", "x", "y", "z", "pad2"], None);
1613 let lhs_slice = (&lhs, 1, 3);
1615 let rhs_slice = (&rhs, 1, 3);
1616 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1617
1618 assert_eq!(result.get(0), Some("ax"));
1619 assert_eq!(result.get(1), Some("by"));
1620 assert_eq!(result.get(2), Some("cz"));
1621 }
1622
1623 #[cfg(feature = "str_arithmetic")]
1624 #[test]
1625 fn test_subtract_str() {
1626 let lhs = string_array::<u32>(&["hello", "goodbye", "test"], None);
1627 let rhs = string_array::<u32>(&["l", "bye", "xyz"], None);
1628 let lhs_slice = (&lhs, 0, lhs.len());
1629 let rhs_slice = (&rhs, 0, rhs.len());
1630 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Subtract).unwrap();
1631
1632 assert_eq!(result.get(0), Some("helo"));
1633 assert_eq!(result.get(1), Some("good"));
1634 assert_eq!(result.get(2), Some("test")); }
1636
1637 #[cfg(feature = "str_arithmetic")]
1638 #[test]
1639 fn test_subtract_str_chunk() {
1640 let lhs = string_array::<u32>(&["pad", "hello", "goodbye", "test", "pad2"], None);
1641 let rhs = string_array::<u32>(&["pad", "l", "bye", "xyz", "pad2"], None);
1642 let lhs_slice = (&lhs, 1, 3);
1644 let rhs_slice = (&rhs, 1, 3);
1645 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Subtract).unwrap();
1646
1647 assert_eq!(result.get(0), Some("helo"));
1648 assert_eq!(result.get(1), Some("good"));
1649 assert_eq!(result.get(2), Some("test")); }
1651
1652 #[cfg(feature = "str_arithmetic")]
1653 #[test]
1654 fn test_multiply_str() {
1655 let lhs = string_array::<u32>(&["x", "ab", "c"], None);
1656 let rhs = string_array::<u32>(&["123", "12", "long_string"], None);
1657 let lhs_slice = (&lhs, 0, lhs.len());
1658 let rhs_slice = (&rhs, 0, rhs.len());
1659 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Multiply).unwrap();
1660
1661 assert_eq!(result.get(0), Some("xxx"));
1662 assert_eq!(result.get(1), Some("abab"));
1663 assert_eq!(
1664 result.get(2),
1665 Some("c".repeat("long_string".len()).as_str())
1666 );
1667 }
1668
1669 #[cfg(feature = "str_arithmetic")]
1670 #[test]
1671 fn test_multiply_str_chunk() {
1672 let lhs = string_array::<u32>(&["pad", "x", "ab", "c", "pad2"], None);
1673 let rhs = string_array::<u32>(&["pad", "123", "12", "long_string", "pad2"], None);
1674 let lhs_slice = (&lhs, 1, 3);
1676 let rhs_slice = (&rhs, 1, 3);
1677 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Multiply).unwrap();
1678
1679 assert_eq!(result.get(0), Some("xxx"));
1680 assert_eq!(result.get(1), Some("abab"));
1681 assert_eq!(
1682 result.get(2),
1683 Some("c".repeat("long_string".len()).as_str())
1684 );
1685 }
1686
1687 #[cfg(feature = "str_arithmetic")]
1688 #[test]
1689 fn test_divide_str() {
1690 let lhs = string_array::<u32>(&["a,b,c", "a--b--c", "abc"], None);
1691 let rhs = string_array::<u32>(&[",", "--", ""], None);
1692 let lhs_slice = (&lhs, 0, lhs.len());
1693 let rhs_slice = (&rhs, 0, rhs.len());
1694 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Divide).unwrap();
1695
1696 assert_eq!(result.get(0), Some("a|b|c"));
1697 assert_eq!(result.get(1), Some("a|b|c"));
1698 assert_eq!(result.get(2), Some("abc"));
1699 }
1700
1701 #[cfg(feature = "str_arithmetic")]
1702 #[test]
1703 fn test_divide_str_chunk() {
1704 let lhs = string_array::<u32>(&["xxx", "a,b,c", "a--b--c", "abc", "yyy"], None);
1705 let rhs = string_array::<u32>(&["", ",", "--", "", ""], None);
1706 let lhs_slice = (&lhs, 1, 3);
1708 let rhs_slice = (&rhs, 1, 3);
1709 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Divide).unwrap();
1710
1711 assert_eq!(result.get(0), Some("a|b|c"));
1712 assert_eq!(result.get(1), Some("a|b|c"));
1713 assert_eq!(result.get(2), Some("abc"));
1714 }
1715
1716 #[cfg(feature = "str_arithmetic")]
1717 #[test]
1718 fn test_nulls_str() {
1719 let lhs = string_array::<u32>(&["a", "b", "c"], Some(&[true, false, true]));
1720 let rhs = string_array::<u32>(&["x", "y", "z"], Some(&[true, true, false]));
1721 let lhs_slice = (&lhs, 0, lhs.len());
1722 let rhs_slice = (&rhs, 0, rhs.len());
1723 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1724
1725 assert_eq!(result.get(0), Some("ax"));
1726 assert_eq!(result.get(1), None);
1727 assert_eq!(result.get(2), None);
1728 }
1729
1730 #[cfg(feature = "str_arithmetic")]
1731 #[test]
1732 fn test_nulls_str_chunk() {
1733 let lhs = string_array::<u32>(
1734 &["0", "a", "b", "c", "9"],
1735 Some(&[false, true, false, true, false]),
1736 );
1737 let rhs = string_array::<u32>(
1738 &["y", "x", "y", "z", "w"],
1739 Some(&[true, true, true, false, false]),
1740 );
1741 let lhs_slice = (&lhs, 1, 3);
1743 let rhs_slice = (&rhs, 1, 3);
1744 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add).unwrap();
1745
1746 assert_eq!(result.get(0), Some("ax"));
1747 assert_eq!(result.get(1), None);
1748 assert_eq!(result.get(2), None);
1749 }
1750
1751 #[cfg(feature = "str_arithmetic")]
1752 #[test]
1753 fn test_mismatched_length_str() {
1754 let lhs = string_array::<u32>(&["a", "b"], None);
1755 let rhs = string_array::<u32>(&["x"], None);
1756 let lhs_slice = (&lhs, 0, lhs.len());
1757 let rhs_slice = (&rhs, 0, rhs.len());
1758 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add);
1759 assert!(matches!(result, Err(KernelError::LengthMismatch(_))));
1760 }
1761
1762 #[cfg(feature = "str_arithmetic")]
1763 #[test]
1764 fn test_mismatched_length_str_chunk() {
1765 let lhs = string_array::<u32>(&["a", "b", "c"], None);
1766 let rhs = string_array::<u32>(&["x"], None);
1767 let lhs_slice = (&lhs, 1, 2);
1769 let rhs_slice = (&rhs, 0, 1);
1770 let result = apply_str_str(lhs_slice, rhs_slice, ArithmeticOperator::Add);
1771 assert!(matches!(result, Err(KernelError::LengthMismatch(_))));
1772 }
1773}