Skip to content

Commit 144437f

Browse files
committed
is-constant
Signed-off-by: Nicholas Gates <nick@nickgates.com>
1 parent 80e2733 commit 144437f

1 file changed

Lines changed: 48 additions & 52 deletions

File tree

  • vortex-array/src/aggregate_fn/fns/is_constant

vortex-array/src/aggregate_fn/fns/is_constant/mod.rs

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,40 @@
33

44
mod bool;
55
mod decimal;
6+
mod extension;
7+
mod fixed_size_list;
8+
mod list;
69
pub mod primitive;
10+
mod struct_;
711
mod varbin;
812

913
use vortex_error::VortexResult;
14+
use vortex_mask::Mask;
1015

1116
use self::bool::check_bool_constant;
1217
use self::decimal::check_decimal_constant;
18+
use self::extension::check_extension_constant;
19+
use self::fixed_size_list::check_fixed_size_list_constant;
20+
use self::list::check_listview_constant;
1321
use self::primitive::check_primitive_constant;
22+
use self::struct_::check_struct_constant;
1423
use self::varbin::check_varbinview_constant;
1524
use crate::ArrayRef;
1625
use crate::Canonical;
1726
use crate::Columnar;
1827
use crate::DynArray;
1928
use crate::ExecutionCtx;
2029
use crate::IntoArray;
30+
use crate::ToCanonical;
2131
use crate::aggregate_fn::Accumulator;
2232
use crate::aggregate_fn::AggregateFnId;
2333
use crate::aggregate_fn::AggregateFnVTable;
2434
use crate::aggregate_fn::DynAccumulator;
2535
use crate::aggregate_fn::EmptyOptions;
36+
use crate::arrays::BoolArray;
2637
use crate::arrays::Constant;
2738
use crate::arrays::Null;
39+
use crate::builtins::ArrayBuiltins;
2840
use crate::dtype::DType;
2941
use crate::dtype::FieldNames;
3042
use crate::dtype::Nullability;
@@ -34,6 +46,37 @@ use crate::expr::stats::Stat;
3446
use crate::expr::stats::StatsProvider;
3547
use crate::expr::stats::StatsProviderExt;
3648
use crate::scalar::Scalar;
49+
use crate::scalar_fn::fns::operators::Operator;
50+
51+
/// Check if two arrays of the same length have equal values at every position (null-safe).
52+
///
53+
/// Two positions are considered equal if they are both null, or both non-null with the same value.
54+
fn arrays_value_equal(a: &ArrayRef, b: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
55+
debug_assert_eq!(a.len(), b.len());
56+
if a.is_empty() {
57+
return Ok(true);
58+
}
59+
60+
// Check validity masks match (null positions must be identical).
61+
let a_mask = a.validity_mask()?;
62+
let b_mask = b.validity_mask()?;
63+
if a_mask != b_mask {
64+
return Ok(false);
65+
}
66+
67+
let valid_count = a_mask.true_count();
68+
if valid_count == 0 {
69+
// Both all-null → equal.
70+
return Ok(true);
71+
}
72+
73+
// Compare values element-wise. Result is null where both inputs are null,
74+
// true/false where both are valid.
75+
let eq_result = a.binary(b.clone(), Operator::Eq)?;
76+
let eq_result = eq_result.execute::<Mask>(ctx)?;
77+
78+
Ok(eq_result.true_count() == valid_count)
79+
}
3780

3881
/// Compute whether an array has constant values.
3982
///
@@ -275,7 +318,7 @@ impl AggregateFnVTable for IsConstant {
275318
&self,
276319
partial: &mut Self::Partial,
277320
batch: &Columnar,
278-
_ctx: &mut ExecutionCtx,
321+
ctx: &mut ExecutionCtx,
279322
) -> VortexResult<()> {
280323
if !partial.is_constant {
281324
return Ok(());
@@ -318,57 +361,10 @@ impl AggregateFnVTable for IsConstant {
318361
Canonical::Bool(b) => check_bool_constant(b),
319362
Canonical::VarBinView(v) => check_varbinview_constant(v),
320363
Canonical::Decimal(d) => check_decimal_constant(d),
321-
Canonical::Struct(s) => {
322-
let children = s.children();
323-
if children.is_empty() {
324-
true
325-
} else {
326-
// For struct, check each child recursively.
327-
let first_scalar = s.scalar_at(0)?;
328-
let mut is_const = true;
329-
for i in 1..s.len() {
330-
if s.scalar_at(i)? != first_scalar {
331-
is_const = false;
332-
break;
333-
}
334-
}
335-
is_const
336-
}
337-
}
338-
Canonical::Extension(e) => {
339-
// Extension arrays delegate to their storage.
340-
let first_scalar = e.scalar_at(0)?;
341-
let mut is_const = true;
342-
for i in 1..e.len() {
343-
if e.scalar_at(i)? != first_scalar {
344-
is_const = false;
345-
break;
346-
}
347-
}
348-
is_const
349-
}
350-
Canonical::List(l) => {
351-
let first_scalar = l.scalar_at(0)?;
352-
let mut is_const = true;
353-
for i in 1..l.len() {
354-
if l.scalar_at(i)? != first_scalar {
355-
is_const = false;
356-
break;
357-
}
358-
}
359-
is_const
360-
}
361-
Canonical::FixedSizeList(f) => {
362-
let first_scalar = f.scalar_at(0)?;
363-
let mut is_const = true;
364-
for i in 1..f.len() {
365-
if f.scalar_at(i)? != first_scalar {
366-
is_const = false;
367-
break;
368-
}
369-
}
370-
is_const
371-
}
364+
Canonical::Struct(s) => check_struct_constant(s, ctx)?,
365+
Canonical::Extension(e) => check_extension_constant(e, ctx)?,
366+
Canonical::List(l) => check_listview_constant(l, ctx)?,
367+
Canonical::FixedSizeList(f) => check_fixed_size_list_constant(f, ctx)?,
372368
Canonical::Null(_) => true,
373369
};
374370

0 commit comments

Comments
 (0)