...

/

Solution: Vector Algebra

Solution: Vector Algebra

Learn about the solution to the vector algebra challenge in this lesson.

Vector algebra using expression templates

Let’s execute the provided solution for the vector algebra challenge and observe the code output. Subsequently, we’ll dissect the code step by step.

Press + to interact
#include <iostream>
#include <vector>
#include <stdexcept>
#include <concepts>
template<typename T, typename C = std::vector<T>>
requires std::is_arithmetic_v<T>
struct vector {
vector() = default;
vector(std::size_t const n) : data_(n) {}
vector(std::initializer_list<T>&& l) : data_(l) {}
vector(C const& other) : data_(other) {}
template<typename U, typename X>
vector(vector<U, X> const& other) : data_(other.size()) {
for (std::size_t i = 0; i < other.size(); ++i)
data_[i] = static_cast<T>(other[i]);
}
template<typename U, typename X>
vector& operator=(vector<U, X> const& other) {
data_.resize(other.size());
for (std::size_t i = 0; i < other.size(); ++i)
data_[i] = static_cast<T>(other[i]);
return *this;
}
std::size_t size() const noexcept {
return data_.size();
}
T operator[](const std::size_t i) const {
return data_[i];
}
T& operator[](const std::size_t i) {
return data_[i];
}
C& data() noexcept { return data_; }
C const& data() const noexcept { return data_; }
private:
C data_;
};
template<typename L, typename R>
struct vector_add {
vector_add(L const& a, R const& b) : lhv(a), rhv(b)
{
if (a.size() != b.size()) {
throw std::runtime_error("The vector size is incompatible for this operation.");
}
}
auto operator[](std::size_t const i) const {
return lhv[i] + rhv[i];
}
std::size_t size() const noexcept {
return lhv.size();
}
private:
L const& lhv;
R const& rhv;
};
template<typename L, typename R>
struct vector_sub {
vector_sub(L const& a, R const& b) : lhv(a), rhv(b)
{
if (a.size() != b.size()) {
throw std::runtime_error("The vector size is incompatible for this operation.");
}
}
auto operator[](std::size_t const i) const {
return lhv[i] - rhv[i];
}
std::size_t size() const noexcept {
return lhv.size();
}
private:
L const& lhv;
R const& rhv;
};
template<typename L, typename R>
struct vector_mul {
vector_mul(L const& a, R const& b) : lhv(a), rhv(b)
{
if (a.size() != b.size()) {
throw std::runtime_error("The vector size is incompatible for this operation.");
}
}
auto operator[](std::size_t const i) const {
return lhv[i] * rhv[i];
}
std::size_t size() const noexcept {
return lhv.size();
}
private:
L const& lhv;
R const& rhv;
};
template<typename S, typename R>
struct vector_scalar_mul {
vector_scalar_mul(S const& s, R const& b) :
scalar(s), rhv(b) {}
auto operator[](std::size_t const i) const {
return scalar * rhv[i];
}
std::size_t size() const noexcept {
return rhv.size();
}
private:
S const& scalar;
R const& rhv;
};
// Calculate the dot product between two vectors
template<typename T, typename L, typename U, typename R>
auto dot(vector<T, L> const& a, vector<U, R> const& b)
{
if (a.size() != b.size()) {
throw std::runtime_error("The vector size is incompatible for this operation.");
}
using result_type = decltype(std::declval<T>() * std::declval<U>());
result_type result = 0;
for (std::size_t i = 0; i < a.size(); ++i)
result += a[i] * static_cast<T>(b[i]);
return result;
}
// Overloaded operator+ for element-wise addition
template<typename T, typename L, typename U, typename R>
auto operator+(vector<T, L> const& a, vector<U, R> const& b) {
using result_type = decltype(std::declval<T>() + std::declval<U>());
return vector<result_type, vector_add<L, R>>(vector_add<L, R>(a.data(), b.data()));
}
// Overloaded operator- for element-wise subtraction
template<typename T, typename L, typename U, typename R>
auto operator-(vector<T, L> const& a, vector<U, R> const& b)
{
using result_type = decltype(std::declval<T>() - std::declval<U>());
return vector<result_type, vector_sub<L, R>>(vector_sub<L, R>(a.data(), b.data()));
}
// Overloaded operator* for element-wise multiplication
template<typename T, typename L, typename U, typename R>
auto operator*(vector<T, L> const& a, vector<U, R> const& b) {
using result_type = decltype(std::declval<T>() + std::declval<U>());
return vector<result_type, vector_mul<L, R>>(vector_mul<L, R>(a.data(), b.data()));
}
// Overloaded operator* for scalar multiplication
template<typename T, typename S, typename E>
auto operator*(S const& a, vector<T, E> const& v) {
using result_type = decltype(std::declval<T>() + std::declval<S>());
return vector<result_type, vector_scalar_mul<S, E>>(vector_scalar_mul<S, E>(a, v.data()));
}
// Overloaded stream operator for printing vector elements
template<typename V>
std::ostream& operator<<(std::ostream& os, const vector<V> v) {
os << "{";
for (std::size_t i = 0; i < v.size(); ++i) {
os << v[i];
if (i < v.size() - 1) {
os << ", ";
}
}
os << "}";
return os;
}
int main() {
vector<int> v1{1, 2, 3, 4};
vector<int> v2{5, 6, 7, 8};
double a{1.5};
try {
// Perform vector operations
vector<double> v3 = v1 + a * v2; // {8.5 11 13.5 16}
vector<int> v4 = v1 * v2 + v1 + v2; // {11 20 31 44}
vector<double> v5 = v1 * v2 - v1 + a * v2; // {11.5 19 28.5 40}
int dot_result = dot(v1, v2); // {70}
// Output the results
std::cout << "v3 = " << v3 << std::endl;
std::cout << "v4 = " << v4 << std::endl;
std::cout << "v5 = " << v5 << std::endl;
std::cout << "dot_result = " << dot_result << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
...