From fcecaff16eeb3f11d84520d3ebcf9b762d0e48eb Mon Sep 17 00:00:00 2001
From: Andrew Kane <andrew@ankane.org>
Date: Mon, 31 Jul 2023 07:41:50 -0700
Subject: [PATCH 1/2] Add gamma and ln_gamma functions to f32 and f64

---
 library/std/src/f32.rs               | 42 ++++++++++++++++++++++++++++
 library/std/src/f32/tests.rs         | 32 +++++++++++++++++++++
 library/std/src/f64.rs               | 42 ++++++++++++++++++++++++++++
 library/std/src/f64/tests.rs         | 32 +++++++++++++++++++++
 library/std/src/lib.rs               |  1 +
 library/std/src/sys/unix/cmath.rs    |  4 +++
 library/std/src/sys/windows/cmath.rs |  6 +++-
 7 files changed, 158 insertions(+), 1 deletion(-)

diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs
index bc532990e94..a53b8535213 100644
--- a/library/std/src/f32.rs
+++ b/library/std/src/f32.rs
@@ -957,4 +957,46 @@ impl f32 {
     pub fn atanh(self) -> f32 {
         0.5 * ((2.0 * self) / (1.0 - self)).ln_1p()
     }
+
+    /// Gamma function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(float_gamma)]
+    /// let x = 5.0f32;
+    ///
+    /// let abs_difference = (x.gamma() - 24.0).abs();
+    ///
+    /// assert!(abs_difference <= f32::EPSILON);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "float_gamma", issue = "99842")]
+    #[inline]
+    pub fn gamma(self) -> f32 {
+        unsafe { cmath::tgammaf(self) }
+    }
+
+    /// Returns the natural logarithm of the gamma function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(float_gamma)]
+    /// let x = 2.0f32;
+    ///
+    /// let abs_difference = (x.ln_gamma().0 - 0.0).abs();
+    ///
+    /// assert!(abs_difference <= f32::EPSILON);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "float_gamma", issue = "99842")]
+    #[inline]
+    pub fn ln_gamma(self) -> (f32, i32) {
+        let mut signgamp: i32 = 0;
+        let x = unsafe { cmath::lgammaf_r(self, &mut signgamp) };
+        (x, signgamp)
+    }
 }
diff --git a/library/std/src/f32/tests.rs b/library/std/src/f32/tests.rs
index e949def00bb..9ca4e8f2f45 100644
--- a/library/std/src/f32/tests.rs
+++ b/library/std/src/f32/tests.rs
@@ -652,6 +652,38 @@ fn test_atanh() {
     assert_approx_eq!((-0.5f32).atanh(), -0.54930614433405484569762261846126285f32);
 }
 
