Assignment 3: Compile-time programming with C++ templates ========================================================= As we have seen in lectures, a significant amount of computation can be carried out on constants and constant expressions at compile time. For this assignment, the objective is to implement a collection of functions that can operate on constant structs representing Fractions: A fraction struct is defined in assign3.h as follows: struct Fraction { int num, den; } The desired functions perform the following: simplify(f) returns a Fraction representing f simplified (e.g. -6/-12 ==> 1/2) add(f1,f2) returns the result of adding fractions f1 and f2, in simplified form mult(f1,f2) returns the result of multiplying fractions f1 and f2, in simplified form div(f1,f2) returns the result of dividing fraction f1 by fraction f2, in simplified form sub(f1,f2) returns the result of subtractiong fraction f2 from fraction f2, in simplified form negate(f) returns the result of negating the numerator of f, in simplified form invert(f) returns the result of inverting fraction f, in simplified form Two supporting functions are needed: gcd(a,b) computes the greatest common divisor of integers a and b lcm(a,b) computes the least common multiple of integers a and b Each of the functions will need to return their result as a constexpr, and should also include appropriate checks to prevent floating point exceptions in the event of divide-by-zero (or mod-by-zero). Sample code for runtime versions of the functions are provided in file runtime_fractions.h, along with a sample program using them in file runtime_example.cpp. Your constexpr function definitions should be added to file assign3.h, and a sample program using them is given in file tester.cpp.