#+TITLE: Task 4: Vector and matrix operations
#+AUTHOR: JirR02

*Note:* All occurrences of the word /vector/ in this assignment without an explicit =std::= prefix mean vector in the mathematical sense.

*Task:*

Write a program that implements the multiplication of integer vectors and matrices using =std::vectors=. The program must support the following three possible operations:

1. Dot product of two vectors (also called scalar product or inner product)
1. Matrix-vector product (application of a matrix to a column vector)
1. Matrix product

Implement the following program structure:

1. Query for the two operands from the input: the left operand first, then the right operand.
1. If none of the three operators described above can be applied, output error then end the program.
1. If the two operands have mismatched sizes (e.g. the dot product of a 2D vector and a 3D vector), output error then end the program.
1. Output the result of the operation then end the program.

Note: We already provide a partial implementation of this program in =main.cpp=.

*Recommendations:*

1. We provide functions to read and write matrices, vectors, and scalars in io.h. Use them!
1. Use one dimensional =std::vectors= to represent vectors and two dimensional =std::vectors= to represent matrices.
1. Use functions to implement the three different operations. Use references with proper constness to access =std::vectors= from functions.
1. You may assume that all input vector/matrices have non-zero dimensions, and that input will not contain values big enough to cause overflow.

*Format:*

The format to represent scalars/vectors/matrices in input/output is the following:

1. A character among s, v and m to denote whether the data represent a scalar, a vector or a matrix (scalar cannot occur in input for this task).
2. A sequence of integers giving the dimensions of the value:
   a. Nothing for scalar
   b. A single integer giving the length for vectors
   c. Two integers giving respectively the number of rows and the number of columns for matrices
3. A new line
4. A sequence of integers giving the content of the value:
   a. The scalar itself for scalars
   b. The content of the vector in order for vectors, on a single line
   c. The content of the matrix in row-major order for matrices (the content of the first row in column order first, then the second row, etc). Each row should be placed on a distinct line.

Note: Check =io.h= and =io.cpp=. The functions provided there can handle a large part of the input/output format.

* Examples

*Dot product of two 3D vectors*

#+begin_src shell
Left operand
v 3
1 2 3
Right operand
v 3
3 2 1
s
10
#+end_src

*Multiply 3x2 matrix by 2D vector*

#+begin_src shell
Left operand
m 3 2
1 2
3 4
5 6
Right operand
v 2
2 1
v 3
4 10 16
#+end_src

*Multiply 2x4 matrix 4x3 matrix*

#+begin_src shell
Left operand
m 2 4
1 2 3 4
5 6 7 8
Right operand
m 4 3
12 11 10
9 8 7
6 5 4
3 2 1
m 2 3
60 50 40
180 154 128
#+end_src

*Multiply 2x3 matrix by 4D vector*

#+begin_src shell
Left operand
m 2 3
1 2 3
4 5 6
Right operand
v 4
4 3 2 1
error
#+end_src

*Note:* The scalar values should be returned only by scalar products. Even if a matrix-vector multiplication or a matrix-matrix multiplication results in a single value, it should be represented by a single-element vector or matrix. Similarly, if the result of a matrix-matrix multiplication looks like a vector, it should still be represented as a matrix, with one dimension equal to 1.

* Solution

#+begin_src cpp
#include <iostream>
// Enable std::vectors
#include <vector>

#include "io.h"

using vec = std::vector<int>;
using mat = std::vector<vec>;

// POST: result of dot product of v1 and v2 is printed to standard output,
// or "error" if dimensions are not compatible

void vector_scalar_product(const vec& v, const int& s) {
  vec res;
  res = v;
  for (unsigned long j = 0; j < v.size(); ++j)
    res[j] = v[j] * s;
  print_vector(res);
}

void matrix_scalar_product(const mat& m, const int& s) {
  mat res;
  res = m;
  for (unsigned long j = 0; j < m.size(); ++j) {
    for (unsigned long i = 0; i < m[0].size(); ++i)
      res[j][i] = m[j][i] * s;
  }
  print_matrix(res);
}

void dot_product(const vec& v1, const vec& v2) {
  int res = 0;
  if (v1.size() == v2.size()) {
    for (unsigned long j = 0; j < v1.size(); ++j)
      res += (v1[j] * v2[j]);
    print_scalar(res);
  } else {
    std::cout << "error";
  }
}

void matrix_vector_product(const mat& m, const vec& v) {
  vec res;
  if (v.size() == m[0].size()) {
    for (unsigned long j = 0; j < m.size(); ++j) {
      int k = 0;
      for (unsigned long i = 0; i < m[0].size(); ++i) {
        k += m[j][i] * v[i];
      }
      res.push_back(k);
    }
    print_vector(res);
  } else {
    std::cout << "error";
  }
}

void matrix_product(const mat& m1, const mat& m2) {
  mat res(m1.size(), vec(m2[0].size()));
  if (m1[0].size() == m2.size()) {
    for (unsigned long i = 0; i < m1.size(); ++i) {
      for (unsigned long j = 0; j < m2.size(); ++j) {
        for (unsigned long k = 0; k < m2[0].size(); ++k) {
          int l = m1[i][j] * m2[j][k];
          res[i][k] += l;
        }
      }
    }
    print_matrix(res);
  }
  else {
    std::cout << "error";
  }
}

int main() {
  std::cout << "Left operand" << std::endl;
  char left_type;
  std::cin >> left_type;

  if (left_type == 'v') {
    // left operand is vector

    // get vector from input
    vec left_operand;
    read_vector(left_operand);

    // get right operand type
    std::cout << "Right operand" << std::endl;
    char right_type;
    std::cin >> right_type;

    if (right_type == 's') {
      // right operand is a scalar => compute scalar product

      // get scalar from input
      int s;
      std::cin >> s;

      // compute vector scalar product
      vector_scalar_product(left_operand, s);
    } else if (right_type == 'v') {
      // right operand is a vector => compute dot product

      // get vector from input
      vec right_operand;
      read_vector(right_operand);

      // compute dot product
      dot_product(left_operand, right_operand);
    } else {
      std::cout << "error";
    }

  } else if (left_type == 'm') {
    // left operand is matrix

    // get matrix from input
    mat left_operand;
    read_matrix(left_operand);

    // get right operand type
    std::cout << "Right operand" << std::endl;
    char right_type;
    std::cin >> right_type;

    if (right_type == 's') {
      // right operand is a scalar => compute scalar product

      // get scalar from input
      int s;
      std::cin >> s;

      // compute matrix scalar product
      matrix_scalar_product(left_operand, s);
    } else if (right_type == 'v') {
      // right operand is a vector => compute dot product

      // get vector from input
      vec right_operand;
      read_vector(right_operand);

      // compute matrix vector product
      matrix_vector_product(left_operand, right_operand);
    } else if (right_type == 'm') {
      // right operand is a matrix => compute matrix vector product

      // get matrix from input
      mat right_operand;
      read_matrix(right_operand);

      // compute matrix product
      matrix_product(left_operand, right_operand);
    } else {
      std::cout << "error";
    }
  } else {
    std::cout << "error";
  }

  return 0;
}
#+end_src

-----

Made by JirR02 in Switzerland 🇨🇭
