Skip to main content

arrow/util/
bench_util.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Utils to make benchmarking easier
19
20use crate::array::*;
21use crate::datatypes::*;
22use crate::util::test_util::seedable_rng;
23use arrow_buffer::{Buffer, IntervalMonthDayNano, NullBuffer};
24use arrow_schema::Field;
25use half::f16;
26use rand::Rng;
27use rand::SeedableRng;
28use rand::distr::uniform::SampleUniform;
29use rand::rng;
30use rand::{
31    distr::{Alphanumeric, Distribution, SampleString, StandardUniform},
32    prelude::StdRng,
33};
34use std::ops::Range;
35use std::sync::Arc;
36
37/// Creates an random (but fixed-seeded) array of a given size and null density
38pub fn create_primitive_array<T>(size: usize, null_density: f32) -> PrimitiveArray<T>
39where
40    T: ArrowPrimitiveType,
41    StandardUniform: Distribution<T::Native>,
42{
43    let mut rng = seedable_rng();
44
45    (0..size)
46        .map(|_| {
47            if rng.random::<f32>() < null_density {
48                None
49            } else {
50                Some(rng.random())
51            }
52        })
53        .collect()
54}
55
56/// Creates an random (but fixed-seeded) array of a given size and null density,
57/// all the values located in the given range
58pub fn create_primitive_array_range<T>(
59    size: usize,
60    null_density: f32,
61    value_range: Range<T::Native>,
62) -> PrimitiveArray<T>
63where
64    T: ArrowPrimitiveType,
65    StandardUniform: Distribution<T::Native>,
66    T::Native: SampleUniform,
67{
68    let mut rng = seedable_rng();
69
70    (0..size)
71        .map(|_| {
72            if rng.random::<f32>() < null_density {
73                None
74            } else {
75                Some(rng.random_range(value_range.clone()))
76            }
77        })
78        .collect()
79}
80
81/// Creates a [`PrimitiveArray`] of a given `size` and `null_density`
82/// filling it with random numbers generated using the provided `seed`.
83pub fn create_primitive_array_with_seed<T>(
84    size: usize,
85    null_density: f32,
86    seed: u64,
87) -> PrimitiveArray<T>
88where
89    T: ArrowPrimitiveType,
90    StandardUniform: Distribution<T::Native>,
91{
92    let mut rng = StdRng::seed_from_u64(seed);
93
94    (0..size)
95        .map(|_| {
96            if rng.random::<f32>() < null_density {
97                None
98            } else {
99                Some(rng.random())
100            }
101        })
102        .collect()
103}
104
105/// Creates a [`PrimitiveArray`] of a given `size` and `null_density`
106/// filling it with random [`IntervalMonthDayNano`] generated using the provided `seed`.
107pub fn create_month_day_nano_array_with_seed(
108    size: usize,
109    null_density: f32,
110    seed: u64,
111) -> IntervalMonthDayNanoArray {
112    let mut rng = StdRng::seed_from_u64(seed);
113
114    (0..size)
115        .map(|_| {
116            if rng.random::<f32>() < null_density {
117                None
118            } else {
119                Some(IntervalMonthDayNano::new(
120                    rng.random(),
121                    rng.random(),
122                    rng.random(),
123                ))
124            }
125        })
126        .collect()
127}
128
129/// Creates a random (but fixed-seeded) array of a given size and null density
130pub fn create_boolean_array(size: usize, null_density: f32, true_density: f32) -> BooleanArray
131where
132    StandardUniform: Distribution<bool>,
133{
134    let mut rng = seedable_rng();
135    (0..size)
136        .map(|_| {
137            if rng.random::<f32>() < null_density {
138                None
139            } else {
140                let value = rng.random::<f32>() < true_density;
141                Some(value)
142            }
143        })
144        .collect()
145}
146
147/// Creates a random array of a given size and null density based on the provided seed
148pub fn create_boolean_array_with_seed(
149    size: usize,
150    null_density: f32,
151    true_density: f32,
152    seed: u64,
153) -> BooleanArray
154where
155    StandardUniform: Distribution<bool>,
156{
157    let mut rng = StdRng::seed_from_u64(seed);
158    (0..size)
159        .map(|_| {
160            if rng.random::<f32>() < null_density {
161                None
162            } else {
163                let value = rng.random::<f32>() < true_density;
164                Some(value)
165            }
166        })
167        .collect()
168}
169
170/// Creates a random (but fixed-seeded) string array of a given size and null density.
171///
172/// Strings have a random length
173/// between 0 and 400 alphanumeric characters. `0..400` is chosen to cover a wide range of common string lengths,
174/// which have a dramatic impact on performance of some queries, e.g. LIKE/ILIKE/regex.
175pub fn create_string_array<Offset: OffsetSizeTrait>(
176    size: usize,
177    null_density: f32,
178) -> GenericStringArray<Offset> {
179    create_string_array_with_max_len(size, null_density, 400)
180}
181
182/// Creates longer string array with same prefix, the prefix should be larger than 4 bytes,
183/// and the string length should be larger than 12 bytes
184/// so that we can compare the performance with StringViewArray, because StringViewArray has 4 bytes inline for view
185pub fn create_longer_string_array_with_same_prefix<Offset: OffsetSizeTrait>(
186    size: usize,
187    null_density: f32,
188) -> GenericStringArray<Offset> {
189    create_string_array_with_len_range_and_prefix(size, null_density, 13, 100, "prefix_")
190}
191
192/// Creates longer string view array with same prefix, the prefix should be larger than 4 bytes,
193/// and the string length should be larger than 12 bytes
194/// so that we can compare the StringArray performance with StringViewArray, because StringViewArray has 4 bytes inline for view
195pub fn create_longer_string_view_array_with_same_prefix(
196    size: usize,
197    null_density: f32,
198) -> StringViewArray {
199    create_string_view_array_with_len_range_and_prefix(size, null_density, 13, 100, "prefix_")
200}
201
202fn create_string_array_with_len_range_and_prefix<Offset: OffsetSizeTrait>(
203    size: usize,
204    null_density: f32,
205    min_str_len: usize,
206    max_str_len: usize,
207    prefix: &str,
208) -> GenericStringArray<Offset> {
209    create_string_array_with_len_range_and_prefix_and_seed(
210        size,
211        null_density,
212        min_str_len,
213        max_str_len,
214        prefix,
215        42,
216    )
217}
218
219/// Creates a random [`GenericStringArray`] of a given `size` and `null_density`
220/// filling it with random strings with lengths in the specified range,
221/// all starting with the provided `prefix`, generated using the provided `seed`.
222pub fn create_string_array_with_len_range_and_prefix_and_seed<Offset: OffsetSizeTrait>(
223    size: usize,
224    null_density: f32,
225    min_str_len: usize,
226    max_str_len: usize,
227    prefix: &str,
228    seed: u64,
229) -> GenericStringArray<Offset> {
230    assert!(
231        min_str_len <= max_str_len,
232        "min_str_len must be <= max_str_len"
233    );
234    assert!(
235        prefix.len() <= max_str_len,
236        "Prefix length must be <= max_str_len"
237    );
238
239    let rng = &mut StdRng::seed_from_u64(seed);
240    (0..size)
241        .map(|_| {
242            if rng.random::<f32>() < null_density {
243                None
244            } else {
245                let remaining_len = rng.random_range(
246                    min_str_len.saturating_sub(prefix.len())..=(max_str_len - prefix.len()),
247                );
248
249                let mut value = prefix.to_string();
250                value.extend(
251                    rng.sample_iter(&Alphanumeric)
252                        .take(remaining_len)
253                        .map(char::from),
254                );
255
256                Some(value)
257            }
258        })
259        .collect()
260}
261/// Creates a string view array of a given range, null density and length
262///
263/// Arguments:
264/// - `size`: number of  string view array
265/// - `null_density`: density of nulls in the string view array
266/// - `range`: range size of each string in the string view array
267/// - `seed`: seed for the random number generator
268pub fn create_string_view_array_with_len_range_and_seed(
269    size: usize,
270    null_density: f32,
271    range: Range<usize>,
272    seed: u64,
273) -> StringViewArray {
274    let rng = &mut StdRng::seed_from_u64(seed);
275    (0..size)
276        .map(|_| {
277            if rng.random::<f32>() < null_density {
278                None
279            } else {
280                let str_len = rng.random_range(range.clone());
281                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
282                let value = String::from_utf8(value).unwrap();
283                Some(value)
284            }
285        })
286        .collect()
287}
288
289fn create_string_view_array_with_len_range_and_prefix(
290    size: usize,
291    null_density: f32,
292    min_str_len: usize,
293    max_str_len: usize,
294    prefix: &str,
295) -> StringViewArray {
296    assert!(
297        min_str_len <= max_str_len,
298        "min_str_len must be <= max_str_len"
299    );
300    assert!(
301        prefix.len() <= max_str_len,
302        "Prefix length must be <= max_str_len"
303    );
304
305    let rng = &mut seedable_rng();
306    (0..size)
307        .map(|_| {
308            if rng.random::<f32>() < null_density {
309                None
310            } else {
311                let remaining_len = rng.random_range(
312                    min_str_len.saturating_sub(prefix.len())..=(max_str_len - prefix.len()),
313                );
314
315                let mut value = prefix.to_string();
316                value.extend(
317                    rng.sample_iter(&Alphanumeric)
318                        .take(remaining_len)
319                        .map(char::from),
320                );
321
322                Some(value)
323            }
324        })
325        .collect()
326}
327
328/// Creates a random (but fixed-seeded) array of rand size with a given max size, null density and length
329pub fn create_string_array_with_max_len<Offset: OffsetSizeTrait>(
330    size: usize,
331    null_density: f32,
332    max_str_len: usize,
333) -> GenericStringArray<Offset> {
334    let rng = &mut seedable_rng();
335    (0..size)
336        .map(|_| {
337            if rng.random::<f32>() < null_density {
338                None
339            } else {
340                let str_len = rng.random_range(0..max_str_len);
341                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
342                let value = String::from_utf8(value).unwrap();
343                Some(value)
344            }
345        })
346        .collect()
347}
348
349/// Creates a random (but fixed-seeded) array of a given size, null density and length
350pub fn create_string_array_with_len<Offset: OffsetSizeTrait>(
351    size: usize,
352    null_density: f32,
353    str_len: usize,
354) -> GenericStringArray<Offset> {
355    let rng = &mut seedable_rng();
356
357    (0..size)
358        .map(|_| {
359            if rng.random::<f32>() < null_density {
360                None
361            } else {
362                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
363                let value = String::from_utf8(value).unwrap();
364                Some(value)
365            }
366        })
367        .collect()
368}
369
370/// Creates a random (but fixed-seeded) string view array of a given size and null density.
371///
372/// See `create_string_array` above for more details.
373pub fn create_string_view_array(size: usize, null_density: f32) -> StringViewArray {
374    create_string_view_array_with_max_len(size, null_density, 400)
375}
376
377/// Creates a random (but fixed-seeded) array of rand size with a given max size, null density and length
378pub fn create_string_view_array_with_max_len(
379    size: usize,
380    null_density: f32,
381    max_str_len: usize,
382) -> StringViewArray {
383    let rng = &mut seedable_rng();
384    (0..size)
385        .map(|_| {
386            if rng.random::<f32>() < null_density {
387                None
388            } else {
389                let str_len = rng.random_range(0..max_str_len);
390                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
391                let value = String::from_utf8(value).unwrap();
392                Some(value)
393            }
394        })
395        .collect()
396}
397
398/// Creates a random (but fixed-seeded) array of a given size, null density and length
399pub fn create_string_view_array_with_fixed_len(
400    size: usize,
401    null_density: f32,
402    str_len: usize,
403) -> StringViewArray {
404    let rng = &mut seedable_rng();
405    (0..size)
406        .map(|_| {
407            if rng.random::<f32>() < null_density {
408                None
409            } else {
410                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
411                let value = String::from_utf8(value).unwrap();
412                Some(value)
413            }
414        })
415        .collect()
416}
417
418/// Creates a random (but fixed-seeded) array of a given size, null density and length
419pub fn create_string_view_array_with_len(
420    size: usize,
421    null_density: f32,
422    str_len: usize,
423    mixed: bool,
424) -> StringViewArray {
425    let rng = &mut seedable_rng();
426
427    let mut lengths = Vec::with_capacity(size);
428
429    // if mixed, we creates first half that string length small than 12 bytes and second half large than 12 bytes
430    if mixed {
431        for _ in 0..size / 2 {
432            lengths.push(rng.random_range(1..12));
433        }
434        for _ in size / 2..size {
435            lengths.push(rng.random_range(12..=std::cmp::max(30, str_len)));
436        }
437    } else {
438        lengths.resize(size, str_len);
439    }
440
441    lengths
442        .into_iter()
443        .map(|len| {
444            if rng.random::<f32>() < null_density {
445                None
446            } else {
447                let value: Vec<u8> = rng.sample_iter(&Alphanumeric).take(len).collect();
448                Some(String::from_utf8(value).unwrap())
449            }
450        })
451        .collect()
452}
453
454/// Creates an random (but fixed-seeded) array of a given size and null density
455/// consisting of random 4 character alphanumeric strings
456pub fn create_string_dict_array<K: ArrowDictionaryKeyType>(
457    size: usize,
458    null_density: f32,
459    str_len: usize,
460) -> DictionaryArray<K> {
461    let rng = &mut seedable_rng();
462
463    let data: Vec<_> = (0..size)
464        .map(|_| {
465            if rng.random::<f32>() < null_density {
466                None
467            } else {
468                let value = rng.sample_iter(&Alphanumeric).take(str_len).collect();
469                let value = String::from_utf8(value).unwrap();
470                Some(value)
471            }
472        })
473        .collect();
474
475    data.iter().map(|x| x.as_deref()).collect()
476}
477
478/// Create a List/LargeList Array  of primitive values
479///
480/// Arguments:
481/// - `size`: number of lists in the array
482/// - `null_density`: density of nulls in the list array
483/// - `list_null_density`: density of nulls in the primitive arrays inside the lists
484/// - `max_list_size`: maximum size of each list (actual size is random between 0 and max_list_size)
485/// - `seed`: seed for the random number generator
486pub fn create_primitive_list_array_with_seed<O, T>(
487    size: usize,
488    null_density: f32,
489    list_null_density: f32,
490    max_list_size: usize,
491    seed: u64,
492) -> GenericListArray<O>
493where
494    O: OffsetSizeTrait,
495    T: ArrowPrimitiveType,
496    StandardUniform: Distribution<T::Native>,
497{
498    let mut rng = StdRng::seed_from_u64(seed);
499
500    let values = (0..size).map(|_| {
501        if rng.random::<f32>() < null_density {
502            None
503        } else {
504            let list_size = rng.random_range(0..=max_list_size);
505            let list_values: Vec<Option<T::Native>> = (0..list_size)
506                .map(|_| {
507                    if rng.random::<f32>() < list_null_density {
508                        None
509                    } else {
510                        Some(rng.random())
511                    }
512                })
513                .collect();
514            Some(list_values)
515        }
516    });
517
518    GenericListArray::<O>::from_iter_primitive::<T, _, _>(values)
519}
520
521/// Create a List/LargeList Array of primitive values using a fixed seed
522///
523/// See [`create_primitive_list_array_with_seed`] for details on arguments.
524pub fn create_primitive_list_array<O, T>(
525    size: usize,
526    null_density: f32,
527    list_null_density: f32,
528    max_list_size: usize,
529) -> GenericListArray<O>
530where
531    O: OffsetSizeTrait,
532    T: ArrowPrimitiveType,
533    StandardUniform: Distribution<T::Native>,
534{
535    let mut rng = seedable_rng();
536
537    let values = (0..size).map(|_| {
538        if rng.random::<f32>() < null_density {
539            None
540        } else {
541            let list_size = rng.random_range(0..=max_list_size);
542            let list_values: Vec<Option<T::Native>> = (0..list_size)
543                .map(|_| {
544                    if rng.random::<f32>() < list_null_density {
545                        None
546                    } else {
547                        Some(rng.random())
548                    }
549                })
550                .collect();
551            Some(list_values)
552        }
553    });
554
555    GenericListArray::<O>::from_iter_primitive::<T, _, _>(values)
556}
557
558/// Create a ListViewArray of primitive values using a fixed seed
559///
560/// See [`create_primitive_list_array_with_seed`] for details on arguments.
561pub fn create_primitive_list_view_array<O, T>(
562    size: usize,
563    null_density: f32,
564    list_null_density: f32,
565    max_list_size: usize,
566) -> GenericListViewArray<O>
567where
568    T: ArrowPrimitiveType,
569    StandardUniform: Distribution<T::Native>,
570    O: OffsetSizeTrait,
571{
572    let mut rng = seedable_rng();
573
574    let values = (0..size).map(|_| {
575        if rng.random::<f32>() < null_density {
576            None
577        } else {
578            let list_size = rng.random_range(0..=max_list_size);
579            let list_values: Vec<Option<T::Native>> = (0..list_size)
580                .map(|_| {
581                    if rng.random::<f32>() < list_null_density {
582                        None
583                    } else {
584                        Some(rng.random())
585                    }
586                })
587                .collect();
588            Some(list_values)
589        }
590    });
591
592    GenericListViewArray::<O>::from_iter_primitive::<T, _, _>(values)
593}
594
595/// Create primitive run array for given logical and physical array lengths
596pub fn create_primitive_run_array<R: RunEndIndexType, V: ArrowPrimitiveType>(
597    logical_array_len: usize,
598    physical_array_len: usize,
599) -> RunArray<R> {
600    assert!(logical_array_len >= physical_array_len);
601    // typical length of each run
602    let run_len = logical_array_len / physical_array_len;
603
604    // Some runs should have extra length
605    let mut run_len_extra = logical_array_len % physical_array_len;
606
607    let mut values: Vec<V::Native> = (0..physical_array_len)
608        .flat_map(|s| {
609            let mut take_len = run_len;
610            if run_len_extra > 0 {
611                take_len += 1;
612                run_len_extra -= 1;
613            }
614            std::iter::repeat_n(V::Native::from_usize(s).unwrap(), take_len)
615        })
616        .collect();
617    while values.len() < logical_array_len {
618        let last_val = values[values.len() - 1];
619        values.push(last_val);
620    }
621    let mut builder = PrimitiveRunBuilder::<R, V>::with_capacity(physical_array_len);
622    builder.extend(values.into_iter().map(Some));
623
624    builder.finish()
625}
626
627/// Create string array to be used by run array builder. The string array
628/// will result in run array with physical length of `physical_array_len`
629/// and logical length of `logical_array_len`
630pub fn create_string_array_for_runs(
631    physical_array_len: usize,
632    logical_array_len: usize,
633    string_len: usize,
634) -> Vec<String> {
635    assert!(logical_array_len >= physical_array_len);
636    let mut rng = rng();
637
638    // typical length of each run
639    let run_len = logical_array_len / physical_array_len;
640
641    // Some runs should have extra length
642    let mut run_len_extra = logical_array_len % physical_array_len;
643
644    let mut values: Vec<String> = (0..physical_array_len)
645        .map(|_| (0..string_len).map(|_| rng.random::<char>()).collect())
646        .flat_map(|s| {
647            let mut take_len = run_len;
648            if run_len_extra > 0 {
649                take_len += 1;
650                run_len_extra -= 1;
651            }
652            std::iter::repeat_n(s, take_len)
653        })
654        .collect();
655    while values.len() < logical_array_len {
656        let last_val = values[values.len() - 1].clone();
657        values.push(last_val);
658    }
659    values
660}
661
662/// Creates an random (but fixed-seeded) binary array of a given size and null density
663pub fn create_binary_array<Offset: OffsetSizeTrait>(
664    size: usize,
665    null_density: f32,
666) -> GenericBinaryArray<Offset> {
667    create_binary_array_with_seed(
668        size,
669        null_density,
670        42, // bytes_seed
671        42, // bytes_length_seed
672    )
673}
674
675/// Creates a random [`GenericBinaryArray`] of a given `size` and `null_density`
676/// filling it with random bytes, generated using the provided `seed`s.
677///
678/// the `bytes_seed` is used to seed the RNG for generating the byte values,
679/// while the `bytes_length_seed` is used to seed the RNG for generating the length of an array item
680///
681/// These values can be the same as they are used to seed different RNGs internally.
682pub fn create_binary_array_with_seed<Offset: OffsetSizeTrait>(
683    size: usize,
684    null_density: f32,
685    bytes_seed: u64,
686    bytes_length_seed: u64,
687) -> GenericBinaryArray<Offset> {
688    let rng = &mut StdRng::seed_from_u64(bytes_seed);
689    let range_rng = &mut StdRng::seed_from_u64(bytes_length_seed);
690
691    (0..size)
692        .map(|_| {
693            if rng.random::<f32>() < null_density {
694                None
695            } else {
696                let value = rng
697                    .sample_iter::<u8, _>(StandardUniform)
698                    .take(range_rng.random_range(0..8))
699                    .collect::<Vec<u8>>();
700                Some(value)
701            }
702        })
703        .collect()
704}
705
706/// Creates a random [`GenericBinaryArray`] of a given `size` and `null_density`
707/// filling it with random bytes with lengths in the specified range,
708/// all starting with the provided `prefix`, generated using the provided `seed`.
709///
710pub fn create_binary_array_with_len_range_and_prefix_and_seed<Offset: OffsetSizeTrait>(
711    size: usize,
712    null_density: f32,
713    min_len: usize,
714    max_len: usize,
715    prefix: &[u8],
716    seed: u64,
717) -> GenericBinaryArray<Offset> {
718    assert!(min_len <= max_len, "min_len must be <= max_len");
719    assert!(prefix.len() <= max_len, "Prefix length must be <= max_len");
720
721    let rng = &mut StdRng::seed_from_u64(seed);
722    (0..size)
723        .map(|_| {
724            if rng.random::<f32>() < null_density {
725                None
726            } else {
727                let remaining_len = rng
728                    .random_range(min_len.saturating_sub(prefix.len())..=(max_len - prefix.len()));
729
730                let remaining = rng
731                    .sample_iter::<u8, _>(StandardUniform)
732                    .take(remaining_len);
733
734                let value = prefix.iter().copied().chain(remaining).collect::<Vec<u8>>();
735                Some(value)
736            }
737        })
738        .collect()
739}
740
741/// Creates an random (but fixed-seeded) array of a given size and null density
742pub fn create_fsb_array(size: usize, null_density: f32, value_len: usize) -> FixedSizeBinaryArray {
743    let rng = &mut seedable_rng();
744
745    FixedSizeBinaryArray::try_from_sparse_iter_with_size(
746        (0..size).map(|_| {
747            if rng.random::<f32>() < null_density {
748                None
749            } else {
750                let value = rng
751                    .sample_iter::<u8, _>(StandardUniform)
752                    .take(value_len)
753                    .collect::<Vec<u8>>();
754                Some(value)
755            }
756        }),
757        value_len as i32,
758    )
759    .unwrap()
760}
761
762/// Creates a random (but fixed-seeded) dictionary array of a given size and null density
763/// with the provided values array
764pub fn create_dict_from_values<K>(
765    size: usize,
766    null_density: f32,
767    values: &dyn Array,
768) -> DictionaryArray<K>
769where
770    K: ArrowDictionaryKeyType,
771    StandardUniform: Distribution<K::Native>,
772    K::Native: SampleUniform,
773{
774    let min_key = K::Native::from_usize(0).unwrap();
775    let max_key = K::Native::from_usize(values.len()).unwrap();
776    create_sparse_dict_from_values(size, null_density, values, min_key..max_key)
777}
778
779/// Creates a random (but fixed-seeded) dictionary array of a given size and null density
780/// with the provided values array and key range
781pub fn create_sparse_dict_from_values<K>(
782    size: usize,
783    null_density: f32,
784    values: &dyn Array,
785    key_range: Range<K::Native>,
786) -> DictionaryArray<K>
787where
788    K: ArrowDictionaryKeyType,
789    StandardUniform: Distribution<K::Native>,
790    K::Native: SampleUniform,
791{
792    let mut rng = seedable_rng();
793    let data_type =
794        DataType::Dictionary(Box::new(K::DATA_TYPE), Box::new(values.data_type().clone()));
795
796    let keys: Buffer = (0..size)
797        .map(|_| rng.random_range(key_range.clone()))
798        .collect();
799
800    let nulls: Option<Buffer> = (null_density != 0.).then(|| {
801        (0..size)
802            .map(|_| rng.random_bool((1.0 - null_density) as _))
803            .collect()
804    });
805
806    let data = ArrayDataBuilder::new(data_type)
807        .len(size)
808        .null_bit_buffer(nulls)
809        .add_buffer(keys)
810        .add_child_data(values.to_data())
811        .build()
812        .unwrap();
813
814    DictionaryArray::from(data)
815}
816
817/// Creates a random (but fixed-seeded) f16 array of a given size and nan-value density
818pub fn create_f16_array(size: usize, nan_density: f32) -> Float16Array {
819    let mut rng = seedable_rng();
820
821    (0..size)
822        .map(|_| {
823            if rng.random::<f32>() < nan_density {
824                Some(f16::NAN)
825            } else {
826                Some(rng.random())
827            }
828        })
829        .collect()
830}
831
832/// Creates a random (but fixed-seeded) f32 array of a given size and nan-value density
833pub fn create_f32_array(size: usize, nan_density: f32) -> Float32Array {
834    let mut rng = seedable_rng();
835
836    (0..size)
837        .map(|_| {
838            if rng.random::<f32>() < nan_density {
839                Some(f32::NAN)
840            } else {
841                Some(rng.random())
842            }
843        })
844        .collect()
845}
846
847/// Creates a random (but fixed-seeded) f64 array of a given size and nan-value density
848pub fn create_f64_array(size: usize, nan_density: f32) -> Float64Array {
849    let mut rng = seedable_rng();
850
851    (0..size)
852        .map(|_| {
853            if rng.random::<f32>() < nan_density {
854                Some(f64::NAN)
855            } else {
856                Some(rng.random())
857            }
858        })
859        .collect()
860}
861
862/// Creates a random f64 array of a given size and nan-value density based on a given seed
863pub fn create_f64_array_with_seed(size: usize, nan_density: f32, seed: u64) -> Float64Array {
864    let mut rng = StdRng::seed_from_u64(seed);
865
866    (0..size)
867        .map(|_| {
868            if rng.random::<f32>() < nan_density {
869                Some(f64::NAN)
870            } else {
871                Some(rng.random())
872            }
873        })
874        .collect()
875}
876
877/// Create a FixedSizeList array of primitive values
878///
879/// Arguments:
880/// - `size`: number of fixed-size lists in the array
881/// - `null_density`: density of nulls in the fixed-size list array (row-level nulls)
882/// - `value_null_density`: density of nulls in the primitive values inside each list
883/// - `list_size`: fixed size of each list element
884pub fn create_primitive_fixed_size_list_array<T>(
885    size: usize,
886    null_density: f32,
887    value_null_density: f32,
888    list_size: i32,
889) -> FixedSizeListArray
890where
891    T: ArrowPrimitiveType,
892    StandardUniform: Distribution<T::Native>,
893{
894    let mut rng = seedable_rng();
895    let list_size_usize = usize::try_from(list_size).expect("list_size must be non-negative");
896    let values: PrimitiveArray<T> = (0..size * list_size_usize)
897        .map(|_| {
898            if rng.random::<f32>() < value_null_density {
899                None
900            } else {
901                Some(rng.random())
902            }
903        })
904        .collect();
905    let field = Arc::new(Field::new("item", T::DATA_TYPE, value_null_density > 0.0));
906    let nulls = (null_density > 0.0).then(|| {
907        NullBuffer::new(arrow_buffer::BooleanBuffer::collect_bool(size, |_| {
908            rng.random::<f32>() >= null_density
909        }))
910    });
911    FixedSizeListArray::new(field, list_size, Arc::new(values), nulls)
912}
913
914/// Create a Map array with string keys and primitive values
915///
916/// Arguments:
917/// - `size`: number of map entries in the array
918/// - `null_density`: density of nulls in the map array (row-level nulls)
919/// - `max_map_size`: maximum number of key-value pairs per map entry
920///   (actual size is random between 0 and max_map_size)
921/// - `key_len`: length of each random string key
922pub fn create_string_map_array<T>(
923    size: usize,
924    null_density: f32,
925    max_map_size: usize,
926    key_len: usize,
927) -> MapArray
928where
929    T: ArrowPrimitiveType,
930    StandardUniform: Distribution<T::Native>,
931{
932    let mut rng = seedable_rng();
933    let mut builder = MapBuilder::new(None, StringBuilder::new(), PrimitiveBuilder::<T>::new());
934    for _ in 0..size {
935        if rng.random::<f32>() < null_density {
936            builder.append(false).unwrap();
937        } else {
938            let n = rng.random_range(0..=max_map_size);
939            for _ in 0..n {
940                builder
941                    .keys()
942                    .append_value(Alphanumeric.sample_string(&mut rng, key_len));
943                builder.values().append_value(rng.random());
944            }
945            builder.append(true).unwrap();
946        }
947    }
948    builder.finish()
949}