fend_core/num/
real.rs

1use crate::DecimalSeparatorStyle;
2use crate::error::{FendError, Interrupt};
3use crate::format::Format;
4use crate::num::Exact;
5use crate::num::bigrat::{BigRat, FormattedBigRat};
6use crate::num::{Base, FormattingStyle};
7use crate::result::FResult;
8use crate::serialize::CborValue;
9use std::cmp::Ordering;
10use std::ops::Neg;
11use std::{fmt, hash};
12
13use super::bigrat;
14use super::biguint::BigUint;
15
16#[derive(Clone)]
17pub(crate) struct Real {
18	pattern: Pattern,
19}
20
21impl fmt::Debug for Real {
22	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23		match &self.pattern {
24			Pattern::Simple(x) => write!(f, "{x:?}"),
25			Pattern::Pi(x) => {
26				if x.is_definitely_one() {
27					write!(f, "pi")
28				} else {
29					write!(f, "{x:?} * pi")
30				}
31			}
32		}
33	}
34}
35
36#[derive(Clone, Debug)]
37pub(crate) enum Pattern {
38	/// a simple fraction
39	Simple(BigRat),
40	// n * pi
41	Pi(BigRat),
42}
43
44impl hash::Hash for Real {
45	fn hash<H: hash::Hasher>(&self, state: &mut H) {
46		match &self.pattern {
47			Pattern::Simple(r) | Pattern::Pi(r) => r.hash(state),
48		}
49	}
50}
51
52impl Real {
53	pub(crate) fn compare<I: Interrupt>(&self, other: &Self, int: &I) -> FResult<Ordering> {
54		Ok(match (&self.pattern, &other.pattern) {
55			(Pattern::Simple(a), Pattern::Simple(b)) | (Pattern::Pi(a), Pattern::Pi(b)) => a.cmp(b),
56			_ => {
57				let a = self.clone().approximate(int)?;
58				let b = other.clone().approximate(int)?;
59				a.cmp(&b)
60			}
61		})
62	}
63
64	pub(crate) fn serialize(&self) -> CborValue {
65		match &self.pattern {
66			Pattern::Simple(s) => s.serialize(),
67			Pattern::Pi(n) => {
68				// private use tag
69				CborValue::Tag(80000, Box::new(n.serialize()))
70			}
71		}
72	}
73
74	pub(crate) fn deserialize(value: CborValue) -> FResult<Self> {
75		Ok(Self {
76			pattern: match value {
77				CborValue::Tag(80000, inner) => Pattern::Pi(BigRat::deserialize(*inner)?),
78				value => Pattern::Simple(BigRat::deserialize(value)?),
79			},
80		})
81	}
82
83	pub(crate) fn is_integer(&self) -> bool {
84		match &self.pattern {
85			Pattern::Simple(s) => s.is_integer(),
86			Pattern::Pi(_) => false,
87		}
88	}
89
90	fn approximate<I: Interrupt>(self, int: &I) -> FResult<BigRat> {
91		match self.pattern {
92			Pattern::Simple(s) => Ok(s),
93			Pattern::Pi(n) => {
94				// This fraction is the 33th convergent of pi, (in the sense of continued fraction),
95				// and as such, is a "best approximation of the second kind" of pi (see e.g. the
96				// book of Khinchin about continued fractions).
97				// The 33th convergent is the last one with both numerators and denominators
98				// fitting in a u64-container.
99				// The difference between this approximation and pi is less than 10^-37.
100				let num = BigRat::from(2_646_693_125_139_304_345);
101				let den = BigRat::from(842_468_587_426_513_207);
102				let pi = num.div(&den, int)?;
103				Ok(n.mul(&pi, int)?)
104			}
105		}
106	}
107
108	pub(crate) fn try_as_biguint<I: Interrupt>(self, int: &I) -> FResult<BigUint> {
109		match self.pattern {
110			Pattern::Simple(s) => s.try_as_biguint(int),
111			Pattern::Pi(n) => {
112				if n == 0.into() {
113					Ok(BigUint::Small(0))
114				} else {
115					Err(FendError::CannotConvertToInteger)
116				}
117			}
118		}
119	}
120
121	pub(crate) fn try_as_i64<I: Interrupt>(self, int: &I) -> FResult<i64> {
122		match self.pattern {
123			Pattern::Simple(s) => s.try_as_i64(int),
124			Pattern::Pi(n) => {
125				if n == 0.into() {
126					Ok(0)
127				} else {
128					Err(FendError::CannotConvertToInteger)
129				}
130			}
131		}
132	}
133
134	pub(crate) fn try_as_usize<I: Interrupt>(self, int: &I) -> FResult<usize> {
135		match self.pattern {
136			Pattern::Simple(s) => s.try_as_usize(int),
137			Pattern::Pi(n) => {
138				if n == 0.into() {
139					Ok(0)
140				} else {
141					Err(FendError::CannotConvertToInteger)
142				}
143			}
144		}
145	}
146
147	// sin works for all real numbers
148	pub(crate) fn sin<I: Interrupt>(self, int: &I) -> FResult<Exact<Self>> {
149		Ok(match self.pattern {
150			Pattern::Simple(s) => s.sin(int)?.apply(Self::from),
151			Pattern::Pi(n) => {
152				if n < 0.into() {
153					let s = Self {
154						pattern: Pattern::Pi(n),
155					};
156					// sin(-x) == -sin(x)
157					return Ok(-Self::sin(-s, int)?);
158				}
159				if let Ok(n_times_6) = n.clone().mul(&6.into(), int)?.try_as_usize(int) {
160					// values from https://en.wikipedia.org/wiki/Exact_trigonometric_values
161					// sin(n pi) == sin( (6n/6) pi +/- 2 pi )
162					//           == sin( (6n +/- 12)/6 pi ) == sin( (6n%12)/6 pi )
163					match n_times_6 % 12 {
164						// sin(0) == sin(pi) == 0
165						0 | 6 => return Ok(Exact::new(Self::from(0), true)),
166						// sin(pi/6) == sin(5pi/6) == 1/2
167						1 | 5 => {
168							return Exact::new(Self::from(1), true)
169								.div(&Exact::new(2.into(), true), int);
170						}
171						// sin(pi/2) == 1
172						3 => return Ok(Exact::new(Self::from(1), true)),
173						// sin(7pi/6) == sin(11pi/6) == -1/2
174						7 | 11 => {
175							return Exact::new(-Self::from(1), true)
176								.div(&Exact::new(2.into(), true), int);
177						}
178						// sin(3pi/2) == -1
179						9 => return Ok(Exact::new(-Self::from(1), true)),
180						_ => {}
181					}
182				}
183				let s = Self {
184					pattern: Pattern::Pi(n),
185				};
186				s.approximate(int)?.sin(int)?.apply(Self::from)
187			}
188		})
189	}
190
191	// cos works for all real numbers
192	pub(crate) fn cos<I: Interrupt>(self, int: &I) -> FResult<Exact<Self>> {
193		Ok(match self.pattern {
194			Pattern::Simple(c) => c.cos(int)?.apply(Self::from),
195			Pattern::Pi(n) => {
196				if n < 0.into() {
197					let c = Self {
198						pattern: Pattern::Pi(n),
199					};
200					// cos(-x) == cos(x)
201					return Self::cos(-c, int);
202				}
203				if let Ok(n_times_6) = n.clone().mul(&6.into(), int)?.try_as_usize(int) {
204					// values from https://en.wikipedia.org/wiki/Exact_trigonometric_values
205					// cos(n pi) == cos( (6n/6) pi +/- 2 pi )
206					//           == cos( (6n +/- 12)/6 pi ) == cos( (6n%12)/6 pi )
207					match n_times_6 % 12 {
208						// cos(0) == 1
209						0 => return Ok(Exact::new(Self::from(1), true)),
210						// cos(pi/3) == cos(5pi/3) == 1/2
211						2 | 10 => {
212							return Exact::new(Self::from(1), true)
213								.div(&Exact::new(2.into(), true), int);
214						}
215						// cos(pi/2) == cos(3pi/2) == 0
216						3 | 9 => return Ok(Exact::new(Self::from(0), true)),
217						// cos(2pi/3) == cos(4pi/3) == -1/2
218						4 | 8 => {
219							return Exact::new(-Self::from(1), true)
220								.div(&Exact::new(2.into(), true), int);
221						}
222						// cos(pi) == -1
223						6 => return Ok(Exact::new(-Self::from(1), true)),
224						_ => {}
225					}
226				}
227				let c = Self {
228					pattern: Pattern::Pi(n),
229				};
230				c.approximate(int)?.cos(int)?.apply(Self::from)
231			}
232		})
233	}
234
235	pub(crate) fn asin<I: Interrupt>(self, int: &I) -> FResult<Self> {
236		Ok(Self::from(self.approximate(int)?.asin(int)?))
237	}
238
239	pub(crate) fn acos<I: Interrupt>(self, int: &I) -> FResult<Self> {
240		Ok(Self::from(self.approximate(int)?.acos(int)?))
241	}
242
243	pub(crate) fn atan<I: Interrupt>(self, int: &I) -> FResult<Self> {
244		Ok(Self::from(self.approximate(int)?.atan(int)?))
245	}
246
247	pub(crate) fn atan2<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
248		Ok(Self::from(
249			self.approximate(int)?.atan2(rhs.approximate(int)?, int)?,
250		))
251	}
252
253	pub(crate) fn sinh<I: Interrupt>(self, int: &I) -> FResult<Self> {
254		Ok(Self::from(self.approximate(int)?.sinh(int)?))
255	}
256
257	pub(crate) fn cosh<I: Interrupt>(self, int: &I) -> FResult<Self> {
258		Ok(Self::from(self.approximate(int)?.cosh(int)?))
259	}
260
261	pub(crate) fn tanh<I: Interrupt>(self, int: &I) -> FResult<Self> {
262		Ok(Self::from(self.approximate(int)?.tanh(int)?))
263	}
264
265	pub(crate) fn asinh<I: Interrupt>(self, int: &I) -> FResult<Self> {
266		Ok(Self::from(self.approximate(int)?.asinh(int)?))
267	}
268
269	pub(crate) fn acosh<I: Interrupt>(self, int: &I) -> FResult<Self> {
270		Ok(Self::from(self.approximate(int)?.acosh(int)?))
271	}
272
273	pub(crate) fn atanh<I: Interrupt>(self, int: &I) -> FResult<Self> {
274		Ok(Self::from(self.approximate(int)?.atanh(int)?))
275	}
276
277	// For all logs: value must be greater than 0
278	pub(crate) fn ln<I: Interrupt>(self, int: &I) -> FResult<Exact<Self>> {
279		Ok(self.approximate(int)?.ln(int)?.apply(Self::from))
280	}
281
282	pub(crate) fn log2<I: Interrupt>(self, int: &I) -> FResult<Self> {
283		Ok(Self::from(self.approximate(int)?.log2(int)?))
284	}
285
286	pub(crate) fn log10<I: Interrupt>(self, int: &I) -> FResult<Self> {
287		Ok(Self::from(self.approximate(int)?.log10(int)?))
288	}
289
290	pub(crate) fn factorial<I: Interrupt>(self, int: &I) -> FResult<Self> {
291		Ok(Self::from(self.approximate(int)?.factorial(int)?))
292	}
293
294	pub(crate) fn floor<I: Interrupt>(self, int: &I) -> FResult<Self> {
295		Ok(Self::from(self.approximate(int)?.floor(int)?))
296	}
297
298	pub(crate) fn ceil<I: Interrupt>(self, int: &I) -> FResult<Self> {
299		Ok(Self::from(self.approximate(int)?.ceil(int)?))
300	}
301
302	pub(crate) fn round<I: Interrupt>(self, int: &I) -> FResult<Self> {
303		Ok(Self::from(self.approximate(int)?.round(int)?))
304	}
305
306	pub(crate) fn format<I: Interrupt>(
307		&self,
308		base: Base,
309		mut style: FormattingStyle,
310		imag: bool,
311		use_parens_if_fraction: bool,
312		decimal_separator: DecimalSeparatorStyle,
313		int: &I,
314	) -> FResult<Exact<Formatted>> {
315		let mut pi = false;
316		if style == FormattingStyle::Exact
317			&& !self.is_zero()
318			&& let Pattern::Pi(_) = self.pattern
319		{
320			pi = true;
321		}
322
323		let term = match (imag, pi) {
324			(false, false) => "",
325			(false, true) => "\u{3c0}", // pi symbol
326			(true, false) => "i",
327			(true, true) => "\u{3c0}i",
328		};
329
330		let mut override_exact = true;
331
332		let rat = match &self.pattern {
333			Pattern::Simple(f) => f,
334			Pattern::Pi(f) => {
335				if pi {
336					f
337				} else {
338					override_exact = false;
339					if style == FormattingStyle::Auto {
340						style = FormattingStyle::DecimalPlaces(10);
341					}
342					&self.clone().approximate(int)?
343				}
344			}
345		};
346
347		let formatted = rat.format(
348			&bigrat::FormatOptions {
349				base,
350				style,
351				term,
352				use_parens_if_fraction,
353				decimal_separator,
354			},
355			int,
356		)?;
357		let exact = formatted.exact && override_exact;
358		Ok(Exact::new(
359			Formatted {
360				num: formatted.value,
361			},
362			exact,
363		))
364	}
365
366	pub(crate) fn exp<I: Interrupt>(self, int: &I) -> FResult<Exact<Self>> {
367		Ok(self.approximate(int)?.exp(int)?.apply(Self::from))
368	}
369
370	pub(crate) fn pow<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Exact<Self>> {
371		// x^1 == x
372		if let Pattern::Simple(n) = &rhs.pattern
373			&& n == &1.into()
374		{
375			return Ok(Exact::new(self, true));
376		}
377
378		// 1^x == 1
379		if let Pattern::Simple(n) = &self.pattern
380			&& n == &1.into()
381		{
382			return Ok(Exact::new(1.into(), true));
383		}
384
385		if let (Pattern::Simple(a), Pattern::Simple(b)) =
386			(self.clone().pattern, rhs.clone().pattern)
387		{
388			Ok(a.pow(b, int)?.apply(Self::from))
389		} else {
390			Ok(self
391				.approximate(int)?
392				.pow(rhs.approximate(int)?, int)?
393				.combine(false)
394				.apply(Self::from))
395		}
396	}
397
398	pub(crate) fn root_n<I: Interrupt>(self, n: &Self, int: &I) -> FResult<Exact<Self>> {
399		// TODO: Combining these match blocks is not currently possible because
400		// 'binding by-move and by-ref in the same pattern is unstable'
401		// https://github.com/rust-lang/rust/pull/76119
402		Ok(match self.pattern {
403			Pattern::Simple(a) => match &n.pattern {
404				Pattern::Simple(b) => a.root_n(b, int)?.apply(Self::from),
405				Pattern::Pi(_) => {
406					let b = n.clone().approximate(int)?;
407					a.root_n(&b, int)?.apply(Self::from).combine(false)
408				}
409			},
410			Pattern::Pi(_) => {
411				let a = self.clone().approximate(int)?;
412				let b = n.clone().approximate(int)?;
413				a.root_n(&b, int)?.apply(Self::from).combine(false)
414			}
415		})
416	}
417
418	pub(crate) fn pi() -> Self {
419		Self {
420			pattern: Pattern::Pi(1.into()),
421		}
422	}
423
424	pub(crate) fn is_zero(&self) -> bool {
425		match &self.pattern {
426			Pattern::Simple(a) | Pattern::Pi(a) => a.is_definitely_zero() || a == &0.into(),
427		}
428	}
429
430	pub(crate) fn is_pos(&self) -> bool {
431		match &self.pattern {
432			Pattern::Simple(a) | Pattern::Pi(a) => !a.is_definitely_zero() && a > &0.into(),
433		}
434	}
435
436	pub(crate) fn is_neg(&self) -> bool {
437		match &self.pattern {
438			Pattern::Simple(a) | Pattern::Pi(a) => !a.is_definitely_zero() && a < &0.into(),
439		}
440	}
441
442	pub(crate) fn between_plus_minus_one_incl<I: Interrupt>(&self, int: &I) -> FResult<bool> {
443		// -1 <= x <= 1
444		Ok(Self::from(1).neg().compare(self, int)? != Ordering::Greater
445			&& self.compare(&1.into(), int)? != Ordering::Greater)
446	}
447
448	pub(crate) fn between_plus_minus_one_excl<I: Interrupt>(&self, int: &I) -> FResult<bool> {
449		// -1 < x < 1
450		Ok(Self::from(1).neg().compare(self, int)? == Ordering::Less
451			&& self.compare(&1.into(), int)? == Ordering::Less)
452	}
453
454	pub(crate) fn is_definitely_zero(&self) -> bool {
455		match &self.pattern {
456			Pattern::Simple(a) | Pattern::Pi(a) => a.is_definitely_zero(),
457		}
458	}
459
460	pub(crate) fn is_definitely_one(&self) -> bool {
461		match &self.pattern {
462			Pattern::Simple(a) => a.is_definitely_one(),
463			Pattern::Pi(_) => false,
464		}
465	}
466
467	pub(crate) fn expect_rational(self) -> FResult<BigRat> {
468		match self.pattern {
469			Pattern::Simple(a) => Ok(a),
470			Pattern::Pi(_) => Err(FendError::ExpectedARationalNumber),
471		}
472	}
473
474	pub(crate) fn modulo<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
475		Ok(Self::from(
476			self.expect_rational()?
477				.modulo(rhs.expect_rational()?, int)?,
478		))
479	}
480
481	pub(crate) fn bitwise<I: Interrupt>(
482		self,
483		rhs: Self,
484		op: crate::ast::BitwiseBop,
485		int: &I,
486	) -> FResult<Self> {
487		Ok(Self::from(self.expect_rational()?.bitwise(
488			rhs.expect_rational()?,
489			op,
490			int,
491		)?))
492	}
493
494	pub(crate) fn combination<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
495		Ok(Self::from(
496			self.expect_rational()?
497				.combination(rhs.expect_rational()?, int)?,
498		))
499	}
500
501	pub(crate) fn permutation<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
502		Ok(Self::from(
503			self.expect_rational()?
504				.permutation(rhs.expect_rational()?, int)?,
505		))
506	}
507}
508
509impl Exact<Real> {
510	pub(crate) fn add<I: Interrupt>(self, rhs: Self, int: &I) -> FResult<Self> {
511		if self.exact && self.value.is_zero() {
512			return Ok(rhs);
513		} else if rhs.exact && rhs.value.is_zero() {
514			return Ok(self);
515		}
516		let args_exact = self.exact && rhs.exact;
517		Ok(
518			match (self.clone().value.pattern, rhs.clone().value.pattern) {
519				(Pattern::Simple(a), Pattern::Simple(b)) => {
520					Self::new(a.add(b, int)?.into(), args_exact)
521				}
522				(Pattern::Pi(a), Pattern::Pi(b)) => Self::new(
523					Real {
524						pattern: Pattern::Pi(a.add(b, int)?),
525					},
526					args_exact,
527				),
528				_ => {
529					let a = self.value.approximate(int)?;
530					let b = rhs.value.approximate(int)?;
531					Self::new(a.add(b, int)?.into(), false)
532				}
533			},
534		)
535	}
536
537	pub(crate) fn mul<I: Interrupt>(self, rhs: Exact<&Real>, int: &I) -> FResult<Self> {
538		if self.exact && self.value.is_zero() {
539			return Ok(self);
540		} else if rhs.exact && rhs.value.is_zero() {
541			return Ok(Self::new(rhs.value.clone(), rhs.exact));
542		}
543		let args_exact = self.exact && rhs.exact;
544		Ok(match self.value.pattern {
545			Pattern::Simple(a) => match &rhs.value.pattern {
546				Pattern::Simple(b) => Self::new(a.mul(b, int)?.into(), args_exact),
547				Pattern::Pi(b) => Self::new(
548					Real {
549						pattern: Pattern::Pi(a.mul(b, int)?),
550					},
551					args_exact,
552				),
553			},
554			Pattern::Pi(a) => match &rhs.value.pattern {
555				Pattern::Simple(b) => Self::new(
556					Real {
557						pattern: Pattern::Pi(a.mul(b, int)?),
558					},
559					args_exact,
560				),
561				Pattern::Pi(_) => Self::new(
562					Real {
563						pattern: Pattern::Pi(a.mul(&rhs.value.clone().approximate(int)?, int)?),
564					},
565					false,
566				),
567			},
568		})
569	}
570
571	pub(crate) fn div<I: Interrupt>(self, rhs: &Self, int: &I) -> FResult<Self> {
572		if rhs.value.is_zero() {
573			return Err(FendError::DivideByZero);
574		}
575		if self.exact && self.value.is_zero() {
576			return Ok(self);
577		}
578		Ok(match self.value.pattern {
579			Pattern::Simple(a) => match &rhs.value.pattern {
580				Pattern::Simple(b) => Self::new(a.div(b, int)?.into(), self.exact && rhs.exact),
581				Pattern::Pi(_) => Self::new(
582					a.div(&rhs.value.clone().approximate(int)?, int)?.into(),
583					false,
584				),
585			},
586			Pattern::Pi(a) => match &rhs.value.pattern {
587				Pattern::Simple(b) => Self::new(
588					Real {
589						pattern: Pattern::Pi(a.div(b, int)?),
590					},
591					self.exact && rhs.exact,
592				),
593				Pattern::Pi(b) => Self::new(a.div(b, int)?.into(), self.exact && rhs.exact),
594			},
595		})
596	}
597}
598
599impl Neg for Real {
600	type Output = Self;
601
602	fn neg(self) -> Self {
603		match self.pattern {
604			Pattern::Simple(s) => Self::from(-s),
605			Pattern::Pi(n) => Self {
606				pattern: Pattern::Pi(-n),
607			},
608		}
609	}
610}
611
612impl From<u64> for Real {
613	fn from(i: u64) -> Self {
614		Self {
615			pattern: Pattern::Simple(i.into()),
616		}
617	}
618}
619
620impl From<BigRat> for Real {
621	fn from(n: BigRat) -> Self {
622		Self {
623			pattern: Pattern::Simple(n),
624		}
625	}
626}
627
628#[derive(Debug)]
629pub(crate) struct Formatted {
630	num: FormattedBigRat,
631}
632
633impl fmt::Display for Formatted {
634	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
635		write!(f, "{}", self.num)
636	}
637}
638
639#[cfg(test)]
640mod tests {
641	use super::*;
642
643	#[test]
644	fn test_pi() {
645		let int = &crate::interrupt::Never;
646		let pi = Real {
647			pattern: Pattern::Pi(BigRat::from(1u64)),
648		};
649		let pi = pi.approximate(int).unwrap();
650		let pi_f64 = pi.clone().into_f64(int).unwrap();
651
652		assert!((pi_f64 - std::f64::consts::PI).abs() < 3. * f64::EPSILON);
653
654		// = 31_415_926_535_897_932_384_626_433_832_795_028_841 / 1O^37
655		let ten_power_37_pi_floor = BigRat::from(BigUint::Large(vec![
656			12_315_011_256_901_221_737,
657			1_703_060_790_043_277_294,
658		]));
659		// = 31_415_926_535_897_932_384_626_433_832_795_028_842 / 1O^37
660		let ten_power_37_pi_ceil = BigRat::from(BigUint::Large(vec![
661			12_315_011_256_901_221_738,
662			1_703_060_790_043_277_294,
663		]));
664		// = 10^37
665		let ten_power_37 = BigRat::from(BigUint::Large(vec![
666			68_739_955_140_067_328,
667			542_101_086_242_752_217,
668		]));
669		let ten_power_minus_37 = BigRat::from(1).div(&ten_power_37, int).unwrap();
670
671		let pi_floor = ten_power_37_pi_floor.mul(&ten_power_minus_37, int).unwrap();
672		let pi_ceil = ten_power_37_pi_ceil.mul(&ten_power_minus_37, int).unwrap();
673
674		// pi_ceil - pi_floor = 10^37
675		assert_eq!(
676			pi_ceil.clone().add(pi_floor.clone().neg(), int).unwrap(),
677			ten_power_minus_37
678		);
679
680		let pi_minus_pi_floor = pi.clone().add(pi_floor.clone().neg(), int).unwrap();
681		let pi_ceil_minus_pi = pi_ceil.clone().add(pi.clone().neg(), int).unwrap();
682
683		// pi_floor < pi < pi_ceil
684		assert!(pi_floor < pi && pi < pi_ceil);
685		// 0 < pi - pi_floor < 10^-37
686		assert!(BigRat::from(0) < pi_minus_pi_floor && pi_minus_pi_floor < ten_power_minus_37);
687		// 0 < pi_ceil - pi < 10^-37
688		assert!(BigRat::from(0) < pi_ceil_minus_pi && pi_ceil_minus_pi < ten_power_minus_37);
689	}
690
691	#[test]
692	fn test_sin_exact_values() {
693		let int = &crate::interrupt::Never;
694		let angles = [
695			(2, 1, -1),  // -2π
696			(11, 6, -1), // -11π/6
697			(3, 2, -1),  // -3π/2
698			(7, 6, -1),  // -7π/6
699			(1, 1, -1),  // -π
700			(5, 6, -1),  // -5π/6
701			(1, 2, -1),  // -π/2
702			(1, 6, -1),  // -π/6
703			(0, 1, 1),   // 0
704			(1, 6, 1),   // π/6
705			(1, 2, 1),   // π/2
706			(5, 6, 1),   // 5π/6
707			(1, 1, 1),   // π
708			(7, 6, 1),   // 7π/6
709			(3, 2, 1),   // 3π/2
710			(11, 6, 1),  // 11π/6
711			(2, 1, 1),   // 2π
712		]
713		.map(|(num, den, sign)| {
714			(
715				Real {
716					pattern: Pattern::Pi(BigRat::from(num).div(&BigRat::from(den), int).unwrap()),
717				},
718				sign,
719			)
720		})
721		.map(|(br, sign)| if sign > 0 { br } else { -br });
722		let expected_sinuses = [
723			(0, 1, 1),  // 0
724			(1, 2, 1),  // 1/2
725			(1, 1, 1),  // 1
726			(1, 2, 1),  // 1/2
727			(0, 1, 1),  // 0
728			(1, 2, -1), // -1/2
729			(1, 1, -1), // -1
730			(1, 2, -1), // -1/2
731			(0, 1, 1),  // 0
732			(1, 2, 1),  // 1/2
733			(1, 1, 1),  // 1
734			(1, 2, 1),  // 1/2
735			(0, 1, 1),  // 0
736			(1, 2, -1), // -1/2
737			(1, 1, -1), // -1
738			(1, 2, -1), // -1/2
739			(0, 1, 1),  // 0
740		]
741		.map(|(num, den, sign)| {
742			(
743				BigRat::from(num).div(&BigRat::from(den), int).unwrap(),
744				sign,
745			)
746		})
747		.map(|(br, sign)| if sign > 0 { br } else { -br });
748
749		let actual_sinuses = angles.map(|r| r.sin(int).unwrap().value);
750
751		for (actual, expected) in actual_sinuses.into_iter().zip(expected_sinuses) {
752			assert_eq!(actual.approximate(int).unwrap(), expected);
753		}
754	}
755
756	#[test]
757	fn test_cos_exact_values() {
758		let int = &crate::interrupt::Never;
759		let angles = [
760			(2, 1, -1), // -2π
761			(5, 3, -1), // -5π/3
762			(3, 2, -1), // -3π/2
763			(4, 3, -1), // -4π/3
764			(1, 1, -1), // -π
765			(2, 3, -1), // -2π/3
766			(1, 2, -1), // -π/2
767			(1, 3, -1), // -π/3
768			(0, 1, 1),  // 0
769			(1, 3, 1),  // π/3
770			(1, 2, 1),  // π/2
771			(2, 3, 1),  // 2π/3
772			(1, 1, 1),  // π
773			(4, 3, 1),  // 4π/3
774			(3, 2, 1),  // 3π/2
775			(5, 3, 1),  // 5π/3
776			(2, 1, 1),  // 2π
777		]
778		.map(|(num, den, sign)| {
779			(
780				Real {
781					pattern: Pattern::Pi(BigRat::from(num).div(&BigRat::from(den), int).unwrap()),
782				},
783				sign,
784			)
785		})
786		.map(|(br, sign)| if sign > 0 { br } else { -br });
787		let expected_cosines = [
788			(1, 1, 1),  // 1
789			(1, 2, 1),  // 1/2
790			(0, 1, 1),  // 0
791			(1, 2, -1), // -1/2
792			(1, 1, -1), // -1
793			(1, 2, -1), // -1/2
794			(0, 1, 1),  // 0
795			(1, 2, 1),  // 1/2
796			(1, 1, 1),  // 1
797			(1, 2, 1),  // 1/2
798			(0, 1, 1),  // 0
799			(1, 2, -1), // -1/2
800			(1, 1, -1), // -1
801			(1, 2, -1), // -1/2
802			(0, 1, 1),  // 0
803			(1, 2, 1),  // 1/2
804			(1, 1, 1),  // 1
805		]
806		.map(|(num, den, sign)| {
807			(
808				BigRat::from(num).div(&BigRat::from(den), int).unwrap(),
809				sign,
810			)
811		})
812		.map(|(br, sign)| if sign > 0 { br } else { -br });
813
814		let actual_cosines = angles.map(|r| r.cos(int).unwrap().value);
815
816		for (actual, expected) in actual_cosines.into_iter().zip(expected_cosines) {
817			assert_eq!(actual.approximate(int).unwrap(), expected);
818		}
819	}
820}