Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add remaining math methods (#524) #525

Merged
merged 11 commits into from
Jul 2, 2020
94 changes: 94 additions & 0 deletions boa/src/builtins/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,21 @@ impl Math {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).ceil()).into())
}

/// Get the number of leading zeros in the 32 bit representation of a number
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.clz32
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
pub(crate) fn clz32(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args
.get(0)
.map_or(32i32, |x| (f64::from(x) as u32).leading_zeros() as i32)
.into())
}

/// Get the cosine of a number.
///
/// More information:
Expand Down Expand Up @@ -201,6 +216,23 @@ impl Math {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).exp()).into())
}

/// The Math.expm1() function returns e^x - 1, where x is the argument, and e the base of
/// the natural logarithms. The result is computed in a way that is accurate even when the
/// value of x is close 0
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.expm1
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1
pub(crate) fn expm1(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args
.get(0)
.map_or(f64::NAN, |x| f64::from(x).exp_m1())
.into())
}

/// Get the highest integer below a number.
///
/// More information:
Expand All @@ -216,6 +248,47 @@ impl Math {
.into())
}

/// Get the nearest 32-bit single precision float representation of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.fround
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
pub(crate) fn fround(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args
.get(0)
.map_or(f64::NAN, |x| (f64::from(x) as f32) as f64)
.into())
}

/// Get an approximation of the square root of the sum of squares of all arguments.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.hypot
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
pub(crate) fn hypot(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.iter().fold(0f64, |x, v| f64::from(v).hypot(x)).into())
}

/// Get the result of the C-like 32-bit multiplication of the two parameters.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.imul
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
pub(crate) fn imul(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let a = args.get(0).map_or(0f64, f64::from);
let b = args.get(1).map_or(0f64, f64::from);
Ok(((a as u32).wrapping_mul(b as u32) as i32).into())
}

/// Get the natural logarithm of a number.
///
/// More information:
Expand All @@ -238,6 +311,21 @@ impl Math {
.into())
}

/// Get approximation to the natural logarithm of 1 + x.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.log1p
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
pub(crate) fn log1p(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args
.get(0)
.map_or(f64::NAN, |x| f64::from(x).ln_1p())
.into())
}

/// Get the base 10 logarithm of the number.
///
/// More information:
Expand Down Expand Up @@ -485,11 +573,17 @@ impl Math {
make_builtin_fn(Self::atan2, "atan2", &math, 2);
make_builtin_fn(Self::cbrt, "cbrt", &math, 1);
make_builtin_fn(Self::ceil, "ceil", &math, 1);
make_builtin_fn(Self::clz32, "clz32", &math, 1);
make_builtin_fn(Self::cos, "cos", &math, 1);
make_builtin_fn(Self::cosh, "cosh", &math, 1);
make_builtin_fn(Self::exp, "exp", &math, 1);
make_builtin_fn(Self::expm1, "expm1", &math, 1);
make_builtin_fn(Self::floor, "floor", &math, 1);
make_builtin_fn(Self::fround, "fround", &math, 1);
make_builtin_fn(Self::hypot, "hypot", &math, 1);
make_builtin_fn(Self::imul, "imul", &math, 1);
make_builtin_fn(Self::log, "log", &math, 1);
make_builtin_fn(Self::log1p, "log1p", &math, 1);
make_builtin_fn(Self::log10, "log10", &math, 1);
make_builtin_fn(Self::log2, "log2", &math, 1);
make_builtin_fn(Self::max, "max", &math, 2);
Expand Down
201 changes: 198 additions & 3 deletions boa/src/builtins/math/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,42 @@ fn ceil() {
assert_eq!(c.to_number(), -7_f64);
}

#[test]
fn clz32() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.clz32();
var b = Math.clz32({});
var c = Math.clz32(-173);
var d = Math.clz32("1");
var e = Math.clz32(2147483647);
var f = Math.clz32(Infinity);
var g = Math.clz32(true);
var h = Math.clz32(0);
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let g = forward_val(&mut engine, "g").unwrap();
let h = forward_val(&mut engine, "h").unwrap();

assert_eq!(a.to_number(), 32_f64);
assert_eq!(b.to_number(), 32_f64);
assert_eq!(c.to_number(), 0_f64);
assert_eq!(d.to_number(), 31_f64);
assert_eq!(e.to_number(), 1_f64);
assert_eq!(f.to_number(), 32_f64);
assert_eq!(g.to_number(), 31_f64);
assert_eq!(h.to_number(), 32_f64);
}

#[test]
fn cos() {
let realm = Realm::create();
Expand Down Expand Up @@ -243,6 +279,36 @@ fn exp() {
assert_eq!(c.to_number(), 7.389_056_098_930_65);
}

#[test]
fn expm1() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.expm1();
var b = Math.expm1({});
var c = Math.expm1(1);
var d = Math.expm1(-1);
var e = Math.expm1(0);
var f = Math.expm1(2);
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward(&mut engine, "a");
let b = forward(&mut engine, "b");
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();

assert_eq!(a, String::from("NaN"));
assert_eq!(b, String::from("NaN"));
assert_eq!(c.to_number(), 1.718_281_828_459_045);
assert_eq!(d.to_number(), -0.632_120_558_828_557_7);
assert_eq!(e.to_number(), 0_f64);
assert_eq!(f.to_number(), 6.389_056_098_930_65);
}

