1use super::block::Block;
4use super::infinite::InfiniteContinuedFraction;
5use crate::quadratic::{QuadraticBase, QuadraticSurd};
6use crate::traits::{Approximation, Computable, WithSigned, WithUnsigned};
7use core::convert::TryFrom;
8use core::str::FromStr;
9use num_integer::Integer;
10use num_rational::Ratio;
11use num_traits::{CheckedAdd, CheckedMul, Num, NumRef, One, RefNum, Signed, Zero};
12use std::fmt;
13use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
14
15#[derive(Clone, Debug, PartialEq)]
19pub struct ContinuedFraction<T> {
20 a_coeffs: Vec<T>,
22
23 p_coeffs: Vec<T>,
25
26 negative: bool,
28}
29
30impl<T> ContinuedFraction<T> {
31 #[inline]
32 pub fn aperiodic_coeffs(&self) -> &[T] {
33 &self.a_coeffs[..]
34 }
35
36 #[inline]
37 pub fn periodic_coeffs(&self) -> &[T] {
38 &self.p_coeffs[..]
39 }
40
41 #[inline]
42 pub fn is_negative(&self) -> bool {
43 self.negative
44 }
45
46 #[inline]
47 pub fn is_rational(&self) -> bool {
48 self.p_coeffs.len() == 0
49 }
50
51 #[inline]
52 pub fn is_integer(&self) -> bool {
53 self.a_coeffs.len() == 1 && self.p_coeffs.len() == 0
54 }
55}
56
57impl<U> ContinuedFraction<U> {
58 pub fn new<T: Num + PartialOrd + AddAssign + WithUnsigned<Unsigned = U>>(
63 a_coeffs: Vec<T>,
64 p_coeffs: Vec<T>,
65 negate: bool,
66 ) -> Self {
67 if a_coeffs.len() == 0 && p_coeffs.len() == 0 {
68 panic!("at least one coefficient is required!");
69 }
70
71 let mut negate = negate; let mut negative = None; let mut dedup_a: Vec<T> = Vec::with_capacity(a_coeffs.len());
74 let mut it = a_coeffs.into_iter();
75
76 loop {
78 match it.next() {
79 Some(a) => {
80 let a = if negate { T::zero() - a } else { a };
82
83 if dedup_a.len() == 0 {
85 if a < T::zero() {
88 dedup_a.push(T::zero() - a);
89 negative = Some(true);
90 negate = !negate;
91 } else {
92 if !a.is_zero() {
93 negative = Some(false);
94 }
95 dedup_a.push(a);
96 }
97 continue;
98 } else if a.is_zero() {
99 if dedup_a.last().unwrap().is_zero() {
101 dedup_a.pop(); } else {
103 dedup_a.push(a); }
105 continue;
106 }
107
108 if a < T::zero() {
110 let mut target = a;
113 loop {
114 let am1 = dedup_a.pop().unwrap(); let has_am2 = dedup_a.len() != 0; debug_assert!(am1 >= T::zero());
117
118 if am1.is_one() {
119 if has_am2 {
120 *dedup_a.last_mut().unwrap() += T::one();
123 dedup_a.push(T::zero() - target - T::one());
124 } else {
125 dedup_a.push(T::zero());
127 dedup_a.push(T::one());
128 dedup_a.push(T::zero() - target - T::one());
129 negative = Some(false);
130 }
131 negate = !negate;
132 break;
133 } else if am1.is_zero() {
134 if has_am2 {
135 let am2 = dedup_a.pop().unwrap();
139 let new_target = am2 + target;
140 if new_target < T::zero() {
141 target = new_target;
143 } else {
144 dedup_a.push(new_target);
145 break;
146 }
147 } else {
148 dedup_a.push(T::zero());
150 dedup_a.push(T::zero() - target);
151 negative = Some(true);
152 negate = !negate;
153 break;
154 }
155 } else {
156 debug_assert!(am1 > T::one());
158 dedup_a.push(am1 - T::one());
159 dedup_a.push(T::one());
160 dedup_a.push(T::zero() - target - T::one());
161 negate = !negate;
162 break;
163 }
164 }
165 } else {
166 if dedup_a.last().unwrap().is_zero() && dedup_a.len() > 1 {
167 dedup_a.pop();
169 *dedup_a.last_mut().unwrap() += a;
170 } else {
171 debug_assert!(!a.is_zero());
172 if matches!(negative, None) {
173 negative = Some(a < T::zero());
174 }
175 dedup_a.push(a);
176 }
177 }
178 }
179 None => {
180 break;
181 }
182 }
183 }
184
185 if dedup_a.len() == 0 && p_coeffs.len() == 0 {
186 panic!("no effective coefficient!")
187 }
188
189 if p_coeffs.len() == 0 {
191 while dedup_a.len() >= 2 {
192 let last_a = dedup_a.last().unwrap();
193 if last_a.is_zero() {
194 dedup_a.pop();
195 dedup_a.pop();
196 } else if last_a.is_one() {
197 let one = dedup_a.pop().unwrap();
198 *dedup_a.last_mut().unwrap() += one;
199 } else {
200 break;
201 }
202 }
203
204 if dedup_a.len() == 1 && dedup_a[0].is_zero() {
205 negative = Some(false);
206 }
207 }
208
209 if matches!(negative, None) {
210 if matches!(p_coeffs.first(), Some(p) if p <= &T::zero()) {
211 unimplemented!()
213 } else {
214 negative = Some(negate);
215 }
216 }
217
218 let a_coeffs: Vec<U> = dedup_a.into_iter().map(|v| v.to_unsigned()).collect();
220 let p_coeffs: Vec<U> = p_coeffs.into_iter().map(|v| v.to_unsigned()).collect();
221 ContinuedFraction {
222 a_coeffs,
223 p_coeffs,
224 negative: negative.unwrap(),
225 }
226 }
227}
228
229#[derive(Debug, Clone)]
231pub struct Coefficients<'a, T> {
232 a_iter: Option<std::slice::Iter<'a, T>>, p_ref: &'a Vec<T>,
234 p_iter: Option<std::slice::Iter<'a, T>>, }
236
237impl<'a, T> Iterator for Coefficients<'a, T> {
238 type Item = &'a T;
239
240 fn next(&mut self) -> Option<Self::Item> {
241 if let Some(it) = self.a_iter.as_mut() {
242 match it.next() {
244 Some(v) => Some(v),
245 None => {
246 self.a_iter = None;
247 if self.p_ref.len() > 0 {
248 let mut new_iter = self.p_ref.iter();
249 let result = new_iter.next();
250 self.p_iter = Some(new_iter);
251 result
252 } else {
253 None
254 }
255 }
256 }
257 } else {
258 if let Some(it) = self.p_iter.as_mut() {
259 match it.next() {
261 Some(v) => Some(v),
262 None => {
263 let mut new_iter = self.p_ref.iter();
264 let result = new_iter.next();
265 self.p_iter = Some(new_iter);
266 result
267 }
268 }
269 } else {
270 None
271 }
272 }
273 }
274}
275
276pub struct GeneralCoefficients<T> {
280 coeffs: T,
281 negative: bool, }
283
284impl<'r, I: Iterator<Item = &'r T>, T: 'r + WithSigned<Signed = U> + Clone, U: Signed> Iterator
285 for GeneralCoefficients<I>
286{
287 type Item = (U, U);
288
289 fn next(&mut self) -> Option<Self::Item> {
290 if self.negative {
291 self.coeffs
292 .next()
293 .map(|v| (U::one(), -v.clone().to_signed()))
294 } else {
295 self.coeffs
296 .next()
297 .map(|v| (U::one(), v.clone().to_signed()))
298 }
299 }
300}
301
302pub struct Convergents<'a, T> {
304 coeffs: Coefficients<'a, T>,
305 block: Block<T>,
306 neg: bool, }
308
309impl<
310 'a,
311 T: Integer + Clone + CheckedAdd + CheckedMul + WithSigned<Signed = U>,
312 U: Integer + Clone + Signed,
313 > Iterator for Convergents<'a, T>
314{
315 type Item = Ratio<U>;
316
317 fn next(&mut self) -> Option<Self::Item> {
318 let a = self.coeffs.next()?;
319 let (p, q) = self.block.checked_rmove(a.clone())?;
320 self.block.update(p.clone(), q.clone());
321
322 let r = Ratio::new(p.to_signed(), q.to_signed());
323 if self.neg {
324 Some(-r)
325 } else {
326 Some(r)
327 }
328 }
329}
330
331pub struct SignedCoefficients<T> {
334 coeffs: T,
335 negative: bool, }
337
338impl<'r, I: Iterator<Item = &'r T>, T: 'r + WithSigned<Signed = U> + Clone, U: Signed> Iterator
339 for SignedCoefficients<I>
340{
341 type Item = U;
342
343 fn next(&mut self) -> Option<Self::Item> {
344 if self.negative {
345 self.coeffs.next().map(|v| -v.clone().to_signed())
346 } else {
347 self.coeffs.next().map(|v| v.clone().to_signed())
348 }
349 }
350}
351
352impl<T> ContinuedFraction<T> {
353 pub fn coeffs(&self) -> Coefficients<T> {
356 Coefficients {
357 a_iter: Some(self.a_coeffs.iter()),
358 p_ref: &self.p_coeffs,
359 p_iter: None,
360 }
361 }
362
363 pub fn generalized(&self) -> GeneralCoefficients<Coefficients<T>> {
366 GeneralCoefficients {
367 coeffs: self.coeffs(),
368 negative: self.negative,
369 }
370 }
371
372 pub fn coeffs_signed(&self) -> SignedCoefficients<Coefficients<T>> {
375 SignedCoefficients {
376 coeffs: self.coeffs(),
377 negative: self.negative,
378 }
379 }
380}
381
382impl<T: WithSigned<Signed = U> + Clone, U: Signed> ContinuedFraction<T> {
383 pub fn expanded(&self) -> InfiniteContinuedFraction<SignedCoefficients<Coefficients<T>>> {
385 self.coeffs_signed().into()
386 }
387}
388
389impl<
390 T: Integer + Clone + CheckedAdd + CheckedMul + WithSigned<Signed = U>,
391 U: Integer + Clone + Signed,
392 > ContinuedFraction<T>
393{
394 pub fn convergents(&self) -> Convergents<T> {
397 Convergents {
398 coeffs: self.coeffs(),
399 block: Block::identity(),
400 neg: self.negative,
401 }
402 }
403
404 #[inline]
406 pub fn to_integer(&self) -> Approximation<U> {
407 let a = self.a_coeffs.first().unwrap().clone().to_signed();
408 let a = if self.negative { -a } else { a };
409 if self.is_integer() {
410 Approximation::Exact(a)
411 } else {
412 Approximation::Approximated(a)
413 }
414 }
415
416 #[inline]
417 pub fn to_rational(&self) -> Approximation<Ratio<U>> {
420 if self.is_rational() {
421 Approximation::Exact(self.convergents().last().unwrap())
422 } else {
423 Approximation::Approximated(
424 self.convergents()
425 .nth(self.a_coeffs.len() + self.p_coeffs.len())
426 .unwrap(),
427 )
428 }
429 }
430}
431
432impl<
433 T: Integer + Clone + CheckedAdd + CheckedMul + WithSigned<Signed = U>,
434 U: Integer + Clone + Signed + CheckedAdd,
435 > Computable<U> for ContinuedFraction<T>
436{
437 fn approximated(&self, limit: &U) -> Approximation<Ratio<U>> {
438 let mut convergents = self.convergents();
439 let mut last_conv = convergents.next().unwrap();
440 if last_conv.denom() > limit {
441 let i = self.a_coeffs.first().unwrap().clone();
442 return Approximation::Approximated(Ratio::from(i.to_signed()));
443 }
444 loop {
445 last_conv = match convergents.next() {
446 Some(v) => {
447 if v.denom() < limit {
448 v
449 } else {
450 return Approximation::Approximated(last_conv);
451 }
452 }
453 None => return Approximation::Exact(last_conv),
454 }
455 }
456 }
457}
458
459impl<T: fmt::Display> fmt::Display for ContinuedFraction<T> {
460 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
461 if self.negative {
462 write!(f, "-")?;
463 }
464
465 write!(f, "[{}", self.a_coeffs.first().unwrap())?;
466 if self.a_coeffs.len() == 1 {
467 if self.p_coeffs.len() == 0 {
468 return write!(f, "]");
469 } else {
470 write!(f, "; ")?;
471 }
472 } else {
473 let mut aiter = self.a_coeffs.iter().skip(1);
474 write!(f, "; {}", aiter.next().unwrap())?;
475 while let Some(v) = aiter.next() {
476 write!(f, ", {}", v)?;
477 }
478 if self.p_coeffs.len() > 0 {
479 write!(f, ", ")?;
480 }
481 }
482
483 if self.p_coeffs.len() > 0 {
484 let mut piter = self.p_coeffs.iter();
485 write!(f, "({}", piter.next().unwrap())?;
486 while let Some(v) = piter.next() {
487 write!(f, ", {}", v)?;
488 }
489 write!(f, ")]")
490 } else {
491 write!(f, "]")
492 }
493 }
494}
495
496impl<T: Zero> ContinuedFraction<T> {
498 #[inline]
499 fn _zero() -> Self {
500 ContinuedFraction {
501 a_coeffs: vec![T::zero()],
502 p_coeffs: Vec::new(),
503 negative: false,
504 }
505 }
506
507 #[inline]
508 fn _is_zero(&self) -> bool {
509 self.a_coeffs.len() == 1 && self.a_coeffs[0].is_zero() && self.p_coeffs.len() == 0
510 }
511}
512
513impl<
514 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
515 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
516 > Zero for ContinuedFraction<U>
517where
518 for<'r> &'r T: RefNum<T>,
519 for<'r> &'r U: RefNum<U>,
520{
521 #[inline]
522 fn zero() -> Self {
523 Self::_zero()
524 }
525
526 #[inline]
527 fn is_zero(&self) -> bool {
528 self._is_zero()
529 }
530}
531
532impl<
533 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
534 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
535 > One for ContinuedFraction<U>
536where
537 for<'r> &'r T: RefNum<T>,
538 for<'r> &'r U: RefNum<U>,
539{
540 fn one() -> Self {
541 ContinuedFraction {
542 a_coeffs: vec![U::one()],
543 p_coeffs: Vec::new(),
544 negative: false,
545 }
546 }
547
548 fn is_one(&self) -> bool {
549 self.a_coeffs.len() == 1 && self.a_coeffs[0].is_one() && self.p_coeffs.len() == 0
550 }
551}
552
553impl<T: Num + WithUnsigned<Unsigned = U> + PartialOrd, U> From<T> for ContinuedFraction<U> {
556 fn from(t: T) -> Self {
557 if t < T::zero() {
558 ContinuedFraction {
559 a_coeffs: vec![(T::zero() - t).to_unsigned()],
560 p_coeffs: Vec::new(),
561 negative: true,
562 }
563 } else {
564 ContinuedFraction {
565 a_coeffs: vec![t.to_unsigned()],
566 p_coeffs: Vec::new(),
567 negative: false,
568 }
569 }
570 }
571}
572
573impl<T: Integer + Clone + WithUnsigned<Unsigned = U>, U: Zero> From<Ratio<T>>
574 for ContinuedFraction<U>
575{
576 fn from(r: Ratio<T>) -> Self {
577 if r.is_zero() {
578 return Self::_zero();
579 }
580
581 let mut coeffs = Vec::new();
582 let (mut n, mut d) = r.into();
583
584 let negative = if n < T::zero() {
585 n = T::zero() - n;
586 true
587 } else {
588 false
589 };
590
591 if n < d {
592 std::mem::swap(&mut n, &mut d);
593 coeffs.push(U::zero());
594 }
595
596 while !d.is_zero() {
597 let (quo, rem) = n.div_rem(&d);
598 coeffs.push(quo.to_unsigned());
599 n = d;
600 d = rem;
601 }
602
603 ContinuedFraction {
604 a_coeffs: coeffs,
605 p_coeffs: Vec::new(),
606 negative,
607 }
608 }
609}
610
611mod parse {
613 use super::{ContinuedFraction, FromStr};
614
615 pub enum ParseContFracError {}
616
617 impl<T> FromStr for ContinuedFraction<T> {
618 type Err = ParseContFracError;
619
620 fn from_str(s: &str) -> Result<Self, Self::Err> {
622 unimplemented!()
623 }
624 }
625}
626
627impl<
628 T: Num + PartialOrd + AddAssign + Signed + WithUnsigned<Unsigned = U>,
629 U: NumRef + PartialOrd + WithSigned<Signed = T>,
630 > Add<T> for ContinuedFraction<U>
631where
632 for<'r> &'r U: RefNum<U>,
633{
634 type Output = Self;
635
636 fn add(self, rhs: T) -> Self {
637 let mut new_a = self.a_coeffs;
638 let i = new_a.first().unwrap();
639
640 let (new_i, flipped) = match (self.negative, rhs.is_negative()) {
642 (true, true) => {
643 let rhs = (-rhs).to_unsigned();
645 (rhs + i, false)
646 }
647 (true, false) => {
648 let rhs = rhs.to_unsigned();
650 if i < &rhs {
651 (rhs - i, true)
652 } else {
653 (i - rhs, false)
654 }
655 }
656 (false, true) => {
657 let rhs = (-rhs).to_unsigned();
659 if i < &rhs {
660 (rhs - i, true)
661 } else {
662 (i - rhs, false)
663 }
664 }
665 (false, false) => {
666 (i + rhs.to_unsigned(), false)
668 }
669 };
670
671 if flipped {
672 let mut new_a: Vec<T> = new_a.into_iter().map(|u| u.to_signed()).collect();
674 let new_p: Vec<T> = self.p_coeffs.into_iter().map(|u| u.to_signed()).collect();
675 *new_a.first_mut().unwrap() = -new_i.to_signed();
676 Self::new(new_a, new_p, self.negative)
677 } else {
678 *new_a.first_mut().unwrap() = new_i;
679
680 let mut result = ContinuedFraction {
681 a_coeffs: new_a,
682 p_coeffs: self.p_coeffs,
683 negative: self.negative,
684 };
685 if result._is_zero() {
686 result.negative = false;
688 }
689 result
690 }
691 }
692}
693
694impl<
695 T: Num + PartialOrd + AddAssign + Signed + WithUnsigned<Unsigned = U>,
696 U: NumRef + PartialOrd + WithSigned<Signed = T>,
697 > Sub<T> for ContinuedFraction<U>
698where
699 for<'r> &'r U: RefNum<U>,
700{
701 type Output = Self;
702
703 fn sub(self, rhs: T) -> Self::Output {
704 self.add(-rhs)
705 }
706}
707
708impl<T: Zero> Neg for ContinuedFraction<T> {
709 type Output = ContinuedFraction<T>;
710
711 fn neg(self) -> Self::Output {
712 let mut result = self;
713 if !result._is_zero() {
714 result.negative = !result.negative;
716 }
717 result
718 }
719}
720
721impl<
722 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
723 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
724 > Mul<T> for ContinuedFraction<U>
725where
726 for<'r> &'r T: RefNum<T>,
727 for<'r> &'r U: RefNum<U>,
728{
729 type Output = Self;
730
731 fn mul(self, rhs: T) -> Self {
732 if self.is_rational() {
733 Self::from(self.to_rational().value() * rhs)
734 } else {
735 Self::try_from(QuadraticSurd::<T>::from(self) * rhs).unwrap()
736 }
737 }
738}
739
740impl<
741 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
742 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
743 > Div<T> for ContinuedFraction<U>
744where
745 for<'r> &'r T: RefNum<T>,
746 for<'r> &'r U: RefNum<U>,
747{
748 type Output = Self;
749
750 fn div(self, rhs: T) -> Self {
751 if self.is_rational() {
752 Self::from(self.to_rational().value() / rhs)
753 } else {
754 Self::try_from(QuadraticSurd::<T>::from(self) / rhs).unwrap()
755 }
756 }
757}
758
759macro_rules! impl_binop_for_ratio_surd {
760 (impl $imp:ident, $method:ident) => {
761 impl<
762 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
763 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
764 > $imp<Ratio<T>> for ContinuedFraction<U>
765 where
766 for<'r> &'r T: RefNum<T>,
767 for<'r> &'r U: RefNum<U>,
768 {
769 type Output = Self;
770
771 fn $method(self, rhs: Ratio<T>) -> Self {
772 if self.is_rational() {
773 Self::from(self.to_rational().value().$method(rhs))
774 } else {
775 Self::try_from(QuadraticSurd::<T>::from(self).$method(rhs)).unwrap()
776 }
777 }
778 }
779
780 impl<
781 T: QuadraticBase + AddAssign + WithUnsigned<Unsigned = U>,
782 U: Integer + Clone + NumRef + CheckedAdd + CheckedMul + WithSigned<Signed = T>,
783 > $imp<ContinuedFraction<U>> for ContinuedFraction<U>
784 where
785 for<'r> &'r T: RefNum<T>,
786 for<'r> &'r U: RefNum<U>,
787 {
788 type Output = Self;
789
790 fn $method(self, rhs: ContinuedFraction<U>) -> Self {
791 if rhs.is_rational() {
792 self.$method(rhs.to_rational().value())
793 } else {
794 Self::try_from(
795 QuadraticSurd::<T>::from(self).$method(QuadraticSurd::<T>::from(rhs)),
796 )
797 .unwrap()
798 }
799 }
800 }
801 };
802}
803
804impl_binop_for_ratio_surd!(impl Add, add);
805impl_binop_for_ratio_surd!(impl Sub, sub);
806impl_binop_for_ratio_surd!(impl Mul, mul);
807impl_binop_for_ratio_surd!(impl Div, div);
808
809trait ParseContinuedFraction {
817 }
821
822impl<I: Iterator<Item = u8>> ParseContinuedFraction for I {}
823
824#[cfg(test)]
825mod tests {
826 use super::*;
827
828 #[test]
829 fn creation_test() {
830 let cf = ContinuedFraction::new(vec![0, 1, 2], vec![3, 4], false);
831 assert_eq!(
832 cf,
833 ContinuedFraction::new(vec![0, 1, 0, 0, 2], vec![3, 4], false)
834 );
835 assert_eq!(
836 cf,
837 ContinuedFraction::new(vec![0, 1, 1, 0, 1], vec![3, 4], false)
838 );
839
840 let cf = ContinuedFraction::new(vec![2, 3, 4], vec![], true);
841 assert_eq!(
842 cf,
843 ContinuedFraction::new(vec![2, 1, 0, 2, 4], vec![], true)
844 );
845 assert_eq!(
846 cf,
847 ContinuedFraction::new(vec![3, -1, -2, -4], vec![], true)
848 );
849 assert_eq!(cf, ContinuedFraction::new(vec![-3, 1, 2, 4], vec![], false));
850 }
851
852 #[test]
853 fn iter_test() {
854 let one = ContinuedFraction::<u32>::new(vec![1], vec![], false);
855 assert_eq!(one.coeffs().cloned().collect::<Vec<_>>(), vec![1]);
856 assert_eq!(one.convergents().collect::<Vec<_>>(), vec![Ratio::from(1)]);
857
858 let n_one = ContinuedFraction::<u32>::new(vec![1], vec![], true);
859 assert_eq!(n_one.coeffs().cloned().collect::<Vec<_>>(), vec![1]);
860 assert_eq!(
861 n_one.convergents().collect::<Vec<_>>(),
862 vec![Ratio::from(-1)]
863 );
864
865 let sq2 = ContinuedFraction::<u32>::new(vec![1], vec![2], false);
866 assert_eq!(
867 sq2.coeffs().take(5).cloned().collect::<Vec<_>>(),
868 vec![1, 2, 2, 2, 2]
869 );
870 assert_eq!(
871 sq2.convergents().take(5).collect::<Vec<_>>(),
872 vec![
873 Ratio::from(1),
874 Ratio::new(3, 2),
875 Ratio::new(7, 5),
876 Ratio::new(17, 12),
877 Ratio::new(41, 29)
878 ]
879 );
880
881 let n_sq2 = ContinuedFraction::<u32>::new(vec![1], vec![2], true);
882 assert_eq!(
883 n_sq2.coeffs().take(5).cloned().collect::<Vec<_>>(),
884 vec![1, 2, 2, 2, 2]
885 );
886 assert_eq!(
887 n_sq2.convergents().take(5).collect::<Vec<_>>(),
888 vec![
889 Ratio::from(-1),
890 Ratio::new(-3, 2),
891 Ratio::new(-7, 5),
892 Ratio::new(-17, 12),
893 Ratio::new(-41, 29)
894 ]
895 );
896 }
897
898 #[test]
899 fn conversion_test() {
900 assert_eq!(
901 ContinuedFraction::from(Ratio::from(3)),
902 ContinuedFraction::new(vec![3u32], vec![], false)
903 );
904 assert_eq!(
905 ContinuedFraction::from(Ratio::new(22, 7)),
906 ContinuedFraction::new(vec![3u32, 7], vec![], false)
907 );
908 assert_eq!(
909 ContinuedFraction::from(Ratio::new(-22, 7)),
910 ContinuedFraction::new(vec![3i32, 7], vec![], true)
911 );
912 assert_eq!(
913 ContinuedFraction::from(Ratio::new(7, 22)),
914 ContinuedFraction::new(vec![0u32, 3, 7], vec![], false)
915 );
916 assert_eq!(
917 ContinuedFraction::from(Ratio::new(-7, 22)),
918 ContinuedFraction::new(vec![0i32, 3, 7], vec![], true)
919 );
920 assert_eq!(
921 ContinuedFraction::from(Ratio::new(355, 113)),
922 ContinuedFraction::new(vec![3u32, 7, 16], vec![], false)
923 );
924 }
925
926 #[test]
927 fn fmt_test() {
928 assert_eq!(
929 format!("{}", ContinuedFraction::new(vec![1], vec![], false)),
930 "[1]"
931 );
932 assert_eq!(
933 format!("{}", ContinuedFraction::new(vec![1, 2, 3], vec![], false)),
934 "[1; 2, 3]"
935 );
936 assert_eq!(
937 format!("{}", ContinuedFraction::new(vec![1], vec![2], false)),
938 "[1; (2)]"
939 );
940 assert_eq!(
941 format!(
942 "{}",
943 ContinuedFraction::new(vec![1, 2, 3], vec![3, 2], false)
944 ),
945 "[1; 2, 3, (3, 2)]"
946 );
947 }
948
949 #[test]
950 fn arithmetic_test() {
951 let one = ContinuedFraction::one();
954 let n_one = ContinuedFraction::<u32>::new(vec![1], vec![], true);
955 let sq2 = ContinuedFraction::new(vec![1], vec![2], false);
956 let n_sq2 = ContinuedFraction::new(vec![1], vec![2], true);
957
958 assert_eq!(one.clone() + 1, ContinuedFraction::from(2));
960 assert_eq!(one.clone() + (-1), ContinuedFraction::zero());
961 assert_eq!(n_one.clone() + 1, ContinuedFraction::zero());
962 assert_eq!(n_one.clone() + (-1), ContinuedFraction::from(-2));
963
964 assert_eq!(
965 sq2.clone() + 1,
966 ContinuedFraction::new(vec![2u32], vec![2], false)
967 );
968 assert_eq!(
969 sq2.clone() + (-1),
970 ContinuedFraction::new(vec![0u32], vec![2], false)
971 );
972 assert_eq!(
973 n_sq2.clone() + 1,
974 ContinuedFraction::new(vec![0i32], vec![2], true)
975 );
976 assert_eq!(
977 n_sq2.clone() + (-1),
978 ContinuedFraction::new(vec![2i32], vec![2], true)
979 );
980
981 assert_eq!(one.clone() * 2, ContinuedFraction::from(2));
983 assert_eq!(one.clone() * -2, ContinuedFraction::from(-2));
984 assert_eq!(n_one.clone() * 2, ContinuedFraction::from(-2));
985 assert_eq!(n_one.clone() * -2, ContinuedFraction::from(2));
986
987 assert_eq!(
988 sq2.clone() * 2,
989 ContinuedFraction::new(vec![2u32], vec![1, 4], false)
990 );
991 assert_eq!(
992 sq2.clone() * -2,
993 ContinuedFraction::new(vec![2i32], vec![1, 4], true)
994 );
995 assert_eq!(
996 n_sq2.clone() * 2,
997 ContinuedFraction::new(vec![2i32], vec![1, 4], true)
998 );
999 assert_eq!(
1000 n_sq2.clone() * -2,
1001 ContinuedFraction::new(vec![2u32], vec![1, 4], false)
1002 );
1003 }
1004}