Rework tests to make use of the new MathOp trait

This commit is contained in:
Trevor Gross
2024-10-31 02:46:21 -05:00
parent 7db74d78e8
commit f7f24a4ed8
5 changed files with 272 additions and 309 deletions

View File

@@ -11,7 +11,7 @@ pub use rug::Float as MpFloat;
use rug::float::Round::Nearest;
use rug::ops::{PowAssignRound, RemAssignRound};
use crate::Float;
use crate::{Float, MathOp};
/// Create a multiple-precision float with the correct number of bits for a concrete float type.
fn new_mpfloat<F: Float>() -> MpFloat {
@@ -29,23 +29,19 @@ where
/// Structures that represent a float operation.
///
/// The struct itself should hold any context that can be reused among calls to `run` (allocated
/// `MpFloat`s).
pub trait MpOp {
/// Inputs to the operation (concrete float types).
type Input;
/// Outputs from the operation (concrete float types).
type Output;
pub trait MpOp: MathOp {
/// The struct itself should hold any context that can be reused among calls to `run` (allocated
/// `MpFloat`s).
type MpTy;
/// Create a new instance.
fn new() -> Self;
fn new_mp() -> Self::MpTy;
/// Perform the operation.
///
/// Usually this means assigning inputs to cached floats, performing the operation, applying
/// subnormal approximation, and converting the result back to concrete values.
fn run(&mut self, input: Self::Input) -> Self::Output;
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet;
}
/// Implement `MpOp` for functions with a single return value.
@@ -53,32 +49,21 @@ macro_rules! impl_mp_op {
// Matcher for unary functions
(
fn_name: $fn_name:ident,
CFn: $CFn:ty,
CArgs: $CArgs:ty,
CRet: $CRet:ty,
RustFn: fn($fty:ty,) -> $_ret:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
RustFn: fn($_fty:ty,) -> $_ret:ty,
fn_extra: $fn_name_normalized:expr,
) => {
paste::paste! {
pub mod $fn_name {
use super::*;
pub struct Operation(MpFloat);
impl MpOp for crate::op::$fn_name::Routine {
type MpTy = MpFloat;
impl MpOp for Operation {
type Input = $RustArgs;
type Output = $RustRet;
fn new_mp() -> Self::MpTy {
new_mpfloat::<Self::FTy>()
}
fn new() -> Self {
Self(new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
let ord = self.0.[< $fn_name_normalized _round >](Nearest);
prep_retval::<Self::Output>(&mut self.0, ord)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.assign(input.0);
let ord = this.[< $fn_name_normalized _round >](Nearest);
prep_retval::<Self::RustRet>(this, ord)
}
}
}
@@ -86,33 +71,22 @@ macro_rules! impl_mp_op {
// Matcher for binary functions
(
fn_name: $fn_name:ident,
CFn: $CFn:ty,
CArgs: $CArgs:ty,
CRet: $CRet:ty,
RustFn: fn($fty:ty, $_fty2:ty,) -> $_ret:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
RustFn: fn($_fty:ty, $_fty2:ty,) -> $_ret:ty,
fn_extra: $fn_name_normalized:expr,
) => {
paste::paste! {
pub mod $fn_name {
use super::*;
pub struct Operation(MpFloat, MpFloat);
impl MpOp for crate::op::$fn_name::Routine {
type MpTy = (MpFloat, MpFloat);
impl MpOp for Operation {
type Input = $RustArgs;
type Output = $RustRet;
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(input.1);
let ord = self.0.[< $fn_name_normalized _round >](&self.1, Nearest);
prep_retval::<Self::Output>(&mut self.0, ord)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
let ord = this.0.[< $fn_name_normalized _round >](&this.1, Nearest);
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
}
@@ -120,34 +94,27 @@ macro_rules! impl_mp_op {
// Matcher for ternary functions
(
fn_name: $fn_name:ident,
CFn: $CFn:ty,
CArgs: $CArgs:ty,
CRet: $CRet:ty,
RustFn: fn($fty:ty, $_fty2:ty, $_fty3:ty,) -> $_ret:ty,
RustArgs: $RustArgs:ty,
RustRet: $RustRet:ty,
RustFn: fn($_fty:ty, $_fty2:ty, $_fty3:ty,) -> $_ret:ty,
fn_extra: $fn_name_normalized:expr,
) => {
paste::paste! {
pub mod $fn_name {
use super::*;
pub struct Operation(MpFloat, MpFloat, MpFloat);
impl MpOp for crate::op::$fn_name::Routine {
type MpTy = (MpFloat, MpFloat, MpFloat);
impl MpOp for Operation {
type Input = $RustArgs;
type Output = $RustRet;
fn new_mp() -> Self::MpTy {
(
new_mpfloat::<Self::FTy>(),
new_mpfloat::<Self::FTy>(),
new_mpfloat::<Self::FTy>(),
)
}
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(input.1);
self.2.assign(input.2);
let ord = self.0.[< $fn_name_normalized _round >](&self.1, &self.2, Nearest);
prep_retval::<Self::Output>(&mut self.0, ord)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
this.2.assign(input.2);
let ord = this.0.[< $fn_name_normalized _round >](&this.1, &this.2, Nearest);
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
}
@@ -156,6 +123,7 @@ macro_rules! impl_mp_op {
libm_macros::for_each_function! {
callback: impl_mp_op,
emit_types: [RustFn],
skip: [
// Most of these need a manual implementation
fabs, ceil, copysign, floor, rint, round, trunc,
@@ -186,29 +154,23 @@ macro_rules! impl_no_round {
($($fn_name:ident, $rug_name:ident;)*) => {
paste::paste! {
// Implement for both f32 and f64
$( impl_no_round!{ @inner_unary [< $fn_name f >], (f32,), $rug_name } )*
$( impl_no_round!{ @inner_unary $fn_name, (f64,), $rug_name } )*
$( impl_no_round!{ @inner_unary [< $fn_name f >], $rug_name } )*
$( impl_no_round!{ @inner_unary $fn_name, $rug_name } )*
}
};
(@inner_unary $fn_name:ident, ($fty:ty,), $rug_name:ident) => {
pub mod $fn_name {
use super::*;
pub struct Operation(MpFloat);
(@inner_unary $fn_name:ident, $rug_name:ident) => {
impl MpOp for crate::op::$fn_name::Routine {
type MpTy = MpFloat;
impl MpOp for Operation {
type Input = ($fty,);
type Output = $fty;
fn new_mp() -> Self::MpTy {
new_mpfloat::<Self::FTy>()
}
fn new() -> Self {
Self(new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.0.$rug_name();
prep_retval::<Self::Output>(&mut self.0, Ordering::Equal)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.assign(input.0);
this.$rug_name();
prep_retval::<Self::RustRet>(this, Ordering::Equal)
}
}
};
@@ -227,132 +189,81 @@ impl_no_round! {
macro_rules! impl_op_for_ty {
($fty:ty, $suffix:literal) => {
paste::paste! {
pub mod [<copysign $suffix>] {
use super::*;
pub struct Operation(MpFloat, MpFloat);
impl MpOp for crate::op::[<copysign $suffix>]::Routine {
type MpTy = (MpFloat, MpFloat);
impl MpOp for Operation {
type Input = ($fty, $fty);
type Output = $fty;
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(input.1);
self.0.copysign_mut(&self.1);
prep_retval::<Self::Output>(&mut self.0, Ordering::Equal)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
this.0.copysign_mut(&this.1);
prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal)
}
}
pub mod [<pow $suffix>] {
use super::*;
pub struct Operation(MpFloat, MpFloat);
impl MpOp for crate::op::[<pow $suffix>]::Routine {
type MpTy = (MpFloat, MpFloat);
impl MpOp for Operation {
type Input = ($fty, $fty);
type Output = $fty;
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(input.1);
let ord = self.0.pow_assign_round(&self.1, Nearest);
prep_retval::<Self::Output>(&mut self.0, ord)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
let ord = this.0.pow_assign_round(&this.1, Nearest);
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
pub mod [<fmod $suffix>] {
use super::*;
pub struct Operation(MpFloat, MpFloat);
impl MpOp for crate::op::[<fmod $suffix>]::Routine {
type MpTy = (MpFloat, MpFloat);
impl MpOp for Operation {
type Input = ($fty, $fty);
type Output = $fty;
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(input.1);
let ord = self.0.rem_assign_round(&self.1, Nearest);
prep_retval::<Self::Output>(&mut self.0, ord)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
let ord = this.0.rem_assign_round(&this.1, Nearest);
prep_retval::<Self::RustRet>(&mut this.0, ord)
}
}
pub mod [<lgamma_r $suffix>] {
use super::*;
pub struct Operation(MpFloat);
impl MpOp for crate::op::[<jn $suffix>]::Routine {
type MpTy = (i32, MpFloat);
impl MpOp for Operation {
type Input = ($fty,);
type Output = ($fty, i32);
fn new_mp() -> Self::MpTy {
(0, new_mpfloat::<Self::FTy>())
}
fn new() -> Self {
Self(new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
let (sign, ord) = self.0.ln_abs_gamma_round(Nearest);
let ret = prep_retval::<$fty>(&mut self.0, ord);
(ret, sign as i32)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0 = input.0;
this.1.assign(input.1);
let ord = this.1.jn_round(this.0, Nearest);
prep_retval::<Self::FTy>(&mut this.1, ord)
}
}
pub mod [<jn $suffix>] {
use super::*;
pub struct Operation(i32, MpFloat);
impl MpOp for crate::op::[<sincos $suffix>]::Routine {
type MpTy = (MpFloat, MpFloat);
impl MpOp for Operation {
type Input = (i32, $fty);
type Output = $fty;
fn new() -> Self {
Self(0, new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0 = input.0;
self.1.assign(input.1);
let ord = self.1.jn_round(self.0, Nearest);
prep_retval::<$fty>(&mut self.1, ord)
}
fn new_mp() -> Self::MpTy {
(new_mpfloat::<Self::FTy>(), new_mpfloat::<Self::FTy>())
}
}
pub mod [<sincos $suffix>] {
use super::*;
pub struct Operation(MpFloat, MpFloat);
impl MpOp for Operation {
type Input = ($fty,);
type Output = ($fty, $fty);
fn new() -> Self {
Self(new_mpfloat::<$fty>(), new_mpfloat::<$fty>())
}
fn run(&mut self, input: Self::Input) -> Self::Output {
self.0.assign(input.0);
self.1.assign(0.0);
let (sord, cord) = self.0.sin_cos_round(&mut self.1, Nearest);
(
prep_retval::<$fty>(&mut self.0, sord),
prep_retval::<$fty>(&mut self.1, cord)
)
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(0.0);
let (sord, cord) = this.0.sin_cos_round(&mut this.1, Nearest);
(
prep_retval::<Self::FTy>(&mut this.0, sord),
prep_retval::<Self::FTy>(&mut this.1, cord)
)
}
}
}
@@ -362,7 +273,33 @@ macro_rules! impl_op_for_ty {
impl_op_for_ty!(f32, "f");
impl_op_for_ty!(f64, "");
// Account for `lgamma_r` not having a simple `f` suffix
pub mod lgammaf_r {
pub use super::lgamma_rf::*;
// `lgamma_r` is not a simple suffix so we can't use the above macro.
impl MpOp for crate::op::lgamma_r::Routine {
type MpTy = MpFloat;
fn new_mp() -> Self::MpTy {
new_mpfloat::<Self::FTy>()
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.assign(input.0);
let (sign, ord) = this.ln_abs_gamma_round(Nearest);
let ret = prep_retval::<Self::FTy>(this, ord);
(ret, sign as i32)
}
}
impl MpOp for crate::op::lgammaf_r::Routine {
type MpTy = MpFloat;
fn new_mp() -> Self::MpTy {
new_mpfloat::<Self::FTy>()
}
fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.assign(input.0);
let (sign, ord) = this.ln_abs_gamma_round(Nearest);
let ret = prep_retval::<Self::FTy>(this, ord);
(ret, sign as i32)
}
}