#[test]
fn floor() {
let realm = Realm::create();
Expand All @@ -264,6 +330,102 @@ fn floor() {
assert_eq!(c.to_number(), 3_f64);
}

#[test]
fn fround() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.fround(NaN);
mr-rodgers marked this conversation as resolved.
Show resolved Hide resolved
var b = Math.fround(Infinity);
var c = Math.fround(5);
var d = Math.fround(5.5);
var e = Math.fround(5.05);
var f = Math.fround(-5.05);
var g = Math.fround();
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward(&mut engine, "a");
let b = forward(&mut engine, "b");
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();
let g = forward(&mut engine, "g");

assert_eq!(a, String::from("NaN"));
assert_eq!(b, String::from("Infinity"));
assert_eq!(c.to_number(), 5f64);
assert_eq!(d.to_number(), 5.5f64);
assert_eq!(e.to_number(), 5.050_000_190_734_863);
assert_eq!(f.to_number(), -5.050_000_190_734_863);
assert_eq!(g, String::from("NaN"));
}

#[test]
fn hypot() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.hypot();
var b = Math.hypot(3, 4);
mr-rodgers marked this conversation as resolved.
Show resolved Hide resolved
var c = Math.hypot(5, 12);
var d = Math.hypot(3, 4, -5);
var e = Math.hypot(4, [5], 6);
var f = Math.hypot(3, -Infinity);
var g = Math.hypot(12);
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward(&mut engine, "e");
let f = forward(&mut engine, "f");
let g = forward_val(&mut engine, "g").unwrap();

assert_eq!(a.to_number(), 0f64);
assert_eq!(b.to_number(), 5f64);
assert_eq!(c.to_number(), 13f64);
assert_eq!(d.to_number(), 7.071_067_811_865_475_5);
assert_eq!(e, String::from("NaN"));
assert_eq!(f, String::from("Infinity"));
assert_eq!(g.to_number(), 12f64);
}

#[test]
fn imul() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.imul(3, 4);
var b = Math.imul(-5, 12);
var c = Math.imul(0xffffffff, 5);
var d = Math.imul(0xfffffffe, 5);
mr-rodgers marked this conversation as resolved.
Show resolved Hide resolved
var e = Math.imul(12);
var f = Math.imul();
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward_val(&mut engine, "d").unwrap();
let e = forward_val(&mut engine, "e").unwrap();
let f = forward_val(&mut engine, "f").unwrap();

assert_eq!(a.to_number(), 12f64);
assert_eq!(b.to_number(), -60f64);
assert_eq!(c.to_number(), -5f64);
assert_eq!(d.to_number(), -10f64);
assert_eq!(e.to_number(), 0f64);
assert_eq!(f.to_number(), 0f64);
}

#[test]
fn log() {
let realm = Realm::create();
Expand All @@ -285,6 +447,39 @@ fn log() {
assert_eq!(c, String::from("NaN"));
}

#[test]
fn log1p() {
let realm = Realm::create();
let mut engine = Interpreter::new(realm);
let init = r#"
var a = Math.log1p(1);
mr-rodgers marked this conversation as resolved.
Show resolved Hide resolved
var b = Math.log1p(0);
var c = Math.log1p(-0.9999999999999999);
var d = Math.log1p(-1);
var e = Math.log1p(-1.000000000000001);
var f = Math.log1p(-2);
var g = Math.log1p();
"#;

eprintln!("{}", forward(&mut engine, init));

let a = forward_val(&mut engine, "a").unwrap();
let b = forward_val(&mut engine, "b").unwrap();
let c = forward_val(&mut engine, "c").unwrap();
let d = forward(&mut engine, "d");
let e = forward(&mut engine, "e");
let f = forward(&mut engine, "f");
let g = forward(&mut engine, "g");

assert_eq!(a.to_number(), f64::consts::LN_2);
assert_eq!(b.to_number(), 0f64);
assert_eq!(c.to_number(), -36.736_800_569_677_1);
assert_eq!(d, "-Infinity");
assert_eq!(e, String::from("NaN"));
assert_eq!(f, String::from("NaN"));
assert_eq!(g, String::from("NaN"));
}

#[test]
fn log10() {
let realm = Realm::create();
Expand Down Expand Up @@ -334,7 +529,7 @@ fn max() {
let init = r#"
var a = Math.max(10, 20);
var b = Math.max(-10, -20);
var c = Math.max(-10, 20);
var c = Math.max(-10, 20);
"#;

eprintln!("{}", forward(&mut engine, init));
Expand All @@ -355,7 +550,7 @@ fn min() {
let init = r#"
var a = Math.min(10, 20);
var b = Math.min(-10, -20);
var c = Math.min(-10, 20);
var c = Math.min(-10, 20);
"#;

eprintln!("{}", forward(&mut engine, init));
Expand Down Expand Up @@ -418,7 +613,7 @@ fn sign() {
let init = r#"
var a = Math.sign(3);
var b = Math.sign(-3);
var c = Math.sign(0);
var c = Math.sign(0);
"#;

eprintln!("{}", forward(&mut engine, init));
Expand Down