From 9c226a14ef7ab2fbed8e8447c83a7c7fabb11c4c Mon Sep 17 00:00:00 2001
From: Cody Michael Jones <46515483+cm-jones@users.noreply.github.com>
Date: Wed, 26 Jun 2024 19:26:43 -0500
Subject: [PATCH] Implement vector class
---
src/vector.cpp | 153 ++++++++++++++++++-------------------------------
1 file changed, 55 insertions(+), 98 deletions(-)
diff --git a/src/vector.cpp b/src/vector.cpp
index 203cca3..025ac6f 100644
--- a/src/vector.cpp
+++ b/src/vector.cpp
@@ -1,31 +1,15 @@
-/*
- * This file is part of libdsc.
- *
- * libdsc is free software: you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- *
- * libdsc is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * libdsc. If not, see .
- */
-
-#include
+// SPDX-License-Identifier: GPL-3.0-or-later
-#include
-#include
+#include "vector.h"
#include
+#include
#include
+#include
namespace cramer {
template
-Vector::Vector() {}
+Vector::Vector() : data() {}
template
Vector::Vector(size_t size) : data(size) {}
@@ -54,10 +38,8 @@ const T& Vector::operator[](size_t index) const {
template
Vector Vector::operator+(const Vector& other) const {
if (size() != other.size()) {
- throw std::invalid_argument(
- "Vectors must have the same size for addition.");
+ throw std::invalid_argument("Vectors must have the same size for addition");
}
-
Vector result(size());
for (size_t i = 0; i < size(); ++i) {
result[i] = data[i] + other[i];
@@ -68,10 +50,8 @@ Vector Vector::operator+(const Vector& other) const {
template
Vector& Vector::operator+=(const Vector& other) {
if (size() != other.size()) {
- throw std::invalid_argument(
- "Vectors must have the same size for addition.");
+ throw std::invalid_argument("Vectors must have the same size for addition");
}
-
for (size_t i = 0; i < size(); ++i) {
data[i] += other[i];
}
@@ -81,10 +61,8 @@ Vector& Vector::operator+=(const Vector& other) {
template
Vector Vector::operator-(const Vector& other) const {
if (size() != other.size()) {
- throw std::invalid_argument(
- "Vectors must have the same size for subtraction.");
+ throw std::invalid_argument("Vectors must have the same size for subtraction");
}
-
Vector result(size());
for (size_t i = 0; i < size(); ++i) {
result[i] = data[i] - other[i];
@@ -95,10 +73,8 @@ Vector Vector::operator-(const Vector& other) const {
template
Vector& Vector::operator-=(const Vector& other) {
if (size() != other.size()) {
- throw std::invalid_argument(
- "Vectors must have the same size for subtraction.");
+ throw std::invalid_argument("Vectors must have the same size for subtraction");
}
-
for (size_t i = 0; i < size(); ++i) {
data[i] -= other[i];
}
@@ -125,37 +101,30 @@ Vector& Vector::operator*=(const T& scalar) {
template
T Vector::dot(const Vector& other) const {
if (size() != other.size()) {
- throw std::invalid_argument(
- "Vectors must have the same size for dot product.");
+ throw std::invalid_argument("Vectors must have the same size for dot product");
}
-
- T result = 0;
- for (size_t i = 0; i < size(); ++i) {
- result += data[i] * other[i];
- }
- return result;
+ return std::inner_product(data.begin(), data.end(), other.data.begin(), T());
}
template
T Vector::norm() const {
- return std::sqrt(dot(*this));
+ return std::sqrt(std::inner_product(data.begin(), data.end(), data.begin(), T()));
}
template
Vector Vector::normalize() const {
- T n = norm();
- if (n == 0) {
- throw std::runtime_error("Cannot normalize a zero vector.");
+ T magnitude = this->norm();
+ if (magnitude == T()) {
+ throw std::runtime_error("Cannot normalize zero vector");
}
- return *this * (1 / n);
+ return *this * (T(1) / magnitude);
}
template
Vector Vector::cross(const Vector& other) const {
if (size() != 3 || other.size() != 3) {
- throw std::invalid_argument("Cross product is only defined for 3D vectors.");
+ throw std::invalid_argument("Cross product is only defined for 3D vectors");
}
-
Vector result(3);
result[0] = data[1] * other[2] - data[2] * other[1];
result[1] = data[2] * other[0] - data[0] * other[2];
@@ -165,20 +134,18 @@ Vector Vector::cross(const Vector& other) const {
template
T Vector::angle(const Vector& other) const {
- T dot_product = dot(other);
- T magnitudes = norm() * other.norm();
-
- if (magnitudes == 0) {
- throw std::runtime_error("Cannot calculate angle with a zero vector.");
+ T magnitude1 = this->norm();
+ T magnitude2 = other.norm();
+ if (magnitude1 == T() || magnitude2 == T()) {
+ throw std::runtime_error("Cannot compute angle with zero vector");
}
-
- return std::acos(dot_product / magnitudes);
+ return std::acos(dot(other) / (magnitude1 * magnitude2));
}
template
Vector Vector::project(const Vector& onto) const {
- T scalar = dot(onto) / onto.dot(onto);
- return onto * scalar;
+ T scaleFactor = dot(onto) / onto.dot(onto);
+ return onto * scaleFactor;
}
template
@@ -188,25 +155,12 @@ Vector Vector::reject(const Vector& from) const {
template
Vector Vector::reflect(const Vector& normal) const {
- return *this - normal * (2 * dot(normal));
-}
-
-template
-Vector Vector::lerp(const Vector& other, T t) const {
- return *this * (1 - t) + other * t;
+ return *this - normal * (T(2) * dot(normal));
}
template
bool Vector::operator==(const Vector& other) const {
- if (size() != other.size()) {
- return false;
- }
- for (size_t i = 0; i < size(); ++i) {
- if (data[i] != other[i]) {
- return false;
- }
- }
- return true;
+ return data == other.data;
}
template
@@ -217,9 +171,8 @@ bool Vector::operator!=(const Vector& other) const {
template
Vector Vector::elementwise_multiply(const Vector& other) const {
if (size() != other.size()) {
- throw std::invalid_argument("Vectors must have the same size for elementwise multiplication.");
+ throw std::invalid_argument("Vectors must have the same size for element-wise multiplication");
}
-
Vector result(size());
for (size_t i = 0; i < size(); ++i) {
result[i] = data[i] * other[i];
@@ -230,13 +183,12 @@ Vector Vector::elementwise_multiply(const Vector& other) const {
template
Vector Vector::elementwise_divide(const Vector& other) const {
if (size() != other.size()) {
- throw std::invalid_argument("Vectors must have the same size for elementwise division.");
+ throw std::invalid_argument("Vectors must have the same size for element-wise division");
}
-
Vector result(size());
for (size_t i = 0; i < size(); ++i) {
- if (other[i] == 0) {
- throw std::runtime_error("Division by zero in elementwise division.");
+ if (other[i] == T()) {
+ throw std::runtime_error("Division by zero in element-wise division");
}
result[i] = data[i] / other[i];
}
@@ -245,7 +197,7 @@ Vector Vector::elementwise_divide(const Vector& other) const {
template
T Vector::sum() const {
- return std::accumulate(data.begin(), data.end(), T(0));
+ return std::accumulate(data.begin(), data.end(), T());
}
template
@@ -256,7 +208,7 @@ T Vector::product() const {
template
T Vector::min() const {
if (size() == 0) {
- throw std::runtime_error("Cannot find minimum of an empty vector.");
+ throw std::runtime_error("Cannot find minimum of empty vector");
}
return *std::min_element(data.begin(), data.end());
}
@@ -264,7 +216,7 @@ T Vector::min() const {
template
T Vector::max() const {
if (size() == 0) {
- throw std::runtime_error("Cannot find maximum of an empty vector.");
+ throw std::runtime_error("Cannot find maximum of empty vector");
}
return *std::max_element(data.begin(), data.end());
}
@@ -272,14 +224,15 @@ T Vector::max() const {
template
Vector Vector::abs() const {
Vector result(size());
- std::transform(data.begin(), data.end(), result.data.begin(), [](const T& x) { return std::abs(x); });
+ std::transform(data.begin(), data.end(), result.data.begin(),
+ [](const T& x) { return std::abs(x); });
return result;
}
template
Vector Vector::pow(T exponent) const {
Vector result(size());
- std::transform(data.begin(), data.end(), result.data.begin(),
+ std::transform(data.begin(), data.end(), result.data.begin(),
[exponent](const T& x) { return std::pow(x, exponent); });
return result;
}
@@ -287,34 +240,38 @@ Vector Vector::pow(T exponent) const {
template
Vector Vector::sqrt() const {
Vector result(size());
- std::transform(data.begin(), data.end(), result.data.begin(),
- [](const T& x) {
- if (x < 0) throw std::runtime_error("Cannot calculate square root of negative number.");
- return std::sqrt(x);
- });
+ for (size_t i = 0; i < size(); ++i) {
+ if (data[i] < T()) {
+ throw std::runtime_error("Cannot compute square root of negative number");
+ }
+ result[i] = std::sqrt(data[i]);
+ }
return result;
}
template
Vector Vector::exp() const {
Vector result(size());
- std::transform(data.begin(), data.end(), result.data.begin(), [](const T& x) { return std::exp(x); });
+ std::transform(data.begin(), data.end(), result.data.begin(),
+ [](const T& x) { return std::exp(x); });
return result;
}
template
Vector Vector::log() const {
Vector result(size());
- std::transform(data.begin(), data.end(), result.data.begin(),
- [](const T& x) {
- if (x <= 0) throw std::runtime_error("Cannot calculate logarithm of non-positive number.");
- return std::log(x);
- });
+ for (size_t i = 0; i < size(); ++i) {
+ if (data[i] <= T()) {
+ throw std::runtime_error("Cannot compute logarithm of non-positive number");
+ }
+ result[i] = std::log(data[i]);
+ }
return result;
}
-} // namespace cramer
+// Explicit instantiation for common types
+template class Vector;
+template class Vector;
+template class Vector;
-// Explicit template instantiations
-template class cramer::Vector;
-template class cramer::Vector;
+} // namespace cramer