Skip to content

Commit

Permalink
Merge pull request #192 from Qrlew/implement_ceil
Browse files Browse the repository at this point in the history
implement ceil, round, ...
  • Loading branch information
ngrislain authored Nov 20, 2023
2 parents c1c9c0a + bcbd1d0 commit b64b478
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
## Added
- `CEIL`, `ROUND`, `FLOOR`and `TRUNC` funtions [#192](https://github.com/Qrlew/qrlew/issues/192)

## [0.5.1] - 2023-11-19
## Added
Expand Down
128 changes: 128 additions & 0 deletions src/data_type/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,48 @@ pub fn coalesce() -> impl Function {
Coalesce
}

// Ceil function
pub fn ceil() -> impl Function {
PartitionnedMonotonic::univariate(
data_type::Float::default(),
|a| a.ceil(),
)
}

// Floor function
pub fn floor() -> impl Function {
PartitionnedMonotonic::univariate(
data_type::Float::default(),
|a| a.floor(),
)
}

// Round function
// monotonic for the 1st variable but not for the second => Pointwise
pub fn round() -> impl Function {
Pointwise::bivariate(
(data_type::Float::default(), data_type::Integer::default()),
data_type::Float::default(),
|a, b| {
let multiplier = 10.0_f64.powi(b as i32);
(a * multiplier).round() / multiplier
}
)
}

// Trunc function
// monotonic for the 1st variable but not for the second (eg: when the 2nd arg is negative )=> Pointwise
pub fn trunc() -> impl Function {
Pointwise::bivariate(
(data_type::Float::default(), data_type::Integer::default()),
data_type::Float::default(),
|a, b| {
let multiplier = 10.0_f64.powi(b as i32);
(a * multiplier).trunc() / multiplier
}
)
}

/*
Aggregation functions
*/
Expand Down Expand Up @@ -3361,6 +3403,92 @@ mod tests {
}

#[test]
fn test_ceil() {
println!("Test ceil");
let fun = ceil();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::float_values([9., 9.1, 9.5, 10.5]);
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10, 11]));

let set = DataType::integer_values([9, 10]);
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10]));
}

#[test]
fn test_floor() {
println!("Test floor");
let fun = floor();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::float_values([9., 9.1, 9.5, 10.5]);
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10]));

let set = DataType::integer_values([9, 10]);
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10]));
}

