LMU ☀️ CMSI 3801
LANGUAGES AND AUTOMATA I
HOMEWORK #5 Due: 2022-11-22

Learning Objectives

With this assignment you will demonstrate:

Readings

Please read:

Please note that many of the exercises you are to complete cover aspects of the language not directly covered during lectures, so you will be researching a bit as you develop proficiency in C++ programming. However, as it is getting late in the semester, you will be given a big chunk of the code in the .h file.

Instructions

You will be adding to the private GitHub repository for this course. After adding and modifying files, your repository will look like this:

  .
  ├── README.md
  ├── javascript/
  │   └── (existing files from previous assignments)
  ├── python/
  │   └── (existing files from previous assignments)
  ├── java/
  │   └── (existing files from previous assignments)
  ├── swift/
  │   └── (existing files from previous assignments)
  └── cpp/
      ├── .gitignore           (You are on your own to figure out what goes here)
      ├── exercises.h          (Given to you below)
      ├── exercises.cpp        (Bulk of your code will go here)
      └── exercises.test.cpp   (The tests, given to you below)

Submit to BrightSpace a link to your repo. Make sure your README has the names of all the students that have worked on the project. Please submit only one solution set for your team. It does not matter under whose GitHub account your work is stored. Make sure both Julian and I are added as contributors to your repo.

You will be graded on your programming style, so make sure you are set up so that your editor or IDE auto-formats your code. Use plugins for your editor that include formatter and perhaps a linter.

Your homework submission will be the state of your repository on the main branch, at the due datetime, which is 11:59 p.m. on the due date in the America/Los Angeles time zone.

Project Setup

There’s nothing special here; just drop the source and test files in the same folder as indicated in the repository layout above. Professional C++ programmers set up and test real projects according to certain conventions, but at this point in the class, just focus on learning the language. You can learn the ecosystem on your own.

Your job is to write the implementation (.cpp) file for the given header (.h) file as described below, and to add whatever lines are appropriate to your .gitignore file(s). For unit tests, use this assert-filled program, provided free of charge, because you are special:

exercises.test.cpp
#include <cassert>
#include <sstream>
#include "exercises.h"

int main() {
  valarray<double> a = {3, 3, 1};
  valarray<double> b = {8, 3, -2};
  valarray<double> c = {1, -1, 10, 3};
  assert(dot(a, b) == 31);
  assert(dot(a, c) == 10);
  assert(dot(b, b) == 77);

  assert(stretched_nonzeros(vector<int>()) == vector<int>());
  assert(stretched_nonzeros(vector {0, 0, 0}) == vector<int>());
  assert(stretched_nonzeros(vector {100}) == (vector {100}));
  assert(stretched_nonzeros(vector {0, 0, 3, 5, 0, 2}) == (vector {3, 5, 5, 2, 2, 2}));

  vector<int> scratch;
  powers(2, 64, [&](int power){scratch.push_back(power);});
  assert(scratch == (vector {1, 2, 4, 8, 16, 32, 64}));
  scratch.clear();
  powers(2, 63, [&](int power){scratch.push_back(power);});
  assert(scratch == (vector {1, 2, 4, 8, 16, 32}));
  scratch.clear();
  powers(-3, 300, [&](int power){scratch.push_back(power);});
  assert(scratch == (vector {1, -3, 9, -27, 81, -243}));

  IntStack s;
  assert(s.size() == 0);
  s.push(13);
  s.push(1);
  s.push(8);
  assert(s.size() == 3);
  assert(s.pop() == 8);
  assert(s.size() == 2);
  assert(s.pop() == 1);
  assert(s.pop() == 13);
  assert(s.size() == 0);
  try {
    s.pop();
    assert(false);
  } catch (logic_error e) {
    assert(true);
  }

  assert(say("A")() == "A");
  assert(say("A")("B")() == "A B");
  assert(say("🐤🦇")("$🦊👏🏽")("!")() == "🐤🦇 $🦊👏🏽 !");
  auto greet = say("Hello")("there");
  assert(greet("nice")("person")() == "Hello there nice person");
  assert(greet("C++")() == "Hello there C++");

  vector<pair<list<string>, vector<pair<string, int>>>> fixture = {
    {{}, {}},
    {{"one"}, {{"one", 1}}},
    {{"a", "b", "b", "b"}, {{"b", 3}, {"a", 1}}}};
  for (auto [words, counts] : fixture) {
    assert(sorted_word_counts(words) == counts);
  }

  Quaternion q(3.5, 2.25, -100, -1.25);
  assert(q.a == 3.5);
  assert(q.b == 2.25);
  assert(q.c == -100.0);
  assert(q.d == -1.25);
  Quaternion q1 = Quaternion(1, 3, 5, 2);
  Quaternion q2 = Quaternion(-2, 2, 8, -1);
  Quaternion q3 = Quaternion(-1, 5, 13, 1);
  Quaternion q4 = Quaternion(-46, -25, 5, 9);
  assert(q1 + q2 == q3);
  assert(q3 - q2 == q1);
  assert(q1 * q2 == q4);
  assert(Quaternion::I * Quaternion::J == Quaternion::K);
  array<double, 4> zeros = { 0, 0, 0, 0 };
  assert(Quaternion::ZERO.coefficients() == zeros);
  array<double, 4> k_coefficients = { 0, 0, 0, 1 };
  assert(Quaternion::K.coefficients() == k_coefficients);
  array<double, 4> arbitrary_coefficients = { 2.0, 1.5, 10.0, -8.0 };
  assert(Quaternion(2, 1.5, 10, -8).coefficients() == arbitrary_coefficients);
  ostringstream stream;
  stream << Quaternion::ZERO;
  assert(stream.str() == "0+0i+0j+0k");
  stream = ostringstream();
  stream << Quaternion(0, -1,  0, 2.25);
  assert(stream.str() == "0-1i+0j+2.25k");
  stream = ostringstream();
  stream << Quaternion::ZERO - Quaternion::K;
  assert(stream.str() == "0+0i+0j-1k");
  stream = ostringstream();
  stream << Quaternion(-20, -1.75, 13, -2.25);
  assert(stream.str() == "-20-1.75i+13j-2.25k");

  cout << "All tests passed\n";
}

