reifydb_engine/function/math/scalar/
round.rs

1// Copyright (c) reifydb.com 2025
2// This file is licensed under the AGPL-3.0-or-later, see license.md file
3
4use reifydb_core::value::column::ColumnData;
5
6use crate::function::{ScalarFunction, ScalarFunctionContext};
7
8pub struct Round;
9
10impl Default for Round {
11	fn default() -> Self {
12		Self {}
13	}
14}
15
16impl Round {
17	pub fn new() -> Self {
18		Self::default()
19	}
20}
21
22impl ScalarFunction for Round {
23	fn scalar(&self, ctx: ScalarFunctionContext) -> crate::Result<ColumnData> {
24		let columns = ctx.columns;
25		let row_count = ctx.row_count;
26
27		if columns.is_empty() {
28			return Ok(ColumnData::utf8(Vec::<String>::new()));
29		}
30
31		let value_column = columns.first().unwrap();
32
33		// Get precision column if provided (default to 0)
34		let precision_column = columns.get(1);
35
36		match value_column.data() {
37			ColumnData::Float4(container) => {
38				let mut result = Vec::with_capacity(row_count);
39
40				for row_idx in 0..row_count {
41					if let Some(&value) = container.get(row_idx) {
42						let precision = if let Some(prec_col) = precision_column {
43							match prec_col.data() {
44								ColumnData::Int4(prec_container) => prec_container
45									.get(row_idx)
46									.copied()
47									.unwrap_or(0),
48								ColumnData::Int1(prec_container) => prec_container
49									.get(row_idx)
50									.map(|&v| v as i32)
51									.unwrap_or(0),
52								ColumnData::Int2(prec_container) => prec_container
53									.get(row_idx)
54									.map(|&v| v as i32)
55									.unwrap_or(0),
56								ColumnData::Int8(prec_container) => prec_container
57									.get(row_idx)
58									.map(|&v| v as i32)
59									.unwrap_or(0),
60								_ => 0,
61							}
62						} else {
63							0
64						};
65
66						let multiplier = 10_f32.powi(precision);
67						let rounded = (value * multiplier).round() / multiplier;
68						result.push(rounded);
69					} else {
70						result.push(0.0);
71					}
72				}
73
74				Ok(ColumnData::float4(result))
75			}
76			ColumnData::Float8(container) => {
77				let mut result = Vec::with_capacity(row_count);
78
79				for row_idx in 0..row_count {
80					if let Some(&value) = container.get(row_idx) {
81						let precision = if let Some(prec_col) = precision_column {
82							match prec_col.data() {
83								ColumnData::Int4(prec_container) => prec_container
84									.get(row_idx)
85									.copied()
86									.unwrap_or(0),
87								ColumnData::Int1(prec_container) => prec_container
88									.get(row_idx)
89									.map(|&v| v as i32)
90									.unwrap_or(0),
91								ColumnData::Int2(prec_container) => prec_container
92									.get(row_idx)
93									.map(|&v| v as i32)
94									.unwrap_or(0),
95								ColumnData::Int8(prec_container) => prec_container
96									.get(row_idx)
97									.map(|&v| v as i32)
98									.unwrap_or(0),
99								_ => 0,
100							}
101						} else {
102							0
103						};
104
105						let multiplier = 10_f64.powi(precision);
106						let rounded = (value * multiplier).round() / multiplier;
107						result.push(rounded);
108					} else {
109						result.push(0.0);
110					}
111				}
112
113				Ok(ColumnData::float8(result))
114			}
115			ColumnData::Int4(container) => {
116				let mut result = Vec::with_capacity(row_count);
117
118				for row_idx in 0..row_count {
119					if let Some(&value) = container.get(row_idx) {
120						result.push(value);
121					} else {
122						result.push(0);
123					}
124				}
125
126				Ok(ColumnData::int4(result))
127			}
128			_ => unimplemented!("Round function currently supports float4, float8, and int4 types"),
129		}
130	}
131}