Task: Greedy algorithm for Egyptian fractions



C#: using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace EgyptianFractions {
    class Program {
        class Rational : IComparable<Rational>, IComparable<int> {
            public BigInteger Num { get; }
            public BigInteger Den { get; }

            public Rational(BigInteger n, BigInteger d) {
                var c = Gcd(n, d);
                Num = n / c;
                Den = d / c;
                if (Den < 0) {
                    Num = -Num;
                    Den = -Den;
                }
            }

            public Rational(BigInteger n) {
                Num = n;
                Den = 1;
            }

            public override string ToString() {
                if (Den == 1) {
                    return Num.ToString();
                } else {
                    return string.Format("{0}/{1}", Num, Den);
                }
            }

            public Rational Add(Rational rhs) {
                return new Rational(Num * rhs.Den + rhs.Num * Den, Den * rhs.Den);
            }

            public Rational Sub(Rational rhs) {
                return new Rational(Num * rhs.Den - rhs.Num * Den, Den * rhs.Den);
            }

            public int CompareTo(Rational rhs) {
                var ad = Num * rhs.Den;
                var bc = Den * rhs.Num;
                return ad.CompareTo(bc);
            }

            public int CompareTo(int rhs) {
                var ad = Num * rhs;
                var bc = Den * rhs;
                return ad.CompareTo(bc);
            }
        }

        static BigInteger Gcd(BigInteger a, BigInteger b) {
            if (b == 0) {
                if (a < 0) {
                    return -a;
                } else {
                    return a;
                }
            } else {
                return Gcd(b, a % b);
            }
        }

        static List<Rational> Egyptian(Rational r) {
            List<Rational> result = new List<Rational>();

            if (r.CompareTo(1) >= 0) {
                if (r.Den == 1) {
                    result.Add(r);
                    result.Add(new Rational(0));
                    return result;
                }
                result.Add(new Rational(r.Num / r.Den));
                r = r.Sub(result[0]);
            }

            BigInteger modFunc(BigInteger m, BigInteger n) {
                return ((m % n) + n) % n;
            }

            while (r.Num != 1) {
                var q = (r.Den + r.Num - 1) / r.Num;
                result.Add(new Rational(1, q));
                r = new Rational(modFunc(-r.Den, r.Num), r.Den * q);
            }

            result.Add(r);
            return result;
        }

        static string FormatList<T>(IEnumerable<T> col) {
            StringBuilder sb = new StringBuilder();
            var iter = col.GetEnumerator();

            sb.Append('[');
            if (iter.MoveNext()) {
                sb.Append(iter.Current);
            }
            while (iter.MoveNext()) {
                sb.AppendFormat(", {0}", iter.Current);
            }
            sb.Append(']');

            return sb.ToString();
        }

        static void Main() {
            List<Rational> rs = new List<Rational> {
                new Rational(43, 48),
                new Rational(5, 121),
                new Rational(2014, 59)
            };
            foreach (var r in rs) {
                Console.WriteLine("{0} => {1}", r, FormatList(Egyptian(r)));
            }

            var lenMax = Tuple.Create(0UL, new Rational(0));
            var denomMax = Tuple.Create(BigInteger.Zero, new Rational(0));

            var query = (from i in Enumerable.Range(1, 100)
                         from j in Enumerable.Range(1, 100)
                         select new Rational(i, j))
                         .Distinct()
                         .ToList();
            foreach (var r in query) {
                var e = Egyptian(r);
                ulong eLen = (ulong) e.Count;
                var eDenom = e.Last().Den;
                if (eLen > lenMax.Item1) {
                    lenMax = Tuple.Create(eLen, r);
                }
                if (eDenom > denomMax.Item1) {
                    denomMax = Tuple.Create(eDenom, r);
                }
            }

            Console.WriteLine("Term max is {0} with {1} terms", lenMax.Item2, lenMax.Item1);
            var dStr = denomMax.Item1.ToString();
            Console.WriteLine("Denominator max is {0} with {1} digits {2}...{3}", denomMax.Item2, dStr.Length, dStr.Substring(0, 5), dStr.Substring(dStr.Length - 5, 5));
        }
    }
}






VB: Imports System.Numerics
Imports System.Text

