#include <iostream>
#include <string>

using namespace std;

class TFraction
{
  public:
    TFraction(int num = 1, int den = 1);
    TFraction(const TFraction &f);

    TFraction operator + () const;
    TFraction operator - () const;
    TFraction & operator = (const TFraction &f);
    TFraction operator + (const TFraction &f) const;
    TFraction operator - (const TFraction &f) const;
    TFraction operator * (const TFraction &f) const;
    TFraction operator / (const TFraction &f) const;

    void reduce();
    string toString() const;
    double value() const;

    void setNum(int num);
    void setDen(int den);

    int getNum() const;
    int getDen() const;

  private:
    int num;
    int den;

    void fixZero();
    void fixSign();
};

//==============================================================================

TFraction::TFraction(int num, int den): num(num), den(den)
{
  fixZero();
  fixSign();
}

TFraction::TFraction(const TFraction &f)
{
  num = f.num;
  den = f.den;

  fixZero();
  fixSign();
}

//==============================================================================

TFraction TFraction::operator + () const
{
  return TFraction(num, den);
}

TFraction TFraction::operator - () const
{
  return TFraction(-num, den);
}

TFraction & TFraction::operator = (const TFraction &f)
{
  if(&f != this)
  {
    num = f.num;
    den = f.den;
  }

  return *this;
}

TFraction TFraction::operator + (const TFraction &f) const
{
  TFraction result(num * f.den + den * f.num, den * f.den);

  result.reduce();

  return result;
}

TFraction TFraction::operator - (const TFraction &f) const
{
  TFraction result(num * f.den - den * f.num, den * f.den);

  result.reduce();

  return result;
}

TFraction TFraction::operator * (const TFraction &f) const
{
  TFraction result(num * f.num, den * f.den);

  result.reduce();

  return result;
}

TFraction TFraction::operator / (const TFraction &f) const
{
  TFraction result(num * f.den, den * f.num);

  result.reduce();

  return result;
}

//==============================================================================

void TFraction::reduce()
{
  int a = abs(num);
  int b = abs(den);

  while(b != 0)
  {
    int r = a % b;
    a = b;
    b = r;
  }

  num = num / a;
  den = den / a;
}

string TFraction::toString() const
{
  string str;

  str = to_string(num) + " / " + to_string(den);

  return str; 
}

double TFraction::value() const
{
  return double(num) / double(den);
}

void TFraction::setNum(int num)
{
  TFraction::num = num;

  fixZero();
  fixSign();
}

void TFraction::setDen(int den)
{
  TFraction::den = den;

  fixZero();
  fixSign();
}

int TFraction::getNum() const
{
  return num;
}

int TFraction::getDen() const
{
  return den;
}

//==============================================================================

void TFraction::fixZero()
{
  if(num * den == 0)
  {
    num = 0;
    den = 1;
  }
}

void TFraction::fixSign()
{
  if(num * den < 0)
  {
    num = -abs(num);
    den = abs(den);
  }
  else
  {
    num = abs(num);
    den = abs(den);
  }
}

//==============================================================================

ostream & operator << (ostream &s, const TFraction &f)
{
  s << f.getNum() << " / " << f.getDen();

  return s;
}

//==============================================================================

int main()
{

  TFraction one;
  TFraction two(2);
  TFraction oneHalf(1, 2);
  TFraction twoCopy(two);
  
  cout << endl << "Wyswietlanie ulamkow:" << endl;
  cout << one.toString() << endl;
  cout << two.toString() << endl;
  cout << oneHalf.toString() << endl;
  cout << twoCopy.toString() << endl;

  cout << endl << "Wartosci ulamkow:" << endl;
  cout << one.value() << endl;
  cout << two.value() << endl;
  cout << oneHalf.value() << endl;

  TFraction zeroFifths(0, -5);
  TFraction threeFifths(-3, 5);
  TFraction fiveEighths(5, -8);

  cout << endl << "Poprawione ulamki:" << endl;
  cout << zeroFifths.toString() << endl;
  cout << threeFifths.toString() << endl;
  cout << fiveEighths.toString() << endl;

  cout << endl << "Zmiana licznika i mianownika:" << endl;
  one.setNum(4);
  one.setDen(3);
  cout << one.toString() << endl;
  cout << one.value() << endl;

  TFraction twoThirds(-52, -78);

  cout << endl << "Przed uproszczeniem:" << endl;
  cout << twoThirds.toString() << endl;
  cout << twoThirds.value() << endl;

  twoThirds.reduce();
  cout << endl << "Po uproszczeniu:" << endl;
  cout << twoThirds.toString() << endl;
  cout << twoThirds.value() << endl;

  TFraction result;

  cout << endl << "Operatory zmiany znaku i przypisania:" << endl;
  result = (-fiveEighths);
  cout << result.toString() << endl;
  cout << result.value() << endl;

  cout << endl << "Operatory arytmetyczne:" << endl;
  result = twoThirds + fiveEighths;
  cout << result.toString() << endl;
  cout << result.value() << endl;

  result = twoThirds - fiveEighths;
  cout << result.toString() << endl;
  cout << result.value() << endl;

  result = twoThirds * fiveEighths;
  cout << result.toString() << endl;
  cout << result.value() << endl;

  result = twoThirds / fiveEighths;
  cout << result.toString() << endl;
  cout << result.value() << endl;

  cout << endl << "Operator <<:" << endl;
  cout << result << endl;

  return 0;
}
