2023-09-13 15:09:07 -07:00
//! Functions related to mathematics.
use anyhow ::Result ;
2023-09-15 15:54:49 -07:00
use crate ::{
2025-04-26 19:33:41 -04:00
errors ::KclError ,
2025-04-09 11:46:54 +12:00
execution ::{
2025-04-30 12:40:11 +12:00
types ::{ ArrayLen , NumericType , RuntimeType } ,
2025-04-09 11:46:54 +12:00
ExecState , KclValue ,
} ,
2025-02-21 12:36:21 +13:00
std ::args ::{ Args , TyF64 } ,
2025-04-09 11:46:54 +12:00
CompilationError ,
2023-09-15 15:54:49 -07:00
} ;
2023-09-13 15:09:07 -07:00
2024-09-26 16:00:07 -05:00
/// Compute the remainder after dividing `num` by `div`.
/// If `num` is negative, the result will be too.
2025-04-09 11:46:54 +12:00
pub async fn rem ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-14 05:58:19 -04:00
let n : TyF64 = args . get_unlabeled_kw_arg_typed ( " number to divide " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
let d : TyF64 = args . get_kw_arg_typed ( " divisor " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-09 11:46:54 +12:00
let ( n , d , ty ) = NumericType ::combine_div ( n , d ) ;
2025-04-23 10:58:35 +12:00
if ty = = NumericType ::Unknown {
2025-04-30 12:40:11 +12:00
exec_state . err ( CompilationError ::err (
2025-04-09 11:46:54 +12:00
args . source_range ,
2025-04-23 10:58:35 +12:00
" Calling `rem` on numbers which have unknown or incompatible units. \n \n You may need to add information about the type of the argument, for example: \n using a numeric suffix: `42{ty}` \n or using type ascription: `foo(): number({ty})` "
2025-04-09 11:46:54 +12:00
) ) ;
}
2025-04-30 15:59:19 +12:00
let remainder = n % d ;
2024-09-26 16:00:07 -05:00
2025-04-09 11:46:54 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( remainder , ty ) ) )
2024-09-26 16:00:07 -05:00
}
2024-08-06 20:27:26 -04:00
/// Compute the cosine of a number (in radians).
2025-04-14 05:58:19 -04:00
pub async fn cos ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-23 10:58:35 +12:00
let num : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::angle ( ) , exec_state ) ? ;
2025-04-30 12:40:11 +12:00
let num = num . to_radians ( ) ;
2025-04-23 10:58:35 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::count ( num . cos ( ) ) ) )
2023-09-13 15:09:07 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the sine of a number (in radians).
2025-04-14 05:58:19 -04:00
pub async fn sin ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-23 10:58:35 +12:00
let num : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::angle ( ) , exec_state ) ? ;
2025-04-30 12:40:11 +12:00
let num = num . to_radians ( ) ;
2025-04-23 10:58:35 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::count ( num . sin ( ) ) ) )
2023-09-13 15:09:07 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the tangent of a number (in radians).
2025-04-14 05:58:19 -04:00
pub async fn tan ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-23 10:58:35 +12:00
let num : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::angle ( ) , exec_state ) ? ;
2025-04-30 12:40:11 +12:00
let num = num . to_radians ( ) ;
2025-04-23 10:58:35 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::count ( num . tan ( ) ) ) )
2023-09-13 15:09:07 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the square root of a number.
2025-04-14 05:58:19 -04:00
pub async fn sqrt ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . sqrt ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the absolute value of a number.
2025-04-26 19:33:41 -04:00
pub async fn abs ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . abs ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-26 19:33:41 -04:00
Ok ( args . make_user_val_from_f64_with_type ( input . map_value ( result ) ) )
2023-09-15 15:54:49 -07:00
}
2024-11-25 10:50:43 +13:00
/// Round a number to the nearest integer.
2025-04-26 19:33:41 -04:00
pub async fn round ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . round ( ) ;
2024-11-25 10:50:43 +13:00
2025-04-26 19:33:41 -04:00
Ok ( args . make_user_val_from_f64_with_type ( input . map_value ( result ) ) )
2024-11-25 10:50:43 +13:00
}
2024-08-06 20:27:26 -04:00
/// Compute the largest integer less than or equal to a number.
2025-04-26 19:33:41 -04:00
pub async fn floor ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . floor ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-26 19:33:41 -04:00
Ok ( args . make_user_val_from_f64_with_type ( input . map_value ( result ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the smallest integer greater than or equal to a number.
2025-04-26 19:33:41 -04:00
pub async fn ceil ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . ceil ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-26 19:33:41 -04:00
Ok ( args . make_user_val_from_f64_with_type ( input . map_value ( result ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the minimum of the given arguments.
2025-04-09 11:46:54 +12:00
pub async fn min ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let nums : Vec < TyF64 > = args . get_unlabeled_kw_arg_typed (
" input " ,
2025-04-30 15:59:19 +12:00
& RuntimeType ::Array ( Box ::new ( RuntimeType ::num_any ( ) ) , ArrayLen ::NonEmpty ) ,
2025-04-26 19:33:41 -04:00
exec_state ,
) ? ;
2025-04-09 11:46:54 +12:00
let ( nums , ty ) = NumericType ::combine_eq_array ( & nums ) ;
2025-04-23 10:58:35 +12:00
if ty = = NumericType ::Unknown {
2025-04-09 11:46:54 +12:00
exec_state . warn ( CompilationError ::err (
args . source_range ,
2025-04-23 10:58:35 +12:00
" Calling `min` on numbers which have unknown or incompatible units. \n \n You may need to add information about the type of the argument, for example: \n using a numeric suffix: `42{ty}` \n or using type ascription: `foo(): number({ty})` " ,
2025-04-09 11:46:54 +12:00
) ) ;
}
2023-09-15 15:54:49 -07:00
2025-04-30 15:59:19 +12:00
let mut result = f64 ::MAX ;
for num in nums {
if num < result {
result = num ;
2023-09-15 15:54:49 -07:00
}
}
2025-04-30 15:59:19 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , ty ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the maximum of the given arguments.
2025-04-09 11:46:54 +12:00
pub async fn max ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let nums : Vec < TyF64 > = args . get_unlabeled_kw_arg_typed (
" input " ,
2025-04-30 15:59:19 +12:00
& RuntimeType ::Array ( Box ::new ( RuntimeType ::num_any ( ) ) , ArrayLen ::NonEmpty ) ,
2025-04-26 19:33:41 -04:00
exec_state ,
) ? ;
2025-04-09 11:46:54 +12:00
let ( nums , ty ) = NumericType ::combine_eq_array ( & nums ) ;
2025-04-23 10:58:35 +12:00
if ty = = NumericType ::Unknown {
2025-04-09 11:46:54 +12:00
exec_state . warn ( CompilationError ::err (
args . source_range ,
2025-04-23 10:58:35 +12:00
" Calling `max` on numbers which have unknown or incompatible units. \n \n You may need to add information about the type of the argument, for example: \n using a numeric suffix: `42{ty}` \n or using type ascription: `foo(): number({ty})` " ,
2025-04-09 11:46:54 +12:00
) ) ;
}
2023-09-15 15:54:49 -07:00
2025-04-30 15:59:19 +12:00
let mut result = f64 ::MIN ;
for num in nums {
if num > result {
result = num ;
2023-09-15 15:54:49 -07:00
}
}
2025-04-30 15:59:19 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , ty ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the number to a power.
2025-04-14 05:58:19 -04:00
pub async fn pow ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
let exp : TyF64 = args . get_kw_arg_typed ( " exp " , & RuntimeType ::count ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . powf ( exp . n ) ;
2023-09-15 15:54:49 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the arccosine of a number (in radians).
2025-04-23 10:58:35 +12:00
pub async fn acos ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::count ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . acos ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-07 19:02:41 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , NumericType ::radians ( ) ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the arcsine of a number (in radians).
2025-04-23 10:58:35 +12:00
pub async fn asin ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::count ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . asin ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-07 19:02:41 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , NumericType ::radians ( ) ) ) )
2023-09-15 15:54:49 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the arctangent of a number (in radians).
2025-04-23 10:58:35 +12:00
pub async fn atan ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::count ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . atan ( ) ;
2023-09-15 15:54:49 -07:00
2025-04-07 19:02:41 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , NumericType ::radians ( ) ) ) )
2023-09-15 15:54:49 -07:00
}
2024-12-12 12:11:07 -06:00
/// Compute the four quadrant arctangent of Y and X (in radians).
2025-04-14 05:58:19 -04:00
pub async fn atan2 ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
let y = args . get_kw_arg_typed ( " y " , & RuntimeType ::length ( ) , exec_state ) ? ;
let x = args . get_kw_arg_typed ( " x " , & RuntimeType ::length ( ) , exec_state ) ? ;
2025-04-23 10:58:35 +12:00
let ( y , x , _ ) = NumericType ::combine_eq_coerce ( y , x ) ;
2025-04-30 15:59:19 +12:00
let result = y . atan2 ( x ) ;
2024-12-12 12:11:07 -06:00
2025-04-07 19:02:41 +12:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , NumericType ::radians ( ) ) ) )
2024-12-12 12:11:07 -06:00
}
2024-08-06 20:27:26 -04:00
/// Compute the logarithm of the number with respect to an arbitrary base.
2023-09-15 17:40:57 -07:00
///
/// The result might not be correctly rounded owing to implementation
/// details; `log2()` can produce more accurate results for base 2,
/// and `log10()` can produce more accurate results for base 10.
2025-04-14 05:58:19 -04:00
pub async fn log ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
let base : TyF64 = args . get_kw_arg_typed ( " base " , & RuntimeType ::count ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . log ( base . n ) ;
2023-09-15 17:40:57 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 17:40:57 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the base 2 logarithm of the number.
2025-04-14 05:58:19 -04:00
pub async fn log2 ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . log2 ( ) ;
2023-09-15 17:40:57 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 17:40:57 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the base 10 logarithm of the number.
2025-04-14 05:58:19 -04:00
pub async fn log10 ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . log10 ( ) ;
2023-09-15 17:40:57 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 17:40:57 -07:00
}
2024-08-06 20:27:26 -04:00
/// Compute the natural logarithm of the number.
2025-04-14 05:58:19 -04:00
pub async fn ln ( exec_state : & mut ExecState , args : Args ) -> Result < KclValue , KclError > {
2025-04-26 19:33:41 -04:00
let input : TyF64 = args . get_unlabeled_kw_arg_typed ( " input " , & RuntimeType ::num_any ( ) , exec_state ) ? ;
2025-04-30 15:59:19 +12:00
let result = input . n . ln ( ) ;
2023-09-15 17:40:57 -07:00
2025-04-14 05:58:19 -04:00
Ok ( args . make_user_val_from_f64_with_type ( TyF64 ::new ( result , exec_state . current_default_units ( ) ) ) )
2023-09-15 17:40:57 -07:00
}