Skip to main content

battler_data/common/
fraction.rs

1use alloc::format;
2use core::{
3    cmp,
4    fmt,
5    fmt::Display,
6    marker::PhantomData,
7    ops::{
8        Add,
9        Div,
10        Mul,
11        Sub,
12    },
13    str::FromStr,
14};
15
16use anyhow::Error;
17use num::{
18    FromPrimitive,
19    Integer,
20    PrimInt,
21    Signed,
22    Zero,
23    integer::Roots,
24    pow::Pow,
25    traits::{
26        SaturatingAdd,
27        SaturatingMul,
28        SaturatingSub,
29        WrappingAdd,
30        WrappingMul,
31        WrappingSub,
32    },
33};
34use serde::{
35    Deserialize,
36    Serialize,
37    Serializer,
38    de::{
39        Unexpected,
40        Visitor,
41    },
42};
43
44/// An integer type that can be used as the inner type of [`Fraction`].
45pub trait FractionInteger: Integer + FromPrimitive + Copy {}
46impl<I> FractionInteger for I where I: Integer + FromPrimitive + Copy {}
47
48/// A fraction, usable in calculations.
49///
50/// A fraction is serializable as:
51/// - A fraction string (`"1/2"`).
52/// - An integer (`20`), which represents an integer (denominator == 1).
53/// - A floating point number (`1.5`), which is converted to a fraction out of 4096.
54/// - A percentage string (`"60%"`).
55/// - A two-length array (`[2,5]`).
56#[derive(Debug, Clone, Copy)]
57pub struct Fraction<I> {
58    num: I,
59    den: I,
60}
61
62impl<I> Fraction<I>
63where
64    I: FractionInteger,
65{
66    /// Creates a new fraction.
67    pub fn new(n: I, d: I) -> Self {
68        Self { num: n, den: d }
69    }
70
71    /// Creates a new percentage as a fraction.
72    pub fn percentage(n: I) -> Self {
73        Fraction {
74            num: n,
75            den: I::from_u8(100).unwrap(),
76        }
77        .simplify()
78    }
79
80    /// Creates a new fraction from an [`f64`].
81    ///
82    /// Floating point precision is preserved by creating a fraction with a denominator of 4096.
83    pub fn from_f64(value: f64) -> Self {
84        let num = I::from_f64(value * 4096f64).unwrap();
85        Self::new(num, I::from_u16(4096).unwrap()).simplify()
86    }
87
88    /// The numerator of the fraction.
89    pub fn numerator(&self) -> I {
90        self.num
91    }
92
93    /// The denominator of the fraction.
94    ///
95    /// A flat percentage is always out of 100.
96    pub fn denominator(&self) -> I {
97        self.den
98    }
99
100    /// Is the fraction whole (i.e., an integer)?
101    pub fn is_whole(&self) -> bool {
102        self.den == I::one()
103    }
104
105    /// Simplifies the fraction.
106    pub fn simplify(&self) -> Self {
107        let n = self.numerator();
108        let d = self.denominator();
109        let gcd = n.gcd(&d);
110        Fraction::new(n.div(gcd), d.div(gcd))
111    }
112
113    /// Returns the floored integer representation of the fraction.
114    ///
115    /// The integer will be truncated, as if performing integer division.
116    pub fn floor(&self) -> I {
117        self.numerator().div(self.denominator())
118    }
119
120    /// Returns the ceiled integer representation of the fraction.
121    pub fn ceil(&self) -> I {
122        num::Integer::div_ceil(&self.numerator(), &self.denominator())
123    }
124
125    /// Returns the rounded integer representation of the fraction.
126    pub fn round(&self) -> I
127    where
128        I: PrimInt,
129    {
130        (self.numerator().add(self.denominator().shr(1))).div(self.denominator())
131    }
132
133    /// Converts the [`Fraction<I>`] to a [`Fraction<T>`], given that `T: From<I>`.
134    pub fn convert<T>(self) -> Fraction<T>
135    where
136        T: FractionInteger + From<I>,
137    {
138        Fraction::new(T::from(self.numerator()), T::from(self.denominator()))
139    }
140
141    /// Attempts converting the [`Fraction<I>`] to a [`Fraction<T>`], given that `T: TryFrom<I>`.
142    pub fn try_convert<T>(self) -> Result<Fraction<T>, T::Error>
143    where
144        T: FractionInteger + TryFrom<I>,
145    {
146        Ok(Fraction::new(
147            T::try_from(self.numerator())?,
148            T::try_from(self.denominator())?,
149        ))
150    }
151
152    /// Returns the inverse of this fraction.
153    pub fn inverse(&self) -> Self {
154        Self::new(self.denominator(), self.numerator())
155    }
156
157    fn normalize(a: &Fraction<I>, b: &Fraction<I>) -> (Fraction<I>, Fraction<I>) {
158        let a1 = a.numerator();
159        let a2 = a.denominator();
160        let b1 = b.numerator();
161        let b2 = b.denominator();
162        // Note: This calculation could overflow if the denominators are large enough.
163        let lcm = a2.lcm(&b2);
164        let a_mul = lcm.div(a2);
165        let b_mul = lcm.div(b2);
166        // Note: This calculation could overflow if the numerators are large enough.
167        (
168            Fraction::new(a1.mul(a_mul), lcm),
169            Fraction::new(b1.mul(b_mul), lcm),
170        )
171    }
172}
173
174impl<I> Fraction<I>
175where
176    I: FractionInteger + Roots,
177{
178    pub fn sqrt(&self) -> Self {
179        Self::new(self.numerator().sqrt(), self.denominator().sqrt()).simplify()
180    }
181
182    pub fn nth_root(&self, n: u32) -> Self {
183        Self::new(self.numerator().nth_root(n), self.denominator().nth_root(n)).simplify()
184    }
185
186    pub fn pow<J>(self, rhs: Fraction<J>) -> Result<Self, <J as TryInto<u32>>::Error>
187    where
188        I: Pow<u32, Output = I> + TryInto<J> + Zero,
189        J: FractionInteger + TryInto<u32> + Signed,
190    {
191        let negative = rhs < J::zero();
192        let rhs_num = rhs.numerator().abs();
193        let rhs_den = rhs.denominator().abs();
194        let num = self
195            .numerator()
196            .pow(rhs_num.try_into()?)
197            .nth_root(rhs_den.try_into()?);
198        let den = self
199            .denominator()
200            .pow(rhs_num.try_into()?)
201            .nth_root(rhs_den.try_into()?);
202        let result = if negative {
203            Self::new(den, num)
204        } else {
205            Self::new(num, den)
206        };
207        Ok(result)
208    }
209}
210
211impl<I> Default for Fraction<I>
212where
213    I: FractionInteger,
214{
215    fn default() -> Self {
216        Self::from(I::zero())
217    }
218}
219
220impl<I> Display for Fraction<I>
221where
222    I: FractionInteger + Display,
223{
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        if self.den == I::one() {
226            write!(f, "{}", self.num)
227        } else {
228            write!(f, "{}/{}", self.num, self.den)
229        }
230    }
231}
232
233impl<I, J> From<I> for Fraction<J>
234where
235    I: FractionInteger,
236    J: FractionInteger + From<I>,
237{
238    fn from(value: I) -> Self {
239        Self::new(J::from(value), J::from(I::one()))
240    }
241}
242
243impl<I> FromStr for Fraction<I>
244where
245    I: FractionInteger + FromStr + Display,
246    <I as FromStr>::Err: Display,
247    <I as FromStr>::Err: Into<anyhow::Error>,
248{
249    type Err = Error;
250    fn from_str(s: &str) -> Result<Self, Self::Err> {
251        if let Some((n, d)) = s.split_once('/') {
252            let n = n.parse().map_err(|err| {
253                Into::<Error>::into(err).context(format!("invalid numerator: {n}"))
254            })?;
255            let d = d.parse().map_err(|err| {
256                Into::<Error>::into(err).context(format!("invalid denominator: {n}"))
257            })?;
258            Ok(Self::new(n, d))
259        } else {
260            let s = match s.strip_suffix('%') {
261                Some(s) => s,
262                None => s,
263            };
264            Ok(Self::percentage(s.parse().map_err(|err| {
265                Into::<Error>::into(err).context(format!("invalid percentage"))
266            })?))
267        }
268    }
269}
270
271impl<I> PartialEq for Fraction<I>
272where
273    I: FractionInteger,
274{
275    fn eq(&self, other: &Self) -> bool {
276        let (a, b) = Self::normalize(self, other);
277        a.numerator().eq(&b.numerator())
278    }
279}
280
281impl<I> Eq for Fraction<I> where I: FractionInteger {}
282
283impl<I> PartialEq<I> for Fraction<I>
284where
285    I: FractionInteger,
286{
287    fn eq(&self, other: &I) -> bool {
288        self.eq(&Fraction::from(*other))
289    }
290}
291
292impl<I> Ord for Fraction<I>
293where
294    I: FractionInteger,
295{
296    fn cmp(&self, other: &Self) -> cmp::Ordering {
297        let (a, b) = Self::normalize(self, other);
298        a.numerator().cmp(&b.numerator())
299    }
300}
301
302impl<I> PartialOrd for Fraction<I>
303where
304    I: FractionInteger,
305{
306    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
307        Some(self.cmp(other))
308    }
309}
310
311impl<I> PartialOrd<I> for Fraction<I>
312where
313    I: FractionInteger,
314{
315    fn partial_cmp(&self, other: &I) -> Option<cmp::Ordering> {
316        self.partial_cmp(&Fraction::from(*other))
317    }
318}
319
320impl<I> Add<I> for Fraction<I>
321where
322    I: FractionInteger,
323{
324    type Output = Self;
325    fn add(self, rhs: I) -> Self::Output {
326        Self::Output::new(
327            self.numerator().add(rhs.mul(self.denominator())),
328            self.denominator(),
329        )
330        .simplify()
331    }
332}
333
334impl<I> Add for Fraction<I>
335where
336    I: FractionInteger,
337{
338    type Output = Self;
339    fn add(self, rhs: Self) -> Self::Output {
340        let (lhs, rhs) = Self::normalize(&self, &rhs);
341        Self::Output::new(lhs.numerator().add(rhs.numerator()), lhs.denominator())
342    }
343}
344
345impl<I> Sub<I> for Fraction<I>
346where
347    I: FractionInteger,
348{
349    type Output = Self;
350    fn sub(self, rhs: I) -> Self::Output {
351        Self::Output::new(
352            self.numerator().sub(rhs.mul(self.denominator())),
353            self.denominator(),
354        )
355        .simplify()
356    }
357}
358
359impl<I> Sub<Fraction<I>> for Fraction<I>
360where
361    I: FractionInteger,
362{
363    type Output = Self;
364    fn sub(self, rhs: Self) -> Self::Output {
365        let (lhs, rhs) = Self::normalize(&self, &rhs);
366        Self::Output::new(lhs.numerator().sub(rhs.numerator()), lhs.denominator())
367    }
368}
369
370impl<I> Mul<I> for Fraction<I>
371where
372    I: FractionInteger,
373{
374    type Output = Self;
375    fn mul(self, rhs: I) -> Self::Output {
376        Self::Output::new(self.numerator().mul(rhs), self.denominator()).simplify()
377    }
378}
379
380impl<I> Mul<Fraction<I>> for Fraction<I>
381where
382    I: FractionInteger,
383{
384    type Output = Self;
385    fn mul(self, rhs: Self) -> Self::Output {
386        Self::Output::new(
387            self.numerator().mul(rhs.numerator()),
388            self.denominator().mul(rhs.denominator()),
389        )
390        .simplify()
391    }
392}
393
394impl<I> Div<I> for Fraction<I>
395where
396    I: FractionInteger,
397{
398    type Output = Self;
399    fn div(self, rhs: I) -> Self::Output {
400        self.mul(Fraction::new(I::one(), rhs))
401    }
402}
403
404impl<I> Div<Fraction<I>> for Fraction<I>
405where
406    I: FractionInteger,
407{
408    type Output = Self;
409    fn div(self, rhs: Self) -> Self::Output {
410        self.mul(rhs.inverse())
411    }
412}
413
414impl<I> WrappingAdd for Fraction<I>
415where
416    I: FractionInteger + WrappingAdd,
417{
418    fn wrapping_add(&self, v: &Self) -> Self {
419        let (lhs, rhs) = Self::normalize(&self, &v);
420        Self::new(
421            lhs.numerator().wrapping_add(&rhs.numerator()),
422            lhs.denominator(),
423        )
424    }
425}
426
427impl<I> WrappingSub for Fraction<I>
428where
429    I: FractionInteger + WrappingSub,
430{
431    fn wrapping_sub(&self, v: &Self) -> Self {
432        let (lhs, rhs) = Self::normalize(&self, &v);
433        Self::new(
434            lhs.numerator().wrapping_sub(&rhs.numerator()),
435            lhs.denominator(),
436        )
437    }
438}
439
440impl<I> WrappingMul for Fraction<I>
441where
442    I: FractionInteger + WrappingMul,
443{
444    fn wrapping_mul(&self, v: &Self) -> Self {
445        Self::new(
446            self.numerator().wrapping_mul(&v.numerator()),
447            self.denominator().wrapping_mul(&v.denominator()),
448        )
449        .simplify()
450    }
451}
452
453impl<I> SaturatingAdd for Fraction<I>
454where
455    I: FractionInteger + SaturatingAdd,
456{
457    fn saturating_add(&self, v: &Self) -> Self {
458        let (lhs, rhs) = Self::normalize(&self, &v);
459        Self::new(
460            lhs.numerator().saturating_add(&rhs.numerator()),
461            lhs.denominator(),
462        )
463    }
464}
465
466impl<I> SaturatingSub for Fraction<I>
467where
468    I: FractionInteger + SaturatingSub,
469{
470    fn saturating_sub(&self, v: &Self) -> Self {
471        let (lhs, rhs) = Self::normalize(&self, &v);
472        Self::new(
473            lhs.numerator().saturating_sub(&rhs.numerator()),
474            lhs.denominator(),
475        )
476    }
477}
478
479impl<I> SaturatingMul for Fraction<I>
480where
481    I: FractionInteger + SaturatingMul,
482{
483    fn saturating_mul(&self, v: &Self) -> Self {
484        Self::new(
485            self.numerator().saturating_mul(&v.numerator()),
486            self.denominator().saturating_mul(&v.denominator()),
487        )
488        .simplify()
489    }
490}
491
492impl<I> Serialize for Fraction<I>
493where
494    I: FractionInteger + Into<u64> + Display,
495{
496    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
497    where
498        S: Serializer,
499    {
500        if self.is_whole() {
501            serializer.serialize_u64(self.floor().into())
502        } else {
503            serializer.serialize_str(&format!("{self}"))
504        }
505    }
506}
507
508struct FractionVisitor<I> {
509    _phantom: PhantomData<I>,
510}
511
512impl<I> FractionVisitor<I>
513where
514    I: Integer,
515{
516    pub fn new() -> Self {
517        Self {
518            _phantom: PhantomData,
519        }
520    }
521}
522
523impl<'de> Visitor<'de> for FractionVisitor<u16> {
524    type Value = Fraction<u16>;
525
526    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
527        write!(
528            formatter,
529            "an integer, a fraction string, a percentage string, or an array of 2 integers"
530        )
531    }
532
533    fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
534    where
535        E: serde::de::Error,
536    {
537        Ok(Self::Value::from(v as u16))
538    }
539
540    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
541    where
542        E: serde::de::Error,
543    {
544        Ok(Self::Value::from(v))
545    }
546
547    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
548    where
549        E: serde::de::Error,
550    {
551        Ok(Self::Value::from(v as u16))
552    }
553
554    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
555    where
556        E: serde::de::Error,
557    {
558        Ok(Self::Value::from(v as u16))
559    }
560
561    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
562    where
563        E: serde::de::Error,
564    {
565        Ok(Self::Value::from_f64(v))
566    }
567
568    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
569    where
570        E: serde::de::Error,
571    {
572        Self::Value::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(&v), &self))
573    }
574
575    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
576    where
577        A: serde::de::SeqAccess<'de>,
578    {
579        let num = match seq.next_element()? {
580            Some(v) => v,
581            None => return Err(serde::de::Error::invalid_length(0, &self)),
582        };
583        let den = match seq.next_element()? {
584            Some(v) => v,
585            None => return Err(serde::de::Error::invalid_length(1, &self)),
586        };
587        if seq.next_element::<u8>()?.is_some() {
588            return Err(serde::de::Error::invalid_length(3, &self));
589        }
590        Ok(Self::Value::new(num, den))
591    }
592}
593
594impl<'de> Deserialize<'de> for Fraction<u16> {
595    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
596    where
597        D: serde::Deserializer<'de>,
598    {
599        deserializer.deserialize_any(FractionVisitor::<u16>::new())
600    }
601}
602
603#[cfg(test)]
604mod fraction_test {
605    use alloc::vec;
606
607    use crate::{
608        Fraction,
609        test_util::{
610            test_deserialization,
611            test_serialization,
612        },
613    };
614
615    #[test]
616    fn serializes_to_string() {
617        test_serialization(Fraction::percentage(25), "\"1/4\"");
618        test_serialization(Fraction::percentage(100), "1");
619        test_serialization(Fraction::new(1, 2), "\"1/2\"");
620        test_serialization(Fraction::new(1, 3), "\"1/3\"");
621        test_serialization(Fraction::new(20, 147), "\"20/147\"");
622    }
623
624    #[test]
625    fn deserializes_integers() {
626        test_deserialization("25", Fraction::new(25, 1));
627        test_deserialization("77", Fraction::new(77, 1));
628        test_deserialization("100", Fraction::new(100, 1));
629    }
630
631    #[test]
632    fn deserializes_floats() {
633        test_deserialization("2.5", Fraction::new(5, 2));
634        test_deserialization("1.33", Fraction::new(5447, 4096));
635        test_deserialization("10.0", Fraction::new(10, 1));
636    }
637
638    #[test]
639    fn deserializes_flat_percentages() {
640        test_deserialization("\"25%\"", Fraction::new(1, 4));
641        test_deserialization("\"77%\"", Fraction::new(77, 100));
642        test_deserialization("\"100%\"", Fraction::new(1, 1));
643    }
644
645    #[test]
646    fn deserializes_fraction_arrays() {
647        test_deserialization("[1,2]", Fraction::new(1, 2));
648        test_deserialization("[33, 100]", Fraction::new(33, 100));
649    }
650
651    #[test]
652    fn percentage_equality() {
653        assert_eq!(Fraction::percentage(10), Fraction::percentage(10));
654        assert_eq!(Fraction::percentage(20), Fraction::new(1, 5));
655        assert_eq!(Fraction::new(35, 100), Fraction::percentage(35));
656        assert_eq!(Fraction::new(3, 4), Fraction::new(12, 16));
657    }
658
659    #[test]
660    fn percentage_inequality() {
661        assert_ne!(Fraction::percentage(10), Fraction::percentage(100));
662        assert_ne!(Fraction::percentage(20), Fraction::new(1, 20));
663        assert_ne!(Fraction::new(35, 100), Fraction::percentage(12));
664        assert_ne!(Fraction::new(3, 4), Fraction::new(3, 5));
665    }
666
667    #[test]
668    fn percentage_ordering() {
669        let mut percentages = vec![
670            Fraction::new(3, 4),
671            Fraction::new(3, 200),
672            Fraction::percentage(1),
673            Fraction::new(2, 7),
674            Fraction::new(2, 100),
675            Fraction::percentage(100),
676            Fraction::new(1, 4),
677            Fraction::new(1, 2),
678            Fraction::percentage(60),
679        ];
680        percentages.sort();
681        pretty_assertions::assert_eq!(
682            percentages,
683            vec![
684                Fraction::percentage(1),
685                Fraction::new(3, 200),
686                Fraction::new(2, 100),
687                Fraction::new(1, 4),
688                Fraction::new(2, 7),
689                Fraction::new(1, 2),
690                Fraction::percentage(60),
691                Fraction::new(3, 4),
692                Fraction::percentage(100),
693            ]
694        );
695    }
696
697    #[test]
698    fn floor_division() {
699        assert_eq!(Fraction::percentage(1).floor(), 0);
700        assert_eq!(Fraction::new(77, 12).floor(), 6);
701        assert_eq!(Fraction::percentage(2500).floor(), 25);
702        assert_eq!(Fraction::new(33, 15).floor(), 2);
703        assert_eq!(Fraction::new(1020, 25).floor(), 40);
704        assert_eq!(Fraction::new(1, 2).floor(), 0);
705    }
706
707    #[test]
708    fn round_division() {
709        assert_eq!(Fraction::percentage(1).round(), 0);
710        assert_eq!(Fraction::new(77, 12).round(), 6);
711        assert_eq!(Fraction::percentage(2500).round(), 25);
712        assert_eq!(Fraction::new(33, 15).round(), 2);
713        assert_eq!(Fraction::new(1020, 25).round(), 41);
714
715        assert_eq!(Fraction::new(1, 2).round(), 1);
716        assert_eq!(Fraction::new(2, 2).round(), 1);
717        assert_eq!(Fraction::new(3, 2).round(), 2);
718        assert_eq!(Fraction::new(4, 2).round(), 2);
719
720        assert_eq!(Fraction::new(1, 7).round(), 0);
721        assert_eq!(Fraction::new(2, 7).round(), 0);
722        assert_eq!(Fraction::new(3, 7).round(), 0);
723        assert_eq!(Fraction::new(4, 7).round(), 1);
724        assert_eq!(Fraction::new(5, 7).round(), 1);
725        assert_eq!(Fraction::new(6, 7).round(), 1);
726        assert_eq!(Fraction::new(7, 7).round(), 1);
727        assert_eq!(Fraction::new(8, 7).round(), 1);
728    }
729
730    #[test]
731    fn ceil_division() {
732        assert_eq!(Fraction::percentage(1).ceil(), 1);
733        assert_eq!(Fraction::new(77, 12).ceil(), 7);
734        assert_eq!(Fraction::percentage(2500).ceil(), 25);
735    }
736
737    #[test]
738    fn integer_addition() {
739        assert_eq!(Fraction::percentage(1) + 10000, Fraction::new(1000001, 100));
740        assert_eq!(Fraction::new(12, 77) + 2, Fraction::new(166, 77));
741        assert_eq!(Fraction::percentage(25) + 0, Fraction::new(1, 4));
742    }
743
744    #[test]
745    fn fraction_addition() {
746        assert_eq!(
747            Fraction::new(12, 77) + Fraction::new(5, 6),
748            Fraction::new(457, 462)
749        );
750        assert_eq!(
751            Fraction::new(12, 12) + Fraction::new(53, 53),
752            Fraction::from(2)
753        );
754        assert_eq!(
755            Fraction::new(1, 4) + Fraction::new(2, 4),
756            Fraction::new(3, 4)
757        );
758    }
759
760    #[test]
761    fn integer_subtraction() {
762        assert_eq!(Fraction::percentage(1) - 10000, Fraction::new(-999999, 100));
763        assert_eq!(Fraction::new(2000, 77) - 2, Fraction::new(1846, 77));
764        assert_eq!(Fraction::percentage(25) - 0, Fraction::new(1, 4));
765    }
766
767    #[test]
768    fn fraction_subtraction() {
769        assert_eq!(
770            Fraction::new(12, 77) - Fraction::new(5, 6),
771            Fraction::new(-313, 462)
772        );
773        assert_eq!(
774            Fraction::new(12, 12) - Fraction::new(53, 53),
775            Fraction::from(0)
776        );
777        assert_eq!(
778            Fraction::new(2, 4) - Fraction::new(1, 4),
779            Fraction::new(1, 4)
780        );
781    }
782
783    #[test]
784    fn integer_multiplication() {
785        assert_eq!(Fraction::percentage(1) * 10000, Fraction::from(100));
786        assert_eq!(Fraction::new(12, 77) * 85, Fraction::new(1020, 77));
787        assert_eq!(Fraction::percentage(25) * 100, Fraction::from(25));
788        assert_eq!(Fraction::percentage(25) * 1, Fraction::new(1, 4));
789        assert_eq!(Fraction::new(10, 50) * 2, Fraction::new(2, 5));
790    }
791
792    #[test]
793    fn fraction_multiplication() {
794        assert_eq!(
795            Fraction::new(12, 77) * Fraction::new(5, 6),
796            Fraction::new(10, 77)
797        );
798        assert_eq!(
799            Fraction::new(12, 12) * Fraction::new(53, 53),
800            Fraction::from(1)
801        );
802        assert_eq!(
803            Fraction::new(1, 4) * Fraction::new(2, 4),
804            Fraction::new(1, 8)
805        );
806    }
807
808    #[test]
809    fn integer_division() {
810        assert_eq!(Fraction::percentage(1) / 10000, Fraction::new(1, 1000000));
811        assert_eq!(Fraction::new(12, 77) / 85, Fraction::new(12, 6545));
812        assert_eq!(Fraction::percentage(25) / 100, Fraction::new(1, 400));
813        assert_eq!(Fraction::percentage(25) / 1, Fraction::new(1, 4));
814        assert_eq!(Fraction::new(10, 50) / 2, Fraction::new(1, 10));
815    }
816
817    #[test]
818    fn fraction_division() {
819        assert_eq!(
820            Fraction::new(12, 77) / Fraction::new(5, 6),
821            Fraction::new(72, 385)
822        );
823        assert_eq!(
824            Fraction::new(12, 12) / Fraction::new(53, 53),
825            Fraction::from(1)
826        );
827        assert_eq!(
828            Fraction::new(1, 4) / Fraction::new(2, 4),
829            Fraction::new(1, 2)
830        );
831    }
832
833    #[test]
834    fn fraction_exponentiation() {
835        assert_eq!(
836            Fraction::from(12).pow::<i32>(Fraction::from(5)),
837            Ok(Fraction::new(248832, 1))
838        );
839        assert_eq!(
840            Fraction::from(-12).pow::<i32>(Fraction::from(5)),
841            Ok(Fraction::new(-248832, 1))
842        );
843        assert_eq!(
844            Fraction::new(25, 2).pow::<i32>(Fraction::from(4)),
845            Ok(Fraction::new(390625, 16))
846        );
847        assert_eq!(
848            Fraction::new(1, 2).pow(Fraction::new(4, 2)),
849            Ok(Fraction::new(1, 4))
850        );
851        // Due to rounding, we lose precision.
852        assert_eq!(
853            Fraction::new(1, 2).pow(Fraction::new(3, 2)),
854            Ok(Fraction::new(1, 2))
855        );
856        assert_eq!(
857            Fraction::new(33, 7).pow(Fraction::new(4, 7)),
858            Ok(Fraction::new(7, 3))
859        );
860
861        assert_eq!(
862            Fraction::new(1, 2).pow(Fraction::new(-4, 2)),
863            Ok(Fraction::new(4, 1))
864        );
865        assert_eq!(
866            Fraction::new(1, 2).pow(Fraction::new(4, -2)),
867            Ok(Fraction::new(4, 1))
868        );
869
870        assert_eq!(
871            Fraction::new(1, 2).pow(Fraction::new(-3, 2)),
872            Ok(Fraction::new(2, 1))
873        );
874        assert_eq!(
875            Fraction::new(33, 7).pow(Fraction::new(4, -7)),
876            Ok(Fraction::new(3, 7))
877        );
878    }
879}