Skip to main content

reifydb_function/math/scalar/
power.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2025 ReifyDB
3
4use num_traits::ToPrimitive;
5use reifydb_core::value::column::{Column, columns::Columns, data::ColumnData};
6use reifydb_type::{
7	fragment::Fragment,
8	value::{container::number::NumberContainer, decimal::Decimal, r#type::Type},
9};
10
11use crate::{ScalarFunction, ScalarFunctionContext, error::ScalarFunctionError, propagate_options};
12
13pub struct Power;
14
15impl Power {
16	pub fn new() -> Self {
17		Self {}
18	}
19}
20
21fn convert_column_to_type(data: &ColumnData, target: Type, row_count: usize) -> ColumnData {
22	match target {
23		Type::Int1 => {
24			let mut result = Vec::with_capacity(row_count);
25			let mut bitvec = Vec::with_capacity(row_count);
26			for i in 0..row_count {
27				if data.is_defined(i) {
28					let val = get_as_i8(data, i);
29					result.push(val);
30					bitvec.push(true);
31				} else {
32					result.push(0);
33					bitvec.push(false);
34				}
35			}
36			ColumnData::int1_with_bitvec(result, bitvec)
37		}
38		Type::Int2 => {
39			let mut result = Vec::with_capacity(row_count);
40			let mut bitvec = Vec::with_capacity(row_count);
41			for i in 0..row_count {
42				if data.is_defined(i) {
43					let val = get_as_i16(data, i);
44					result.push(val);
45					bitvec.push(true);
46				} else {
47					result.push(0);
48					bitvec.push(false);
49				}
50			}
51			ColumnData::int2_with_bitvec(result, bitvec)
52		}
53		Type::Int4 => {
54			let mut result = Vec::with_capacity(row_count);
55			let mut bitvec = Vec::with_capacity(row_count);
56			for i in 0..row_count {
57				if data.is_defined(i) {
58					let val = get_as_i32(data, i);
59					result.push(val);
60					bitvec.push(true);
61				} else {
62					result.push(0);
63					bitvec.push(false);
64				}
65			}
66			ColumnData::int4_with_bitvec(result, bitvec)
67		}
68		Type::Int8 => {
69			let mut result = Vec::with_capacity(row_count);
70			let mut bitvec = Vec::with_capacity(row_count);
71			for i in 0..row_count {
72				if data.is_defined(i) {
73					let val = get_as_i64(data, i);
74					result.push(val);
75					bitvec.push(true);
76				} else {
77					result.push(0);
78					bitvec.push(false);
79				}
80			}
81			ColumnData::int8_with_bitvec(result, bitvec)
82		}
83		Type::Int16 => {
84			let mut result = Vec::with_capacity(row_count);
85			let mut bitvec = Vec::with_capacity(row_count);
86			for i in 0..row_count {
87				if data.is_defined(i) {
88					let val = get_as_i128(data, i);
89					result.push(val);
90					bitvec.push(true);
91				} else {
92					result.push(0);
93					bitvec.push(false);
94				}
95			}
96			ColumnData::int16_with_bitvec(result, bitvec)
97		}
98		Type::Uint1 => {
99			let mut result = Vec::with_capacity(row_count);
100			let mut bitvec = Vec::with_capacity(row_count);
101			for i in 0..row_count {
102				if data.is_defined(i) {
103					let val = get_as_u8(data, i);
104					result.push(val);
105					bitvec.push(true);
106				} else {
107					result.push(0);
108					bitvec.push(false);
109				}
110			}
111			ColumnData::uint1_with_bitvec(result, bitvec)
112		}
113		Type::Uint2 => {
114			let mut result = Vec::with_capacity(row_count);
115			let mut bitvec = Vec::with_capacity(row_count);
116			for i in 0..row_count {
117				if data.is_defined(i) {
118					let val = get_as_u16(data, i);
119					result.push(val);
120					bitvec.push(true);
121				} else {
122					result.push(0);
123					bitvec.push(false);
124				}
125			}
126			ColumnData::uint2_with_bitvec(result, bitvec)
127		}
128		Type::Uint4 => {
129			let mut result = Vec::with_capacity(row_count);
130			let mut bitvec = Vec::with_capacity(row_count);
131			for i in 0..row_count {
132				if data.is_defined(i) {
133					let val = get_as_u32(data, i);
134					result.push(val);
135					bitvec.push(true);
136				} else {
137					result.push(0);
138					bitvec.push(false);
139				}
140			}
141			ColumnData::uint4_with_bitvec(result, bitvec)
142		}
143		Type::Uint8 => {
144			let mut result = Vec::with_capacity(row_count);
145			let mut bitvec = Vec::with_capacity(row_count);
146			for i in 0..row_count {
147				if data.is_defined(i) {
148					let val = get_as_u64(data, i);
149					result.push(val);
150					bitvec.push(true);
151				} else {
152					result.push(0);
153					bitvec.push(false);
154				}
155			}
156			ColumnData::uint8_with_bitvec(result, bitvec)
157		}
158		Type::Uint16 => {
159			let mut result = Vec::with_capacity(row_count);
160			let mut bitvec = Vec::with_capacity(row_count);
161			for i in 0..row_count {
162				if data.is_defined(i) {
163					let val = get_as_u128(data, i);
164					result.push(val);
165					bitvec.push(true);
166				} else {
167					result.push(0);
168					bitvec.push(false);
169				}
170			}
171			ColumnData::uint16_with_bitvec(result, bitvec)
172		}
173		Type::Float4 => {
174			let mut result = Vec::with_capacity(row_count);
175			let mut bitvec = Vec::with_capacity(row_count);
176			for i in 0..row_count {
177				if data.is_defined(i) {
178					let val = get_as_f32(data, i);
179					result.push(val);
180					bitvec.push(true);
181				} else {
182					result.push(0.0);
183					bitvec.push(false);
184				}
185			}
186			ColumnData::float4_with_bitvec(result, bitvec)
187		}
188		Type::Float8 => {
189			let mut result = Vec::with_capacity(row_count);
190			let mut bitvec = Vec::with_capacity(row_count);
191			for i in 0..row_count {
192				if data.is_defined(i) {
193					let val = get_as_f64(data, i);
194					result.push(val);
195					bitvec.push(true);
196				} else {
197					result.push(0.0);
198					bitvec.push(false);
199				}
200			}
201			ColumnData::float8_with_bitvec(result, bitvec)
202		}
203		Type::Int => {
204			use reifydb_type::value::int::Int;
205			let mut result = Vec::with_capacity(row_count);
206			let mut bitvec = Vec::with_capacity(row_count);
207			for i in 0..row_count {
208				if data.is_defined(i) {
209					let val = get_as_i128(data, i);
210					result.push(Int::from(val));
211					bitvec.push(true);
212				} else {
213					result.push(Int::default());
214					bitvec.push(false);
215				}
216			}
217			ColumnData::int_with_bitvec(result, bitvec)
218		}
219		Type::Uint => {
220			use reifydb_type::value::uint::Uint;
221			let mut result = Vec::with_capacity(row_count);
222			let mut bitvec = Vec::with_capacity(row_count);
223			for i in 0..row_count {
224				if data.is_defined(i) {
225					let val = get_as_u128(data, i);
226					result.push(Uint::from(val));
227					bitvec.push(true);
228				} else {
229					result.push(Uint::default());
230					bitvec.push(false);
231				}
232			}
233			ColumnData::uint_with_bitvec(result, bitvec)
234		}
235		Type::Decimal => {
236			use reifydb_type::value::decimal::Decimal;
237			let mut result = Vec::with_capacity(row_count);
238			let mut bitvec = Vec::with_capacity(row_count);
239			for i in 0..row_count {
240				if data.is_defined(i) {
241					let val = get_as_f64(data, i);
242					result.push(Decimal::from(val));
243					bitvec.push(true);
244				} else {
245					result.push(Decimal::default());
246					bitvec.push(false);
247				}
248			}
249			ColumnData::decimal_with_bitvec(result, bitvec)
250		}
251		// For same type or unsupported conversions, clone the original
252		_ => data.clone(),
253	}
254}
255
256fn get_as_i8(data: &ColumnData, i: usize) -> i8 {
257	match data {
258		ColumnData::Int1(c) => c.get(i).copied().unwrap_or(0),
259		ColumnData::Uint1(c) => c.get(i).map(|&v| v as i8).unwrap_or(0),
260		_ => 0,
261	}
262}
263
264fn get_as_u8(data: &ColumnData, i: usize) -> u8 {
265	match data {
266		ColumnData::Uint1(c) => c.get(i).copied().unwrap_or(0),
267		ColumnData::Int1(c) => c.get(i).map(|&v| v as u8).unwrap_or(0),
268		_ => 0,
269	}
270}
271
272fn get_as_i16(data: &ColumnData, i: usize) -> i16 {
273	match data {
274		ColumnData::Int1(c) => c.get(i).map(|&v| v as i16).unwrap_or(0),
275		ColumnData::Int2(c) => c.get(i).copied().unwrap_or(0),
276		ColumnData::Uint1(c) => c.get(i).map(|&v| v as i16).unwrap_or(0),
277		_ => 0,
278	}
279}
280
281fn get_as_i32(data: &ColumnData, i: usize) -> i32 {
282	match data {
283		ColumnData::Int1(c) => c.get(i).map(|&v| v as i32).unwrap_or(0),
284		ColumnData::Int2(c) => c.get(i).map(|&v| v as i32).unwrap_or(0),
285		ColumnData::Int4(c) => c.get(i).copied().unwrap_or(0),
286		ColumnData::Uint1(c) => c.get(i).map(|&v| v as i32).unwrap_or(0),
287		ColumnData::Uint2(c) => c.get(i).map(|&v| v as i32).unwrap_or(0),
288		_ => 0,
289	}
290}
291
292fn get_as_i64(data: &ColumnData, i: usize) -> i64 {
293	match data {
294		ColumnData::Int1(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
295		ColumnData::Int2(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
296		ColumnData::Int4(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
297		ColumnData::Int8(c) => c.get(i).copied().unwrap_or(0),
298		ColumnData::Uint1(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
299		ColumnData::Uint2(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
300		ColumnData::Uint4(c) => c.get(i).map(|&v| v as i64).unwrap_or(0),
301		_ => 0,
302	}
303}
304
305fn get_as_i128(data: &ColumnData, i: usize) -> i128 {
306	match data {
307		ColumnData::Int1(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
308		ColumnData::Int2(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
309		ColumnData::Int4(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
310		ColumnData::Int8(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
311		ColumnData::Int16(c) => c.get(i).copied().unwrap_or(0),
312		ColumnData::Uint1(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
313		ColumnData::Uint2(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
314		ColumnData::Uint4(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
315		ColumnData::Uint8(c) => c.get(i).map(|&v| v as i128).unwrap_or(0),
316		_ => 0,
317	}
318}
319
320fn get_as_u16(data: &ColumnData, i: usize) -> u16 {
321	match data {
322		ColumnData::Uint1(c) => c.get(i).map(|&v| v as u16).unwrap_or(0),
323		ColumnData::Uint2(c) => c.get(i).copied().unwrap_or(0),
324		_ => 0,
325	}
326}
327
328fn get_as_u32(data: &ColumnData, i: usize) -> u32 {
329	match data {
330		ColumnData::Uint1(c) => c.get(i).map(|&v| v as u32).unwrap_or(0),
331		ColumnData::Uint2(c) => c.get(i).map(|&v| v as u32).unwrap_or(0),
332		ColumnData::Uint4(c) => c.get(i).copied().unwrap_or(0),
333		_ => 0,
334	}
335}
336
337fn get_as_u64(data: &ColumnData, i: usize) -> u64 {
338	match data {
339		ColumnData::Uint1(c) => c.get(i).map(|&v| v as u64).unwrap_or(0),
340		ColumnData::Uint2(c) => c.get(i).map(|&v| v as u64).unwrap_or(0),
341		ColumnData::Uint4(c) => c.get(i).map(|&v| v as u64).unwrap_or(0),
342		ColumnData::Uint8(c) => c.get(i).copied().unwrap_or(0),
343		_ => 0,
344	}
345}
346
347fn get_as_u128(data: &ColumnData, i: usize) -> u128 {
348	match data {
349		ColumnData::Uint1(c) => c.get(i).map(|&v| v as u128).unwrap_or(0),
350		ColumnData::Uint2(c) => c.get(i).map(|&v| v as u128).unwrap_or(0),
351		ColumnData::Uint4(c) => c.get(i).map(|&v| v as u128).unwrap_or(0),
352		ColumnData::Uint8(c) => c.get(i).map(|&v| v as u128).unwrap_or(0),
353		ColumnData::Uint16(c) => c.get(i).copied().unwrap_or(0),
354		_ => 0,
355	}
356}
357
358fn get_as_f32(data: &ColumnData, i: usize) -> f32 {
359	match data {
360		ColumnData::Int1(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
361		ColumnData::Int2(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
362		ColumnData::Int4(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
363		ColumnData::Int8(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
364		ColumnData::Int16(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
365		ColumnData::Uint1(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
366		ColumnData::Uint2(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
367		ColumnData::Uint4(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
368		ColumnData::Uint8(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
369		ColumnData::Uint16(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
370		ColumnData::Float4(c) => c.get(i).copied().unwrap_or(0.0),
371		ColumnData::Float8(c) => c.get(i).map(|&v| v as f32).unwrap_or(0.0),
372		ColumnData::Int {
373			container,
374			..
375		} => container.get(i).map(|v| v.0.to_f32().unwrap_or(0.0)).unwrap_or(0.0),
376		ColumnData::Uint {
377			container,
378			..
379		} => container.get(i).map(|v| v.0.to_f32().unwrap_or(0.0)).unwrap_or(0.0),
380		ColumnData::Decimal {
381			container,
382			..
383		} => container.get(i).map(|v| v.0.to_f32().unwrap_or(0.0)).unwrap_or(0.0),
384		_ => 0.0,
385	}
386}
387
388fn get_as_f64(data: &ColumnData, i: usize) -> f64 {
389	match data {
390		ColumnData::Int1(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
391		ColumnData::Int2(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
392		ColumnData::Int4(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
393		ColumnData::Int8(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
394		ColumnData::Int16(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
395		ColumnData::Uint1(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
396		ColumnData::Uint2(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
397		ColumnData::Uint4(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
398		ColumnData::Uint8(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
399		ColumnData::Uint16(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
400		ColumnData::Float4(c) => c.get(i).map(|&v| v as f64).unwrap_or(0.0),
401		ColumnData::Float8(c) => c.get(i).copied().unwrap_or(0.0),
402		ColumnData::Int {
403			container,
404			..
405		} => container.get(i).map(|v| v.0.to_f64().unwrap_or(0.0)).unwrap_or(0.0),
406		ColumnData::Uint {
407			container,
408			..
409		} => container.get(i).map(|v| v.0.to_f64().unwrap_or(0.0)).unwrap_or(0.0),
410		ColumnData::Decimal {
411			container,
412			..
413		} => container.get(i).map(|v| v.0.to_f64().unwrap_or(0.0)).unwrap_or(0.0),
414		_ => 0.0,
415	}
416}
417
418/// Promotes two numeric types to a common type for power computation.
419/// This handles types that the standard Type::promote doesn't handle well,
420/// including Int, Uint, and Decimal.
421fn promote_numeric_types(left: Type, right: Type) -> Type {
422	use Type::*;
423
424	if matches!(left, Float4 | Float8 | Decimal) || matches!(right, Float4 | Float8 | Decimal) {
425		return Decimal;
426	}
427
428	// If any type is the arbitrary-precision Int or Uint, promote to the largest fixed type
429	// Int -> Int16 (largest signed), Uint -> Uint16 (largest unsigned)
430	// But if mixing signed/unsigned, go to Int16
431	if left == Int || right == Int {
432		return Int16;
433	}
434	if left == Uint || right == Uint {
435		// If the other type is signed, go to Int16
436		if matches!(left, Int1 | Int2 | Int4 | Int8 | Int16)
437			|| matches!(right, Int1 | Int2 | Int4 | Int8 | Int16)
438		{
439			return Int16;
440		}
441		return Uint16;
442	}
443
444	// For standard fixed-size types, use the standard promotion logic
445	Type::promote(left, right)
446}
447
448impl ScalarFunction for Power {
449	fn scalar(&self, ctx: ScalarFunctionContext) -> crate::error::ScalarFunctionResult<ColumnData> {
450		if let Some(result) = propagate_options(self, &ctx) {
451			return result;
452		}
453		let columns = ctx.columns;
454		let row_count = ctx.row_count;
455
456		// Validate exactly 2 arguments
457		if columns.len() != 2 {
458			return Err(ScalarFunctionError::ArityMismatch {
459				function: ctx.fragment.clone(),
460				expected: 2,
461				actual: columns.len(),
462			});
463		}
464
465		let base_column = columns.get(0).unwrap();
466		let exponent_column = columns.get(1).unwrap();
467
468		match (base_column.data(), exponent_column.data()) {
469			(ColumnData::Int1(base_container), ColumnData::Int1(exp_container)) => {
470				let mut result = Vec::with_capacity(row_count);
471				let mut bitvec = Vec::with_capacity(row_count);
472
473				for row_idx in 0..row_count {
474					let base = base_container.get(row_idx);
475					let exp = exp_container.get(row_idx);
476
477					match (base, exp) {
478						(Some(&base_val), Some(&exp_val)) => {
479							let power_result = if exp_val < 0 {
480								0 // Integer power with negative exponent results in 0
481							} else {
482								(base_val as i32).pow(exp_val as u32)
483							};
484							result.push(power_result);
485							bitvec.push(true);
486						}
487						_ => {
488							result.push(0);
489							bitvec.push(false);
490						}
491					}
492				}
493
494				Ok(ColumnData::int4_with_bitvec(result, bitvec))
495			}
496			(ColumnData::Int2(base_container), ColumnData::Int2(exp_container)) => {
497				let mut result = Vec::with_capacity(row_count);
498				let mut bitvec = Vec::with_capacity(row_count);
499
500				for row_idx in 0..row_count {
501					let base = base_container.get(row_idx);
502					let exp = exp_container.get(row_idx);
503
504					match (base, exp) {
505						(Some(&base_val), Some(&exp_val)) => {
506							let power_result = if exp_val < 0 {
507								0
508							} else {
509								(base_val as i32).pow(exp_val as u32)
510							};
511							result.push(power_result);
512							bitvec.push(true);
513						}
514						_ => {
515							result.push(0);
516							bitvec.push(false);
517						}
518					}
519				}
520
521				Ok(ColumnData::int4_with_bitvec(result, bitvec))
522			}
523			(ColumnData::Int4(base_container), ColumnData::Int4(exp_container)) => {
524				let mut result = Vec::with_capacity(row_count);
525				let mut bitvec = Vec::with_capacity(row_count);
526
527				for row_idx in 0..row_count {
528					let base = base_container.get(row_idx);
529					let exp = exp_container.get(row_idx);
530
531					match (base, exp) {
532						(Some(&base_val), Some(&exp_val)) => {
533							let power_result = if exp_val < 0 {
534								0
535							} else {
536								base_val.saturating_pow(exp_val as u32)
537							};
538							result.push(power_result);
539							bitvec.push(true);
540						}
541						_ => {
542							result.push(0);
543							bitvec.push(false);
544						}
545					}
546				}
547
548				Ok(ColumnData::int4_with_bitvec(result, bitvec))
549			}
550			(ColumnData::Int8(base_container), ColumnData::Int8(exp_container)) => {
551				let mut result = Vec::with_capacity(row_count);
552				let mut bitvec = Vec::with_capacity(row_count);
553
554				for row_idx in 0..row_count {
555					let base = base_container.get(row_idx);
556					let exp = exp_container.get(row_idx);
557
558					match (base, exp) {
559						(Some(&base_val), Some(&exp_val)) => {
560							let power_result = if exp_val < 0 {
561								0
562							} else {
563								base_val.saturating_pow(exp_val as u32)
564							};
565							result.push(power_result);
566							bitvec.push(true);
567						}
568						_ => {
569							result.push(0);
570							bitvec.push(false);
571						}
572					}
573				}
574
575				Ok(ColumnData::int8_with_bitvec(result, bitvec))
576			}
577			(ColumnData::Int16(base_container), ColumnData::Int16(exp_container)) => {
578				let mut result = Vec::with_capacity(row_count);
579				let mut bitvec = Vec::with_capacity(row_count);
580
581				for row_idx in 0..row_count {
582					let base = base_container.get(row_idx);
583					let exp = exp_container.get(row_idx);
584
585					match (base, exp) {
586						(Some(&base_val), Some(&exp_val)) => {
587							let power_result = if exp_val < 0 {
588								0
589							} else {
590								base_val.saturating_pow(exp_val as u32)
591							};
592							result.push(power_result);
593							bitvec.push(true);
594						}
595						_ => {
596							result.push(0);
597							bitvec.push(false);
598						}
599					}
600				}
601
602				Ok(ColumnData::int16_with_bitvec(result, bitvec))
603			}
604			(ColumnData::Uint1(base_container), ColumnData::Uint1(exp_container)) => {
605				let mut result = Vec::with_capacity(row_count);
606				let mut bitvec = Vec::with_capacity(row_count);
607
608				for row_idx in 0..row_count {
609					let base = base_container.get(row_idx);
610					let exp = exp_container.get(row_idx);
611
612					match (base, exp) {
613						(Some(&base_val), Some(&exp_val)) => {
614							let power_result =
615								(base_val as u32).saturating_pow(exp_val as u32);
616							result.push(power_result);
617							bitvec.push(true);
618						}
619						_ => {
620							result.push(0);
621							bitvec.push(false);
622						}
623					}
624				}
625
626				Ok(ColumnData::uint4_with_bitvec(result, bitvec))
627			}
628			(ColumnData::Uint2(base_container), ColumnData::Uint2(exp_container)) => {
629				let mut result = Vec::with_capacity(row_count);
630				let mut bitvec = Vec::with_capacity(row_count);
631
632				for row_idx in 0..row_count {
633					let base = base_container.get(row_idx);
634					let exp = exp_container.get(row_idx);
635
636					match (base, exp) {
637						(Some(&base_val), Some(&exp_val)) => {
638							let power_result =
639								(base_val as u32).saturating_pow(exp_val as u32);
640							result.push(power_result);
641							bitvec.push(true);
642						}
643						_ => {
644							result.push(0);
645							bitvec.push(false);
646						}
647					}
648				}
649
650				Ok(ColumnData::uint4_with_bitvec(result, bitvec))
651			}
652			(ColumnData::Uint4(base_container), ColumnData::Uint4(exp_container)) => {
653				let mut result = Vec::with_capacity(row_count);
654				let mut bitvec = Vec::with_capacity(row_count);
655
656				for row_idx in 0..row_count {
657					let base = base_container.get(row_idx);
658					let exp = exp_container.get(row_idx);
659
660					match (base, exp) {
661						(Some(&base_val), Some(&exp_val)) => {
662							let power_result = base_val.saturating_pow(exp_val);
663							result.push(power_result);
664							bitvec.push(true);
665						}
666						_ => {
667							result.push(0);
668							bitvec.push(false);
669						}
670					}
671				}
672
673				Ok(ColumnData::uint4_with_bitvec(result, bitvec))
674			}
675			(ColumnData::Uint8(base_container), ColumnData::Uint8(exp_container)) => {
676				let mut result = Vec::with_capacity(row_count);
677				let mut bitvec = Vec::with_capacity(row_count);
678
679				for row_idx in 0..row_count {
680					let base = base_container.get(row_idx);
681					let exp = exp_container.get(row_idx);
682
683					match (base, exp) {
684						(Some(&base_val), Some(&exp_val)) => {
685							let power_result = base_val.saturating_pow(exp_val as u32);
686							result.push(power_result);
687							bitvec.push(true);
688						}
689						_ => {
690							result.push(0);
691							bitvec.push(false);
692						}
693					}
694				}
695
696				Ok(ColumnData::uint8_with_bitvec(result, bitvec))
697			}
698			(ColumnData::Uint16(base_container), ColumnData::Uint16(exp_container)) => {
699				let mut result = Vec::with_capacity(row_count);
700				let mut bitvec = Vec::with_capacity(row_count);
701
702				for row_idx in 0..row_count {
703					let base = base_container.get(row_idx);
704					let exp = exp_container.get(row_idx);
705
706					match (base, exp) {
707						(Some(&base_val), Some(&exp_val)) => {
708							let power_result = base_val.saturating_pow(exp_val as u32);
709							result.push(power_result);
710							bitvec.push(true);
711						}
712						_ => {
713							result.push(0);
714							bitvec.push(false);
715						}
716					}
717				}
718
719				Ok(ColumnData::uint16_with_bitvec(result, bitvec))
720			}
721			(ColumnData::Float4(base_container), ColumnData::Float4(exp_container)) => {
722				let mut result = Vec::with_capacity(row_count);
723				let mut bitvec = Vec::with_capacity(row_count);
724
725				for row_idx in 0..row_count {
726					let base = base_container.get(row_idx);
727					let exp = exp_container.get(row_idx);
728
729					match (base, exp) {
730						(Some(&base_val), Some(&exp_val)) => {
731							result.push(base_val.powf(exp_val));
732							bitvec.push(true);
733						}
734						_ => {
735							result.push(0.0);
736							bitvec.push(false);
737						}
738					}
739				}
740
741				Ok(ColumnData::float4_with_bitvec(result, bitvec))
742			}
743			(ColumnData::Float8(base_container), ColumnData::Float8(exp_container)) => {
744				let mut result = Vec::with_capacity(row_count);
745				let mut bitvec = Vec::with_capacity(row_count);
746
747				for row_idx in 0..row_count {
748					let base = base_container.get(row_idx);
749					let exp = exp_container.get(row_idx);
750
751					match (base, exp) {
752						(Some(&base_val), Some(&exp_val)) => {
753							result.push(base_val.powf(exp_val));
754							bitvec.push(true);
755						}
756						_ => {
757							result.push(0.0);
758							bitvec.push(false);
759						}
760					}
761				}
762
763				Ok(ColumnData::float8_with_bitvec(result, bitvec))
764			}
765			(
766				ColumnData::Int {
767					container: base_container,
768					max_bytes,
769				},
770				ColumnData::Int {
771					container: exp_container,
772					..
773				},
774			) => {
775				use reifydb_type::value::int::Int;
776				let mut result = Vec::with_capacity(row_count);
777				let mut bitvec = Vec::with_capacity(row_count);
778
779				for row_idx in 0..row_count {
780					let base = base_container.get(row_idx);
781					let exp = exp_container.get(row_idx);
782
783					match (base, exp) {
784						(Some(base_val), Some(exp_val)) => {
785							let b = base_val.0.to_f64().unwrap_or(0.0);
786							let e = exp_val.0.to_f64().unwrap_or(0.0);
787							result.push(Int::from(b.powf(e) as i64));
788							bitvec.push(true);
789						}
790						_ => {
791							result.push(Int::default());
792							bitvec.push(false);
793						}
794					}
795				}
796
797				Ok(ColumnData::Int {
798					container: NumberContainer::new(result),
799					max_bytes: *max_bytes,
800				})
801			}
802			(
803				ColumnData::Uint {
804					container: base_container,
805					max_bytes,
806				},
807				ColumnData::Uint {
808					container: exp_container,
809					..
810				},
811			) => {
812				use reifydb_type::value::uint::Uint;
813				let mut result = Vec::with_capacity(row_count);
814				let mut bitvec = Vec::with_capacity(row_count);
815
816				for row_idx in 0..row_count {
817					let base = base_container.get(row_idx);
818					let exp = exp_container.get(row_idx);
819
820					match (base, exp) {
821						(Some(base_val), Some(exp_val)) => {
822							let b = base_val.0.to_f64().unwrap_or(0.0);
823							let e = exp_val.0.to_f64().unwrap_or(0.0);
824							result.push(Uint::from(b.powf(e) as u64));
825							bitvec.push(true);
826						}
827						_ => {
828							result.push(Uint::default());
829							bitvec.push(false);
830						}
831					}
832				}
833
834				Ok(ColumnData::Uint {
835					container: NumberContainer::new(result),
836					max_bytes: *max_bytes,
837				})
838			}
839			(
840				ColumnData::Decimal {
841					container: base_container,
842					precision,
843					scale,
844				},
845				ColumnData::Decimal {
846					container: exp_container,
847					..
848				},
849			) => {
850				let mut result = Vec::with_capacity(row_count);
851				let mut bitvec = Vec::with_capacity(row_count);
852
853				for row_idx in 0..row_count {
854					let base = base_container.get(row_idx);
855					let exp = exp_container.get(row_idx);
856
857					match (base, exp) {
858						(Some(base_val), Some(exp_val)) => {
859							let b = base_val.0.to_f64().unwrap_or(0.0);
860							let e = exp_val.0.to_f64().unwrap_or(0.0);
861
862							result.push(Decimal::from(b.powf(e)));
863							bitvec.push(true);
864						}
865						_ => {
866							result.push(Decimal::default());
867							bitvec.push(false);
868						}
869					}
870				}
871
872				Ok(ColumnData::Decimal {
873					container: NumberContainer::new(result),
874					precision: *precision,
875					scale: *scale,
876				})
877			}
878			// Mixed-type case: promote both columns to a common type and recurse
879			(base_data, exp_data) => {
880				let base_type = base_data.get_type();
881				let exp_type = exp_data.get_type();
882
883				if !base_type.is_number() || !exp_type.is_number() {
884					return Err(ScalarFunctionError::InvalidArgumentType {
885						function: ctx.fragment.clone(),
886						argument_index: 0,
887						expected: vec![
888							Type::Int1,
889							Type::Int2,
890							Type::Int4,
891							Type::Int8,
892							Type::Int16,
893							Type::Uint1,
894							Type::Uint2,
895							Type::Uint4,
896							Type::Uint8,
897							Type::Uint16,
898							Type::Float4,
899							Type::Float8,
900							Type::Int,
901							Type::Uint,
902							Type::Decimal,
903						],
904						actual: base_type,
905					});
906				}
907
908				let promoted_type = promote_numeric_types(base_type, exp_type);
909
910				let promoted_base = convert_column_to_type(base_data, promoted_type.clone(), row_count);
911				let promoted_exp = convert_column_to_type(exp_data, promoted_type, row_count);
912
913				let base_col = Column::new(Fragment::internal("base"), promoted_base);
914				let exp_col = Column::new(Fragment::internal("exp"), promoted_exp);
915				let promoted_columns = Columns::new(vec![base_col, exp_col]);
916
917				let new_ctx = ScalarFunctionContext {
918					fragment: ctx.fragment.clone(),
919					columns: &promoted_columns,
920					row_count,
921					clock: ctx.clock,
922				};
923				self.scalar(new_ctx)
924			}
925		}
926	}
927
928	fn return_type(&self, input_types: &[Type]) -> Type {
929		promote_numeric_types(input_types[0].clone(), input_types[1].clone())
930	}
931}