+#[test]
+fn test_gamma() {
+    // precision can differ between platforms
+    assert_approx_eq!(1.0f32.gamma(), 1.0f32);
+    assert_approx_eq!(2.0f32.gamma(), 1.0f32);
+    assert_approx_eq!(3.0f32.gamma(), 2.0f32);
+    assert_approx_eq!(4.0f32.gamma(), 6.0f32);
+    assert_approx_eq!(5.0f32.gamma(), 24.0f32);
+    assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt());
+    assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt());
+    assert_eq!(0.0f32.gamma(), f32::INFINITY);
+    assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY);
+    assert!((-1.0f32).gamma().is_nan());
+    assert!((-2.0f32).gamma().is_nan());
+    assert!(f32::NAN.gamma().is_nan());
+    assert!(f32::NEG_INFINITY.gamma().is_nan());
+    assert_eq!(f32::INFINITY.gamma(), f32::INFINITY);
+    assert_eq!(171.71f32.gamma(), f32::INFINITY);
+}
+
+#[test]
+fn test_ln_gamma() {
+    assert_approx_eq!(1.0f32.ln_gamma().0, 0.0f32);
+    assert_eq!(1.0f32.ln_gamma().1, 1);
+    assert_approx_eq!(2.0f32.ln_gamma().0, 0.0f32);
+    assert_eq!(2.0f32.ln_gamma().1, 1);
+    assert_approx_eq!(3.0f32.ln_gamma().0, 2.0f32.ln());
+    assert_eq!(3.0f32.ln_gamma().1, 1);
+    assert_approx_eq!((-0.5f32).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln());
+    assert_eq!((-0.5f32).ln_gamma().1, -1);
+}
+
 #[test]
 fn test_real_consts() {
     use super::consts;
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index 5af9f8bcb23..a1cec22c97a 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -957,4 +957,46 @@ impl f64 {
     pub fn atanh(self) -> f64 {
         0.5 * ((2.0 * self) / (1.0 - self)).ln_1p()
     }
+
+    /// Gamma function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(float_gamma)]
+    /// let x = 5.0f64;
+    ///
+    /// let abs_difference = (x.gamma() - 24.0).abs();
+    ///
+    /// assert!(abs_difference <= f64::EPSILON);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "float_gamma", issue = "99842")]
+    #[inline]
+    pub fn gamma(self) -> f64 {
+        unsafe { cmath::tgamma(self) }
+    }
+
+    /// Returns the natural logarithm of the gamma function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(float_gamma)]
+    /// let x = 2.0f64;
+    ///
+    /// let abs_difference = (x.ln_gamma().0 - 0.0).abs();
+    ///
+    /// assert!(abs_difference <= f64::EPSILON);
+    /// ```
+    #[rustc_allow_incoherent_impl]
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "float_gamma", issue = "99842")]
+    #[inline]
+    pub fn ln_gamma(self) -> (f64, i32) {
+        let mut signgamp: i32 = 0;
+        let x = unsafe { cmath::lgamma_r(self, &mut signgamp) };
+        (x, signgamp)
+    }
 }
diff --git a/library/std/src/f64/tests.rs b/library/std/src/f64/tests.rs
index 53d351cceef..f88d01593b5 100644
--- a/library/std/src/f64/tests.rs
+++ b/library/std/src/f64/tests.rs
@@ -635,6 +635,38 @@ fn test_atanh() {
     assert_approx_eq!((-0.5f64).atanh(), -0.54930614433405484569762261846126285f64);
 }
 
