Skip to main content

sample_arrow_rs/
primitive.rs

1//! Samplers for generating an arrow [`PrimitiveArray`].
2
3use std::marker::PhantomData;
4use std::ops::Range;
5use std::sync::Arc;
6
7use arrow_array::{ArrayRef, ArrowPrimitiveType, PrimitiveArray};
8use arrow_buffer::ScalarBuffer;
9use sample_std::{
10    arbitrary, sampler_choice, valid_f32, valid_f64, Random, Sample, Shrunk, VecSampler,
11};
12
13use crate::{ArrowLenSampler, ArrowSampler, Bitmap, SampleLen, SetLen};
14
15#[derive(Debug, Clone)]
16pub struct PrimitiveArraySampler<PT, V, T> {
17    len: usize,
18    inner: PT,
19    validity: V,
20    _phantom: PhantomData<T>,
21}
22
23impl<PT, V, T> SetLen for PrimitiveArraySampler<PT, V, T>
24where
25    V: SetLen,
26{
27    fn set_len(&mut self, len: usize) {
28        self.len = len;
29        self.validity.set_len(len);
30    }
31}
32
33impl<PT, V, T> SampleLen for PrimitiveArraySampler<PT, V, T>
34where
35    PT: Sample + 'static,
36    T: ArrowPrimitiveType + 'static,
37    T::Native: From<PT::Output>,
38    V: Sample<Output = Option<Bitmap>> + SetLen + 'static,
39{
40}
41
42impl<PT, V, T> Sample for PrimitiveArraySampler<PT, V, T>
43where
44    PT: Sample,
45    T: ArrowPrimitiveType,
46    T::Native: From<PT::Output>,
47    V: Sample<Output = Option<Bitmap>> + SetLen,
48{
49    type Output = ArrayRef;
50
51    fn generate(&mut self, g: &mut Random) -> Self::Output {
52        // Generate values and convert to T::Native
53        let values: Vec<T::Native> = (0..self.len)
54            .map(|_| T::Native::from(self.inner.generate(g)))
55            .collect();
56
57        // Convert to ScalarBuffer
58        let values_buffer = ScalarBuffer::from(values);
59
60        // Generate validity and convert to NullBuffer if present
61        let null_buffer = self.validity.generate(g).map(|bitmap| bitmap);
62
63        // Create the primitive array and return as Arc<dyn Array>
64        Arc::new(PrimitiveArray::<T>::new(values_buffer, null_buffer))
65    }
66
67    fn shrink(&self, _: Self::Output) -> Shrunk<Self::Output> {
68        Box::new(std::iter::empty())
69    }
70}
71
72pub fn primitive_len_sampler<PT, V, T>(inner: PT, validity: V) -> ArrowLenSampler
73where
74    PT: Sample + 'static,
75    T: ArrowPrimitiveType + 'static,
76    T::Native: From<PT::Output>,
77    V: Sample<Output = Option<Bitmap>> + SetLen + 'static,
78{
79    Box::new(PrimitiveArraySampler::<PT, V, T> {
80        len: 0,
81        inner,
82        validity,
83        _phantom: PhantomData,
84    })
85}
86
87// Helper to implement samplers for int types (i8, i16, i32, etc.)
88macro_rules! primitive_samplers {
89    ($type:ty, $arrow_type:ty, $fn_name:ident) => {
90        pub fn $fn_name(
91            validity: impl Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
92        ) -> ArrowLenSampler {
93            primitive_len_sampler::<_, _, $arrow_type>(arbitrary::<$type>(), validity)
94        }
95    };
96}
97
98// Implement primitive samplers for different arrow types
99primitive_samplers!(i8, arrow_array::types::Int8Type, i8_sampler);
100primitive_samplers!(i16, arrow_array::types::Int16Type, i16_sampler);
101primitive_samplers!(i32, arrow_array::types::Int32Type, i32_sampler);
102primitive_samplers!(i64, arrow_array::types::Int64Type, i64_sampler);
103primitive_samplers!(u8, arrow_array::types::UInt8Type, u8_sampler);
104primitive_samplers!(u16, arrow_array::types::UInt16Type, u16_sampler);
105primitive_samplers!(u32, arrow_array::types::UInt32Type, u32_sampler);
106primitive_samplers!(u64, arrow_array::types::UInt64Type, u64_sampler);
107primitive_samplers!(f32, arrow_array::types::Float32Type, f32_sampler);
108primitive_samplers!(f64, arrow_array::types::Float64Type, f64_sampler);
109
110pub fn valid_float_len_sampler<V>(valid: V) -> ArrowLenSampler
111where
112    V: Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
113{
114    Box::new(sampler_choice([
115        primitive_len_sampler::<_, _, arrow_array::types::Float32Type>(valid_f32(), valid.clone()),
116        primitive_len_sampler::<_, _, arrow_array::types::Float64Type>(valid_f64(), valid),
117    ]))
118}
119
120pub fn arbitrary_int_len_sampler<V>(valid: V) -> ArrowLenSampler
121where
122    V: Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
123{
124    Box::new(sampler_choice([
125        i8_sampler(valid.clone()),
126        i16_sampler(valid.clone()),
127        i32_sampler(valid.clone()),
128        i64_sampler(valid.clone()),
129    ]))
130}
131
132pub fn arbitrary_uint_len_sampler<V>(valid: V) -> ArrowLenSampler
133where
134    V: Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
135{
136    Box::new(sampler_choice([
137        u8_sampler(valid.clone()),
138        u16_sampler(valid.clone()),
139        u32_sampler(valid.clone()),
140        u64_sampler(valid.clone()),
141    ]))
142}
143
144pub fn valid_primitive_len<V>(valid: V) -> ArrowLenSampler
145where
146    V: Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
147{
148    Box::new(sampler_choice([
149        valid_float_len_sampler(valid.clone()),
150        arbitrary_int_len_sampler(valid.clone()),
151        arbitrary_uint_len_sampler(valid.clone()),
152    ]))
153}
154
155pub fn arbitrary_primitive_len<V>(valid: V) -> ArrowLenSampler
156where
157    V: Sample<Output = Option<Bitmap>> + SetLen + Clone + 'static,
158{
159    valid_primitive_len(valid)
160}
161
162#[derive(Debug, Clone)]
163pub struct ProtoNullablePrimitiveArray<PT, T> {
164    inner: VecSampler<Range<usize>, PT>,
165    _phantom: PhantomData<T>,
166}
167
168impl<PT, N, T> Sample for ProtoNullablePrimitiveArray<PT, T>
169where
170    PT: Sample<Output = Option<N>> + Clone + 'static,
171    N: Clone + 'static,
172    T: ArrowPrimitiveType + 'static,
173    T::Native: From<N>,
174{
175    type Output = ArrayRef;
176
177    fn generate(&mut self, g: &mut Random) -> Self::Output {
178        // Generate optional values
179        let values: Vec<Option<T::Native>> = self
180            .inner
181            .generate(g)
182            .into_iter()
183            .map(|opt| opt.map(T::Native::from))
184            .collect();
185
186        // Use from_iter to create a PrimitiveArray with nulls
187        Arc::new(PrimitiveArray::<T>::from_iter(values))
188    }
189
190    fn shrink(&self, _: Self::Output) -> Shrunk<Self::Output> {
191        Box::new(std::iter::empty())
192    }
193}
194
195#[derive(Debug, Clone)]
196pub struct ProtoPrimitiveArray<PT, T> {
197    inner: VecSampler<Range<usize>, PT>,
198    _phantom: PhantomData<T>,
199}
200
201impl<PT, N, T> Sample for ProtoPrimitiveArray<PT, T>
202where
203    PT: Sample<Output = N> + Clone + 'static,
204    N: Clone + 'static,
205    T: ArrowPrimitiveType + 'static,
206    T::Native: From<N>,
207{
208    type Output = ArrayRef;
209
210    fn generate(&mut self, g: &mut Random) -> Self::Output {
211        // Generate values
212        let values: Vec<T::Native> = self
213            .inner
214            .generate(g)
215            .into_iter()
216            .map(T::Native::from)
217            .collect();
218
219        // Create a ScalarBuffer
220        let buffer = ScalarBuffer::from(values);
221
222        // Create PrimitiveArray and return as Arc<dyn Array>
223        Arc::new(PrimitiveArray::<T>::new(buffer, None))
224    }
225
226    fn shrink(&self, _: Self::Output) -> Shrunk<Self::Output> {
227        Box::new(std::iter::empty())
228    }
229}
230
231#[derive(Clone)]
232pub struct ProtoBoxedNullablePrimitiveArray<PT, T> {
233    inner: ProtoNullablePrimitiveArray<PT, T>,
234}
235
236impl<PT, N, T> Sample for ProtoBoxedNullablePrimitiveArray<PT, T>
237where
238    PT: Sample<Output = Option<N>> + Clone + 'static,
239    N: Clone + 'static,
240    T: ArrowPrimitiveType + 'static,
241    T::Native: From<N>,
242{
243    type Output = ArrayRef;
244
245    fn generate(&mut self, g: &mut Random) -> Self::Output {
246        self.inner.generate(g)
247    }
248
249    fn shrink(&self, _: Self::Output) -> Shrunk<Self::Output> {
250        Box::new(std::iter::empty())
251    }
252}
253
254pub fn boxed_nullable<GT, N, T>(len: Range<usize>, el: GT) -> ArrowSampler
255where
256    GT: Sample<Output = Option<N>> + Send + Sync + Clone + 'static,
257    N: Clone + Send + Sync + 'static,
258    T: ArrowPrimitiveType + Send + Sync + 'static,
259    T::Native: From<N>,
260{
261    Box::new(ProtoBoxedNullablePrimitiveArray {
262        inner: ProtoNullablePrimitiveArray {
263            inner: VecSampler { length: len, el },
264            _phantom: PhantomData::<T>,
265        },
266    })
267}
268
269pub fn boxed<GT, N, T>(len: Range<usize>, el: GT) -> ArrowSampler
270where
271    GT: Sample<Output = N> + Send + Sync + Clone + 'static,
272    N: Clone + Send + Sync + 'static,
273    T: ArrowPrimitiveType + Send + Sync + 'static,
274    T::Native: From<N>,
275{
276    Box::new(ProtoPrimitiveArray {
277        inner: VecSampler { length: len, el },
278        _phantom: PhantomData::<T>,
279    })
280}
281
282#[derive(Clone)]
283struct Nullable<SI, V> {
284    inner: SI,
285    null: V,
286}
287
288impl<SI, V> Sample for Nullable<SI, V>
289where
290    SI: Sample,
291    V: Sample<Output = bool>,
292{
293    type Output = Option<SI::Output>;
294    fn generate(&mut self, g: &mut Random) -> Self::Output {
295        if self.null.generate(g) {
296            None
297        } else {
298            Some(self.inner.generate(g))
299        }
300    }
301
302    fn shrink(&self, v: Self::Output) -> Shrunk<Self::Output> {
303        if let Some(v) = v {
304            Box::new(std::iter::once(None).chain(self.inner.shrink(v).map(Some)))
305        } else {
306            Box::new(std::iter::empty())
307        }
308    }
309}
310
311// Helper macro to create boxed_primitive functions
312macro_rules! boxed_primitives {
313    ($type:ty, $arrow_type:ty, $fn_name:ident) => {
314        pub fn $fn_name<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
315        where
316            V: Sample<Output = bool> + Clone + Send + Sync + 'static,
317        {
318            match null {
319                Some(null) => boxed_nullable::<_, $type, $arrow_type>(
320                    len.clone(),
321                    Nullable {
322                        inner: arbitrary::<$type>(),
323                        null,
324                    },
325                ),
326                None => boxed::<_, $type, $arrow_type>(len.clone(), arbitrary::<$type>()),
327            }
328        }
329    };
330}
331
332// Implement boxed_primitive functions
333boxed_primitives!(i8, arrow_array::types::Int8Type, i8_array);
334boxed_primitives!(i16, arrow_array::types::Int16Type, i16_array);
335boxed_primitives!(i32, arrow_array::types::Int32Type, i32_array);
336boxed_primitives!(i64, arrow_array::types::Int64Type, i64_array);
337boxed_primitives!(u8, arrow_array::types::UInt8Type, u8_array);
338boxed_primitives!(u16, arrow_array::types::UInt16Type, u16_array);
339boxed_primitives!(u32, arrow_array::types::UInt32Type, u32_array);
340boxed_primitives!(u64, arrow_array::types::UInt64Type, u64_array);
341boxed_primitives!(f32, arrow_array::types::Float32Type, f32_array);
342boxed_primitives!(f64, arrow_array::types::Float64Type, f64_array);
343
344pub fn valid_float_array<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
345where
346    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
347{
348    Box::new(sampler_choice([
349        f32_array(len.clone(), null.clone()),
350        f64_array(len, null),
351    ]))
352}
353
354pub fn arbitrary_float_array<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
355where
356    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
357{
358    valid_float_array(len, null)
359}
360
361pub fn arbitrary_int_array<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
362where
363    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
364{
365    Box::new(sampler_choice([
366        i8_array(len.clone(), null.clone()),
367        i16_array(len.clone(), null.clone()),
368        i32_array(len.clone(), null.clone()),
369        i64_array(len.clone(), null.clone()),
370    ]))
371}
372
373pub fn arbitrary_uint_array<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
374where
375    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
376{
377    Box::new(sampler_choice([
378        u8_array(len.clone(), null.clone()),
379        u16_array(len.clone(), null.clone()),
380        u32_array(len.clone(), null.clone()),
381        u64_array(len.clone(), null.clone()),
382    ]))
383}
384
385pub fn valid_primitive<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
386where
387    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
388{
389    Box::new(sampler_choice([
390        valid_float_array(len.clone(), null.clone()),
391        arbitrary_int_array(len.clone(), null.clone()),
392        arbitrary_uint_array(len.clone(), null.clone()),
393    ]))
394}
395
396pub fn arbitrary_primitive<V>(len: Range<usize>, null: Option<V>) -> ArrowSampler
397where
398    V: Sample<Output = bool> + Clone + Send + Sync + 'static,
399{
400    valid_primitive(len, null)
401}
402
403#[cfg(test)]
404mod tests {
405    use sample_std::Chance;
406
407    use super::*;
408
409    #[test]
410    fn gen_float() {
411        let mut gen = valid_float_array(50..51, Some(Chance(0.5)));
412        let mut r = Random::new();
413        let arr = gen.generate(&mut r);
414        assert_eq!(arr.len(), 50);
415    }
416}