oxinum_float/native/context.rs
1//! [`FloatContext`] — precision + rounding-mode builder for native `BigFloat`
2//! transcendentals.
3//!
4//! Captures `(precision, rounding)` once so callers avoid repeating them at
5//! every transcendental call site.
6//!
7//! # Design
8//!
9//! The native [`BigFloat`] transcendental methods all take `(prec: u32, mode:
10//! RoundingMode)` as explicit parameters. `FloatContext` is a thin wrapper that
11//! stores these two values and forwards to the methods unchanged — it does not
12//! add guard bits or change any computation.
13//!
14//! # Naming
15//!
16//! The type is named `FloatContext` rather than `Context` to avoid a collision
17//! with `dashu::Context<R>`, which is re-exported in the same crate.
18//!
19//! # Constant methods
20//!
21//! [`FloatContext::pi`], [`FloatContext::e_const`], and [`FloatContext::ln2`]
22//! return `OxiNumResult<BigFloat>` because the underlying constant generators
23//! (`constants::pi`, etc.) are fallible — they may propagate arithmetic errors
24//! from the internal Chudnovsky / binary-splitting computations.
25//!
26//! # Examples
27//!
28//! ```
29//! use oxinum_float::native::{BigFloat, FloatContext, RoundingMode};
30//!
31//! let ctx = FloatContext::new(200);
32//! let two = BigFloat::from_i64(2, 200, RoundingMode::HalfEven);
33//! let ln2 = ctx.ln(&two).expect("ln(2)");
34//! let back = ctx.exp(&ln2).expect("exp(ln(2))");
35//! assert!(!back.is_zero());
36//! ```
37
38use oxinum_core::OxiNumResult;
39
40use super::constants;
41use super::float::{BigFloat, RoundingMode};
42
43/// Precision context for native `BigFloat` computations.
44///
45/// Stores `precision` (bits) and `rounding` together so that a single
46/// `FloatContext` value can be used across multiple transcendental calls
47/// without repeating the parameters.
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub struct FloatContext {
50 precision: u32,
51 rounding: RoundingMode,
52}
53
54impl FloatContext {
55 /// Create a context with the given `precision` in bits and
56 /// [`RoundingMode::HalfEven`] rounding.
57 ///
58 /// # Panics
59 ///
60 /// Panics if `precision == 0` (the `BigFloat` precision invariant requires
61 /// `prec > 0`).
62 pub fn new(precision: u32) -> Self {
63 assert!(precision > 0, "FloatContext precision must be > 0");
64 Self {
65 precision,
66 rounding: RoundingMode::HalfEven,
67 }
68 }
69
70 /// Return a new context identical to `self` but with `rounding` replaced.
71 ///
72 /// Uses the builder pattern so callers can chain:
73 ///
74 /// ```
75 /// use oxinum_float::native::{FloatContext, RoundingMode};
76 /// let ctx = FloatContext::new(128).with_rounding(RoundingMode::ToInf);
77 /// assert_eq!(ctx.rounding(), RoundingMode::ToInf);
78 /// ```
79 #[must_use]
80 pub fn with_rounding(self, mode: RoundingMode) -> Self {
81 Self {
82 rounding: mode,
83 ..self
84 }
85 }
86
87 /// Returns the precision (bits) stored in this context.
88 pub fn precision(&self) -> u32 {
89 self.precision
90 }
91
92 /// Returns the rounding mode stored in this context.
93 pub fn rounding(&self) -> RoundingMode {
94 self.rounding
95 }
96
97 // -----------------------------------------------------------------------
98 // Transcendental forwarders
99 // -----------------------------------------------------------------------
100
101 /// Return `sqrt(x)` at the context precision.
102 pub fn sqrt(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
103 x.sqrt(self.precision, self.rounding)
104 }
105
106 /// Return `e^x` at the context precision.
107 pub fn exp(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
108 x.exp(self.precision, self.rounding)
109 }
110
111 /// Return `ln(x)` at the context precision.
112 pub fn ln(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
113 x.ln(self.precision, self.rounding)
114 }
115
116 /// Return `log_base(x)` at the context precision.
117 pub fn log(&self, x: &BigFloat, base: &BigFloat) -> OxiNumResult<BigFloat> {
118 x.log(base, self.precision, self.rounding)
119 }
120
121 /// Return `x^exp` at the context precision.
122 pub fn pow(&self, x: &BigFloat, exp: &BigFloat) -> OxiNumResult<BigFloat> {
123 x.pow(exp, self.precision, self.rounding)
124 }
125
126 /// Return `sin(x)` at the context precision.
127 pub fn sin(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
128 x.sin(self.precision, self.rounding)
129 }
130
131 /// Return `cos(x)` at the context precision.
132 pub fn cos(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
133 x.cos(self.precision, self.rounding)
134 }
135
136 /// Return `tan(x)` at the context precision.
137 pub fn tan(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
138 x.tan(self.precision, self.rounding)
139 }
140
141 /// Return `atan(x)` at the context precision.
142 pub fn atan(&self, x: &BigFloat) -> OxiNumResult<BigFloat> {
143 x.atan(self.precision, self.rounding)
144 }
145
146 /// Return `atan2(y, x)` at the context precision.
147 ///
148 /// `y` is the first argument (`self` in the inherent method sense);
149 /// `x` is the second.
150 pub fn atan2(&self, y: &BigFloat, x: &BigFloat) -> OxiNumResult<BigFloat> {
151 y.atan2(x, self.precision, self.rounding)
152 }
153
154 // -----------------------------------------------------------------------
155 // Mathematical constants
156 // -----------------------------------------------------------------------
157
158 /// Return π at the context precision.
159 pub fn pi(&self) -> OxiNumResult<BigFloat> {
160 constants::pi(self.precision)
161 }
162
163 /// Return e (Euler's number) at the context precision.
164 pub fn e_const(&self) -> OxiNumResult<BigFloat> {
165 constants::e_const(self.precision)
166 }
167
168 /// Return ln 2 at the context precision.
169 pub fn ln2(&self) -> OxiNumResult<BigFloat> {
170 constants::ln2(self.precision)
171 }
172}