| | 1 | | namespace SharpFractions; |
| | 2 | |
|
| | 3 | | public readonly partial struct Fraction : IComparable<Fraction>, IEquatable<Fraction> |
| | 4 | | { |
| | 5 | | public readonly BigInteger Numerator; |
| | 6 | | public readonly BigInteger Denominator; |
| | 7 | |
|
| | 8 | | /// <summary> |
| | 9 | | /// Creates a fraction with numerator=<c>num</c> and denominator=<c>den</c> |
| | 10 | | /// </summary> |
| | 11 | | /// <param name="num"></param> |
| | 12 | | /// <param name="den"></param> |
| | 13 | | /// <exception cref="DivideByZeroException"></exception> |
| | 14 | | public Fraction(BigInteger num, BigInteger den) |
| 177 | 15 | | { |
| 178 | 16 | | if (den == 0) throw new DivideByZeroException(); |
| | 17 | |
|
| 176 | 18 | | Numerator = num; |
| 176 | 19 | | Denominator = den; |
| 176 | 20 | | } |
| | 21 | |
|
| | 22 | | /// <summary> |
| | 23 | | /// Creates a fraction with numerator=<c>integer</c> and denominator=1 |
| | 24 | | /// </summary> |
| | 25 | | /// <param name="integer"></param> |
| 78 | 26 | | public Fraction(BigInteger integer) : this(integer, 1) { } |
| | 27 | |
|
| | 28 | |
|
| 6 | 29 | | public bool IsInt => Numerator % Denominator == 0; |
| 13 | 30 | | public bool IsZero => Numerator == 0; |
| 2 | 31 | | public bool IsOne => Numerator == Denominator; |
| | 32 | |
|
| | 33 | | public int Sign |
| | 34 | | { |
| | 35 | | get |
| 6 | 36 | | { |
| 7 | 37 | | if (Numerator == 0) return 0; |
| 5 | 38 | | return Numerator.Sign == Denominator.Sign ? 1 : -1; |
| 6 | 39 | | } |
| | 40 | | } |
| | 41 | |
|
| 17 | 42 | | public BigInteger Whole => Numerator / Denominator; |
| 13 | 43 | | public BigInteger Part => Numerator % Denominator; |
| | 44 | |
|
| | 45 | | /// <summary> |
| | 46 | | /// Returns the fraction in mixed form: 8/3 = 2 and 2/3 |
| | 47 | | /// </summary> |
| 1 | 48 | | public (BigInteger, Fraction) Mixed => (Whole, new(Part, Denominator)); |
| | 49 | |
|
| 1 | 50 | | public static Fraction MinusOne => new(-1); |
| 2 | 51 | | public static Fraction One => new(1); |
| 9 | 52 | | public static Fraction Zero => new(0); |
| | 53 | |
|
| | 54 | |
|
| | 55 | | public Fraction ExpandBy(BigInteger integer) |
| 23 | 56 | | { |
| 23 | 57 | | return new(Numerator * integer, Denominator * integer); |
| 23 | 58 | | } |
| | 59 | |
|
| | 60 | | public Fraction Invert() |
| 14 | 61 | | { |
| 15 | 62 | | if (Numerator.IsZero) throw new DivideByZeroException(); |
| 13 | 63 | | return new(Denominator, Numerator); |
| 13 | 64 | | } |
| | 65 | |
|
| | 66 | | public Fraction Pow(int power) |
| 5 | 67 | | { |
| 6 | 68 | | if (power == 0) return One; |
| 5 | 69 | | if (power == 1) return this; |
| | 70 | |
|
| 4 | 71 | | if (power < 0) return Invert().Pow(-power); |
| | 72 | |
|
| 2 | 73 | | return new( |
| 2 | 74 | | BigInteger.Pow(Numerator, power), |
| 2 | 75 | | BigInteger.Pow(Denominator, power)); |
| 5 | 76 | | } |
| | 77 | |
|
| 1 | 78 | | public Fraction SwapSign() => new(-Numerator, -Denominator); |
| | 79 | |
|
| | 80 | | public Fraction ToSimplest() |
| 1 | 81 | | { |
| 1 | 82 | | BigInteger gcdOfNumDen = BigInteger.GreatestCommonDivisor(Numerator, Denominator); |
| 1 | 83 | | return new(Numerator / gcdOfNumDen, Denominator / gcdOfNumDen); |
| 1 | 84 | | } |
| | 85 | |
|
| | 86 | |
|
| | 87 | | public static Fraction Abs(Fraction fraction) |
| 3 | 88 | | { |
| 4 | 89 | | if (fraction.Sign != -1) return fraction; |
| | 90 | |
|
| | 91 | | //Make sure to remove the minus sign: |
| 3 | 92 | | if (fraction.Numerator < 0) return new(fraction.Numerator * -1, fraction.Denominator); |
| | 93 | |
|
| 1 | 94 | | return new(fraction.Numerator, fraction.Denominator * -1); |
| 3 | 95 | | } |
| | 96 | |
|
| | 97 | | public static List<BigInteger> ContinuedFraction(Fraction frac) |
| 4 | 98 | | { |
| 5 | 99 | | if (frac.IsInt) return new() { frac.Whole }; // Also trivial |
| | 100 | |
|
| | 101 | | //Ensure positive denom: |
| | 102 | |
|
| 4 | 103 | | if (frac.Denominator < 0) frac = new(-frac.Numerator, -frac.Denominator); |
| | 104 | |
|
| | 105 | | //For the first term we'll have to deal with the possibility of frac being negative: |
| | 106 | |
|
| | 107 | | BigInteger first; |
| | 108 | |
|
| 3 | 109 | | if (frac.Numerator < 0) |
| 2 | 110 | | { |
| 2 | 111 | | first = frac.Whole - 1; |
| 2 | 112 | | } |
| | 113 | | else |
| 1 | 114 | | { |
| 1 | 115 | | first = frac.Whole; |
| 1 | 116 | | } |
| | 117 | |
|
| 3 | 118 | | List<BigInteger> coeffiecients = new() { first }; |
| | 119 | |
|
| 3 | 120 | | frac = (frac - first).Invert(); |
| | 121 | |
|
| | 122 | | // Now, redo until completion: |
| | 123 | |
|
| 11 | 124 | | while (true) |
| 11 | 125 | | { |
| 11 | 126 | | coeffiecients.Add(frac.Whole); |
| | 127 | |
|
| 11 | 128 | | frac = new(frac.Part, frac.Denominator); |
| | 129 | |
|
| 14 | 130 | | if (frac.IsZero) break; |
| | 131 | |
|
| 8 | 132 | | frac = frac.Invert(); |
| 8 | 133 | | } |
| | 134 | |
|
| 3 | 135 | | return coeffiecients; |
| 4 | 136 | | } |
| | 137 | |
|
| | 138 | | /// <summary> |
| | 139 | | /// Puts frac1 and frac2 on their lowest common denominator |
| | 140 | | /// </summary> |
| | 141 | | /// <param name="frac1"></param> |
| | 142 | | /// <param name="frac2"></param> |
| | 143 | | /// <returns>Two new fractions</returns> |
| | 144 | | public static (Fraction, Fraction) PutOnCommonDenominator(Fraction frac1, Fraction frac2) |
| 35 | 145 | | { |
| 59 | 146 | | if (frac1.Denominator == frac2.Denominator) return (frac1, frac2); |
| | 147 | |
|
| 11 | 148 | | BigInteger newDenom = BigIntExtra.LeastCommonMultiple(frac1.Denominator, frac2.Denominator); |
| 11 | 149 | | BigInteger toExpandFrac1By = newDenom / frac1.Denominator; |
| 11 | 150 | | BigInteger toExpandFrac2By = newDenom / frac2.Denominator; |
| | 151 | |
|
| 11 | 152 | | return (frac1.ExpandBy(toExpandFrac1By), frac2.ExpandBy(toExpandFrac2By)); |
| 35 | 153 | | } |
| | 154 | | } |