Add SQL math::pow() function and ** operator (#1239)
This commit is contained in:
parent
df954a9554
commit
cef01ad790
8 changed files with 106 additions and 0 deletions
|
@ -120,6 +120,10 @@ pub fn percentile((array, n): (Value, Number)) -> Result<Value, Error> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn pow((arg, pow): (Number, Number)) -> Result<Value, Error> {
|
||||
Ok(arg.pow(pow).into())
|
||||
}
|
||||
|
||||
pub fn product((array,): (Value,)) -> Result<Value, Error> {
|
||||
Ok(match array {
|
||||
Value::Array(v) => v.as_numbers().into_iter().product::<Number>().into(),
|
||||
|
|
|
@ -121,6 +121,7 @@ pub fn synchronous(ctx: &Context<'_>, name: &str, args: Vec<Value>) -> Result<Va
|
|||
"math::mode" => math::mode,
|
||||
"math::nearestrank" => math::nearestrank,
|
||||
"math::percentile" => math::percentile,
|
||||
"math::pow" => math::pow,
|
||||
"math::product" => math::product,
|
||||
"math::round" => math::round,
|
||||
"math::spread" => math::spread,
|
||||
|
|
|
@ -49,6 +49,10 @@ pub fn div(a: Value, b: Value) -> Result<Value, Error> {
|
|||
Ok(a.div(b))
|
||||
}
|
||||
|
||||
pub fn pow(a: Value, b: Value) -> Result<Value, Error> {
|
||||
Ok(a.pow(b))
|
||||
}
|
||||
|
||||
pub fn exact(a: &Value, b: &Value) -> Result<Value, Error> {
|
||||
Ok(Value::from(a == b))
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ impl Expression {
|
|||
Operator::Sub => fnc::operate::sub(l, r),
|
||||
Operator::Mul => fnc::operate::mul(l, r),
|
||||
Operator::Div => fnc::operate::div(l, r),
|
||||
Operator::Pow => fnc::operate::pow(l, r),
|
||||
Operator::Equal => fnc::operate::equal(&l, &r),
|
||||
Operator::Exact => fnc::operate::exact(&l, &r),
|
||||
Operator::NotEqual => fnc::operate::not_equal(&l, &r),
|
||||
|
|
|
@ -340,6 +340,7 @@ fn function_math(i: &str) -> IResult<&str, &str> {
|
|||
alt((
|
||||
tag("math::nearestrank"),
|
||||
tag("math::percentile"),
|
||||
tag("math::pow"),
|
||||
tag("math::product"),
|
||||
tag("math::round"),
|
||||
tag("math::spread"),
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::err::Error;
|
|||
use crate::sql::ending::number as ending;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::serde::is_internal_serialization;
|
||||
use bigdecimal::num_traits::Pow;
|
||||
use bigdecimal::BigDecimal;
|
||||
use bigdecimal::FromPrimitive;
|
||||
use bigdecimal::ToPrimitive;
|
||||
|
@ -396,6 +397,21 @@ impl Number {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pow(self, power: Number) -> Number {
|
||||
match (self, power) {
|
||||
(Number::Int(v), Number::Int(p)) if p >= 0 && p < u32::MAX as i64 => {
|
||||
Number::Int(v.pow(p as u32))
|
||||
}
|
||||
(Number::Decimal(v), Number::Int(p)) if p >= 0 && p < u32::MAX as i64 => {
|
||||
let (as_int, scale) = v.as_bigint_and_exponent();
|
||||
Number::Decimal(BigDecimal::new(as_int.pow(p as u32), scale * p))
|
||||
}
|
||||
// TODO: (Number::Decimal(v), Number::Float(p)) => todo!(),
|
||||
// TODO: (Number::Decimal(v), Number::Decimal(p)) => todo!(),
|
||||
(v, p) => Number::Float(v.as_float().pow(p.as_float())),
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// -----------------------------------
|
||||
|
@ -736,4 +752,72 @@ mod tests {
|
|||
assert_eq!("-123.45", format!("{}", out));
|
||||
assert_eq!(out, Number::from(-123.45));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_int() {
|
||||
let res = number("3");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("4");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(81));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_negatives() {
|
||||
let res = number("4");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("-0.5");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(0.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_float() {
|
||||
let res = number("2.5");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("2");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from(6.25));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_bigdecimal_one() {
|
||||
let res = number("13.5719384719384719385639856394139476937756394756");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("1");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(
|
||||
res.pow(power),
|
||||
Number::from("13.5719384719384719385639856394139476937756394756")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_pow_bigdecimal_int() {
|
||||
let res = number("13.5719384719384719385639856394139476937756394756");
|
||||
assert!(res.is_ok());
|
||||
let res = res.unwrap().1;
|
||||
|
||||
let power = number("2");
|
||||
assert!(power.is_ok());
|
||||
let power = power.unwrap().1;
|
||||
|
||||
assert_eq!(res.pow(power), Number::from("184.19751388608358465578173996877942643463869043732548087725588482334195240945031617770904299536"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ pub enum Operator {
|
|||
Sub, // -
|
||||
Mul, // *
|
||||
Div, // /
|
||||
Pow, // **
|
||||
Inc, // +=
|
||||
Dec, // -=
|
||||
//
|
||||
|
@ -89,6 +90,7 @@ impl fmt::Display for Operator {
|
|||
Self::Sub => "-",
|
||||
Self::Mul => "*",
|
||||
Self::Div => "/",
|
||||
Self::Pow => "**",
|
||||
Self::Inc => "+=",
|
||||
Self::Dec => "-=",
|
||||
Self::Equal => "=",
|
||||
|
@ -168,6 +170,7 @@ pub fn symbols(i: &str) -> IResult<&str, Operator> {
|
|||
map(char('∙'), |_| Operator::Mul),
|
||||
map(char('/'), |_| Operator::Div),
|
||||
map(char('÷'), |_| Operator::Div),
|
||||
map(tag("**"), |_| Operator::Pow),
|
||||
)),
|
||||
alt((
|
||||
map(char('∋'), |_| Operator::Contain),
|
||||
|
|
|
@ -1229,6 +1229,14 @@ impl Value {
|
|||
_ => self.partial_cmp(other),
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
// Mathematical operations
|
||||
// -----------------------------------
|
||||
|
||||
pub fn pow(self, other: Value) -> Value {
|
||||
self.as_number().pow(other.as_number()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
|
|
Loading…
Reference in a new issue