#[test]
fn test_round() {
println!("Test round");
let fun = round();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::from(Struct::from_data_types(&[
DataType::float_values([8.1, 9.16, 10.226, 11.333]),
DataType::integer_values([0, 2]),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::float_values([8., 9., 10., 11., 8.1, 9.16, 10.23, 11.33]));

let set = DataType::from(Struct::from_data_types(&[
DataType::integer_values([9, 10]),
DataType::integer_values([0, 2]),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10]));
}

#[test]
fn test_trunc() {
println!("Test trunc");
let fun = trunc();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::from(Struct::from_data_types(&[
DataType::float_values([8.1, 9.16, 10.226, 11.333]),
DataType::integer_values([0, 2]),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::float_values([8., 9., 10., 11., 8.1, 9.16, 10.22, 11.33]));

let set = DataType::from(Struct::from_data_types(&[
DataType::integer_values([9, 10]),
DataType::integer_values([0, 2]),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::integer_values([9, 10]));
}
fn test_cast_as_text() {
println!("Test cast as text");
let fun = cast(DataType::text());
Expand Down
24 changes: 21 additions & 3 deletions src/expr/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ pub enum Function {
Rtrim,
Ltrim,
Substr,
SubstrWithSize
SubstrWithSize,
Ceil,
Floor,
Round,
Trunc
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
Expand Down Expand Up @@ -124,6 +128,8 @@ impl Function {
| Function::CastAsInteger
| Function::CastAsBoolean
| Function::CastAsDateTime
| Function::Ceil
| Function::Floor
| Function::CastAsDate
| Function::CastAsTime
// Binary Functions
Expand All @@ -135,6 +141,8 @@ impl Function {
| Function::Rtrim
| Function::Ltrim
| Function::Substr
| Function::Round
| Function::Trunc
// Ternary Function
| Function::Case
| Function::SubstrWithSize
Expand Down Expand Up @@ -188,7 +196,9 @@ impl Function {
| Function::CastAsBoolean
| Function::CastAsDateTime
| Function::CastAsDate
| Function::CastAsTime => Arity::Unary,
| Function::CastAsTime
| Function::Ceil
| Function::Floor => Arity::Unary,
// Binary Function
Function::Pow
| Function::Position
Expand All @@ -197,7 +207,11 @@ impl Function {
| Function::Coalesce
| Function::Rtrim
| Function::Ltrim
| Function::Substr => Arity::Nary(2),
| Function::Substr
| Function::Round
| Function::Trunc => {
Arity::Nary(2)
}
// Ternary Function
Function::Case | Function::SubstrWithSize => Arity::Nary(3),
// Nary Function
Expand Down Expand Up @@ -271,6 +285,8 @@ impl fmt::Display for Function {
Function::CastAsFloat => "cast_as_float",
Function::CastAsBoolean => "cast_as_boolean",
Function::CastAsDateTime => "cast_as_date_time",
Function::Ceil => "ceil",
Function::Floor => "floor",
Function::CastAsDate => "cast_as_date",
Function::CastAsTime => "cast_as_time",
// Binary Functions
Expand All @@ -282,6 +298,8 @@ impl fmt::Display for Function {
Function::Rtrim => "rtrim",
Function::Ltrim => "ltrim",
Function::Substr => "substr",
Function::Round => "round",
Function::Trunc => "trunc",
// Ternary Functions
Function::Case => "case",
Function::SubstrWithSize => "substr",
Expand Down
10 changes: 6 additions & 4 deletions src/expr/implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ macro_rules! function_implementations {
}

// All functions:
// Unary: Opposite, Not, Exp, Ln, Abs, Sin, Cos, CharLength, Lower, Upper, Md5
// Binary: Plus, Minus, Multiply, Divide, Modulo, StringConcat, Gt, Lt, GtEq, LtEq, Eq, NotEq, And, Or, Xor, BitwiseOr, BitwiseAnd, BitwiseXor, Position, Concat, Greatest, Least
// Unary: Opposite, Not, Exp, Ln, Abs, Sin, Cos, CharLength, Lower, Upper, Md5, Ceil, Floor
// Binary: Plus, Minus, Multiply, Divide, Modulo, StringConcat, Gt, Lt, GtEq, LtEq, Eq, NotEq, And, Or, Xor, BitwiseOr, BitwiseAnd, BitwiseXor, Position, Concat, Greatest, Least, Round, Trunc
// Ternary: Case, Position
// Nary: Concat
function_implementations!(
[Opposite, Not, Exp, Ln, Log, Abs, Sin, Cos, Sqrt, Md5],
[Opposite, Not, Exp, Ln, Log, Abs, Sin, Cos, Sqrt, Md5, Ceil, Floor],
[
Plus,
Minus,
Expand Down Expand Up @@ -74,7 +74,9 @@ function_implementations!(
Greatest,
Rtrim,
Ltrim,
Substr
Substr,
Round,
Trunc
],
[Case, Position, SubstrWithSize],
x,
Expand Down
8 changes: 6 additions & 2 deletions src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ impl_unary_function_constructors!(
CastAsBoolean,
CastAsDateTime,
CastAsDate,
CastAsTime
CastAsTime,
Ceil,
Floor
); // TODO Complete that

/// Implement binary function constructors
Expand Down Expand Up @@ -339,7 +341,9 @@ impl_binary_function_constructors!(
Coalesce,
Rtrim,
Ltrim,
Substr
Substr,
Trunc,
Round
);

/// Implement ternary function constructors
Expand Down
64 changes: 63 additions & 1 deletion src/expr/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ impl<'a> expr::Visitor<'a, ast::Expr> for FromExprVisitor {
| expr::function::Function::Rtrim
| expr::function::Function::Ltrim
| expr::function::Function::Substr
| expr::function::Function::SubstrWithSize => ast::Expr::Function(ast::Function {
| expr::function::Function::SubstrWithSize
| expr::function::Function::Ceil
| expr::function::Function::Floor => ast::Expr::Function(ast::Function {
name: ast::ObjectName(vec![ast::Ident::new(function.to_string())]),
args: arguments
.into_iter()
Expand All @@ -197,6 +199,20 @@ impl<'a> expr::Visitor<'a, ast::Expr> for FromExprVisitor {
filter: None,
null_treatment: None,
}),
expr::function::Function::Round
| expr::function::Function::Trunc => ast::Expr::Function(ast::Function {
name: ast::ObjectName(vec![ast::Ident::new(function.to_string())]),
args: arguments
.into_iter()
.filter_map(|e| (e!=ast::Expr::Value(ast::Value::Number("1".to_string(), false))).then_some(ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr(e))))
.collect(),
over: None,
distinct: false,
special: false,
order_by: vec![],
filter: None,
null_treatment: None,
}),
expr::function::Function::Case => ast::Expr::Case {
operand: None,
conditions: vec![arguments[0].clone()],
Expand Down Expand Up @@ -555,6 +571,7 @@ mod tests {
println!("ast::expr = {gen_expr}");
assert_eq!(gen_expr, parse_expr("substr(a, 0, 5)").unwrap());
}

#[test]
fn test_cast() {
let str_expr = "cast(a as varchar)";
Expand Down Expand Up @@ -589,4 +606,49 @@ mod tests {
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr, gen_expr);
}

#[test]
fn test_ceil() {
let str_expr = "ceil(a)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr.to_string().to_lowercase(), gen_expr.to_string().to_lowercase());
}


#[test]
fn test_floor() {
let str_expr = "floor(a)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr.to_string().to_lowercase(), gen_expr.to_string().to_lowercase());
}

#[test]
fn test_round() {
let str_expr = "round(a, 2)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr.to_string().to_lowercase(), gen_expr.to_string().to_lowercase());
}

#[test]
fn test_trunc() {
let str_expr = "trunc(a, 4)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr.to_string().to_lowercase(), gen_expr.to_string().to_lowercase());
}
}
Loading

0 comments on commit b64b478

Please sign in to comment.