#include<iostream>
#include<cassert>

using namespace std;

class FractionType
{
public:

	void Initialize(int num, int den);
	FractionType Add(FractionType second);
	FractionType Sub(FractionType second);
	FractionType Mult(FractionType second);
	FractionType Div(FractionType second);
	void Reduction();
	void Print();
	int gcd(int m, int n);
	int lcm(int m, int n);
	
private:
	int num;
	int den;
};

int main()
{
	FractionType bunsu1, bunsu2, result_bunsu;
	int bunja, bunmo;
	int pre_bunja, pre_bunmo;
	cout << "분수 계산기" << endl;
	cout << "음수인 분수를 입력하시려면 분자에다가 음수 입력 요망" << endl;
	cout << "분모에 0을 넣으면 오류 발생" << endl;
	cout << "2개의 분수를 분자, 분모 순으로 입력해주세요 :";

	cin >> bunja >> bunmo;
	pre_bunja = bunja;
	pre_bunmo = bunmo;
	bunsu1.Initialize(bunja, bunmo);// 분수 1개 입력완료, 초기 내용 설정
	//뒤의 입력을 통해 새로 입력받는 값에의해 분수 1의 분자 분모가 삭제되므로 다른 변수에 입력값을 저장해놓는다.
	bunsu1.Print();  // initialize에 입력한 분수를 출력(bunsu1.initialize에 초기화된값은 첫번째 분수의 분자, 분모이다.)
	cin >> bunja >> bunmo;  // 분수 2의 분자, 분모 입력<전의 분수 1의 값은 입력으로 사라짐>
	bunsu2.Initialize(bunja, bunmo);  //두번째 분수 입력완료, 초기 내용 설정
	cout << "+";
	bunsu2.Print();//initialize에 입력한 분수를 출력(bunsu2.initialize에 초기화된값은 두번째 분수의 분자, 분모이다.)
	cout << "=";
	result_bunsu = bunsu1.Add(bunsu2); //계산식 연산<첫번째분수는 private의 저장된값으로, 두번째분수는 인자로써 들어가 연산>
	result_bunsu.Reduction();
	result_bunsu.Print();

	bunsu1.Initialize(bunja, bunmo); // 분수 1개 입력완료, 초기 내용 설정
	bunsu1.Print();  // initialize에 입력한 분수를 출력(bunsu1.initialize에 초기화된값은 첫번째 분수의 분자, 분모이다.)
	bunsu2.Initialize(bunja, bunmo);  //두번째 분수 입력완료, 초기 내용 설정
	cout << "-";
	bunsu2.Print();//initialize에 입력한 분수를 출력(bunsu2.initialize에 초기화된값은 두번째 분수의 분자, 분모이다.)
	cout << "=";
	result_bunsu = bunsu1.Sub(bunsu2);

	result_bunsu.Reduction();
	result_bunsu.Print();
	cout << endl;
	
	bunsu1.Initialize(bunja, bunmo); // 분수 1개 입력완료, 초기 내용 설정
	bunsu1.Print();  // initialize에 입력한 분수를 출력(bunsu1.initialize에 초기화된값은 첫번째 분수의 분자, 분모이다.)
	bunsu2.Initialize(bunja, bunmo);  //두번째 분수 입력완료, 초기 내용 설정
	cout << "*";
	bunsu2.Print();//initialize에 입력한 분수를 출력(bunsu2.initialize에 초기화된값은 두번째 분수의 분자, 분모이다.)
	cout << "=";
	result_bunsu = bunsu1.Mult(bunsu2);

	result_bunsu.Reduction();
	result_bunsu.Print();
	cout << endl;
	
	bunsu1.Initialize(bunja, bunmo); // 분수 1개 입력완료, 초기 내용 설정
	bunsu1.Print();  // initialize에 입력한 분수를 출력(bunsu1.initialize에 초기화된값은 첫번째 분수의 분자, 분모이다.)
	bunsu2.Initialize(bunja, bunmo);  //두번째 분수 입력완료, 초기 내용 설정
	cout << "/";
	bunsu2.Print();//initialize에 입력한 분수를 출력(bunsu2.initialize에 초기화된값은 두번째 분수의 분자, 분모이다.)
	cout << "=";
	result_bunsu = bunsu1.Div(bunsu2);

	result_bunsu.Reduction();
	result_bunsu.Print();



	return 0;
}

void FractionType::Initialize(int input_num, int input_den)  //Initialize : 초기 내용 설정, 수를 입력받은 것을 class의 private의 num, den에 저장
{

num = input_num;
den = input_den;

}

FractionType FractionType::Add(FractionType second){

	FractionType result_bunsu;
	
	result_bunsu.num = (num * second.den) + (second.num * den);
    result_bunsu.den = lcm(den, second.den);

	return result_bunsu;
}
FractionType FractionType::Sub(FractionType second){

	FractionType result_bunsu;

	result_bunsu.num = (num * second.den) - (second.num * den);
	result_bunsu.den = lcm(den, second.den);

	return result_bunsu;
}
FractionType FractionType::Mult(FractionType second){

	FractionType result_bunsu;

	result_bunsu.num = num* second.num;
	result_bunsu.den = den*second.den;

	return result_bunsu;
}
FractionType FractionType::Div(FractionType second){

	FractionType result_bunsu;

	result_bunsu.num = num*second.den;
	result_bunsu.den = den*second.num;

	return result_bunsu;
}
void FractionType::Reduction()
{
	gcd(num, den);
	num = num / gcd(num, den);
	den = den / gcd(num, den);
}

void FractionType::Print()
{
	if (den != 1){
		cout <<"("<< num << "/" << den <<")";
	}
	else
	{
		cout << num;
	}
}

int FractionType::gcd(int m, int n) {


	if (n > m)  // 최대공약수 통분할때 뒷수가 앞수보다 크면 자리 배치 바꿈, 손으로 쓰는 통분은 앞수가 작아도 되는데 이 방법은 좀 상이해서 바꿔줘야한다.
	{
		int temp;
		temp = n;
		n = m;
		m = temp;  // 자리를 바꾸었다.

		if (m == 0){ // 중첩 if문  만약 gcd에 0이들어오면<그럴일은 없겠지만...> 그냥 n이 최대공약수
			return n;
		}
		else  // 0이아니면 
		{
			return gcd(m, n%m);
		}

	}
	else
	{
		if (m == 0)
			return n;
		else
			return gcd(m, n%m);

	}

}
int FractionType::lcm(int m, int n)
{
	assert(m>0 && n>0);

	int m1 = m / gcd(m, n);  // 최소공배수 통분했을때 나오는 최대공약수 제외한 나머지 값
	int n1 = n / gcd(m, n);

	return gcd(n, m) * m1 * n1;  // 최대 공약수 랑 통분했을 시 나오는 수들의 곱셈

}