Module Module1

    Function Gcd(a As BigInteger, b As BigInteger) As BigInteger
        If b = 0 Then
            If a < 0 Then
                Return -a
            Else
                Return a
            End If
        Else
            Return Gcd(b, a Mod b)
        End If
    End Function

    Function Lcm(a As BigInteger, b As BigInteger) As BigInteger
        Return a / Gcd(a, b) * b
    End Function

    Public Class Rational
        Dim num As BigInteger
        Dim den As BigInteger

        Public Sub New(n As BigInteger, d As BigInteger)
            Dim c = Gcd(n, d)
            num = n / c
            den = d / c
            If den < 0 Then
                num = -num
                den = -den
            End If
        End Sub

        Public Sub New(n As BigInteger)
            num = n
            den = 1
        End Sub

        Public Function Numerator() As BigInteger
            Return num
        End Function

        Public Function Denominator() As BigInteger
            Return den
        End Function

        Public Overrides Function ToString() As String
            If den = 1 Then
                Return num.ToString()
            Else
                Return String.Format("{0}/{1}", num, den)
            End If
        End Function

        
        Public Shared Operator +(lhs As Rational, rhs As Rational) As Rational
            Return New Rational(lhs.num * rhs.den + rhs.num * lhs.den, lhs.den * rhs.den)
        End Operator

        Public Shared Operator -(lhs As Rational, rhs As Rational) As Rational
            Return New Rational(lhs.num * rhs.den - rhs.num * lhs.den, lhs.den * rhs.den)
        End Operator

        

        Public Shared Operator =(lhs As Rational, rhs As Rational) As Boolean
            Return lhs.num = rhs.num AndAlso lhs.den = rhs.den
        End Operator

        Public Shared Operator <>(lhs As Rational, rhs As Rational) As Boolean
            Return lhs.num <> rhs.num OrElse lhs.den <> rhs.den
        End Operator

        Public Shared Operator <(lhs As Rational, rhs As Rational) As Boolean
            
            
            Dim ad = lhs.num * rhs.den
            Dim bc = lhs.den * rhs.num
            Return ad < bc
        End Operator

        Public Shared Operator >(lhs As Rational, rhs As Rational) As Boolean
            
            
            Dim ad = lhs.num * rhs.den
            Dim bc = lhs.den * rhs.num
            Return ad > bc
        End Operator

        Public Shared Operator <=(lhs As Rational, rhs As Rational) As Boolean
            Return lhs < rhs OrElse lhs = rhs
        End Operator

        Public Shared Operator >=(lhs As Rational, rhs As Rational) As Boolean
            Return lhs > rhs OrElse lhs = rhs
        End Operator

        
        Public Shared Widening Operator CType(ByVal bi As BigInteger) As Rational
            Return New Rational(bi)
        End Operator
        Public Shared Widening Operator CType(ByVal lo As Long) As Rational
            Return New Rational(lo)
        End Operator
    End Class

    Function Egyptian(r As Rational) As List(Of Rational)
        Dim result As New List(Of Rational)

        If r >= 1 Then
            If r.Denominator() = 1 Then
                result.Add(r)
                result.Add(New Rational(0))
                Return result
            End If
            result.Add(New Rational(r.Numerator / r.Denominator))
            r -= result(0)
        End If

        Dim modFunc = Function(m As BigInteger, n As BigInteger)
                          Return ((m Mod n) + n) Mod n
                      End Function

        While r.Numerator() <> 1
            Dim q = (r.Denominator() + r.Numerator() - 1) / r.Numerator()
            result.Add(New Rational(1, q))
            r = New Rational(modFunc(-r.Denominator(), r.Numerator()), r.Denominator * q)
        End While

        result.Add(r)
        Return result
    End Function

    Function FormatList(Of T)(col As List(Of T)) As String
        Dim iter = col.GetEnumerator()
        Dim sb As New StringBuilder

        sb.Append("[")
        If iter.MoveNext() Then
            sb.Append(iter.Current)
        End If
        While iter.MoveNext()
            sb.Append(", ")
            sb.Append(iter.Current)
        End While
        sb.Append("]")
        Return sb.ToString()
    End Function

    Sub Main()
        Dim rs = {New Rational(43, 48), New Rational(5, 121), New Rational(2014, 59)}
        For Each r In rs
            Console.WriteLine("{0} => {1}", r, FormatList(Egyptian(r)))
        Next

        Dim lenMax As Tuple(Of ULong, Rational) = Tuple.Create(0UL, New Rational(0))
        Dim denomMax As Tuple(Of BigInteger, Rational) = Tuple.Create(New BigInteger(0), New Rational(0))

        Dim query = (From i In Enumerable.Range(1, 100)
                     From j In Enumerable.Range(1, 100)
                     Select New Rational(i, j)).Distinct().ToList()
        For Each r In query
            Dim e = Egyptian(r)
            Dim eLen As ULong = e.Count
            Dim eDenom = e.Last().Denominator()
            If eLen > lenMax.Item1 Then
                lenMax = Tuple.Create(eLen, r)
            End If
            If eDenom > denomMax.Item1 Then
                denomMax = Tuple.Create(eDenom, r)
            End If
        Next

        Console.WriteLine("Term max is {0} with {1} terms", lenMax.Item2, lenMax.Item1)
        Dim dStr = denomMax.Item1.ToString()
        Console.WriteLine("Denominator max is {0} with {1} digits {2}...{3}", denomMax.Item2, dStr.Length, dStr.Substring(0, 5), dStr.Substring(dStr.Length - 5, 5))
    End Sub

End Module