using System;

public class Fraction
{
	private int numerator;
	private int denominator;

	// Tworzy ułamka na podstawie podanego 
	// licznika i mianownika
	public Fraction(int numerator, int denominator)
	{
		this.numerator = numerator;
		this.denominator = denominator;
	}

	// Przeciążony konstruktor tworzący
	// ułamek na podstawie liczby całkowitej
	public Fraction(int wholeNumber)
	{
		Console.WriteLine("Konstruktor przyjmujący liczby całkowite");
		numerator = wholeNumber;
		denominator = 1;
	}

	// Niejawna konwersja liczb całkowitych na ułamki
	public static implicit operator Fraction(int theInt)
	{
		Console.WriteLine("Niejawna konwersja z typu int na typ Fraction");
		return new Fraction(theInt);
	}

	// Jawna konwersja ułamków na liczby całkowite
	public static explicit operator int(Fraction theFraction)
	{
		Console.WriteLine("Jawna konwersja z typu Fraction na typ int");
		return theFraction.numerator / theFraction.denominator;
	}

	// Przeciążony operator + przyjmuje dwa ułamki
	// i zwraca ich sumę
	public static Fraction operator +(Fraction lhs, Fraction rhs)
	{
		// Ułamki o wspólnym mianowniku można dodawać
		// sumując ich liczniki
		if (lhs.denominator == rhs.denominator)
		{
			return new Fraction(lhs.numerator + rhs.numerator,
			   lhs.denominator);
		}

		// Uproszczone rozwiązania dla ułamków o różnych mianownikach
		// 1/2 + 3/4 == (1*4) + (3*2) / (2*4) == 10/8.
		// Ta technika nie przeprowadza redukcji
		int firstProduct = lhs.numerator * rhs.denominator;
		int secondProduct = rhs.numerator * lhs.denominator;
		return new Fraction(
		   firstProduct + secondProduct,
		   lhs.denominator * rhs.denominator);
	}


	// Ten operator sprawdza, czy dwa ułamki są sobie równe
	public static bool operator ==(Fraction lhs, Fraction rhs)
	{
		if (lhs.denominator == rhs.denominator &&
		   lhs.numerator == rhs.numerator)
		{
			return true;
		}
		// Tu kod do obsługi ułamków o różnych mianownikach
		return false;
	}

	// Delegacja operacji do operatora ==
	public static bool operator !=(Fraction lhs, Fraction rhs)
	{
		bool equality = lhs == rhs;
		return !(equality);
	}

	// Sprawdza, czy typy są takie same, a następnie deleguje porównanie
	public override bool Equals(object o)
	{
		if (!(o is Fraction))
		{
			return false;
		}
		return this == (Fraction)o;
	}

	// Zwraca łańcuchową reprezentację ułamka
	public override string ToString()
	{
		String s = numerator.ToString() + "/" +
		denominator.ToString();
		return s;
	}
}

public class Tester
{
	public void Run()
	{
		Fraction f1 = new Fraction(3, 4);
		Fraction f2 = new Fraction(2, 4);
		Fraction f3 = f1 + f2;

		Console.WriteLine("dodawanie f3 + 5...");
		Fraction f4 = f3 + 5;
		Console.WriteLine("f3 + 5 = f4: {0}", f4.ToString());

		Console.WriteLine("\nPrzypisywanie f4 do liczby typu int...");
		int truncated = (int)f4;
		Console.WriteLine("Po obcięciu f4 otrzymujemy {0}",
		   truncated);
	}
	static void Main()
	{
		Tester t = new Tester();
		t.Run();
	}
}
