Avatar of artemkorsakov

artemkorsakov's solution

to Rational Numbers in the C# Track

Published at Feb 22 2019 · 0 comments
Instructions
Test suite
Solution

Note:

This exercise has changed since this solution was written.

A rational number is defined as the quotient of two integers a and b, called the numerator and denominator, respectively, where b != 0.

The absolute value |r| of the rational number r = a/b is equal to |a|/|b|.

The sum of two rational numbers r1 = a1/b1 and r2 = a2/b2 is r1 + r2 = a1/b1 + a2/b2 = (a1 * b2 + a2 * b1) / (b1 * b2).

The difference of two rational numbers r1 = a1/b1 and r2 = a2/b2 is r1 - r2 = a1/b1 - a2/b2 = (a1 * b2 - a2 * b1) / (b1 * b2).

The product (multiplication) of two rational numbers r1 = a1/b1 and r2 = a2/b2 is r1 * r2 = (a1 * a2) / (b1 * b2).

Dividing a rational number r1 = a1/b1 by another r2 = a2/b2 is r1 / r2 = (a1 * b2) / (a2 * b1) if a2 * b1 is not zero.

Exponentiation of a rational number r = a/b to a non-negative integer power n is r^n = (a^n)/(b^n).

Exponentiation of a rational number r = a/b to a negative integer power n is r^n = (b^m)/(a^m), where m = |n|.

Exponentiation of a rational number r = a/b to a real (floating-point) number x is the quotient (a^x)/(b^x), which is a real number.

Exponentiation of a real number x to a rational number r = a/b is x^(a/b) = root(x^a, b), where root(p, q) is the qth root of p.

Implement the following operations:

  • addition, subtraction, multiplication and division of two rational numbers,
  • absolute value, exponentiation of a given rational number to an integer power, exponentiation of a given rational number to a real (floating-point) power, exponentiation of a real number to a rational number.

Your implementation of rational numbers should always be reduced to lowest terms. For example, 4/4 should reduce to 1/1, 30/60 should reduce to 1/2, 12/8 should reduce to 3/2, etc. To reduce a rational number r = a/b, divide a and b by the greatest common divisor (gcd) of a and b. So, for example, gcd(12, 8) = 4, so r = 12/8 can be reduced to (12/4)/(8/4) = 3/2.

Assume that the programming language you are using does not have an implementation of rational numbers.

Hints

This exercise requires you to write an extension method. For more information, see this page.

This exercise also requires you to write operator overloading methods for +, -, * and / operators. For more information, see this page.

Running the tests

To run the tests, run the command dotnet test from within the exercise directory.

Initially, only the first test will be enabled. This is to encourage you to solve the exercise one step at a time. Once you get the first test passing, remove the Skip property from the next test and work on getting that test passing. Once none of the tests are skipped and they are all passing, you can submit your solution using exercism submit RationalNumbers.cs

Further information

For more detailed information about the C# track, including how to get help if you're having trouble, please visit the exercism.io C# language page.

Source

Wikipedia https://en.wikipedia.org/wiki/Rational_number

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

RationalNumbersTest.cs

// This file was auto-generated based on version 1.0.0 of the canonical data.

using Xunit;

