From 7f4277381b1c50f1a183702ac24812872e803adb Mon Sep 17 00:00:00 2001
From: Nao Pross <naopross@thearcway.org>
Date: Sun, 20 Jan 2019 23:21:53 +0100
Subject: Initial commit, add mmvec.hpp with example, makefile and .gitignore

---
 .gitignore  |   1 +
 example.cpp |  56 ++++++++++++++
 makefile    |   8 ++
 mmvec.hpp   | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 317 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 example.cpp
 create mode 100644 makefile
 create mode 100644 mmvec.hpp

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..378eac2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+build
diff --git a/example.cpp b/example.cpp
new file mode 100644
index 0000000..e72d65b
--- /dev/null
+++ b/example.cpp
@@ -0,0 +1,56 @@
+#include "mmvec.hpp"
+
+#include <iostream>
+#include <complex>
+
+int main(int argc, char *argv[]) {
+	// N dimensional vectors
+	std::cout << "N dimensional (int) vectors" << std::endl;
+	mm::vec<int, 5> u {3, 2, 1, 0, 1};
+	mm::vec<int, 5> v {1, 2, 3, 4, 5};
+
+	std::cout << "u = " << u << std::endl;
+	std::cout << "v = " << v << std::endl;
+	std::cout << std::endl;
+
+	// basic operations
+	std::cout << "u + v = " << u + v << std::endl;
+	std::cout << "u - v = " << u - v << std::endl;
+	std::cout << "2 * v = " << 2 * v << std::endl;
+	std::cout << "v * 2 = " << v * 2 << std::endl;
+	std::cout << "u * v = " << u * v << std::endl;
+	std::cout << std::endl;
+
+	// three dimensional vectors
+	std::cout << "three dimensional (double) vectors" << std::endl;
+
+	mm::vec3<double> a {1, 2, 3};
+	mm::vec3<double> b {3, 2, 1};
+
+	std::cout << "a = " << a << std::endl;
+	std::cout << "b = " << b << std::endl;
+	std::cout << std::endl;
+
+	std::cout << "a x b        = " << mm::vec3<double>::cross(a, b) << std::endl;
+	std::cout << "zenith(a)    = " << a.zenith() << std::endl;
+	std::cout << "azimuth(a)   = " << a.azimuth() << std::endl;
+	std::cout << "spherical(a) = " << a.spherical() << std::endl;
+	std::cout << std::endl;
+
+	// two dimensional vector
+	std::cout << "two dimensional (complex) vectors" << std::endl;
+
+	mm::vec2<std::complex<float>> j {{1, 2}, {3, -1}};
+	mm::vec2<std::complex<float>> k { 5, {-2, 1}};
+
+	std::cout << "j = " << j << std::endl;
+	std::cout << "k = " << k << std::endl;
+	std::cout << std::endl;
+
+	std::cout << "j x k    = " << mm::vec2<std::complex<float>>::cross(j, k) << std::endl;
+	std::cout << "angle(j) = " << j.angle() << std::endl;
+	std::cout << "polar(j) = " << j.polar() << std::endl;
+	std::cout << std::endl;
+
+	return 0;
+}
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..c18edc5
--- /dev/null
+++ b/makefile
@@ -0,0 +1,8 @@
+CPPC    := c++
+CPPARGS := -Wall -Werror -I. -std=c++17 -pedantic
+
+all: build/example
+
+build/%: %.cpp mmvec.hpp
+	mkdir -p build
+	$(CPPC) $(CPPARGS) $< -o $@
diff --git a/mmvec.hpp b/mmvec.hpp
new file mode 100644
index 0000000..5173e85
--- /dev/null
+++ b/mmvec.hpp
@@ -0,0 +1,252 @@
+/* vector.hpp
+ * Part of Mathematical library built (ab)using Modern C++ 17 abstractions.
+ *
+ * This library is not intended to be _performant_, it does not contain
+ * hand written SMID / SSE / AVX optimizations. It is instead an example
+ * of highly abstracted code, where Vectors can contain any data type.
+ *
+ * As a challenge, the vector data structure has been built on a container
+ * of static capacity. But if a dynamic base container is needed, the code
+ * should be easily modifiable to add further abstraction, by templating
+ * the container, and by consequence the allocator.
+ *
+ * Naoki Pross <naopross@thearcway.org>
+ * 2018 ~ 2019
+ */
+#include <iostream>
+
+#include <cassert>
+#include <cmath>
+
+#include <array>
+#include <algorithm>
+#include <numeric>
+#include <complex>
+#include <initializer_list>
+
+namespace mm {
+    // generic implementation
+    template<typename T, std::size_t d>
+    struct basic_vec;
+
+    // usable specializations
+    template<typename T, std::size_t d>
+    struct vec;
+    template<typename T>
+    struct vec3;
+    template<typename T>
+    struct vec2;
+}
+
+template<typename T, std::size_t d>
+struct mm::basic_vec : public std::array<T, d> {
+    using type = T;
+    static constexpr std::size_t dimensions = d;
+
+    // TODO: template away these
+    static constexpr T null_element = static_cast<T>(0);
+    static constexpr T unit_element = static_cast<T>(1);
+    static constexpr T unit_additive_inverse_element = static_cast<T>(-1);
+
+    basic_vec();
+    basic_vec(const std::initializer_list<T> l);
+    template<std::size_t n> basic_vec(const basic_vec<T, n>& other);
+
+    T length() const;
+};
+
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d>::basic_vec() : std::array<T, d>() {
+    this->fill(basic_vec<T, d>::null_element);
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d>::basic_vec(const std::initializer_list<T> l) {
+    // construct with empty values
+    basic_vec();
+
+    // why can't this sh*t be a constexpr with static_assert???
+    assert(l.size() <= d);
+    std::copy(l.begin(), l.end(), this->begin());
+}
+
+template<typename T, std::size_t d>
+template<std::size_t n>
+mm::basic_vec<T, d>::basic_vec(const mm::basic_vec<T, n>& other) {
+    // construct with empty values
+    basic_vec();
+
+    static_assert(
+        d >= n, 
+        "cannot copy higher dimensional vector into a smaller one"
+    );
+
+    std::copy(other.begin(), other.end(), this->begin());
+}
+
+template<typename T, std::size_t d>
+T mm::basic_vec<T, d>::length() const {
+    return std::sqrt(std::accumulate(this->begin(), this->end(),
+        basic_vec<T, d>::null_element,
+        [](const T& init, const T& val) -> T {
+            return init + val * val;
+        }
+    ));
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator+(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+    mm::basic_vec<T, d> out;
+    
+    std::transform(rhs.begin(), rhs.end(), lhs.begin(), out.begin(),
+        [](const T& r, const T& l) -> T {
+            return r + l;
+        }
+    );
+
+    return out;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator*(const mm::basic_vec<T, d>& rhs, const T& lhs) {
+    return lhs * rhs;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator*(const T& rhs, const mm::basic_vec<T, d>& lhs) {
+    mm::basic_vec<T, d> out;
+
+    std::transform(lhs.begin(), lhs.end(), out.begin(), 
+        [rhs](const T& t) -> T {
+            return t * rhs;
+    });
+
+    return out;
+}
+
+template<typename T, std::size_t d>
+mm::basic_vec<T, d> operator-(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+    return rhs + mm::basic_vec<T, d>::unit_additive_inverse_element * lhs;
+}
+
+template<typename T, std::size_t d>
+T operator*(const mm::basic_vec<T, d>& rhs, const mm::basic_vec<T, d>& lhs) {
+    return std::inner_product(rhs.begin(), rhs.end(), lhs.begin(), 0);
+}
+
+template<typename T, std::size_t d>
+std::ostream& operator<<(std::ostream& os, const mm::basic_vec<T, d>& v) {
+    os << "<";
+    std::for_each(v.begin(), v.end() -1, [&](const T& el) {
+        os << el << ", ";
+    });
+    os << v.back() << ">";
+
+    return os;
+}
+
+// actual classes to use in your code
+template<typename T, std::size_t d>
+class mm::vec: public mm::basic_vec<T, d> {
+public:
+    vec(std::initializer_list<T> l) : basic_vec<T, d>(l) {}
+
+    template<std::size_t n>
+    vec(const basic_vec<T, n>& other) : basic_vec<T, d>(other) {}
+};
+
+// three dimensional specialization with a static cross product
+template<typename T>
+class mm::vec3 : public mm::basic_vec<T, 3> {
+public:
+    vec3() : basic_vec<T, 3>() {}
+    vec3(std::initializer_list<T> l) : basic_vec<T, 3>(l) {}
+
+    template<std::size_t n>
+    vec3(const basic_vec<T, n>& other) : basic_vec<T, 3>(other) {}
+
+    T& x() { return this->at(0); }
+    T& y() { return this->at(1); }
+    T& z() { return this->at(2); }
+
+    const T& x() const { return this->at(0); }
+    const T& y() const { return this->at(1); }
+    const T& z() const { return this->at(2); }
+
+    T zenith() const;
+    T azimuth() const;
+    vec3<T> spherical() const;
+
+    static vec3<T> cross(const vec3<T>& rhs, const vec3<T>& lhs);
+};
+
+template<typename T>
+T mm::vec3<T>::zenith() const {
+    return std::acos(this->z() / this->length());
+}
+
+template<typename T>
+T mm::vec3<T>::azimuth() const {
+    return std::atan(this->y() / this->x());
+}
+
+template<typename T>
+mm::vec3<T> mm::vec3<T>::spherical() const {
+    return mm::vec3<T> {
+        this->length(),
+        this->zenith(),
+        this->azimuth(),
+    };
+}
+
+template<typename T>
+mm::vec3<T> mm::vec3<T>::cross(const vec3<T>& rhs, const vec3<T>& lhs) {
+    mm::vec3<T> res;
+
+    res.x() = (rhs.y() * lhs.z()) - (rhs.z() * lhs.y());
+    res.y() = (rhs.z() * lhs.x()) - (rhs.x() * lhs.z());
+    res.z() = (rhs.x() * lhs.y()) - (rhs.y() * lhs.x());
+
+    return res;
+}
+
+// two dimensional specialization with a polar conversion
+template<typename T>
+class mm::vec2: public mm::basic_vec<T, 2> {
+public:
+    vec2() : basic_vec<T, 2>() {}
+    vec2(std::initializer_list<T> l) : basic_vec<T, 2>(l) {}
+
+    template<std::size_t n>
+    vec2(const basic_vec<T, n>& other) : basic_vec<T, 2>(other) {}
+
+    T& x() { return this->at(0); }
+    T& y() { return this->at(1); }
+
+    const T& x() const { return this->at(0); }
+    const T& y() const { return this->at(1); }
+
+    T angle() const;
+    vec2<T> polar() const;
+
+    static vec3<T> cross(const vec2<T>& rhs, const vec2<T>& lhs);
+};
+
+template<typename T>
+T mm::vec2<T>::angle() const {
+    return std::atan(this->y() / this->x());
+}
+
+template<typename T>
+mm::vec2<T> mm::vec2<T>::polar() const {
+    return mm::vec2 {
+        this->length(),
+        this->angle()
+    };
+}
+
+template<typename T>
+mm::vec3<T> mm::vec2<T>::cross(const mm::vec2<T>& rhs, const mm::vec2<T>& lhs) {
+    return mm::vec3<T>::cross(mm::vec3<T>(rhs), mm::vec3<T>(lhs));
+}
-- 
cgit v1.2.1