1use std::sync::Arc;
19
20use arrow::array::{
21 Array, ArrayRef, DurationMicrosecondArray, Float64Array, IntervalMonthDayNanoArray,
22 IntervalYearMonthArray,
23};
24use arrow::datatypes::DataType::{self, Duration, Float64, Int64, Interval};
25use arrow::datatypes::IntervalUnit::{MonthDayNano, YearMonth};
26use datafusion_common::cast::{
27 as_duration_microsecond_array, as_float64_array, as_int64_array,
28 as_interval_mdn_array, as_interval_ym_array,
29};
30use datafusion_common::types::{
31 NativeType, logical_duration_microsecond, logical_float64, logical_int64,
32 logical_interval_mdn, logical_interval_year_month,
33};
34use datafusion_common::{Result, exec_err, internal_err};
35use datafusion_expr::sort_properties::{ExprProperties, SortProperties};
36use datafusion_expr::{
37 ColumnarValue, ScalarFunctionArgs, ScalarUDFImpl, Signature, TypeSignature,
38 TypeSignatureClass,
39};
40use datafusion_functions::utils::make_scalar_function;
41
42use arrow::array::{Int64Array, Int64Builder};
43use arrow::datatypes::TimeUnit::Microsecond;
44use datafusion_expr::Coercion;
45use datafusion_expr::Volatility::Immutable;
46
47#[derive(Debug, PartialEq, Eq, Hash)]
48pub struct SparkWidthBucket {
49 signature: Signature,
50}
51
52impl Default for SparkWidthBucket {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58impl SparkWidthBucket {
59 pub fn new() -> Self {
60 let numeric = Coercion::new_implicit(
61 TypeSignatureClass::Native(logical_float64()),
62 vec![TypeSignatureClass::Numeric],
63 NativeType::Float64,
64 );
65 let duration = Coercion::new_implicit(
66 TypeSignatureClass::Native(logical_duration_microsecond()),
67 vec![TypeSignatureClass::Duration],
68 NativeType::Duration(Microsecond),
69 );
70 let interval_ym = Coercion::new_exact(TypeSignatureClass::Native(
71 logical_interval_year_month(),
72 ));
73 let interval_mdn =
74 Coercion::new_exact(TypeSignatureClass::Native(logical_interval_mdn()));
75 let bucket = Coercion::new_implicit(
76 TypeSignatureClass::Native(logical_int64()),
77 vec![TypeSignatureClass::Integer],
78 NativeType::Int64,
79 );
80 let type_signature = Signature::one_of(
81 vec![
82 TypeSignature::Coercible(vec![
83 numeric.clone(),
84 numeric.clone(),
85 numeric.clone(),
86 bucket.clone(),
87 ]),
88 TypeSignature::Coercible(vec![
89 duration.clone(),
90 duration.clone(),
91 duration.clone(),
92 bucket.clone(),
93 ]),
94 TypeSignature::Coercible(vec![
95 interval_ym.clone(),
96 interval_ym.clone(),
97 interval_ym.clone(),
98 bucket.clone(),
99 ]),
100 TypeSignature::Coercible(vec![
101 interval_mdn.clone(),
102 interval_mdn.clone(),
103 interval_mdn.clone(),
104 bucket.clone(),
105 ]),
106 ],
107 Immutable,
108 )
109 .with_parameter_names(vec!["expr", "min", "max", "num_buckets"])
110 .expect("valid parameter names");
111 Self {
112 signature: type_signature,
113 }
114 }
115}
116
117impl ScalarUDFImpl for SparkWidthBucket {
118 fn name(&self) -> &str {
119 "width_bucket"
120 }
121
122 fn signature(&self) -> &Signature {
123 &self.signature
124 }
125
126 fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
127 Ok(Int64)
128 }
129
130 fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
131 make_scalar_function(width_bucket_kern, vec![])(&args.args)
132 }
133
134 fn output_ordering(&self, input: &[ExprProperties]) -> Result<SortProperties> {
135 if input.len() == 1 {
136 let value = &input[0];
137 Ok(value.sort_properties)
138 } else {
139 Ok(SortProperties::default())
140 }
141 }
142}
143
144fn width_bucket_kern(args: &[ArrayRef]) -> Result<ArrayRef> {
145 let [v, minv, maxv, nb] = args else {
146 return exec_err!(
147 "width_bucket expects exactly 4 argument, got {}",
148 args.len()
149 );
150 };
151
152 match v.data_type() {
153 Float64 => {
154 let v = as_float64_array(v)?;
155 let min = as_float64_array(minv)?;
156 let max = as_float64_array(maxv)?;
157 let n_bucket = as_int64_array(nb)?;
158 Ok(Arc::new(width_bucket_float64(v, min, max, n_bucket)))
159 }
160 Duration(Microsecond) => {
161 let v = as_duration_microsecond_array(v)?;
162 let min = as_duration_microsecond_array(minv)?;
163 let max = as_duration_microsecond_array(maxv)?;
164 let n_bucket = as_int64_array(nb)?;
165 Ok(Arc::new(width_bucket_i64_as_float(v, min, max, n_bucket)))
166 }
167 Interval(YearMonth) => {
168 let v = as_interval_ym_array(v)?;
169 let min = as_interval_ym_array(minv)?;
170 let max = as_interval_ym_array(maxv)?;
171 let n_bucket = as_int64_array(nb)?;
172 Ok(Arc::new(width_bucket_i32_as_float(v, min, max, n_bucket)))
173 }
174 Interval(MonthDayNano) => {
175 let v = as_interval_mdn_array(v)?;
176 let min = as_interval_mdn_array(minv)?;
177 let max = as_interval_mdn_array(maxv)?;
178 let n_bucket = as_int64_array(nb)?;
179 Ok(Arc::new(width_bucket_interval_mdn_exact(
180 v, min, max, n_bucket,
181 )))
182 }
183
184 other => internal_err!(
185 "width_bucket received unexpected data types: {:?}, {:?}, {:?}, {:?}",
186 other,
187 minv.data_type(),
188 maxv.data_type(),
189 nb.data_type()
190 ),
191 }
192}
193
194macro_rules! width_bucket_kernel_impl {
195 ($name:ident, $arr_ty:ty, $to_f64:expr, $check_nan:expr) => {
196 pub(crate) fn $name(
197 v: &$arr_ty,
198 min: &$arr_ty,
199 max: &$arr_ty,
200 n_bucket: &Int64Array,
201 ) -> Int64Array {
202 let len = v.len();
203 let mut b = Int64Builder::with_capacity(len);
204
205 for i in 0..len {
206 if v.is_null(i) || min.is_null(i) || max.is_null(i) || n_bucket.is_null(i)
207 {
208 b.append_null();
209 continue;
210 }
211 let x = ($to_f64)(v, i);
212 let l = ($to_f64)(min, i);
213 let h = ($to_f64)(max, i);
214 let buckets = n_bucket.value(i);
215
216 if buckets <= 0 {
217 b.append_null();
218 continue;
219 }
220 let next_bucket = (buckets + 1) as i64;
221 if $check_nan {
222 if !x.is_finite() || !l.is_finite() || !h.is_finite() {
223 b.append_null();
224 continue;
225 }
226 }
227
228 let ord = match l.partial_cmp(&h) {
229 Some(o) => o,
230 None => {
231 b.append_null();
232 continue;
233 }
234 };
235 if ord == std::cmp::Ordering::Equal {
236 b.append_null();
237 continue;
238 }
239 let asc = ord == std::cmp::Ordering::Less;
240
241 if asc {
242 if x < l {
243 b.append_value(0);
244 continue;
245 }
246 if x >= h {
247 b.append_value(next_bucket);
248 continue;
249 }
250 } else {
251 if x > l {
252 b.append_value(0);
253 continue;
254 }
255 if x <= h {
256 b.append_value(next_bucket);
257 continue;
258 }
259 }
260
261 let width = (h - l) / (buckets as f64);
262 if width == 0.0 || !width.is_finite() {
263 b.append_null();
264 continue;
265 }
266 let mut bucket = ((x - l) / width).floor() as i64 + 1;
267 if bucket < 1 {
268 bucket = 1;
269 }
270 if bucket > next_bucket {
271 bucket = next_bucket;
272 }
273
274 b.append_value(bucket);
275 }
276
277 b.finish()
278 }
279 };
280}
281
282width_bucket_kernel_impl!(
283 width_bucket_float64,
284 Float64Array,
285 |arr: &Float64Array, i: usize| arr.value(i),
286 true
287);
288
289width_bucket_kernel_impl!(
290 width_bucket_i64_as_float,
291 DurationMicrosecondArray,
292 |arr: &DurationMicrosecondArray, i: usize| arr.value(i) as f64,
293 false
294);
295
296width_bucket_kernel_impl!(
297 width_bucket_i32_as_float,
298 IntervalYearMonthArray,
299 |arr: &IntervalYearMonthArray, i: usize| arr.value(i) as f64,
300 false
301);
302const NS_PER_DAY_I128: i128 = 86_400_000_000_000;
303pub(crate) fn width_bucket_interval_mdn_exact(
304 v: &IntervalMonthDayNanoArray,
305 lo: &IntervalMonthDayNanoArray,
306 hi: &IntervalMonthDayNanoArray,
307 n: &Int64Array,
308) -> Int64Array {
309 let len = v.len();
310 let mut b = Int64Builder::with_capacity(len);
311
312 for i in 0..len {
313 if v.is_null(i) || lo.is_null(i) || hi.is_null(i) || n.is_null(i) {
314 b.append_null();
315 continue;
316 }
317 let buckets = n.value(i);
318 if buckets <= 0 {
319 b.append_null();
320 continue;
321 }
322 let next_bucket = buckets + 1;
323
324 let x = v.value(i);
325 let l = lo.value(i);
326 let h = hi.value(i);
327
328 let asc = (l.months, l.days, l.nanoseconds) < (h.months, h.days, h.nanoseconds);
331 if (l.months, l.days, l.nanoseconds) == (h.months, h.days, h.nanoseconds) {
332 b.append_null();
333 continue;
334 }
335
336 if l.days == h.days && l.nanoseconds == h.nanoseconds && l.months != h.months {
338 let x_m = x.months as f64;
339 let l_m = l.months as f64;
340 let h_m = h.months as f64;
341
342 if asc {
343 if x_m < l_m {
344 b.append_value(0);
345 continue;
346 }
347 if x_m >= h_m {
348 b.append_value(next_bucket);
349 continue;
350 }
351 } else {
352 if x_m > l_m {
353 b.append_value(0);
354 continue;
355 }
356 if x_m <= h_m {
357 b.append_value(next_bucket);
358 continue;
359 }
360 }
361
362 let width = (h_m - l_m) / (buckets as f64);
363 if width == 0.0 || !width.is_finite() {
364 b.append_null();
365 continue;
366 }
367
368 let mut bucket = ((x_m - l_m) / width).floor() as i64 + 1;
369 if bucket < 1 {
370 bucket = 1;
371 }
372 if bucket > next_bucket {
373 bucket = next_bucket;
374 }
375 b.append_value(bucket);
376 continue;
377 }
378
379 if l.months == h.months {
381 let base_days = l.days as i128;
382 let base_ns = l.nanoseconds as i128;
383
384 let xf = (x.days as i128 - base_days) * NS_PER_DAY_I128
385 + (x.nanoseconds as i128 - base_ns);
386 let hf = (h.days as i128 - base_days) * NS_PER_DAY_I128
387 + (h.nanoseconds as i128 - base_ns);
388
389 let x_f = xf as f64;
390 let l_f = 0.0;
391 let h_f = hf as f64;
392
393 if asc {
394 if x_f < l_f {
395 b.append_value(0);
396 continue;
397 }
398 if x_f >= h_f {
399 b.append_value(next_bucket);
400 continue;
401 }
402 } else {
403 if x_f > l_f {
404 b.append_value(0);
405 continue;
406 }
407 if x_f <= h_f {
408 b.append_value(next_bucket);
409 continue;
410 }
411 }
412
413 let width = (h_f - l_f) / (buckets as f64);
414 if width == 0.0 || !width.is_finite() {
415 b.append_null();
416 continue;
417 }
418
419 let mut bucket = ((x_f - l_f) / width).floor() as i64 + 1;
420 if bucket < 1 {
421 bucket = 1;
422 }
423 if bucket > next_bucket {
424 bucket = next_bucket;
425 }
426 b.append_value(bucket);
427 continue;
428 }
429
430 b.append_null();
431 }
432
433 b.finish()
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439 use arrow::datatypes::Int64Type;
440
441 use arrow::array::{
442 ArrayRef, AsArray, DurationMicrosecondArray, Float64Array, Int32Array,
443 Int64Array, IntervalYearMonthArray,
444 };
445 use arrow::datatypes::IntervalMonthDayNano;
446
447 fn i64_array_all(len: usize, val: i64) -> Arc<Int64Array> {
450 Arc::new(Int64Array::from(vec![val; len]))
451 }
452
453 fn f64_array(vals: &[f64]) -> Arc<Float64Array> {
454 Arc::new(Float64Array::from(vals.to_vec()))
455 }
456
457 fn f64_array_opt(vals: &[Option<f64>]) -> Arc<Float64Array> {
458 Arc::new(Float64Array::from(vals.to_vec()))
459 }
460
461 fn dur_us_array(vals: &[i64]) -> Arc<DurationMicrosecondArray> {
462 Arc::new(DurationMicrosecondArray::from(vals.to_vec()))
463 }
464
465 fn ym_array(vals: &[i32]) -> Arc<IntervalYearMonthArray> {
466 Arc::new(IntervalYearMonthArray::from(vals.to_vec()))
467 }
468
469 fn mdn_array(vals: &[(i32, i32, i64)]) -> Arc<IntervalMonthDayNanoArray> {
470 let data: Vec<IntervalMonthDayNano> = vals
471 .iter()
472 .map(|(m, d, ns)| IntervalMonthDayNano::new(*m, *d, *ns))
473 .collect();
474 Arc::new(IntervalMonthDayNanoArray::from(data))
475 }
476
477 #[test]
480 fn test_width_bucket_f64_basic() {
481 let v = f64_array(&[0.5, 1.0, 9.9, -1.0, 10.0]);
482 let lo = f64_array(&[0.0, 0.0, 0.0, 0.0, 0.0]);
483 let hi = f64_array(&[10.0, 10.0, 10.0, 10.0, 10.0]);
484 let n = i64_array_all(5, 10);
485
486 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
487 let out = out.as_primitive::<Int64Type>();
488 assert_eq!(out.values(), &[1, 2, 10, 0, 11]);
489 }
490
491 #[test]
492 fn test_width_bucket_f64_descending_range() {
493 let v = f64_array(&[9.9, 10.0, 0.0, -0.1, 10.1]);
494 let lo = f64_array(&[10.0; 5]);
495 let hi = f64_array(&[0.0; 5]);
496 let n = i64_array_all(5, 10);
497
498 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
499 let out = out.as_primitive::<Int64Type>();
500
501 assert_eq!(out.values(), &[1, 1, 11, 11, 0]);
502 }
503 #[test]
504 fn test_width_bucket_f64_bounds_inclusive_exclusive_asc() {
505 let v = f64_array(&[0.0, 9.999999999, 10.0]);
506 let lo = f64_array(&[0.0; 3]);
507 let hi = f64_array(&[10.0; 3]);
508 let n = i64_array_all(3, 10);
509
510 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
511 let out = out.as_primitive::<Int64Type>();
512 assert_eq!(out.values(), &[1, 10, 11]);
513 }
514
515 #[test]
516 fn test_width_bucket_f64_bounds_inclusive_exclusive_desc() {
517 let v = f64_array(&[10.0, 0.0, -0.000001]);
518 let lo = f64_array(&[10.0; 3]);
519 let hi = f64_array(&[0.0; 3]);
520 let n = i64_array_all(3, 10);
521
522 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
523 let out = out.as_primitive::<Int64Type>();
524 assert_eq!(out.values(), &[1, 11, 11]);
525 }
526
527 #[test]
528 fn test_width_bucket_f64_edge_cases() {
529 let v = f64_array(&[1.0, 5.0, 9.0]);
530 let lo = f64_array(&[0.0, 0.0, 0.0]);
531 let hi = f64_array(&[10.0, 10.0, 10.0]);
532 let n = Arc::new(Int64Array::from(vec![0, -1, 10]));
533 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
534 let out = out.as_primitive::<Int64Type>();
535 assert!(out.is_null(0));
536 assert!(out.is_null(1));
537 assert_eq!(out.value(2), 10);
538
539 let v = f64_array(&[1.0]);
540 let lo = f64_array(&[5.0]);
541 let hi = f64_array(&[5.0]);
542 let n = i64_array_all(1, 10);
543 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
544 let out = out.as_primitive::<Int64Type>();
545 assert!(out.is_null(0));
546
547 let v = f64_array_opt(&[Some(f64::NAN)]);
548 let lo = f64_array(&[0.0]);
549 let hi = f64_array(&[10.0]);
550 let n = i64_array_all(1, 10);
551 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
552 let out = out.as_primitive::<Int64Type>();
553 assert!(out.is_null(0));
554 }
555
556 #[test]
557 fn test_width_bucket_f64_nulls_propagate() {
558 let v = f64_array_opt(&[None, Some(1.0), Some(2.0), Some(3.0)]);
559 let lo = f64_array(&[0.0; 4]);
560 let hi = f64_array(&[10.0; 4]);
561 let n = i64_array_all(4, 10);
562
563 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
564 let out = out.as_primitive::<Int64Type>();
565 assert!(out.is_null(0));
566 assert_eq!(out.value(1), 2);
567 assert_eq!(out.value(2), 3);
568 assert_eq!(out.value(3), 4);
569
570 let v = f64_array(&[1.0]);
571 let lo = f64_array_opt(&[None]);
572 let hi = f64_array(&[10.0]);
573 let n = i64_array_all(1, 10);
574 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
575 let out = out.as_primitive::<Int64Type>();
576 assert!(out.is_null(0));
577 }
578
579 #[test]
582 fn test_width_bucket_duration_us() {
583 let v = dur_us_array(&[1_000_000, 0, -1]);
584 let lo = dur_us_array(&[0, 0, 0]);
585 let hi = dur_us_array(&[2_000_000, 2_000_000, 2_000_000]);
586 let n = i64_array_all(3, 2);
587
588 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
589 let out = out.as_primitive::<Int64Type>();
590 assert_eq!(out.values(), &[2, 1, 0]);
591 }
592
593 #[test]
594 fn test_width_bucket_duration_us_equal_bounds() {
595 let v = dur_us_array(&[0]);
596 let lo = dur_us_array(&[1]);
597 let hi = dur_us_array(&[1]);
598 let n = i64_array_all(1, 10);
599 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
600 assert!(out.as_primitive::<Int64Type>().is_null(0));
601 }
602
603 #[test]
606 fn test_width_bucket_interval_ym_basic() {
607 let v = ym_array(&[0, 5, 11, 12, 13]);
608 let lo = ym_array(&[0; 5]);
609 let hi = ym_array(&[12; 5]);
610 let n = i64_array_all(5, 12);
611
612 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
613 let out = out.as_primitive::<Int64Type>();
614 assert_eq!(out.values(), &[1, 6, 12, 13, 13]);
615 }
616
617 #[test]
618 fn test_width_bucket_interval_ym_desc() {
619 let v = ym_array(&[11, 12, 0, -1, 13]);
620 let lo = ym_array(&[12; 5]);
621 let hi = ym_array(&[0; 5]);
622 let n = i64_array_all(5, 12);
623
624 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
625 let out = out.as_primitive::<Int64Type>();
626 assert_eq!(out.values(), &[2, 1, 13, 13, 0]);
627 }
628
629 #[test]
632 fn test_width_bucket_interval_mdn_months_only_basic() {
633 let v = mdn_array(&[(0, 0, 0), (5, 0, 0), (11, 0, 0), (12, 0, 0), (13, 0, 0)]);
634 let lo = mdn_array(&[(0, 0, 0); 5]);
635 let hi = mdn_array(&[(12, 0, 0); 5]);
636 let n = i64_array_all(5, 12);
637
638 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
639 let out = out.as_primitive::<Int64Type>();
640 assert_eq!(out.values(), &[1, 6, 12, 13, 13]);
641 }
642
643 #[test]
644 fn test_width_bucket_interval_mdn_months_only_desc() {
645 let v = mdn_array(&[(11, 0, 0), (12, 0, 0), (0, 0, 0), (-1, 0, 0), (13, 0, 0)]);
646 let lo = mdn_array(&[(12, 0, 0); 5]);
647 let hi = mdn_array(&[(0, 0, 0); 5]);
648 let n = i64_array_all(5, 12);
649
650 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
651 let out = out.as_primitive::<Int64Type>();
652 assert_eq!(out.values(), &[2, 1, 13, 13, 0]);
654 }
655
656 #[test]
657 fn test_width_bucket_interval_mdn_day_nano_basic() {
658 let v = mdn_array(&[
659 (0, 0, 0),
660 (0, 5, 0),
661 (0, 9, 0),
662 (0, 10, 0),
663 (0, -1, 0),
664 (0, 11, 0),
665 ]);
666 let lo = mdn_array(&[(0, 0, 0); 6]);
667 let hi = mdn_array(&[(0, 10, 0); 6]);
668 let n = i64_array_all(6, 10);
669
670 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
671 let out = out.as_primitive::<Int64Type>();
672 assert_eq!(out.values(), &[1, 6, 10, 11, 0, 11]);
674 }
675
676 #[test]
677 fn test_width_bucket_interval_mdn_day_nano_desc() {
678 let v = mdn_array(&[(0, 9, 0), (0, 10, 0), (0, 0, 0), (0, -1, 0), (0, 11, 0)]);
679 let lo = mdn_array(&[(0, 10, 0); 5]);
680 let hi = mdn_array(&[(0, 0, 0); 5]);
681 let n = i64_array_all(5, 10);
682
683 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
684 let out = out.as_primitive::<Int64Type>();
685
686 assert_eq!(out.values(), &[2, 1, 11, 11, 0]);
687 }
688 #[test]
689 fn test_width_bucket_interval_mdn_day_nano_desc_inside() {
690 let v = mdn_array(&[(0, 9, 1), (0, 10, 0), (0, 0, 0), (0, -1, 0), (0, 11, 0)]);
691 let lo = mdn_array(&[(0, 10, 0); 5]);
692 let hi = mdn_array(&[(0, 0, 0); 5]);
693 let n = i64_array_all(5, 10);
694
695 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
696 let out = out.as_primitive::<Int64Type>();
697
698 assert_eq!(out.values(), &[1, 1, 11, 11, 0]);
699 }
700
701 #[test]
702 fn test_width_bucket_interval_mdn_mixed_months_and_days_is_null() {
703 let v = mdn_array(&[(0, 1, 0)]);
704 let lo = mdn_array(&[(0, 0, 0)]);
705 let hi = mdn_array(&[(1, 1, 0)]);
706 let n = i64_array_all(1, 4);
707
708 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
709 let out = out.as_primitive::<Int64Type>();
710 assert!(out.is_null(0));
711 }
712
713 #[test]
714 fn test_width_bucket_interval_mdn_equal_bounds_is_null() {
715 let v = mdn_array(&[(0, 0, 0)]);
716 let lo = mdn_array(&[(1, 2, 3)]);
717 let hi = mdn_array(&[(1, 2, 3)]); let n = i64_array_all(1, 10);
719
720 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
721 assert!(out.as_primitive::<Int64Type>().is_null(0));
722 }
723
724 #[test]
725 fn test_width_bucket_interval_mdn_invalid_n_is_null() {
726 let v = mdn_array(&[(0, 0, 0)]);
727 let lo = mdn_array(&[(0, 0, 0)]);
728 let hi = mdn_array(&[(0, 10, 0)]);
729 let n = Arc::new(Int64Array::from(vec![0])); let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
732 assert!(out.as_primitive::<Int64Type>().is_null(0));
733 }
734
735 #[test]
736 fn test_width_bucket_interval_mdn_nulls_propagate() {
737 let v = Arc::new(IntervalMonthDayNanoArray::from(vec![
738 None,
739 Some(IntervalMonthDayNano::new(0, 5, 0)),
740 ]));
741 let lo = mdn_array(&[(0, 0, 0), (0, 0, 0)]);
742 let hi = mdn_array(&[(0, 10, 0), (0, 10, 0)]);
743 let n = i64_array_all(2, 10);
744
745 let out = width_bucket_kern(&[v, lo, hi, n]).unwrap();
746 let out = out.as_primitive::<Int64Type>();
747 assert!(out.is_null(0));
748 assert_eq!(out.value(1), 6);
749 }
750
751 #[test]
754 fn test_width_bucket_wrong_arg_count() {
755 let v = f64_array(&[1.0]);
756 let lo = f64_array(&[0.0]);
757 let hi = f64_array(&[10.0]);
758 let err = width_bucket_kern(&[v, lo, hi]).unwrap_err();
759 let msg = format!("{err}");
760 assert!(msg.contains("expects exactly 4"), "unexpected error: {msg}");
761 }
762
763 #[test]
764 fn test_width_bucket_unsupported_type() {
765 let v: ArrayRef = Arc::new(Int32Array::from(vec![1, 2, 3]));
766 let lo = f64_array(&[0.0, 0.0, 0.0]);
767 let hi = f64_array(&[10.0, 10.0, 10.0]);
768 let n = i64_array_all(3, 10);
769
770 let err = width_bucket_kern(&[v, lo, hi, n]).unwrap_err();
771 let msg = format!("{err}");
772 assert!(
773 msg.contains("width_bucket received unexpected data types"),
774 "unexpected error: {msg}"
775 );
776 }
777}