public class RationalNumbersTest
{
    [Fact]
    public void Add_two_positive_rational_numbers()
    {
        Assert.Equal(new RationalNumber(7, 6), new RationalNumber(1, 2) + (new RationalNumber(2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Add_a_positive_rational_number_and_a_negative_rational_number()
    {
        Assert.Equal(new RationalNumber(-1, 6), new RationalNumber(1, 2) + (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Add_two_negative_rational_numbers()
    {
        Assert.Equal(new RationalNumber(-7, 6), new RationalNumber(-1, 2) + (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Add_a_rational_number_to_its_additive_inverse()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(1, 2) + (new RationalNumber(-1, 2)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Subtract_two_positive_rational_numbers()
    {
        Assert.Equal(new RationalNumber(-1, 6), new RationalNumber(1, 2) - (new RationalNumber(2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Subtract_a_positive_rational_number_and_a_negative_rational_number()
    {
        Assert.Equal(new RationalNumber(7, 6), new RationalNumber(1, 2) - (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Subtract_two_negative_rational_numbers()
    {
        Assert.Equal(new RationalNumber(1, 6), new RationalNumber(-1, 2) - (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Subtract_a_rational_number_from_itself()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(1, 2) - (new RationalNumber(1, 2)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_two_positive_rational_numbers()
    {
        Assert.Equal(new RationalNumber(1, 3), new RationalNumber(1, 2) * (new RationalNumber(2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_a_negative_rational_number_by_a_positive_rational_number()
    {
        Assert.Equal(new RationalNumber(-1, 3), new RationalNumber(-1, 2) * (new RationalNumber(2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_two_negative_rational_numbers()
    {
        Assert.Equal(new RationalNumber(1, 3), new RationalNumber(-1, 2) * (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_a_rational_number_by_its_reciprocal()
    {
        Assert.Equal(new RationalNumber(1, 1), new RationalNumber(1, 2) * (new RationalNumber(2, 1)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_a_rational_number_by_1()
    {
        Assert.Equal(new RationalNumber(1, 2), new RationalNumber(1, 2) * (new RationalNumber(1, 1)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Multiply_a_rational_number_by_0()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(1, 2) * (new RationalNumber(0, 1)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Divide_two_positive_rational_numbers()
    {
        Assert.Equal(new RationalNumber(3, 4), new RationalNumber(1, 2) / (new RationalNumber(2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Divide_a_positive_rational_number_by_a_negative_rational_number()
    {
        Assert.Equal(new RationalNumber(-3, 4), new RationalNumber(1, 2) / (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Divide_two_negative_rational_numbers()
    {
        Assert.Equal(new RationalNumber(3, 4), new RationalNumber(-1, 2) / (new RationalNumber(-2, 3)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Divide_a_rational_number_by_1()
    {
        Assert.Equal(new RationalNumber(1, 2), new RationalNumber(1, 2) / (new RationalNumber(1, 1)));
    }

    [Fact(Skip = "Remove to run test")]
    public void Absolute_value_of_a_positive_rational_number()
    {
        Assert.Equal(new RationalNumber(1, 2), new RationalNumber(1, 2).Abs());
    }

    [Fact(Skip = "Remove to run test")]
    public void Absolute_value_of_a_negative_rational_number()
    {
        Assert.Equal(new RationalNumber(1, 2), new RationalNumber(-1, 2).Abs());
    }

    [Fact(Skip = "Remove to run test")]
    public void Absolute_value_of_zero()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(0, 1).Abs());
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_positive_rational_number_to_a_positive_integer_power()
    {
        Assert.Equal(new RationalNumber(1, 8), new RationalNumber(1, 2).Exprational(3));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_negative_rational_number_to_a_positive_integer_power()
    {
        Assert.Equal(new RationalNumber(-1, 8), new RationalNumber(-1, 2).Exprational(3));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_zero_to_an_integer_power()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(0, 1).Exprational(5));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_one_to_an_integer_power()
    {
        Assert.Equal(new RationalNumber(1, 1), new RationalNumber(1, 1).Exprational(4));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_positive_rational_number_to_the_power_of_zero()
    {
        Assert.Equal(new RationalNumber(1, 1), new RationalNumber(1, 2).Exprational(0));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_negative_rational_number_to_the_power_of_zero()
    {
        Assert.Equal(new RationalNumber(1, 1), new RationalNumber(-1, 2).Exprational(0));
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_real_number_to_a_positive_rational_number()
    {
        Assert.Equal(16, 8.Expreal(new RationalNumber(4, 3)), precision: 7);
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_real_number_to_a_negative_rational_number()
    {
        Assert.Equal(0.3333333, 9.Expreal(new RationalNumber(-1, 2)), precision: 7);
    }

    [Fact(Skip = "Remove to run test")]
    public void Raise_a_real_number_to_a_zero_rational_number()
    {
        Assert.Equal(1, 2.Expreal(new RationalNumber(0, 1)), precision: 7);
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_a_positive_rational_number_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(1, 2), new RationalNumber(2, 4).Reduce());
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_a_negative_rational_number_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(-2, 3), new RationalNumber(-4, 6).Reduce());
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_a_rational_number_with_a_negative_denominator_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(-1, 3), new RationalNumber(3, -9).Reduce());
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_zero_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(0, 1), new RationalNumber(0, 6).Reduce());
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_an_integer_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(-2, 1), new RationalNumber(-14, 7).Reduce());
    }

    [Fact(Skip = "Remove to run test")]
    public void Reduce_one_to_lowest_terms()
    {
        Assert.Equal(new RationalNumber(1, 1), new RationalNumber(13, 13).Reduce());
    }
}
using System;
using System.Diagnostics;

public static class RealNumberExtension
{
    public static double Expreal(this int realNumber, RationalNumber r)
    {
        return r.Expreal(realNumber);
    }

    public static int GetMaxFactor(int numerator, int denominator)
    {
        if (numerator % denominator == 0)
        {
            return denominator;
        }
        if (denominator % numerator == 0)
        {
            return numerator;
        }

        var factor = 1;
        for (var i = 2; i <= Math.Min(Math.Abs(numerator), Math.Abs(denominator)) / 2; i++)
        {
            if (numerator % i == 0 && denominator % i == 0)
            {
                factor = i;
            }
        }
        return factor;
    }
}

public struct RationalNumber
{
    private int _numerator;
    private int _denominator;

    public RationalNumber(int numerator, int denominator)
    {
        if (denominator == 0)
        {
            throw new ArgumentException("denominator should not be zero");
        }
        if (numerator == 0)
        {
            _numerator = 0;
            _denominator = 1;
        }
        else if (denominator < 0)
        {
            _numerator = -1 * numerator;
            _denominator = -1 * denominator;
        }
        else
        {
            _numerator = numerator;
            _denominator = denominator;
        }
    }

    public RationalNumber Add(RationalNumber r)
    {
        var numerator = _numerator * r._denominator + _denominator * r._numerator;
        var denominator = _denominator * r._denominator;
        return new RationalNumber(numerator, denominator).Reduce();
    }

    public static RationalNumber operator +(RationalNumber r1, RationalNumber r2)
    {
        return r1.Add(r2);
    }

    public RationalNumber Sub(RationalNumber r)
    {
        var numerator = _numerator * r._denominator - _denominator * r._numerator;
        var denominator = _denominator * r._denominator;
        return new RationalNumber(numerator, denominator).Reduce();
    }

    public static RationalNumber operator -(RationalNumber r1, RationalNumber r2)
    {
        return r1.Sub(r2);
    }

    public RationalNumber Mul(RationalNumber r)
    {
        var numerator = _numerator * r._numerator;
        var denominator = _denominator * r._denominator;
        return new RationalNumber(numerator, denominator).Reduce();
    }

    public static RationalNumber operator *(RationalNumber r1, RationalNumber r2)
    {
        return r1.Mul(r2);
    }

    public RationalNumber Div(RationalNumber r)
    {
        var numerator = _numerator * r._denominator;
        var denominator = _denominator * r._numerator;
        return new RationalNumber(numerator, denominator).Reduce();
    }

    public static RationalNumber operator /(RationalNumber r1, RationalNumber r2)
    {
        return r1.Div(r2);
    }

    public RationalNumber Abs()
    {
        return new RationalNumber(Math.Abs(_numerator), Math.Abs(_denominator));
    }

    public RationalNumber Reduce()
    {
        var factor = RealNumberExtension.GetMaxFactor(_numerator, _denominator);
        return new RationalNumber(_numerator / factor, _denominator / factor);
    }

    public RationalNumber Exprational(int power)
    {
        return new RationalNumber((int)Math.Pow(_numerator, power), (int)Math.Pow(_denominator, power));
    }

    public double Expreal(int baseNumber)
    {
        return Math.Pow(Math.Pow(baseNumber, _numerator), 1.0 / _denominator);
    }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?