1use std::fmt::Display;
5use std::fmt::Formatter;
6use std::hash::Hash;
7use std::hash::Hasher;
8
9use num_traits::cast::FromPrimitive;
10use prost::Message;
11use smallvec::smallvec;
12use vortex_array::Array;
13use vortex_array::ArrayEq;
14use vortex_array::ArrayHash;
15use vortex_array::ArrayId;
16use vortex_array::ArrayParts;
17use vortex_array::ArrayRef;
18use vortex_array::ArrayView;
19use vortex_array::EqMode;
20use vortex_array::ExecutionCtx;
21use vortex_array::ExecutionResult;
22use vortex_array::buffer::BufferHandle;
23use vortex_array::dtype::DType;
24use vortex_array::dtype::NativePType;
25use vortex_array::dtype::Nullability;
26use vortex_array::dtype::Nullability::NonNullable;
27use vortex_array::dtype::PType;
28use vortex_array::expr::stats::Precision as StatPrecision;
29use vortex_array::expr::stats::Stat;
30use vortex_array::match_each_integer_ptype;
31use vortex_array::match_each_native_ptype;
32use vortex_array::match_each_pvalue;
33use vortex_array::scalar::PValue;
34use vortex_array::scalar::Scalar;
35use vortex_array::scalar::ScalarValue;
36use vortex_array::serde::ArrayChildren;
37use vortex_array::stats::StatsSet;
38use vortex_array::validity::Validity;
39use vortex_array::vtable::OperationsVTable;
40use vortex_array::vtable::VTable;
41use vortex_array::vtable::ValidityVTable;
42use vortex_error::VortexExpect;
43use vortex_error::VortexResult;
44use vortex_error::vortex_bail;
45use vortex_error::vortex_ensure;
46use vortex_error::vortex_err;
47use vortex_error::vortex_panic;
48use vortex_session::VortexSession;
49use vortex_session::registry::CachedId;
50
51use crate::compress::sequence_decompress;
52use crate::rules::RULES;
53
54pub type SequenceArray = Array<Sequence>;
56
57#[derive(Clone, prost::Message)]
58pub struct SequenceMetadata {
59 #[prost(message, tag = "1")]
60 base: Option<vortex_proto::scalar::ScalarValue>,
61 #[prost(message, tag = "2")]
62 multiplier: Option<vortex_proto::scalar::ScalarValue>,
63}
64
65pub(super) const SLOT_NAMES: [&str; 0] = [];
66
67#[derive(Clone, Debug)]
68pub struct SequenceData {
70 base: PValue,
71 multiplier: PValue,
72}
73
74impl Display for SequenceData {
75 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
76 write!(f, "base: {}, multiplier: {}", self.base, self.multiplier)
77 }
78}
79
80pub struct SequenceDataParts {
81 pub base: PValue,
82 pub multiplier: PValue,
83 pub ptype: PType,
84}
85
86impl SequenceData {
87 pub(crate) fn try_new_typed<T: NativePType + Into<PValue>>(
88 base: T,
89 multiplier: T,
90 nullability: Nullability,
91 length: usize,
92 ) -> VortexResult<Self> {
93 Self::try_new(
94 base.into(),
95 multiplier.into(),
96 T::PTYPE,
97 nullability,
98 length,
99 )
100 }
101
102 pub(crate) fn try_new(
104 base: PValue,
105 multiplier: PValue,
106 ptype: PType,
107 nullability: Nullability,
108 length: usize,
109 ) -> VortexResult<Self> {
110 let dtype = DType::Primitive(ptype, nullability);
111 Self::validate(base, multiplier, &dtype, length)?;
112 let (base, multiplier) = Self::normalize(base, multiplier, ptype)?;
113
114 Ok(unsafe { Self::new_unchecked(base, multiplier) })
115 }
116
117 pub fn validate(
118 base: PValue,
119 multiplier: PValue,
120 dtype: &DType,
121 length: usize,
122 ) -> VortexResult<()> {
123 let DType::Primitive(ptype, _) = dtype else {
124 vortex_bail!("only primitive dtypes are supported in SequenceArray currently");
125 };
126
127 if !ptype.is_int() {
128 vortex_bail!("only integer ptype are supported in SequenceArray currently")
129 }
130
131 vortex_ensure!(length > 0, "SequenceArray length must be greater than zero");
132 Self::try_last(base, multiplier, *ptype, length).map_err(|e| {
133 e.with_context(format!(
134 "final value not expressible, base = {base:?}, multiplier = {multiplier:?}, len = {length} ",
135 ))
136 })?;
137
138 Ok(())
139 }
140
141 fn normalize(base: PValue, multiplier: PValue, ptype: PType) -> VortexResult<(PValue, PValue)> {
142 match_each_integer_ptype!(ptype, |P| {
143 Ok((
144 PValue::from(base.cast::<P>()?),
145 PValue::from(multiplier.cast::<P>()?),
146 ))
147 })
148 }
149
150 pub(crate) unsafe fn new_unchecked(base: PValue, multiplier: PValue) -> Self {
158 Self { base, multiplier }
159 }
160
161 pub fn ptype(&self) -> PType {
162 self.base.ptype()
163 }
164
165 pub fn base(&self) -> PValue {
166 self.base
167 }
168
169 pub fn multiplier(&self) -> PValue {
170 self.multiplier
171 }
172
173 pub fn into_parts(self) -> SequenceDataParts {
174 SequenceDataParts {
175 base: self.base,
176 multiplier: self.multiplier,
177 ptype: self.base.ptype(),
178 }
179 }
180
181 pub(crate) fn try_last(
182 base: PValue,
183 multiplier: PValue,
184 ptype: PType,
185 length: usize,
186 ) -> VortexResult<PValue> {
187 match_each_integer_ptype!(ptype, |P| {
188 let len_t = <P>::from_usize(length - 1)
189 .ok_or_else(|| vortex_err!("cannot convert length {} into {}", length, ptype))?;
190
191 let base = base.cast::<P>()?;
192 let multiplier = multiplier.cast::<P>()?;
193 let last = len_t
194 .checked_mul(multiplier)
195 .and_then(|offset| offset.checked_add(base))
196 .ok_or_else(|| vortex_err!("last value computation overflows"))?;
197 Ok(PValue::from(last))
198 })
199 }
200
201 pub(crate) fn index_value(&self, idx: usize) -> PValue {
202 match_each_native_ptype!(self.ptype(), |P| {
203 let base = self.base.cast::<P>().vortex_expect("must be able to cast");
204 let multiplier = self
205 .multiplier
206 .cast::<P>()
207 .vortex_expect("must be able to cast");
208 let value = base + (multiplier * <P>::from_usize(idx).vortex_expect("must fit"));
209
210 PValue::from(value)
211 })
212 }
213}
214
215impl ArrayHash for SequenceData {
216 fn array_hash<H: Hasher>(&self, state: &mut H, _accuracy: EqMode) {
217 self.base.hash(state);
218 self.multiplier.hash(state);
219 }
220}
221
222impl ArrayEq for SequenceData {
223 fn array_eq(&self, other: &Self, _accuracy: EqMode) -> bool {
224 self.base == other.base && self.multiplier == other.multiplier
225 }
226}
227
228impl VTable for Sequence {
229 type TypedArrayData = SequenceData;
230
231 type OperationsVTable = Self;
232 type ValidityVTable = Self;
233
234 fn id(&self) -> ArrayId {
235 static ID: CachedId = CachedId::new("vortex.sequence");
236 *ID
237 }
238
239 fn validate(
240 &self,
241 data: &Self::TypedArrayData,
242 dtype: &DType,
243 len: usize,
244 _slots: &[Option<ArrayRef>],
245 ) -> VortexResult<()> {
246 SequenceData::validate(data.base, data.multiplier, dtype, len)
247 }
248
249 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
250 0
251 }
252
253 fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
254 vortex_panic!("SequenceArray buffer index {idx} out of bounds")
255 }
256
257 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
258 vortex_panic!("SequenceArray buffer_name index {idx} out of bounds")
259 }
260
261 fn with_buffers(
262 &self,
263 array: ArrayView<'_, Self>,
264 buffers: &[BufferHandle],
265 ) -> VortexResult<ArrayParts<Self>> {
266 vortex_array::vtable::with_empty_buffers(self, array, buffers)
267 }
268
269 fn serialize(
270 array: ArrayView<'_, Self>,
271 _session: &VortexSession,
272 ) -> VortexResult<Option<Vec<u8>>> {
273 let metadata = SequenceMetadata {
274 base: Some((&array.base()).into()),
275 multiplier: Some((&array.multiplier()).into()),
276 };
277
278 Ok(Some(metadata.encode_to_vec()))
279 }
280
281 fn deserialize(
282 &self,
283 dtype: &DType,
284 len: usize,
285 metadata: &[u8],
286 buffers: &[BufferHandle],
287 children: &dyn ArrayChildren,
288 session: &VortexSession,
289 ) -> VortexResult<ArrayParts<Self>> {
290 vortex_ensure!(
291 buffers.is_empty(),
292 "SequenceArray expects 0 buffers, got {}",
293 buffers.len()
294 );
295 vortex_ensure!(
296 children.is_empty(),
297 "SequenceArray expects 0 children, got {}",
298 children.len()
299 );
300 let metadata = SequenceMetadata::decode(metadata)?;
301
302 let ptype = dtype.as_ptype();
303
304 let base = Scalar::from_proto_value(
306 metadata
307 .base
308 .as_ref()
309 .ok_or_else(|| vortex_err!("base required"))?,
310 &DType::Primitive(ptype, NonNullable),
311 session,
312 )?
313 .as_primitive()
314 .pvalue()
315 .vortex_expect("sequence array base should be a non-nullable primitive");
316
317 let multiplier = Scalar::from_proto_value(
318 metadata
319 .multiplier
320 .as_ref()
321 .ok_or_else(|| vortex_err!("multiplier required"))?,
322 &DType::Primitive(ptype, NonNullable),
323 session,
324 )?
325 .as_primitive()
326 .pvalue()
327 .vortex_expect("sequence array multiplier should be a non-nullable primitive");
328
329 let data = SequenceData::try_new(base, multiplier, ptype, dtype.nullability(), len)?;
330 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data))
331 }
332
333 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
334 SLOT_NAMES[idx].to_string()
335 }
336
337 fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
338 sequence_decompress(&array).map(ExecutionResult::done)
339 }
340
341 fn reduce_parent(
342 array: ArrayView<'_, Self>,
343 parent: &ArrayRef,
344 child_idx: usize,
345 ) -> VortexResult<Option<ArrayRef>> {
346 RULES.evaluate(array, parent, child_idx)
347 }
348}
349
350impl OperationsVTable<Sequence> for Sequence {
351 fn scalar_at(
352 array: ArrayView<'_, Sequence>,
353 index: usize,
354 _ctx: &mut ExecutionCtx,
355 ) -> VortexResult<Scalar> {
356 Scalar::try_new(
357 array.dtype().clone(),
358 Some(ScalarValue::Primitive(array.index_value(index))),
359 )
360 }
361}
362
363impl ValidityVTable<Sequence> for Sequence {
364 fn validity(_array: ArrayView<'_, Sequence>) -> VortexResult<Validity> {
365 Ok(Validity::AllValid)
366 }
367}
368
369#[derive(Clone, Debug)]
370pub struct Sequence;
371
372impl Sequence {
373 fn stats(multiplier: PValue) -> StatsSet {
374 let (is_sorted, is_strict_sorted) = match_each_pvalue!(
377 multiplier,
378 uint: |v| { (true, v > 0) },
379 int: |v| { (v >= 0, v > 0) },
380 float: |_v| { unreachable!("float multiplier not supported") }
381 );
382
383 unsafe {
385 StatsSet::new_unchecked(smallvec![
386 (Stat::IsSorted, StatPrecision::Exact(is_sorted.into())),
387 (
388 Stat::IsStrictSorted,
389 StatPrecision::Exact(is_strict_sorted.into()),
390 ),
391 ])
392 }
393 }
394
395 pub(crate) unsafe fn new_unchecked(
401 base: PValue,
402 multiplier: PValue,
403 ptype: PType,
404 nullability: Nullability,
405 length: usize,
406 ) -> SequenceArray {
407 let dtype = DType::Primitive(ptype, nullability);
408 let (base, multiplier) = SequenceData::normalize(base, multiplier, ptype)
409 .vortex_expect("SequenceArray parts must be normalized to the target ptype");
410 let stats = Self::stats(multiplier);
411 let data = unsafe { SequenceData::new_unchecked(base, multiplier) };
412 unsafe { Array::from_parts_unchecked(ArrayParts::new(Sequence, dtype, length, data)) }
413 .with_stats_set(stats)
414 }
415
416 pub fn try_new(
418 base: PValue,
419 multiplier: PValue,
420 ptype: PType,
421 nullability: Nullability,
422 length: usize,
423 ) -> VortexResult<SequenceArray> {
424 let dtype = DType::Primitive(ptype, nullability);
425 let data = SequenceData::try_new(base, multiplier, ptype, nullability, length)?;
426 let stats = Self::stats(data.multiplier());
427 Ok(
428 unsafe { Array::from_parts_unchecked(ArrayParts::new(Sequence, dtype, length, data)) }
429 .with_stats_set(stats),
430 )
431 }
432
433 pub fn try_new_typed<T: NativePType + Into<PValue>>(
435 base: T,
436 multiplier: T,
437 nullability: Nullability,
438 length: usize,
439 ) -> VortexResult<SequenceArray> {
440 let ptype = T::PTYPE;
441 let dtype = DType::Primitive(ptype, nullability);
442 let data = SequenceData::try_new_typed(base, multiplier, nullability, length)?;
443 let stats = Self::stats(data.multiplier());
444 Ok(
445 unsafe { Array::from_parts_unchecked(ArrayParts::new(Sequence, dtype, length, data)) }
446 .with_stats_set(stats),
447 )
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use std::sync::LazyLock;
454
455 use vortex_array::VortexSessionExecute;
456 use vortex_array::arrays::PrimitiveArray;
457 use vortex_array::assert_arrays_eq;
458 use vortex_array::dtype::Nullability;
459 use vortex_array::expr::stats::Precision as StatPrecision;
460 use vortex_array::expr::stats::Stat;
461 use vortex_array::expr::stats::StatsProviderExt;
462 use vortex_array::scalar::Scalar;
463 use vortex_array::scalar::ScalarValue;
464 use vortex_error::VortexResult;
465 use vortex_session::VortexSession;
466
467 use crate::Sequence;
468
469 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
470 let session = vortex_array::array_session();
471 crate::initialize(&session);
472 session
473 });
474
475 #[test]
476 fn test_sequence_canonical() {
477 let arr = Sequence::try_new_typed(2i64, 3, Nullability::NonNullable, 4).unwrap();
478
479 let canon = PrimitiveArray::from_iter((0..4).map(|i| 2i64 + i * 3));
480
481 assert_arrays_eq!(arr, canon, &mut SESSION.create_execution_ctx());
482 }
483
484 #[test]
485 fn test_sequence_slice_canonical() {
486 let arr = Sequence::try_new_typed(2i64, 3, Nullability::NonNullable, 4)
487 .unwrap()
488 .slice(2..3)
489 .unwrap();
490
491 let canon = PrimitiveArray::from_iter((2..3).map(|i| 2i64 + i * 3));
492
493 assert_arrays_eq!(arr, canon, &mut SESSION.create_execution_ctx());
494 }
495
496 #[test]
497 fn test_sequence_scalar_at() {
498 let scalar = Sequence::try_new_typed(2i64, 3, Nullability::NonNullable, 4)
499 .unwrap()
500 .execute_scalar(2, &mut SESSION.create_execution_ctx())
501 .unwrap();
502
503 assert_eq!(
504 scalar,
505 Scalar::try_new(scalar.dtype().clone(), Some(ScalarValue::from(8i64))).unwrap()
506 )
507 }
508
509 #[test]
510 fn test_sequence_min_max() {
511 assert!(Sequence::try_new_typed(-127i8, -1i8, Nullability::NonNullable, 2).is_ok());
512 assert!(Sequence::try_new_typed(126i8, -1i8, Nullability::NonNullable, 2).is_ok());
513 }
514
515 #[test]
516 fn test_sequence_too_big() {
517 assert!(Sequence::try_new_typed(127i8, 1i8, Nullability::NonNullable, 2).is_err());
518 assert!(Sequence::try_new_typed(-128i8, -1i8, Nullability::NonNullable, 2).is_err());
519 }
520
521 #[test]
522 fn positive_multiplier_is_strict_sorted() -> VortexResult<()> {
523 let arr = Sequence::try_new_typed(0i64, 3, Nullability::NonNullable, 4)?;
524
525 let is_sorted = arr
526 .statistics()
527 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsSorted));
528 assert_eq!(is_sorted, StatPrecision::Exact(true));
529
530 let is_strict_sorted = arr
531 .statistics()
532 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsStrictSorted));
533 assert_eq!(is_strict_sorted, StatPrecision::Exact(true));
534 Ok(())
535 }
536
537 #[test]
538 fn zero_multiplier_is_sorted_not_strict() -> VortexResult<()> {
539 let arr = Sequence::try_new_typed(5i64, 0, Nullability::NonNullable, 4)?;
540
541 let is_sorted = arr
542 .statistics()
543 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsSorted));
544 assert_eq!(is_sorted, StatPrecision::Exact(true));
545
546 let is_strict_sorted = arr
547 .statistics()
548 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsStrictSorted));
549 assert_eq!(is_strict_sorted, StatPrecision::Exact(false));
550 Ok(())
551 }
552
553 #[test]
554 fn negative_multiplier_not_sorted() -> VortexResult<()> {
555 let arr = Sequence::try_new_typed(10i64, -1, Nullability::NonNullable, 4)?;
556
557 let is_sorted = arr
558 .statistics()
559 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsSorted));
560 assert_eq!(is_sorted, StatPrecision::Exact(false));
561
562 let is_strict_sorted = arr
563 .statistics()
564 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsStrictSorted));
565 assert_eq!(is_strict_sorted, StatPrecision::Exact(false));
566 Ok(())
567 }
568
569 #[test]
572 fn test_large_multiplier_sorted() -> VortexResult<()> {
573 let large_multiplier = (i64::MAX as u64) + 1;
574 let arr = Sequence::try_new_typed(0, large_multiplier, Nullability::NonNullable, 2)?;
575
576 let is_sorted = arr
577 .statistics()
578 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsSorted));
579
580 let is_strict_sorted = arr
581 .statistics()
582 .with_typed_stats_set(|s| s.get_as::<bool>(Stat::IsStrictSorted));
583
584 assert_eq!(is_sorted, StatPrecision::Exact(true));
585 assert_eq!(is_strict_sorted, StatPrecision::Exact(true));
586
587 Ok(())
588 }
589}