Skip to content

Commit ac99f43

Browse files
RohanNagarbrendandburns
authored andcommitted
Move Fractions library inside project (#194)
* Move Fractions library inside project * Add README and license
1 parent aec5c99 commit ac99f43

18 files changed

+1493
-1
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using System;
2+
3+
namespace Fractions.Extensions {
4+
internal static class MathExt {
5+
/// <summary>
6+
/// Checks for an even number.
7+
/// </summary>
8+
/// <param name="number"></param>
9+
/// <returns><c>true</c> if the number is even.</returns>
10+
public static bool IsEven(this long number) {
11+
return (number & 1) == 0;
12+
}
13+
14+
/// <summary>
15+
/// Checks for an odd number.
16+
/// </summary>
17+
/// <param name="number"></param>
18+
/// <returns><c>true</c> if the number is odd.</returns>
19+
public static bool IsOdd(this long number) {
20+
return (number & 1) != 0;
21+
}
22+
23+
/// <summary>
24+
/// Get the greatest common divisor (GCD) of <paramref name="a"/> and <paramref name="b"/>.
25+
/// </summary>
26+
/// <param name="a">First number.</param>
27+
/// <param name="b">Second number.</param>
28+
/// <returns>The largest positive integer that divides <paramref name="a"/> and <paramref name="b"/> without a remainder.</returns>
29+
public static long GreatestCommonDivisor(long a, long b) {
30+
a = Math.Abs(a);
31+
b = Math.Abs(b);
32+
33+
if (a == 0) {
34+
// ggT(0, b) = b
35+
// Denn alles teilt durch 0.
36+
return b;
37+
}
38+
39+
if (b == 0) {
40+
// ggT(a, 0) = a
41+
return a;
42+
}
43+
44+
if (a == 1 || b == 1) {
45+
// trivial
46+
return 1;
47+
}
48+
49+
return a == b
50+
? a // Beide Zahlen sind identisch, wir haben bereits den ggT gefunden.
51+
: BinaryGreatestCommonDivisorAlgorithm(a, b);
52+
}
53+
54+
private static long BinaryGreatestCommonDivisorAlgorithm(long a, long b) {
55+
56+
// Solange 'a' und 'b' beide gerade Zahlen sind, teile die Zahlen durch 2
57+
// und merke wie oft dies möglich war in 'k'.
58+
int k;
59+
for (k = 0; (a | b).IsEven(); ++k) {
60+
a >>= 1; // a = (a / 2);
61+
b >>= 1; // b = (b / 2);
62+
}
63+
64+
// Teile 'a' solange durch 2 bis die Zahl ungerade ist.
65+
while (a.IsEven()) {
66+
a >>= 1; // a = (a / 2);
67+
}
68+
69+
// Ab hier ist 'a' definitiv ungerade. Für 'b' muss dies allerdings noch nicht gelten!
70+
do {
71+
// Teile 'b' solange durch 2 bis die Zahl ungerade ist.
72+
while (b.IsEven()) {
73+
b >>= 1; // b = (b / 2);
74+
}
75+
76+
// 'a' und 'b' sind hier beide ungerade. Falls 'a' >= 'b'
77+
// muss der Inhalt beider Variablen geswappt werden,
78+
// damit die notwendige Subtraktion durchgeführt werden
79+
// kann.
80+
if (a > b) {
81+
var temp = b;
82+
b = a;
83+
a = temp;
84+
}
85+
86+
b = b - a;
87+
88+
} while (b != 0);
89+
90+
return a << k; // a * 2^k
91+
}
92+
93+
/// <summary>
94+
/// Get the least common multiple (LCM) of <paramref name="a"/> and <paramref name="b"/>.
95+
/// </summary>
96+
/// <param name="a">The first number.</param>
97+
/// <param name="b">The second number.</param>
98+
/// <returns>The smallest positive integer that is divisible by both <paramref name="a"/> and <paramref name="b"/> or 0 if either <paramref name="a"/> or <paramref name="b"/> is 0</returns>
99+
/// <exception cref="ArgumentException">If <paramref name="a"/> and <paramref name="b"/> are 0</exception>
100+
public static long LeastCommonMultiple(long a, long b) {
101+
if (a == 0 && b == 0) {
102+
throw new ArgumentException("The least common multiple is not defined if both numbers are zero.");
103+
}
104+
105+
a = Math.Abs(a);
106+
b = Math.Abs(b);
107+
108+
if (a == b) {
109+
return a;
110+
}
111+
112+
// Es gilt LCM(a,b) = (|a*b|) / GCD(a,b)
113+
114+
var gcd = GreatestCommonDivisor(a, b);
115+
return a / gcd * b;
116+
}
117+
118+
/// <summary>
119+
/// Returns <c>true</c> if there are remaining digits after the decimal point.
120+
/// </summary>
121+
/// <param name="remainingDigits">A <see cref="double"/> value with possible remaining digits</param>
122+
/// <returns><c>true</c> if <paramref name="remainingDigits"/> has digits after the decimal point</returns>
123+
124+
public static bool RemainingDigitsAfterTheDecimalPoint(double remainingDigits) {
125+
return Math.Abs(remainingDigits - Math.Floor(remainingDigits)) > double.Epsilon;
126+
}
127+
}
128+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace Fractions.Formatter {
4+
/// <summary>
5+
/// Default <see cref="Fraction.ToString()"/> formatter.
6+
/// </summary>
7+
public class DefaultFractionFormatProvider : IFormatProvider {
8+
/// <summary>
9+
/// Singleton instance
10+
/// </summary>
11+
public static readonly IFormatProvider Instance = new DefaultFractionFormatProvider();
12+
13+
object IFormatProvider.GetFormat(Type formatType) {
14+
return formatType == typeof (Fraction)
15+
? DefaultFractionFormatter.Instance
16+
: null;
17+
}
18+
}
19+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Numerics;
4+
using System.Text;
5+
6+
namespace Fractions.Formatter {
7+
internal class DefaultFractionFormatter : ICustomFormatter {
8+
public static readonly ICustomFormatter Instance = new DefaultFractionFormatter();
9+
10+
public string Format(string format, object arg, IFormatProvider formatProvider) {
11+
if (arg == null) {
12+
return string.Empty;
13+
}
14+
15+
if (!(arg is Fraction)) {
16+
throw new FormatException(string.Format("The type {0} is not supported.", arg.GetType()));
17+
}
18+
19+
var fraction = (Fraction)arg;
20+
21+
if (string.IsNullOrEmpty(format) || format == "G") {
22+
return FormatGeneral(fraction);
23+
}
24+
25+
var sb = new StringBuilder(32);
26+
foreach (var character in format) {
27+
switch (character) {
28+
case 'G':
29+
sb.Append(FormatGeneral(fraction));
30+
break;
31+
case 'n':
32+
sb.Append(fraction.Numerator.ToString(CultureInfo.InvariantCulture));
33+
break;
34+
case 'd':
35+
sb.Append(fraction.Denominator.ToString(CultureInfo.InvariantCulture));
36+
break;
37+
case 'z':
38+
sb.Append(FormatInteger(fraction));
39+
break;
40+
case 'r':
41+
sb.Append(FormatRemainder(fraction));
42+
break;
43+
case 'm':
44+
sb.Append(FormatMixed(fraction));
45+
break;
46+
default:
47+
sb.Append(character);
48+
break;
49+
}
50+
}
51+
return sb.ToString();
52+
}
53+
54+
private static string FormatMixed(Fraction fraction) {
55+
if (BigInteger.Abs(fraction.Numerator) < BigInteger.Abs(fraction.Denominator)) {
56+
return FormatGeneral(fraction);
57+
}
58+
59+
var integer = fraction.Numerator / fraction.Denominator;
60+
var remainder = Fraction.Abs(fraction - integer);
61+
62+
return remainder.IsZero
63+
? integer.ToString(CultureInfo.InvariantCulture)
64+
: string.Concat(
65+
integer.ToString(CultureInfo.InvariantCulture),
66+
" ",
67+
FormatGeneral(remainder));
68+
}
69+
70+
private static string FormatInteger(Fraction fraction) {
71+
return (fraction.Numerator / fraction.Denominator)
72+
.ToString(CultureInfo.InvariantCulture);
73+
}
74+
75+
private static string FormatRemainder(Fraction fraction) {
76+
if (BigInteger.Abs(fraction.Numerator) < BigInteger.Abs(fraction.Denominator)) {
77+
return FormatGeneral(fraction);
78+
}
79+
var integer = fraction.Numerator / fraction.Denominator;
80+
var remainder = fraction - integer;
81+
return FormatGeneral(remainder);
82+
}
83+
84+
private static string FormatGeneral(Fraction fraction) {
85+
if (fraction.Denominator == BigInteger.One) {
86+
return fraction.Numerator.ToString(CultureInfo.InvariantCulture);
87+
88+
}
89+
return string.Concat(
90+
fraction.Numerator.ToString(CultureInfo.InvariantCulture),
91+
"/",
92+
fraction.Denominator.ToString(CultureInfo.InvariantCulture));
93+
}
94+
}
95+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Numerics;
3+
4+
namespace Fractions {
5+
public partial struct Fraction
6+
{
7+
/// <summary>
8+
/// Compares the calculated value with the supplied <paramref name="other"/>.
9+
/// </summary>
10+
/// <param name="other">Fraction that shall be compared with.</param>
11+
/// <returns>
12+
/// Less than 0 if <paramref name="other"/> is greater.
13+
/// Zero (0) if both calculated values are equal.
14+
/// Greater then zero (0) if <paramref name="other"/> less.</returns>
15+
/// <exception cref="ArgumentException">If <paramref name="other"/> is not of type <see cref="Fraction"/>.</exception>
16+
public int CompareTo(object other) {
17+
if (other == null) {
18+
return 1;
19+
}
20+
21+
if (other.GetType() != typeof(Fraction)) {
22+
throw new ArgumentException(
23+
string.Format("The comparing instance must be of type {0}. The supplied argument is of type {1}", GetType(), other.GetType()), nameof(other));
24+
}
25+
26+
return CompareTo((Fraction)other);
27+
}
28+
29+
/// <summary>
30+
/// Compares the calculated value with the supplied <paramref name="other"/>.
31+
/// </summary>
32+
/// <param name="other">Fraction that shall be compared with.</param>
33+
/// <returns>
34+
/// Less than 0 if <paramref name="other"/> is greater.
35+
/// Zero (0) if both calculated values are equal.
36+
/// Greater then zero (0) if <paramref name="other"/> less.</returns>
37+
38+
public int CompareTo(Fraction other) {
39+
if (_denominator == other._denominator) {
40+
return _numerator.CompareTo(other._numerator);
41+
}
42+
43+
if (IsZero != other.IsZero) {
44+
if (IsZero) {
45+
return other.IsPositive ? -1 : 1;
46+
}
47+
return IsPositive ? 1 : -1;
48+
}
49+
50+
var gcd = BigInteger.GreatestCommonDivisor(_denominator, other._denominator);
51+
52+
var thisMultiplier = BigInteger.Divide(_denominator, gcd);
53+
var otherMultiplier = BigInteger.Divide(other._denominator, gcd);
54+
55+
var a = BigInteger.Multiply(_numerator, otherMultiplier);
56+
var b = BigInteger.Multiply(other._numerator, thisMultiplier);
57+
58+
return a.CompareTo(b);
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)