You can test with:

$ g++ -std=c++2a -o runcpptests exercises.cpp exercises.test.cpp && ./runcpptests

You may use Replit.

The Module You Are To Write

In the file exercises.cpp write the following functions and classes. For details on how everything should work, consult the unit tests.

  1. A function that returns the dot product of two valarrays of doubles.
  2. A function that accepts a vector of integers and returns a new vector formed as follows: starting with the input vector, remove all zeros then repeat the (1-based) i-th element (of the filtered vector) i times.
  3. A function that generates powers of a given (integer) base, starting at the 0th power (namely, 1) through a given limit, consuming each with a function. Note that “through” here means “including the limit value.”
  4. A class for stacks-of-ints, built using a singly-linked implementation using smart pointers. Stacks must not be copyable.
  5. The world-famous “say” function. As an implementation restriction: you must build an object with an overloaded function call operator.
  6. A function that, given a list of strings, produces a vector of pairs containing words and their counts, in order of descending frequency.
  7. A struct for quaternions, that works similar to what you’ve seen in all previous assignments. You will need to implement an overloading of the << operator.

The .h file you must use (verbatim) is:

exercises.h
#include <iostream>
#include <list>
#include <string>
#include <vector>
#include <array>
#include <valarray>
#include <iostream>

using namespace std;

double dot(valarray<double> a, valarray<double> b);

vector<int> stretched_nonzeros(vector<int> v);

void powers(int base, int limit, function<void(int)> consumer);

class IntStack {
  struct Node {
    int value;
    shared_ptr<Node> next;
  };
  shared_ptr<Node> top;
public:
  IntStack() {}
  IntStack(const IntStack&) = delete;
  IntStack& operator=(const IntStack&) = delete;
  int size();
  void push(int item);
  int pop();
};

struct Sayer {
  string words;
  string operator()();
  Sayer operator()(string word);
};
extern Sayer say;

vector<pair<string, int>> sorted_word_counts(list<string> words);

struct Quaternion {
  const double a;
  const double b;
  const double c;
  const double d;
  Quaternion(double a, double b, double c, double d);
  Quaternion& operator=(const Quaternion&) = delete;
  array<double, 4> coefficients();
  Quaternion operator+(const Quaternion& other);
  Quaternion operator-(const Quaternion& other);
  Quaternion operator*(const Quaternion& other);
  bool operator==(const Quaternion& other) const;
  static Quaternion ZERO;
  static Quaternion I;
  static Quaternion J;
  static Quaternion K;
  friend ostream& operator<<(ostream& o, Quaternion q);
};