Week 7#

Part A - Cartesian and polar coordinates#

#include <cassert>
#include <cmath>
#include <iostream>

#define PI 3.141593

struct CartesianPoint
{
    double x;
    double y;
};

struct PolarPoint
{
    double r;
    double theta;
};

PolarPoint cart2polar(const CartesianPoint &cart)
{
    double r = std::sqrt(std::pow(cart.x, 2) + std::pow(cart.y, 2));
    double theta = std::atan(cart.y / cart.x);
    return {r, theta};
}

CartesianPoint polar2cart(const PolarPoint &polar)
{
    double x = polar.r * std::cos(polar.theta);
    double y = polar.r * std::sin(polar.theta);
    return {x, y};
}

void test_conversion()
{
    CartesianPoint c_point{1, 1};
    PolarPoint p_point{1, PI / 2};

    CartesianPoint actual_c = polar2cart(p_point);
    CartesianPoint expected_c{0, 1};

    double tol = 1e-6;
    assert(std::abs(actual_c.x - expected_c.x) < tol);
    assert(std::abs(actual_c.y - expected_c.y) < tol);

    PolarPoint actual_p = cart2polar(c_point);
    PolarPoint expected_p{std::sqrt(2), PI / 4};

    assert(std::abs(actual_p.r - expected_p.r) < tol);
    assert(std::abs(actual_p.theta - expected_p.theta) < tol);
}

PolarPoint scale(const PolarPoint &point, int s)
{
    return {point.r * s, point.theta};
}

CartesianPoint scale(const CartesianPoint &point, int s)
{
    return {point.x * s, point.y * s};
}

void test_scale()
{
    CartesianPoint c_point{3, 3};

    CartesianPoint actual_c = scale(c_point, 2);
    CartesianPoint expected_c{6, 6};

    double tol = 1e-6;
    assert(std::abs(actual_c.x - expected_c.x) < tol);
    assert(std::abs(actual_c.y - expected_c.y) < tol);

    PolarPoint p_point{3, PI / 2};
    PolarPoint actual_p = scale(p_point, 2);
    PolarPoint expected_p{6, p_point.theta};

    assert(std::abs(actual_p.r - expected_p.r) < tol);
    assert(std::abs(actual_p.theta - expected_p.theta) < tol);
}

PolarPoint rotate(const PolarPoint &point, double w)
{
    return {point.r, point.theta + w};
}

CartesianPoint rotate(const CartesianPoint &point, double w)
{
    double x = std::cos(w) * point.x - std::sin(w) * point.y;
    double y = std::sin(w) * point.x + std::cos(w) * point.y;
    return {x, y};
}

void test_rotate()
{
    CartesianPoint c_point{3, 0};
    CartesianPoint actual_c = rotate(c_point, PI / 2);
    CartesianPoint expected_c{0, 3};

    double tol = 1e-6;
    assert(std::abs(actual_c.x - expected_c.x) < tol);
    assert(std::abs(actual_c.y - expected_c.y) < tol);

    PolarPoint p_point{3, PI / 4};
    PolarPoint actual_p = rotate(p_point, PI / 4);
    PolarPoint expected_p{p_point.r, PI / 2};

    assert(std::abs(actual_p.r - expected_p.r) < tol);
    assert(std::abs(actual_p.theta - expected_p.theta) < tol);
}

int main()
{
    test_conversion();
    test_scale();
    test_rotate();

    return 0;
}

Part B - Affine Transformations#

#include <cassert>
#include <cmath>
#include <iostream>

struct CartesianPoint
{
    double x;
    double y;
};

class AffineTransform
{
  private:
    double _a, _b, _c, _d, _e, _f;

  public:
    inline AffineTransform(double a, double b, double c, double d, double e, double f)
        : _a{a}, _b{b}, _c{c}, _d{d}, _e{e}, _f{f}
    {
    }

    inline AffineTransform(double a, double b, double c, double d) : _a{a}, _b{b}, _c{c}, _d{d}, _e{0}, _f{0}
    {
    }

    // Similar to pythons magic method __call__.
    inline CartesianPoint operator()(double x, double y)
    {
        double f_x = _a * x + _b * y + _e;
        double f_y = _c * x + _d * y + _f;
        return {f_x, f_y};
    }
};

void test_affine()
{
    AffineTransform a{1, 2, 3, 4, 5, 6};

    CartesianPoint actual = a(1, 2);
    CartesianPoint expected{10, 17};

    double tol = 1e-6;
    assert(std::abs(actual.x - expected.x) < tol);
    assert(std::abs(actual.y - expected.y) < tol);
}

int main()
{
    test_affine();
    return 0;
}

Part C - A class for polynomials#

#include <cassert>
#include <cmath>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <string>

class Polynomial
{
  private:
    std::map<int, double> _coeffs;

  public:
    inline Polynomial(std::map<int, double> coeffs) : _coeffs{coeffs}
    {
    }

    inline double operator()(double x)
    {
        double sum = 0;

        for (std::pair<int, double> pair : _coeffs)
        {
            sum += pair.second * std::pow(x, pair.first);
        }

        return sum;
    }

    void print()
    {
        std::ostringstream stream;
        // A map is default sorted ascending. We therefore
        // traverse the liste backward, hence rbegin and rend, instead
        // of begin and end. We replace std::map<int, double>::reverse_iterator
        // with auto.
        for (auto pair = _coeffs.rbegin(); pair != _coeffs.rend(); ++pair)
        {
            stream << pair->second << "x^" << pair->first << " + ";
        }
        std::string result = stream.str();

        // Fix up uglyness (not important)
        result = std::regex_replace(result, std::regex{" \\+ -"}, " - ");
        result = std::regex_replace(result, std::regex{"1x"}, "x");
        result = std::regex_replace(result, std::regex{"\\^0"}, "");

        // Remove last ' + '
        result = result.substr(0, result.size() - 3);

        std::cout << result << std::endl;
    }
};

void test_poly()
{
    Polynomial p{{{10, 1}, {5, -5}, {0, 1}}};
    p.print();

    double tol = 1e-6;

    auto expected = 1185;
    auto actual = p(-2);
    assert(std::abs(actual - expected) < tol);

    expected = 1;
    actual = p(0);
    assert(std::abs(actual - expected) < tol);

    expected = 865;
    actual = p(2);
    assert(std::abs(actual - expected) < tol);
}

int main()
{
    test_poly();

    return 0;
}