+#[test]
+fn test_gamma() {
+    // precision can differ between platforms
+    assert_approx_eq!(1.0f64.gamma(), 1.0f64);
+    assert_approx_eq!(2.0f64.gamma(), 1.0f64);
+    assert_approx_eq!(3.0f64.gamma(), 2.0f64);
+    assert_approx_eq!(4.0f64.gamma(), 6.0f64);
+    assert_approx_eq!(5.0f64.gamma(), 24.0f64);
+    assert_approx_eq!(0.5f64.gamma(), consts::PI.sqrt());
+    assert_approx_eq!((-0.5f64).gamma(), -2.0 * consts::PI.sqrt());
+    assert_eq!(0.0f64.gamma(), f64::INFINITY);
+    assert_eq!((-0.0f64).gamma(), f64::NEG_INFINITY);
+    assert!((-1.0f64).gamma().is_nan());
+    assert!((-2.0f64).gamma().is_nan());
+    assert!(f64::NAN.gamma().is_nan());
+    assert!(f64::NEG_INFINITY.gamma().is_nan());
+    assert_eq!(f64::INFINITY.gamma(), f64::INFINITY);
+    assert_eq!(171.71f64.gamma(), f64::INFINITY);
+}
+
+#[test]
+fn test_ln_gamma() {
+    assert_approx_eq!(1.0f64.ln_gamma().0, 0.0f64);
+    assert_eq!(1.0f64.ln_gamma().1, 1);
+    assert_approx_eq!(2.0f64.ln_gamma().0, 0.0f64);
+    assert_eq!(2.0f64.ln_gamma().1, 1);
+    assert_approx_eq!(3.0f64.ln_gamma().0, 2.0f64.ln());
+    assert_eq!(3.0f64.ln_gamma().1, 1);
+    assert_approx_eq!((-0.5f64).ln_gamma().0, (2.0 * consts::PI.sqrt()).ln());
+    assert_eq!((-0.5f64).ln_gamma().1, -1);
+}
+
 #[test]
 fn test_real_consts() {
     use super::consts;
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 4cd251d0ac2..c54b65af67c 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -286,6 +286,7 @@
 #![feature(exact_size_is_empty)]
 #![feature(exclusive_wrapper)]
 #![feature(extend_one)]
+#![feature(float_gamma)]
 #![feature(float_minimum_maximum)]
 #![feature(float_next_up_down)]
 #![feature(hasher_prefixfree_extras)]
diff --git a/library/std/src/sys/unix/cmath.rs b/library/std/src/sys/unix/cmath.rs
index 2bf80d7a4cb..5346d229116 100644
--- a/library/std/src/sys/unix/cmath.rs
+++ b/library/std/src/sys/unix/cmath.rs
@@ -30,4 +30,8 @@ extern "C" {
     pub fn tanf(n: f32) -> f32;
     pub fn tanh(n: f64) -> f64;
     pub fn tanhf(n: f32) -> f32;
+    pub fn tgamma(n: f64) -> f64;
+    pub fn tgammaf(n: f32) -> f32;
+    pub fn lgamma_r(n: f64, s: &mut i32) -> f64;
+    pub fn lgammaf_r(n: f32, s: &mut i32) -> f32;
 }
diff --git a/library/std/src/sys/windows/cmath.rs b/library/std/src/sys/windows/cmath.rs
index 43ab8c7ee65..1b2a86f3c0e 100644
--- a/library/std/src/sys/windows/cmath.rs
+++ b/library/std/src/sys/windows/cmath.rs
@@ -1,6 +1,6 @@
 #![cfg(not(test))]
 
-use libc::{c_double, c_float};
+use libc::{c_double, c_float, c_int};
 
 extern "C" {
     pub fn acos(n: c_double) -> c_double;
@@ -23,6 +23,10 @@ extern "C" {
     pub fn sinh(n: c_double) -> c_double;
     pub fn tan(n: c_double) -> c_double;
     pub fn tanh(n: c_double) -> c_double;
+    pub fn tgamma(n: c_double) -> c_double;
+    pub fn tgammaf(n: c_float) -> c_float;
+    pub fn lgamma_r(n: c_double, s: &mut c_int) -> c_double;
+    pub fn lgammaf_r(n: c_float, s: &mut c_int) -> c_float;
 }
 
 pub use self::shims::*;

From a75e2284fb6c97fcc703a37e2ec1d1d1a0b4d501 Mon Sep 17 00:00:00 2001
From: Andrew Kane <andrew@ankane.org>
Date: Mon, 7 Aug 2023 16:38:09 -0700
Subject: [PATCH 2/2] Bump compiler_builtins to 0.1.100

---
 Cargo.lock             | 4 ++--
 library/std/Cargo.toml | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 45959c039e7..f863123531c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -641,9 +641,9 @@ dependencies = [
 
 [[package]]
 name = "compiler_builtins"
-version = "0.1.98"
+version = "0.1.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbefa16407456e5cad1ad066c84dfcb980afe4437103a0007d1c2f136130210"
+checksum = "d6c0f24437059853f0fa64afc51f338f93647a3de4cf3358ba1bb4171a199775"
 dependencies = [
  "cc",
  "rustc-std-workspace-core",
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index c47f910dadf..73197532dcc 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -18,7 +18,7 @@ panic_unwind = { path = "../panic_unwind", optional = true }
 panic_abort = { path = "../panic_abort" }
 core = { path = "../core", public = true }
 libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true }
-compiler_builtins = { version = "0.1.98" }
+compiler_builtins = { version = "0.1.100" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
 hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep-of-std'] }