1use std::collections::{BTreeMap, BTreeSet};
5use std::sync::Arc;
6
7use arrow_array::cast::AsArray;
8use arrow_array::types::{Float16Type, Float32Type, Float64Type};
9use arrow_array::{
10 new_null_array, Array, ArrayRef, ArrowPrimitiveType, Decimal128Array, Float64Array,
11 RecordBatch, RecordBatchOptions, UInt64Array,
12};
13use arrow_schema::{DataType, Field, Schema};
14use arrow_select::interleave::interleave;
15
16use crate::dictionary::dictionary_index;
17use crate::filter::{resolve, Filter, Predicate};
18use crate::sorted::{Entry, SortedRows};
19use crate::value::{canonical, prefix, unscale, Kind, Value, ValueFn};
20use crate::view::{entry, entry_order, Rows, SortColumn};
21use crate::{DataError, RowId, RowRef};
22
23const DECIMAL_LIMIT: u128 = 10u128.pow(38);
25
26const fn power_of_two(exponent: i32) -> f64 {
28 f64::from_bits(((1023 + exponent) as u64) << 52)
29}
30
31const HUGE: f64 = power_of_two(960);
35const UNSCALE: f64 = power_of_two(-128);
36const SCALE: f64 = power_of_two(128);
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40enum Function {
41 Count,
42 Sum(String),
43 Min(String),
44 Max(String),
45 Mean(String),
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct Aggregate {
52 function: Function,
53 name: Option<String>,
54}
55
56impl Aggregate {
57 #[must_use]
59 pub fn count() -> Self {
60 Self::of(Function::Count)
61 }
62
63 #[must_use]
69 pub fn sum(column: impl Into<String>) -> Self {
70 Self::of(Function::Sum(column.into()))
71 }
72
73 #[must_use]
76 pub fn min(column: impl Into<String>) -> Self {
77 Self::of(Function::Min(column.into()))
78 }
79
80 #[must_use]
83 pub fn max(column: impl Into<String>) -> Self {
84 Self::of(Function::Max(column.into()))
85 }
86
87 #[must_use]
90 pub fn mean(column: impl Into<String>) -> Self {
91 Self::of(Function::Mean(column.into()))
92 }
93
94 #[must_use]
96 pub fn named(mut self, name: impl Into<String>) -> Self {
97 self.name = Some(name.into());
98 self
99 }
100
101 fn of(function: Function) -> Self {
102 Self {
103 function,
104 name: None,
105 }
106 }
107
108 fn name(&self) -> String {
109 if let Some(name) = &self.name {
110 return name.clone();
111 }
112 let (function, column) = match &self.function {
113 Function::Count => return "count".to_owned(),
114 Function::Sum(column) => ("sum", column),
115 Function::Min(column) => ("min", column),
116 Function::Max(column) => ("max", column),
117 Function::Mean(column) => ("mean", column),
118 };
119 format!("{function}({column})")
120 }
121}
122
123#[derive(Debug, Clone, Default, PartialEq)]
153pub struct GroupSpec {
154 keys: Vec<String>,
155 aggregates: Vec<Aggregate>,
156 filter: Option<Filter>,
157}
158
159impl GroupSpec {
160 #[must_use]
163 pub fn new() -> Self {
164 Self::default()
165 }
166
167 #[must_use]
171 pub fn group_by(mut self, column: impl Into<String>) -> Self {
172 self.keys.push(column.into());
173 self
174 }
175
176 #[must_use]
178 pub fn aggregate(mut self, aggregate: Aggregate) -> Self {
179 self.aggregates.push(aggregate);
180 self
181 }
182
183 #[must_use]
185 pub fn filter(mut self, filter: Filter) -> Self {
186 self.filter = Some(filter);
187 self
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
194enum Part {
195 Bool(bool),
196 Int(i64),
197 UInt(u64),
198 Decimal(i128),
199 Float(u64),
201 Str(Box<str>),
202 Null,
203}
204
205impl Part {
206 fn of(value: Value<'_>) -> Self {
207 match value {
208 Value::Null => Self::Null,
209 Value::Bool(v) => Self::Bool(v),
210 Value::Int(v) => Self::Int(v),
211 Value::UInt(v) => Self::UInt(v),
212 Value::Decimal(v) => Self::Decimal(v),
213 Value::Float(_) => Self::Float(prefix(value, false)),
214 Value::Str(v) => Self::Str(v.into()),
215 }
216 }
217}
218
219#[derive(Debug, Clone)]
221struct Source {
222 column: usize,
223 read: ValueFn,
224 kind: Kind,
225 data_type: DataType,
226 name: String,
227}
228
229impl Source {
230 fn new(schema: &Schema, name: &str) -> Result<Self, DataError> {
231 let (column, kind, read) = resolve(schema, name)?;
232 Ok(Self {
233 column,
234 read,
235 kind,
236 data_type: schema.field(column).data_type().clone(),
237 name: name.to_owned(),
238 })
239 }
240
241 fn read<'a>(&self, batches: &'a [RecordBatch], at: RowRef) -> Value<'a> {
242 (self.read)(batches[at.batch].column(self.column).as_ref(), at.row)
243 }
244
245 fn output_type(&self) -> &DataType {
248 match &self.data_type {
249 DataType::Dictionary(_, values) => values,
250 other => other,
251 }
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257enum Numeric {
258 Integer,
259 Decimal { scale: i8 },
260 Float,
261}
262
263impl Numeric {
264 fn of(data_type: &DataType) -> Option<Self> {
265 Some(match data_type {
266 DataType::Decimal128(_, scale) => Self::Decimal { scale: *scale },
267 t if t.is_integer() => Self::Integer,
268 t if t.is_floating() => Self::Float,
269 _ => return None,
270 })
271 }
272}
273
274#[derive(Debug, Clone)]
277enum Track {
278 Sum(Numeric),
279 Order(SortColumn),
280}
281
282#[derive(Debug, Clone)]
283struct Tracker {
284 source: Source,
285 track: Track,
286}
287
288impl Tracker {
289 fn apply(&self, state: &mut State, rows: Rows<'_>, id: RowId, at: RowRef, sign: i8) {
291 match (&self.track, state, self.source.read(rows.batches, at)) {
292 (_, _, Value::Null) => {}
293 (Track::Order(column), State::Ordered(sorted), _) => {
294 let sort = std::slice::from_ref(column);
295 let probe = entry(sort, rows.batches, id, at);
296 let order = |other: Entry| entry_order(sort, rows, other, probe, at);
297 if sign > 0 {
298 sorted.insert(probe, order);
299 } else {
300 let found = sorted.remove(id, order);
301 debug_assert!(found, "a counted value is among the sorted values");
302 }
303 }
304 (_, State::Exact(sum), Value::Int(v)) => sum.apply(i128::from(v), sign),
305 (_, State::Exact(sum), Value::UInt(v)) => sum.apply(i128::from(v), sign),
306 (_, State::Exact(sum), Value::Decimal(v)) => sum.apply(v, sign),
307 (_, State::Float(sum), Value::Float(v)) => sum.apply(v, sign),
308 _ => debug_assert!(false, "a tracker's state does not fit its column"),
309 }
310 }
311}
312
313fn step(count: u64, sign: i8) -> u64 {
315 if sign > 0 {
316 count + 1
317 } else {
318 count - 1
319 }
320}
321
322#[derive(Debug, Clone, Copy, Default)]
325struct ExactSum {
326 sum: i128,
327 carry: i64,
328 values: u64,
329}
330
331impl ExactSum {
332 fn apply(&mut self, value: i128, sign: i8) {
333 let (sum, wrapped) = if sign > 0 {
334 self.sum.overflowing_add(value)
335 } else {
336 self.sum.overflowing_sub(value)
337 };
338 if wrapped {
339 self.carry += if (sign > 0) == (value > 0) { 1 } else { -1 };
341 }
342 self.sum = sum;
343 self.values = step(self.values, sign);
344 }
345
346 fn total(&self) -> Option<i128> {
348 (self.values > 0 && self.carry == 0).then_some(self.sum)
349 }
350
351 fn mean(&self) -> Option<f64> {
352 let total = self.sum as f64 + self.carry as f64 * SCALE;
354 (self.values > 0).then(|| total / self.values as f64)
355 }
356}
357
358#[derive(Debug, Clone, Copy, Default)]
360struct Compensated {
361 sum: f64,
362 compensation: f64,
363 values: u64,
364}
365
366impl Compensated {
367 fn apply(&mut self, value: f64, sign: i8) {
368 self.values = step(self.values, sign);
369 if self.values == 0 {
370 *self = Self::default();
372 return;
373 }
374 let value = if sign > 0 { value } else { -value };
375 let total = self.sum + value;
376 if self.sum.abs() >= value.abs() {
377 self.compensation += (self.sum - total) + value;
378 } else {
379 self.compensation += (value - total) + self.sum;
380 }
381 self.sum = total;
382 }
383
384 fn total(&self) -> f64 {
385 self.sum + self.compensation
386 }
387}
388
389#[derive(Debug, Clone, Copy, Default)]
392struct FloatSum {
393 normal: Compensated,
394 huge: Compensated,
396 nan: u64,
397 infinity: u64,
398 negative_infinity: u64,
399}
400
401impl FloatSum {
402 fn apply(&mut self, value: f64, sign: i8) {
403 if value.is_nan() {
404 self.nan = step(self.nan, sign);
405 } else if value == f64::INFINITY {
406 self.infinity = step(self.infinity, sign);
407 } else if value == f64::NEG_INFINITY {
408 self.negative_infinity = step(self.negative_infinity, sign);
409 } else if value.abs() >= HUGE {
410 self.huge.apply(value * UNSCALE, sign);
411 } else {
412 self.normal.apply(value, sign);
413 }
414 }
415
416 fn values(&self) -> u64 {
417 self.normal.values + self.huge.values + self.nan + self.infinity + self.negative_infinity
418 }
419
420 fn sum(&self) -> Option<f64> {
421 (self.values() > 0).then(|| self.divided(1.0))
422 }
423
424 fn mean(&self) -> Option<f64> {
425 let values = self.values();
426 (values > 0).then(|| self.divided(values as f64))
427 }
428
429 fn divided(&self, divisor: f64) -> f64 {
431 if self.nan > 0 || (self.infinity > 0 && self.negative_infinity > 0) {
432 return f64::NAN;
433 }
434 if self.infinity > 0 {
435 return f64::INFINITY;
436 }
437 if self.negative_infinity > 0 {
438 return f64::NEG_INFINITY;
439 }
440 let normal = self.normal.total() / divisor;
441 let huge = self.huge.total() / divisor;
442 if huge == 0.0 {
443 normal
444 } else {
445 (huge + normal * UNSCALE) * SCALE
446 }
447 }
448}
449
450#[derive(Debug, Clone)]
452enum State {
453 Exact(ExactSum),
454 Float(FloatSum),
455 Ordered(SortedRows),
457}
458
459impl State {
460 fn new(track: &Track) -> Self {
461 match track {
462 Track::Sum(Numeric::Float) => Self::Float(FloatSum::default()),
463 Track::Sum(_) => Self::Exact(ExactSum::default()),
464 Track::Order(_) => Self::Ordered(SortedRows::default()),
465 }
466 }
467
468 fn exact(&self) -> Option<&ExactSum> {
469 match self {
470 Self::Exact(sum) => Some(sum),
471 _ => None,
472 }
473 }
474
475 fn float(&self) -> Option<&FloatSum> {
476 match self {
477 Self::Float(sum) => Some(sum),
478 _ => None,
479 }
480 }
481
482 fn extreme(&self, max: bool) -> Option<RowId> {
484 match self {
485 Self::Ordered(sorted) if max => sorted.last(),
486 Self::Ordered(sorted) => sorted.first(),
487 _ => None,
488 }
489 }
490}
491
492#[derive(Debug, Clone, Copy)]
494enum Output {
495 Count,
496 Sum(usize, Numeric),
497 Mean(usize, Numeric),
498 Min(usize),
499 Max(usize),
500}
501
502#[derive(Debug, Clone)]
504struct Compiled {
505 output: Output,
506 name: String,
507}
508
509#[derive(Debug, Clone)]
511struct Group {
512 rows: BTreeSet<RowId>,
513 states: Vec<State>,
514}
515
516impl Group {
517 fn new(trackers: &[Tracker]) -> Self {
518 Self {
519 rows: BTreeSet::new(),
520 states: trackers.iter().map(|t| State::new(&t.track)).collect(),
521 }
522 }
523}
524
525#[derive(Debug, Clone)]
527pub(crate) struct GroupState {
528 keys: Vec<Source>,
529 trackers: Vec<Tracker>,
530 aggregates: Vec<Compiled>,
531 filter: Option<Predicate>,
532 groups: BTreeMap<Vec<Part>, Group>,
533 member: Vec<bool>,
535 revision: u64,
536}
537
538impl GroupState {
539 pub(crate) fn compile(spec: &GroupSpec, schema: &Schema) -> Result<Self, DataError> {
540 let keys = spec
541 .keys
542 .iter()
543 .map(|k| Source::new(schema, k))
544 .collect::<Result<Vec<_>, _>>()?;
545 let mut trackers = Vec::new();
546 let aggregates = spec
547 .aggregates
548 .iter()
549 .map(|aggregate| {
550 let output = match &aggregate.function {
551 Function::Count => Output::Count,
552 Function::Sum(column) => {
553 let (index, numeric) = sum_tracker(&mut trackers, schema, column)?;
554 Output::Sum(index, numeric)
555 }
556 Function::Mean(column) => {
557 let (index, numeric) = sum_tracker(&mut trackers, schema, column)?;
558 Output::Mean(index, numeric)
559 }
560 Function::Min(column) => {
561 Output::Min(order_tracker(&mut trackers, schema, column)?)
562 }
563 Function::Max(column) => {
564 Output::Max(order_tracker(&mut trackers, schema, column)?)
565 }
566 };
567 Ok(Compiled {
568 output,
569 name: aggregate.name(),
570 })
571 })
572 .collect::<Result<Vec<_>, DataError>>()?;
573 let mut names = BTreeSet::new();
574 let all_names = keys
575 .iter()
576 .map(|k| &k.name)
577 .chain(aggregates.iter().map(|a| &a.name));
578 for name in all_names {
579 if !names.insert(name) {
580 return Err(DataError::DuplicateName(name.clone()));
581 }
582 }
583 let filter = spec
584 .filter
585 .as_ref()
586 .map(|f| Predicate::compile(f, schema))
587 .transpose()?;
588 let mut state = Self {
589 keys,
590 trackers,
591 aggregates,
592 filter,
593 groups: BTreeMap::new(),
594 member: Vec::new(),
595 revision: 0,
596 };
597 state.clear();
598 Ok(state)
599 }
600
601 fn clear(&mut self) {
603 self.groups.clear();
604 self.member.clear();
605 if self.keys.is_empty() {
606 self.groups.insert(Vec::new(), Group::new(&self.trackers));
607 }
608 }
609
610 fn passes(&self, batches: &[RecordBatch], at: RowRef) -> bool {
611 self.filter
612 .as_ref()
613 .is_none_or(|f| f.eval(&batches[at.batch], at.row) == Some(true))
614 }
615
616 fn key(&self, batches: &[RecordBatch], at: RowRef) -> Vec<Part> {
617 self.keys
618 .iter()
619 .map(|k| Part::of(k.read(batches, at)))
620 .collect()
621 }
622
623 fn count(&mut self, rows: Rows<'_>, id: RowId, at: RowRef, sorted: bool) -> bool {
626 if !self.passes(rows.batches, at) {
627 return false;
628 }
629 let key = self.key(rows.batches, at);
630 let trackers = &self.trackers;
631 let group = self
632 .groups
633 .entry(key)
634 .or_insert_with(|| Group::new(trackers));
635 group.rows.insert(id);
636 for (tracker, state) in trackers.iter().zip(&mut group.states) {
637 if sorted || !matches!(tracker.track, Track::Order(_)) {
638 tracker.apply(state, rows, id, at, 1);
639 }
640 }
641 if self.member.len() <= id as usize {
642 self.member.resize(id as usize + 1, false);
643 }
644 self.member[id as usize] = true;
645 true
646 }
647
648 pub(crate) fn insert(&mut self, rows: Rows<'_>, id: RowId, at: RowRef) {
650 if self.count(rows, id, at, true) {
651 self.revision += 1;
652 }
653 }
654
655 pub(crate) fn remove(&mut self, rows: Rows<'_>, id: RowId, at: RowRef) {
657 if !self.member.get(id as usize).copied().unwrap_or(false) {
658 return;
659 }
660 let key = self.key(rows.batches, at);
661 let Some(group) = self.groups.get_mut(&key) else {
662 debug_assert!(false, "a counted row's group exists");
663 return;
664 };
665 group.rows.remove(&id);
666 for (tracker, state) in self.trackers.iter().zip(&mut group.states) {
667 tracker.apply(state, rows, id, at, -1);
668 }
669 if group.rows.is_empty() && !self.keys.is_empty() {
671 self.groups.remove(&key);
672 }
673 self.member[id as usize] = false;
674 self.revision += 1;
675 }
676
677 pub(crate) fn rebuild(&mut self, rows: Rows<'_>) {
679 let had_rows = self.groups.values().any(|g| !g.rows.is_empty());
680 self.clear();
681 let mut counted = false;
682 for (id, slot) in rows.slots.iter().enumerate() {
683 if let (Ok(id), Some(slot)) = (RowId::try_from(id), slot) {
684 counted |= self.count(rows, id, slot.row_ref(), false);
685 }
686 }
687 self.sort_values(rows);
688 if had_rows || counted {
689 self.revision += 1;
690 }
691 }
692
693 fn sort_values(&mut self, rows: Rows<'_>) {
696 for (index, tracker) in self.trackers.iter().enumerate() {
697 let Track::Order(column) = &tracker.track else {
698 continue;
699 };
700 let sort = std::slice::from_ref(column);
701 for group in self.groups.values_mut() {
702 let mut entries: Vec<Entry> = group
703 .rows
704 .iter()
705 .map(|&id| (id, rows.at(id)))
706 .filter(|&(_, at)| tracker.source.read(rows.batches, at) != Value::Null)
707 .map(|(id, at)| entry(sort, rows.batches, id, at))
708 .collect();
709 entries.sort_unstable_by(|&a, &b| entry_order(sort, rows, a, b, rows.at(b.id)));
710 if let State::Ordered(sorted) = &mut group.states[index] {
711 sorted.rebuild(&entries);
712 }
713 }
714 }
715 }
716}
717
718fn sum_tracker(
720 trackers: &mut Vec<Tracker>,
721 schema: &Schema,
722 column: &str,
723) -> Result<(usize, Numeric), DataError> {
724 let source = Source::new(schema, column)?;
725 let numeric =
726 Numeric::of(&source.data_type).ok_or_else(|| DataError::NotNumeric(column.to_owned()))?;
727 Ok((tracker(trackers, source, Track::Sum(numeric)), numeric))
728}
729
730fn order_tracker(
732 trackers: &mut Vec<Tracker>,
733 schema: &Schema,
734 column: &str,
735) -> Result<usize, DataError> {
736 let source = Source::new(schema, column)?;
737 let sort = SortColumn::ascending(source.column, source.read);
738 Ok(tracker(trackers, source, Track::Order(sort)))
739}
740
741fn tracker(trackers: &mut Vec<Tracker>, source: Source, track: Track) -> usize {
744 let same = |t: &Tracker| {
745 t.source.column == source.column
746 && std::mem::discriminant(&t.track) == std::mem::discriminant(&track)
747 };
748 if let Some(index) = trackers.iter().position(same) {
749 return index;
750 }
751 trackers.push(Tracker { source, track });
752 trackers.len() - 1
753}
754
755#[derive(Debug, Clone, Copy)]
757pub struct Groups<'a> {
758 pub(crate) state: &'a GroupState,
759 pub(crate) rows: Rows<'a>,
760}
761
762impl Groups<'_> {
763 pub fn len(&self) -> usize {
766 self.state.groups.len()
767 }
768
769 pub fn is_empty(&self) -> bool {
771 self.len() == 0
772 }
773
774 pub fn revision(&self) -> u64 {
777 self.state.revision
778 }
779
780 pub fn to_batch(&self) -> Result<RecordBatch, DataError> {
791 let groups: Vec<&Group> = self.state.groups.values().collect();
792 let mut fields = Vec::new();
793 let mut columns = Vec::new();
794 for key in &self.state.keys {
795 let rows = groups.iter().map(|g| g.rows.first().copied());
796 columns.push(self.gather(key, rows)?);
797 fields.push(Field::new(&key.name, key.output_type().clone(), true));
798 }
799 for aggregate in &self.state.aggregates {
800 let (column, data_type) = self.column(aggregate.output, &groups)?;
801 let nullable = !matches!(aggregate.output, Output::Count);
802 fields.push(Field::new(&aggregate.name, data_type, nullable));
803 columns.push(column);
804 }
805 let schema = Arc::new(Schema::new(fields));
806 let options = RecordBatchOptions::new().with_row_count(Some(groups.len()));
807 RecordBatch::try_new_with_options(schema, columns, &options).map_err(DataError::arrow)
808 }
809
810 fn column(&self, output: Output, groups: &[&Group]) -> Result<(ArrayRef, DataType), DataError> {
811 Ok(match output {
812 Output::Count => {
813 let counts: UInt64Array =
815 groups.iter().map(|g| Some(g.rows.len() as u64)).collect();
816 (Arc::new(counts), DataType::UInt64)
817 }
818 Output::Sum(index, numeric) => sums(numeric, groups.iter().map(|g| &g.states[index]))?,
819 Output::Mean(index, numeric) => {
820 let means: Float64Array = groups
821 .iter()
822 .map(|g| mean(&g.states[index], numeric))
823 .collect();
824 (Arc::new(means), DataType::Float64)
825 }
826 Output::Min(index) | Output::Max(index) => {
827 let max = matches!(output, Output::Max(_));
828 let rows = groups.iter().map(|g| g.states[index].extreme(max));
829 let source = &self.state.trackers[index].source;
830 (self.gather(source, rows)?, source.output_type().clone())
831 }
832 })
833 }
834
835 fn gather(
838 &self,
839 source: &Source,
840 ids: impl Iterator<Item = Option<RowId>>,
841 ) -> Result<ArrayRef, DataError> {
842 let batches = self.rows.batches;
843 let column = |batch: usize| batches[batch].column(source.column).as_ref();
844 let decode = matches!(source.data_type, DataType::Dictionary(..));
845 let null = new_null_array(source.output_type(), 1);
846 let mut arrays: Vec<&dyn Array> = (0..batches.len())
847 .map(|batch| {
848 let array = column(batch);
849 match array.as_any_dictionary_opt() {
850 Some(dictionary) if decode => dictionary.values().as_ref(),
851 _ => array,
852 }
853 })
854 .collect();
855 arrays.push(null.as_ref());
856 let null_at = (arrays.len() - 1, 0);
857 let indices: Vec<(usize, usize)> = ids
858 .map(|id| {
859 let place = id.and_then(|id| {
860 let at = self.rows.at(id);
861 let row = if decode {
862 dictionary_index(column(at.batch), at.row)?
863 } else {
864 at.row
865 };
866 Some((at.batch, row))
867 });
868 place.unwrap_or(null_at)
869 })
870 .collect();
871 let array = interleave(&arrays, &indices).map_err(DataError::arrow)?;
872 Ok(if source.kind == Kind::Float {
873 canonical_floats(&array)
874 } else {
875 array
876 })
877 }
878}
879
880fn canonical_floats(array: &ArrayRef) -> ArrayRef {
882 type F16 = <Float16Type as ArrowPrimitiveType>::Native;
883 if let Some(a) = array.as_primitive_opt::<Float64Type>() {
884 Arc::new(a.unary::<_, Float64Type>(canonical))
885 } else if let Some(a) = array.as_primitive_opt::<Float32Type>() {
886 Arc::new(a.unary::<_, Float32Type>(|v| canonical(f64::from(v)) as f32))
888 } else if let Some(a) = array.as_primitive_opt::<Float16Type>() {
889 Arc::new(a.unary::<_, Float16Type>(|v| F16::from_f64(canonical(v.into()))))
890 } else {
891 Arc::clone(array)
892 }
893}
894
895fn mean(state: &State, numeric: Numeric) -> Option<f64> {
896 match (state, numeric) {
897 (State::Float(sum), _) => sum.mean(),
898 (State::Exact(sum), Numeric::Decimal { scale }) => sum.mean().map(|m| unscale(m, scale)),
899 (State::Exact(sum), _) => sum.mean(),
900 (State::Ordered(_), _) => None,
901 }
902}
903
904fn sums<'g>(
906 numeric: Numeric,
907 states: impl Iterator<Item = &'g State>,
908) -> Result<(ArrayRef, DataType), DataError> {
909 let scale = match numeric {
910 Numeric::Float => {
911 let sums: Float64Array = states.map(|s| s.float().and_then(FloatSum::sum)).collect();
912 return Ok((Arc::new(sums), DataType::Float64));
913 }
914 Numeric::Integer => 0,
916 Numeric::Decimal { scale } => scale,
917 };
918 let sums: Decimal128Array = states
919 .map(|s| {
920 let sum = s.exact().and_then(ExactSum::total);
921 sum.filter(|v| v.unsigned_abs() < DECIMAL_LIMIT)
922 })
923 .collect();
924 let sums = sums
925 .with_precision_and_scale(38, scale)
926 .map_err(DataError::arrow)?;
927 Ok((Arc::new(sums), DataType::Decimal128(38, scale)))
928}
929
930#[cfg(test)]
931mod tests {
932 use super::*;
933
934 #[test]
935 fn a_compensated_sum_keeps_what_a_plain_sum_loses() {
936 for (first, second) in [(1e16, 1.0), (1.0, 1e16)] {
938 let mut sum = FloatSum::default();
939 sum.apply(first, 1);
940 sum.apply(second, 1);
941 sum.apply(1e16, -1);
942 assert_eq!(sum.sum(), Some(1.0), "{first} then {second}");
943 }
944 }
945
946 #[test]
947 fn a_float_sum_forgets_its_rounding_once_its_values_are_gone() {
948 let huge = [1.7e308, 1.3e308, 0.9e308, 1.1e308];
949 let mut sum = FloatSum::default();
950 sum.apply(1.0, 1);
951 for v in huge {
952 sum.apply(v, 1);
953 }
954 for v in huge.iter().rev() {
955 sum.apply(*v, -1);
956 }
957 assert_eq!(sum.sum(), Some(1.0));
958 let mut sum = FloatSum::default();
959 for v in [0.1, 0.2, 0.3] {
960 sum.apply(v, 1);
961 }
962 for v in [0.2, 0.1, 0.3] {
963 sum.apply(v, -1);
964 }
965 sum.apply(1e-30, 1);
966 assert_eq!(sum.sum(), Some(1e-30));
967 }
968
969 #[test]
970 fn nan_and_infinities_leave_a_float_sum_with_their_values() {
971 let mut sum = FloatSum::default();
972 for _ in 0..100 {
973 sum.apply(1.0, 1);
974 }
975 sum.apply(f64::NAN, 1);
976 assert!(sum.sum().is_some_and(f64::is_nan));
977 sum.apply(f64::NAN, -1);
978 assert_eq!(sum.sum(), Some(100.0));
979 sum.apply(f64::INFINITY, 1);
980 assert_eq!(sum.sum(), Some(f64::INFINITY));
981 sum.apply(f64::NEG_INFINITY, 1);
982 assert!(sum.sum().is_some_and(f64::is_nan), "inf + -inf");
983 sum.apply(f64::INFINITY, -1);
984 assert_eq!(sum.sum(), Some(f64::NEG_INFINITY));
985 sum.apply(f64::NEG_INFINITY, -1);
986 assert_eq!(sum.sum(), Some(100.0));
987 assert_eq!(sum.mean(), Some(1.0));
988 }
989
990 #[test]
991 fn huge_floats_sum_past_f64_and_back() {
992 let mut sum = FloatSum::default();
993 sum.apply(1.0, 1);
994 sum.apply(f64::MAX, 1);
995 sum.apply(f64::MAX, 1);
996 assert_eq!(sum.sum(), Some(f64::INFINITY));
997 let mean = sum.mean().unwrap();
998 let expected = f64::MAX / 3.0 * 2.0;
999 assert!(
1000 (mean - expected).abs() <= expected * 1e-15,
1001 "{mean} vs {expected}"
1002 );
1003 sum.apply(f64::MAX, -1);
1004 assert_eq!(sum.sum(), Some(f64::MAX), "f64::MAX + 1");
1005 sum.apply(f64::MAX, -1);
1006 assert_eq!(sum.sum(), Some(1.0));
1007 }
1008
1009 #[test]
1010 fn a_sum_just_below_overflow_does_not_round_to_infinity() {
1011 let mut sum = FloatSum::default();
1012 sum.apply(power_of_two(1023), 1);
1013 sum.apply(power_of_two(1023), 1);
1014 for _ in 0..4_096 {
1015 sum.apply(-power_of_two(959), 1);
1016 }
1017 assert_eq!(sum.sum(), Some(f64::MAX));
1019 }
1020
1021 #[test]
1022 fn huge_values_keep_their_rounding_too() {
1023 let (a, b, c) = (f64::MAX, 1e308, -1.5e308);
1025 let mut sum = FloatSum::default();
1026 for v in [a, b, c] {
1027 sum.apply(v, 1);
1028 }
1029 assert_eq!(sum.sum(), Some((a + c) + b));
1031 }
1032
1033 #[test]
1034 fn an_exact_sum_past_i128_comes_back_when_the_values_leave() {
1035 for big in [i128::MAX, i128::MIN] {
1036 let mut sum = ExactSum::default();
1037 sum.apply(7, 1);
1038 sum.apply(big, 1);
1039 sum.apply(big, 1);
1040 assert_eq!(sum.total(), None, "7 + 2 * {big}");
1041 let mean = sum.mean().unwrap();
1042 assert!((mean - big as f64 * 2.0 / 3.0).abs() <= 1e25, "{mean}");
1043 sum.apply(big, -1);
1044 assert_eq!(sum.total(), big.checked_add(7));
1045 sum.apply(big, -1);
1046 assert_eq!(sum.total(), Some(7));
1047 }
1048 }
1049}