1use crate::Consts;
4use crate::Error;
5use crate::ExactNum;
6use crate::Exponent;
7use crate::RoundingMode;
8use crate::EXPONENT_MAX;
9use crate::EXPONENT_MIN;
10
11#[derive(Debug)]
13pub struct Context {
14 cc: Consts,
15 p: usize,
16 rm: RoundingMode,
17 emin: Exponent,
18 emax: Exponent,
19}
20
21impl Context {
22 pub fn new(p: usize, rm: RoundingMode, cc: Consts, emin: Exponent, emax: Exponent) -> Self {
26 Context {
27 cc,
28 p,
29 rm,
30 emin: emin.clamp(EXPONENT_MIN, 0),
31 emax: emax.clamp(0, EXPONENT_MAX),
32 }
33 }
34
35 pub fn to_raw_parts(self) -> (usize, RoundingMode, Consts, Exponent, Exponent) {
38 let Context {
39 p,
40 rm,
41 cc,
42 emin,
43 emax,
44 } = self;
45 (p, rm, cc, emin, emax)
46 }
47
48 pub fn set_precision(&mut self, p: usize) {
50 self.p = p;
51 }
52
53 pub fn set_rounding_mode(&mut self, rm: RoundingMode) {
55 self.rm = rm;
56 }
57
58 pub fn set_consts(&mut self, cc: Consts) {
60 self.cc = cc;
61 }
62
63 pub fn set_emin(&mut self, emin: Exponent) {
66 self.emin = emin.clamp(EXPONENT_MIN, 0);
67 }
68
69 pub fn set_emax(&mut self, emax: Exponent) {
72 self.emax = emax.clamp(0, EXPONENT_MAX);
73 }
74
75 pub fn precision(&self) -> usize {
77 self.p
78 }
79
80 pub fn rounding_mode(&self) -> RoundingMode {
82 self.rm
83 }
84
85 pub fn consts(&mut self) -> &mut Consts {
87 &mut self.cc
88 }
89
90 pub fn const_pi(&mut self) -> ExactNum {
92 self.cc.pi(self.p, self.rm)
93 }
94
95 pub fn const_e(&mut self) -> ExactNum {
97 self.cc.e(self.p, self.rm)
98 }
99
100 pub fn const_ln2(&mut self) -> ExactNum {
102 self.cc.ln_2(self.p, self.rm)
103 }
104
105 pub fn const_ln10(&mut self) -> ExactNum {
107 self.cc.ln_10(self.p, self.rm)
108 }
109
110 pub fn const_sqrt2(&mut self) -> ExactNum {
112 self.cc.sqrt2(self.p, self.rm)
113 }
114
115 pub fn const_phi(&mut self) -> ExactNum {
117 self.cc.phi(self.p, self.rm)
118 }
119
120 pub fn const_euler_gamma(&mut self) -> ExactNum {
122 self.cc.euler_gamma(self.p, self.rm)
123 }
124
125 pub fn emin(&self) -> Exponent {
127 self.emin
128 }
129
130 pub fn emax(&self) -> Exponent {
132 self.emax
133 }
134
135 pub fn with_rounding_mode<F, R>(&mut self, rm: RoundingMode, f: F) -> R
138 where
139 F: FnOnce(&mut Self) -> R,
140 {
141 let old = self.rm;
142 self.rm = rm;
143 let out = f(self);
144 self.rm = old;
145 out
146 }
147
148 #[allow(clippy::should_implement_trait)]
154 pub fn clone(&self) -> Result<Self, Error> {
155 let cc = Consts::new()?;
156 Ok(Context {
157 p: self.p,
158 rm: self.rm,
159 cc,
160 emin: self.emin,
161 emax: self.emax,
162 })
163 }
164}
165
166pub trait Contextable {
187 fn precision(&self) -> usize;
189
190 fn rounding_mode(&self) -> RoundingMode;
192
193 fn consts(&mut self) -> &mut Consts;
195
196 fn const_pi(&mut self) -> ExactNum;
198
199 fn const_e(&mut self) -> ExactNum;
201
202 fn const_ln2(&mut self) -> ExactNum;
204
205 fn const_ln10(&mut self) -> ExactNum;
207
208 fn const_sqrt2(&mut self) -> ExactNum {
210 let p = self.precision();
211 let rm = self.rounding_mode();
212 self.consts().sqrt2(p, rm)
213 }
214
215 fn const_phi(&mut self) -> ExactNum {
217 let p = self.precision();
218 let rm = self.rounding_mode();
219 self.consts().phi(p, rm)
220 }
221
222 fn const_euler_gamma(&mut self) -> ExactNum {
224 let p = self.precision();
225 let rm = self.rounding_mode();
226 self.consts().euler_gamma(p, rm)
227 }
228
229 fn emin(&self) -> Exponent;
231
232 fn emax(&self) -> Exponent;
234}
235
236impl Contextable for (usize, RoundingMode, &mut Consts) {
237 fn precision(&self) -> usize {
238 self.0
239 }
240
241 fn rounding_mode(&self) -> RoundingMode {
242 self.1
243 }
244
245 fn consts(&mut self) -> &mut Consts {
246 self.2
247 }
248
249 fn const_pi(&mut self) -> ExactNum {
250 let (p, rm) = (self.0, self.1);
251 self.consts().pi(p, rm)
252 }
253
254 fn const_e(&mut self) -> ExactNum {
255 let (p, rm) = (self.0, self.1);
256 self.consts().e(p, rm)
257 }
258
259 fn const_ln2(&mut self) -> ExactNum {
260 let (p, rm) = (self.0, self.1);
261 self.consts().ln_2(p, rm)
262 }
263
264 fn const_ln10(&mut self) -> ExactNum {
265 let (p, rm) = (self.0, self.1);
266 self.consts().ln_10(p, rm)
267 }
268
269 fn emin(&self) -> Exponent {
270 EXPONENT_MIN
271 }
272
273 fn emax(&self) -> Exponent {
274 EXPONENT_MAX
275 }
276}
277
278impl Contextable for (usize, RoundingMode, &mut Consts, Exponent, Exponent) {
279 fn precision(&self) -> usize {
280 self.0
281 }
282
283 fn rounding_mode(&self) -> RoundingMode {
284 self.1
285 }
286
287 fn consts(&mut self) -> &mut Consts {
288 self.2
289 }
290
291 fn const_pi(&mut self) -> ExactNum {
292 let (p, rm) = (self.0, self.1);
293 self.consts().pi(p, rm)
294 }
295
296 fn const_e(&mut self) -> ExactNum {
297 let (p, rm) = (self.0, self.1);
298 self.consts().e(p, rm)
299 }
300
301 fn const_ln2(&mut self) -> ExactNum {
302 let (p, rm) = (self.0, self.1);
303 self.consts().ln_2(p, rm)
304 }
305
306 fn const_ln10(&mut self) -> ExactNum {
307 let (p, rm) = (self.0, self.1);
308 self.consts().ln_10(p, rm)
309 }
310
311 fn emin(&self) -> Exponent {
312 self.3.clamp(EXPONENT_MIN, 0)
313 }
314
315 fn emax(&self) -> Exponent {
316 self.4.clamp(0, EXPONENT_MAX)
317 }
318}
319
320impl Contextable for Context {
321 fn precision(&self) -> usize {
322 Context::precision(self)
323 }
324
325 fn rounding_mode(&self) -> RoundingMode {
326 Context::rounding_mode(self)
327 }
328
329 fn consts(&mut self) -> &mut Consts {
330 Context::consts(self)
331 }
332
333 fn const_pi(&mut self) -> ExactNum {
334 Context::const_pi(self)
335 }
336
337 fn const_e(&mut self) -> ExactNum {
338 Context::const_e(self)
339 }
340
341 fn const_ln2(&mut self) -> ExactNum {
342 Context::const_ln2(self)
343 }
344
345 fn const_ln10(&mut self) -> ExactNum {
346 Context::const_ln10(self)
347 }
348
349 fn const_sqrt2(&mut self) -> ExactNum {
350 Context::const_sqrt2(self)
351 }
352
353 fn const_phi(&mut self) -> ExactNum {
354 Context::const_phi(self)
355 }
356
357 fn const_euler_gamma(&mut self) -> ExactNum {
358 Context::const_euler_gamma(self)
359 }
360
361 fn emin(&self) -> Exponent {
362 Context::emin(self)
363 }
364
365 fn emax(&self) -> Exponent {
366 Context::emax(self)
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373 use crate::Consts;
374 use crate::RoundingMode;
375
376 #[test]
377 fn with_rounding_mode_restores() {
378 let mut ctx = Context::new(
379 128,
380 RoundingMode::ToEven,
381 Consts::new().unwrap(),
382 -1000,
383 1000,
384 );
385 let inner = ctx.with_rounding_mode(RoundingMode::Down, |c| c.rounding_mode());
386 assert_eq!(inner, RoundingMode::Down);
387 assert_eq!(ctx.rounding_mode(), RoundingMode::ToEven);
388 }
389}