Skip to content

Commit

Permalink
Merge pull request #645 from rben01/math-functions
Browse files Browse the repository at this point in the history
Math functions
  • Loading branch information
sharkdp authored Nov 8, 2024
2 parents 31dcd1e + bb1087d commit 2d6e1e2
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 3 deletions.
4 changes: 4 additions & 0 deletions book/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ def list_of_functions(file_name, document):
"title": "Statistics",
"modules": ["math::statistics"],
},
{
"title": "Combinatorics",
"modules": ["math::combinatorics"],
},
{
"title": "Random sampling, distributions",
"modules": ["core::random", "math::distributions"],
Expand Down
96 changes: 95 additions & 1 deletion book/src/list-functions-math.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Mathematical functions

[Basics](#basics) · [Transcendental functions](#transcendental-functions) · [Trigonometry](#trigonometry) · [Statistics](#statistics) · [Random sampling, distributions](#random-sampling-distributions) · [Number theory](#number-theory) · [Numerical methods](#numerical-methods) · [Percentage calculations](#percentage-calculations) · [Geometry](#geometry) · [Algebra](#algebra) · [Trigonometry (extra)](#trigonometry-(extra))
[Basics](#basics) · [Transcendental functions](#transcendental-functions) · [Trigonometry](#trigonometry) · [Statistics](#statistics) · [Combinatorics](#combinatorics) · [Random sampling, distributions](#random-sampling-distributions) · [Number theory](#number-theory) · [Numerical methods](#numerical-methods) · [Percentage calculations](#percentage-calculations) · [Geometry](#geometry) · [Algebra](#algebra) · [Trigonometry (extra)](#trigonometry-(extra))

## Basics

Expand Down Expand Up @@ -272,6 +272,37 @@ Truncate in centimeters.

</details>

### `fract` (Fractional part)
Returns the fractional part of \\( x \\), i.e. the remainder when divided by 1.
If \\( x < 0 \\), then so will be `fract(x)`. Note that due to floating point error, a
number’s fractional part can be slightly “off”; for instance, `fract(1.2) ==
0.1999...996 != 0.2`.
More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.fract).

```nbt
fn fract(x: Scalar) -> Scalar
```

<details>
<summary>Examples</summary>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=fract%280%2E0%29')""></button></div><code class="language-nbt hljs numbat">fract(0.0)

= 0
</code></pre>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=fract%285%2E5%29')""></button></div><code class="language-nbt hljs numbat">fract(5.5)

= 0.5
</code></pre>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=fract%28%2D5%2E5%29')""></button></div><code class="language-nbt hljs numbat">fract(-5.5)

= -0.5
</code></pre>

</details>

### `mod` (Modulo)
Calculates the least nonnegative remainder of \\( a (\mod b) \\).
More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.rem_euclid).
Expand Down Expand Up @@ -597,6 +628,69 @@ fn median<D: Dim>(xs: List<D>) -> D

</details>

## Combinatorics

Defined in: `math::combinatorics`

### `factorial` (Factorial)
The product of the integers 1 through n. Numbat also supports calling this via the postfix operator `n!`.
More information [here](https://en.wikipedia.org/wiki/Factorial).

```nbt
fn factorial(n: Scalar) -> Scalar
```

<details>
<summary>Examples</summary>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=factorial%284%29')""></button></div><code class="language-nbt hljs numbat">factorial(4)

= 24
</code></pre>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=4%21')""></button></div><code class="language-nbt hljs numbat">4!

= 24
</code></pre>

</details>

### `falling_factorial` (Falling factorial)
Equal to \\( n⋅(n-1)⋅…⋅(n-k+2)⋅(n-k+1) \\) (k terms total). If n is an integer, this is the number of k-element permutations from a set of size n. k must always be an integer.
More information [here](https://en.wikipedia.org/wiki/Falling_and_rising_factorials).

```nbt
fn falling_factorial(n: Scalar, k: Scalar) -> Scalar
```

<details>
<summary>Examples</summary>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=falling%5Ffactorial%284%2C%202%29')""></button></div><code class="language-nbt hljs numbat">falling_factorial(4, 2)

= 12
</code></pre>

</details>

### `binom` (Binomial coefficient)
Equal to falling_factorial(n, k)/k!, this is the coefficient of \\( x^k \\) in the series expansion of \\( (1+x)^n \\) (see “binomial series”). If n is an integer, then this this is the number of k-element subsets of a set of size n, often read "n choose k". k must always be an integer.
More information [here](https://en.wikipedia.org/wiki/Binomial_coefficient).

```nbt
fn binom(n: Scalar, k: Scalar) -> Scalar
```

<details>
<summary>Examples</summary>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=binom%285%2C%202%29')""></button></div><code class="language-nbt hljs numbat">binom(5, 2)

= 10
</code></pre>

</details>

## Random sampling, distributions

Defined in: `core::random`, `math::distributions`
Expand Down
22 changes: 22 additions & 0 deletions book/src/list-functions-other.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,28 @@ fn is_nonzero<D: Dim>(value: D) -> Bool

</details>

### `is_integer`
Returns true if the input is an integer.

```nbt
fn is_integer(x: Scalar) -> Bool
```

<details>
<summary>Examples</summary>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=is%5Finteger%283%29')""></button></div><code class="language-nbt hljs numbat">is_integer(3)

= true [Bool]
</code></pre>

<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code" onclick=" window.open('https://numbat.dev/?q=is%5Finteger%28pi%29')""></button></div><code class="language-nbt hljs numbat">is_integer(pi)

= false [Bool]
</code></pre>

</details>

## Quantities

Defined in: `core::quantities`
Expand Down
12 changes: 11 additions & 1 deletion examples/tests/core.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ assert_eq(ceil(-1.2), -1)
assert_eq(1.2 m |> ceil_in(m), 2 m)
assert_eq(1.2 m |> ceil_in(cm), 120 cm)

# trunc, trunc_in
# trunc, trunc_in, fract

assert_eq(trunc(1.2), 1)
assert_eq(trunc(1.8), 1)
Expand All @@ -67,3 +67,13 @@ assert_eq(trunc(-1.8), -1)

assert_eq(1.8 m |> trunc_in(m), 1 m)
assert_eq(1.8 m |> trunc_in(cm), 180 cm)

assert_eq(fract(1.2), 0.2, 1e-12)
assert_eq(fract(1.8), 0.8, 1e-12)
assert_eq(fract(0), 0)
assert_eq(fract(1), 0)
assert_eq(fract(1e10), 0)
assert_eq(fract(-1.2), -0.2, 1e-12)
assert_eq(fract(-1.8), -0.8, 1e-12)
assert(is_nan(fract(NaN)))
assert(is_nan(fract(inf)))
78 changes: 78 additions & 0 deletions examples/tests/math_functions.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,84 @@ assert_eq(gamma(2.5), 1.329_340_388, 1e-8)
assert_eq(gamma(3), 2)
assert_eq(gamma(4), 6)

# factorial

assert_eq(factorial(0), 1)
assert_eq(factorial(1), 1)
assert_eq(factorial(2), 2)
assert_eq(factorial(3), 6)
assert_eq(factorial(4), 24)
assert_eq(factorial(20), 2432902008176640000)

# falling factorial

assert_eq(falling_factorial(0, 0), 1)
assert_eq(falling_factorial(1, 0), 1)
assert_eq(falling_factorial(2, 0), 1)
assert_eq(falling_factorial(42.5, 0), 1)

assert_eq(falling_factorial(0, 1), 0)
assert_eq(falling_factorial(1, 1), 1)
assert_eq(falling_factorial(2, 1), 2)
assert_eq(falling_factorial(42.5, 1), 42.5)

assert_eq(falling_factorial(0, 2), 0)
assert_eq(falling_factorial(1, 2), 0)
assert_eq(falling_factorial(2, 2), 2)
assert_eq(falling_factorial(42.5, 2), 1763.75)

assert_eq(falling_factorial(4, 0), 1)
assert_eq(falling_factorial(4, 1), 4)
assert_eq(falling_factorial(4, 2), 12)
assert_eq(falling_factorial(4, 3), 24)
assert_eq(falling_factorial(4, 4), 24)
assert_eq(falling_factorial(4, 5), 0)
assert_eq(falling_factorial(4, 6), 0)

assert_eq(falling_factorial(20, 0), 1)
assert_eq(falling_factorial(20, 1), 20)
assert_eq(falling_factorial(20, 2), 380)
assert_eq(falling_factorial(20, 20), 2432902008176640000)
assert_eq(falling_factorial(20, 21), 0)

# binomial coefficient
assert_eq(binom(0, -1), 0)
assert_eq(binom(0, 0), 1)
assert_eq(binom(0, 1), 0)

assert_eq(binom(1, -1), 0)
assert_eq(binom(1, 0), 1)
assert_eq(binom(1, 1), 1)
assert_eq(binom(1, 2), 0)

assert_eq(binom(2, -1), 0)
assert_eq(binom(2, 0), 1)
assert_eq(binom(2, 1), 2)
assert_eq(binom(2, 2), 1)
assert_eq(binom(2, 3), 0)

assert_eq(binom(3, -1), 0)
assert_eq(binom(3, 0), 1)
assert_eq(binom(3, 1), 3)
assert_eq(binom(3, 2), 3)
assert_eq(binom(3, 3), 1)
assert_eq(binom(3, 4), 0)

assert_eq(binom(4, -1), 0)
assert_eq(binom(4, 0), 1)
assert_eq(binom(4, 1), 4)
assert_eq(binom(4, 2), 6)
assert_eq(binom(4, 3), 4)
assert_eq(binom(4, 4), 1)
assert_eq(binom(4, 5), 0)

assert_eq(binom(1.5, -1), 0)
assert_eq(binom(1.5, 0), 1)
assert_eq(binom(1.5, 1), 1.5)
assert_eq(binom(1.5, 2), 0.375)
assert_eq(binom(1.5, 3), -0.0625)
assert_eq(binom(1.5, 4), 0.0234375)

# maximum

assert_eq(maximum([1]), 1)
Expand Down
11 changes: 11 additions & 0 deletions numbat/modules/core/functions.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ fn trunc(x: Scalar) -> Scalar
@example("trunc_in(cm, 5.7 m)", "Truncate in centimeters.")
fn trunc_in<D: Dim>(base: D, value: D) -> D = trunc(value / base) × base

@name("Fractional part")
@description("Returns the fractional part of $x$, i.e. the remainder when divided by 1.
If $x < 0$, then so will be `fract(x)`. Note that due to floating point error, a
number’s fractional part can be slightly “off”; for instance, `fract(1.2) ==
0.1999...996 != 0.2`.")
@url("https://doc.rust-lang.org/std/primitive.f64.html#method.fract")
@example("fract(0.0)")
@example("fract(5.5)")
@example("fract(-5.5)")
fn fract(x: Scalar) -> Scalar

@name("Modulo")
@description("Calculates the least nonnegative remainder of $a (\\mod b)$.")
@url("https://doc.rust-lang.org/std/primitive.f64.html#method.rem_euclid")
Expand Down
10 changes: 9 additions & 1 deletion numbat/modules/core/numbers.nbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use core::scalar
use core::functions

@description("Returns true if the input is `NaN`.")
@url("https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan")
@example("is_nan(37)")
Expand All @@ -23,4 +26,9 @@ fn is_zero<D: Dim>(value: D) -> Bool = value == 0
@description("Returns true unless the input is 0 (zero).")
@example("is_nonzero(37)")
@example("is_nonzero(0)")
fn is_nonzero<D: Dim>(value: D) -> Bool = !is_zero(value)
fn is_nonzero<D: Dim>(value: D) -> Bool = !is_zero(value)

@description("Returns true if the input is an integer.")
@example("is_integer(3)")
@example("is_integer(pi)")
fn is_integer(x: Scalar) -> Bool = is_zero(fract(x))
35 changes: 35 additions & 0 deletions numbat/modules/math/combinatorics.nbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use core::error
use core::functions
use core::numbers
use math::transcendental

@name("Factorial")
@description("The product of the integers 1 through n. Numbat also supports calling this via the postfix operator `n!`.")
@url("https://en.wikipedia.org/wiki/Factorial")
@example("factorial(4)")
@example("4!")
fn factorial(n: Scalar) -> Scalar = n!

@name("Falling factorial")
@description("Equal to $n⋅(n-1)⋅…⋅(n-k+2)⋅(n-k+1)$ (k terms total). If n is an integer, this is the number of k-element permutations from a set of size n. k must always be an integer.")
@url("https://en.wikipedia.org/wiki/Falling_and_rising_factorials")
@example("falling_factorial(4, 2)")
fn falling_factorial(n: Scalar, k: Scalar) -> Scalar =
if k < 0 || !is_integer(k) then
error("in falling_factorial(n, k), k must be a nonnegative integer")
else if is_zero(k) then
1
else
n * falling_factorial(n-1, k-1)

@name("Binomial coefficient")
@description("Equal to falling_factorial(n, k)/k!, this is the coefficient of $x^k$ in the series expansion of $(1+x)^n$ (see “binomial series”). If n is an integer, then this this is the number of k-element subsets of a set of size n, often read \"n choose k\". k must always be an integer.")
@url("https://en.wikipedia.org/wiki/Binomial_coefficient")
@example("binom(5, 2)")
fn binom(n: Scalar, k: Scalar) -> Scalar =
if !is_integer(k) then
error("in binom(n, k), k must be an integer")
else if k < 0 || (k > n && is_integer(n)) then
0
else
falling_factorial(n, k) / k!
1 change: 1 addition & 0 deletions numbat/modules/prelude.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use math::number_theory
use math::distributions
use math::geometry
use math::percentage_calculations
use math::combinatorics

use units::si
use units::time
Expand Down
1 change: 1 addition & 0 deletions numbat/src/ffi/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub(crate) fn functions() -> &'static HashMap<String, ForeignFunction> {
insert_function!(floor, 1..=1);
insert_function!(ceil, 1..=1);
insert_function!(trunc, 1..=1);
insert_function!(fract, 1..=1);

insert_function!(sin, 1..=1);
insert_function!(cos, 1..=1);
Expand Down
1 change: 1 addition & 0 deletions numbat/src/ffi/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ simple_scalar_math_function!(round, round);
simple_scalar_math_function!(floor, floor);
simple_scalar_math_function!(ceil, ceil);
simple_scalar_math_function!(trunc, trunc);
simple_scalar_math_function!(fract, fract);

simple_scalar_math_function!(sin, sin);
simple_scalar_math_function!(cos, cos);
Expand Down

0 comments on commit 2d6e1e2

Please sign in to comment.