Samuel Jaffe пре 9 година
комит
ac1800bcb7
5 измењених фајлова са 720 додато и 0 уклоњено
  1. 1 0
      .gitignore
  2. 264 0
      matrix.hpp
  3. 106 0
      matrix.t.h
  4. 266 0
      matrix.xcodeproj/project.pbxproj
  5. 83 0
      matrix_helpers.hpp

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+matrix_tc.cpp

+ 264 - 0
matrix.hpp

@@ -0,0 +1,264 @@
+//
+//  matrix.hpp
+//  math
+//
+//  Created by Sam Jaffe on 5/28/16.
+//
+
+#pragma once
+
+#include "math/vector/vector.hpp"
+#include "expect/expect.hpp"
+
+namespace math { namespace matrix {
+  
+  template <typename T>
+  struct is_matrix { static const constexpr bool value = false; };
+  
+  template <typename T, std::size_t R, std::size_t C>
+  class matrix;
+  template <typename T, size_t R, size_t C>
+  struct is_matrix<matrix<T, R, C> > { static const constexpr bool value = true; };
+  
+  namespace concat_strategy {
+    struct {} horizonal;
+    using horizontal_concat_t = decltype(horizonal);
+    struct {} vertical;
+    using vertical_concat_t = decltype(vertical);
+    struct {} diagonal;
+    using diagonal_concat_t = decltype(diagonal);
+  };
+  
+#define MATRIX_DISABLE_IF_MATRIX(_type, t, r, c) \
+typename std::enable_if<!is_matrix<_type>::value, matrix<t, r, c> >::type
+  
+#define MATRIX_FOR_EACH_RANGE(i, i_max, j, j_max) for (size_t i = 0; i < i_max; ++i) for (size_t j = 0; j < j_max; ++j)
+#define MATRIX_FOR_EACH(i, j) MATRIX_FOR_EACH_RANGE(i, R, j, C)
+  
+  template <typename T, std::size_t R, std::size_t C>
+  class matrix {
+  public:
+    using value_type = T;
+    
+    template <typename M>
+    using mul_t = decltype(std::declval<T>()*std::declval<M>());
+    template <typename M>
+    using div_t = decltype(std::declval<T>()/std::declval<M>());
+  public:
+    
+    template <typename S>
+    class row_reference {
+    public:
+      row_reference(S ( & h )[C]) : _handle(h) {}
+      row_reference(row_reference const &) = delete;
+
+      row_reference & operator=(row_reference const & other) {
+        return operator=<S>(other);
+      }
+      template <typename S2>
+      row_reference & operator=(row_reference<S2> const & other) {
+        VECTOR_FOR_EACH_RANGE(i, C) { _handle[i] = other[i]; }
+        return *this;
+      }
+
+      S const & operator[](std::size_t col) const { return _handle[col]; }
+      S & operator[](std::size_t col) { return _handle[col]; }
+      S const & at(std::size_t col) const {
+        expects(col < C, std::out_of_range, "column index out of range");
+        return operator[](col);
+      }
+      S & at(std::size_t col) {
+        expects(col < C, std::out_of_range, "column index out of range");
+        return operator[](col);
+      }
+      
+    private:
+      S ( & _handle )[C];
+    };
+    
+    matrix() = default;
+    matrix(std::array<std::array<T, C>, R> const & init) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] = init[i][j];
+      }
+    }
+    matrix(std::initializer_list<std::array<T, C>> const & init) {
+      expects(init.size() == R, "initializer size mismatch");
+      size_t i = 0;
+      for (auto it = init.begin(), end = init.end(); it != end && i < R; ++it, ++i) {
+        for (size_t j = 0; j < C; ++j) {
+          _data[i][j] = (*it)[j];
+        }
+      }
+    }
+    template <size_t N>
+    matrix(vector::vector<typename std::enable_if<C == 1 && N == R, T>::type, N> const & other) {
+      VECTOR_FOR_EACH(i) {
+        _data[i][0] = other[i];
+      }
+    }
+    matrix(matrix const& other) {
+      *this = other;
+    }
+    matrix(matrix && other) {
+      *this = std::move(other);
+    }
+    matrix & operator=(matrix const& other) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] = other._data[i][j];
+      }
+      return *this;
+    }
+    matrix & operator=(matrix && other) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] = std::move(other._data[i][j]);
+      }
+      return *this;
+    }
+    
+    template <size_t R2, size_t C2>
+    matrix(matrix<T, R2, C2> const & other) {
+      MATRIX_FOR_EACH_RANGE(i, std::min(R, R2), j, std::min(C, C2)) {
+        _data[i][j] = other(i, j);
+      }
+    }
+    
+    matrix<T, C, R> transpose() const {
+      matrix<T, C, R> out;
+      MATRIX_FOR_EACH(i, j) { out(j,i) = _data[i][j]; }
+      return out;
+    }
+    
+    template <size_t C2>
+    matrix<T, R, C + C2> concat(matrix<T, R, C2> const & other, concat_strategy::horizontal_concat_t) const {
+      matrix<T, R, C + C2> accum{*this};
+      MATRIX_FOR_EACH_RANGE(i, R, j, C2) { accum(i, j + C) = other(i, j); }
+      return accum;
+    }
+    
+    template <size_t R2>
+    matrix<T, R + R2, C> concat(matrix<T, R2, C> const & other, concat_strategy::vertical_concat_t) const {
+      matrix<T, R + R2, C> accum{*this};
+      MATRIX_FOR_EACH_RANGE(i, R2, j, C) { accum(i + R, j) = other(i, j); }
+      return accum;
+    }
+    
+    template <size_t R2, size_t C2>
+    matrix<T, R + R2, C + C2> concat(matrix<T, R2, C2> const & other, concat_strategy::diagonal_concat_t) const {
+      matrix<T, R + R2, C + C2> accum{*this};
+      MATRIX_FOR_EACH_RANGE(i, R2, j, C2) { accum(i + R, j + C) = other(i, j); }
+      return accum;
+    }
+    
+    T const & operator()(std::size_t row, std::size_t col) const {
+      return _data[row][col];
+    }
+    T & operator()(std::size_t row, std::size_t col) {
+      return _data[row][col];
+    }
+    row_reference<const T> operator[](std::size_t row) const {
+      return { _data[row] };
+    }
+    row_reference<T> operator[](std::size_t row) {
+      return { _data[row] };
+    }
+    row_reference<const T> at(std::size_t row) const {
+      expects(row >= R, std::out_of_range, "row index out of range");
+      return operator[](row);
+    }
+    row_reference<T> at(std::size_t row) {
+      expects(row >= R, std::out_of_range, "row index out of range");
+      return operator[](row);
+    }
+    value_type const & at(std::size_t row, std::size_t col) const {
+      expects(row < R && col < C, std::out_of_range, "coordinates out of range");
+      return _data[row][col];
+    }
+    value_type & at(std::size_t row, std::size_t col) {
+      expects(row < R && col < C, std::out_of_range, "coordinates out of range");
+      return _data[row][col];
+    }
+    
+    matrix& operator+=(matrix const & other) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] += other[i][j];
+      }
+      return *this;
+    }
+    matrix operator+(matrix const & other) const {
+      return matrix{*this} += other;
+    }
+    matrix& operator-=(matrix const & other) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] -= other[i][j];
+      }
+      return *this;
+    }
+    matrix operator-(matrix const & other) const {
+      return matrix{*this} -= other;
+    }
+    
+    vector::vector<T, C> operator*(vector::vector<T, C> const & vec) const {
+      vector::vector<T, C> rval;
+      MATRIX_FOR_EACH(i, j) {
+        rval[i] += _data[i][j] * vec[j];
+      }
+      return rval;
+    }
+    
+    template <std::size_t C2>
+    matrix<T, R, C2> operator*(matrix<T, C, C2> const & other) const {
+      matrix<T, R, C2> rval;
+      MATRIX_FOR_EACH(i, j) {
+        for (size_t k = 0; k < C2; ++k) {
+          rval[i][k] += _data[i][j] * other[j][k];
+        }
+      }
+      return rval;
+    }
+    
+    matrix<T, R, C>& operator*=(T c) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] *= c;
+      }
+      return *this;
+    }
+    
+    template <typename M>
+    MATRIX_DISABLE_IF_MATRIX(M, mul_t<M>, R, C) operator*(M c) const {
+      return matrix<mul_t<M>, R, C>{*this} *= c;
+    }
+    
+    template <typename M>
+    friend MATRIX_DISABLE_IF_MATRIX(M, mul_t<M>, R, C) operator*(M c, matrix const& matr) {
+      return matrix<mul_t<M>, R, C>{matr} *= c;
+    }
+    
+    template <typename M>
+    matrix<div_t<M>, R, C>& operator/=(M c) {
+      MATRIX_FOR_EACH(i, j) {
+        _data[i][j] /= c;
+      }
+      return *this;
+    }
+    template <typename M>
+    matrix<div_t<M>, R, C> operator/(M c) const {
+      return matrix<mul_t<M>, R, C>{*this} /= c;
+    }
+    
+    bool operator==(matrix const & other) const {
+      MATRIX_FOR_EACH(i, j) {
+        if (_data[i][j] != other._data[i][j]) {
+          return false;
+        }
+      }
+      return true;
+    }
+    bool operator!=(matrix const & other) const {
+      return !operator==(other);
+    }
+  private:
+    value_type _data[R][C] = {value_type()};
+  };
+
+} }

+ 106 - 0
matrix.t.h

@@ -0,0 +1,106 @@
+//
+//  matrix_tc.h
+//  math
+//
+//  Created by Sam Jaffe on 8/18/16.
+//
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+
+#include "matrix.hpp"
+#include "matrix_helpers.hpp"
+
+class matrix_TestSuite : public CxxTest::TestSuite {
+public:
+  using matr2i = math::matrix::matrix<int, 2, 2>;
+  using matr2 = math::matrix::matrix<double, 2, 2>;
+public:
+  void test_matrix_equals() const {
+    using math::matrix::identity;
+    TS_ASSERT_EQUALS((identity<int, 2>()), (matr2i{{1,0},{0,1}}));
+  }
+  
+  void test_matrix_sum() const {
+    auto iden = math::matrix::identity<int, 2>();
+    auto result = matr2i{{2,0},{0,2}};
+    TS_ASSERT_EQUALS(iden + iden, result);
+  }
+  
+  void test_matrix_default_zero() const {
+    auto zero = matr2i{{0,0},{0,0}};
+    TS_ASSERT_EQUALS(matr2i{}, zero);
+  }
+
+  void test_matrix_subtract() const {
+    auto zero = matr2i{{0,0},{0,0}};
+    auto iden = math::matrix::identity<int, 2>();
+    TS_ASSERT_EQUALS(iden-iden, zero);
+  }
+  
+//  void test_matrix_negate() const {
+//    auto iden = math::matrix::identity<int, 2>();
+//    TS_ASSERT_EQUALS(-iden, (matr2i{{-1,0},{0,-1}}));
+//  }
+  
+  void test_matrix_scaling() const {
+    auto iden = math::matrix::identity<double, 2>();
+    TS_ASSERT_EQUALS(2*iden, (matr2{{2.0,0.0},{0.0,2.0}}));
+    TS_ASSERT_EQUALS(iden*2, (matr2{{2.0,0.0},{0.0,2.0}}));
+    TS_ASSERT_EQUALS(iden/2.0, (matr2{{0.5,0.0},{0.0,0.5}}));
+  }
+  
+  void test_matrix_multiplication_same_dim() const {
+    auto A = matr2i{{1,2},{2,3}};
+    auto B = matr2i{{1,0},{1,1}};
+    TS_ASSERT_EQUALS(A*B, (matr2i{{3,2},{5,3}}));
+    TS_ASSERT_EQUALS(B*A, (matr2i{{1,2},{3,5}}));
+  }
+  
+  void test_matrix_multiplication_diff_dim() const {
+    auto A = math::matrix::matrix<int, 3, 2>{{1,0},{0,1},{1,1}};
+    auto B = math::matrix::matrix<int, 2, 3>{{0,1,0},{1,0,1}};
+    TS_ASSERT_EQUALS(A*B, (math::matrix::matrix<int, 3, 3>{{0,1,0},{1,0,1},{1,1,1}}));
+    TS_ASSERT_EQUALS(B*A, (math::matrix::matrix<int, 2, 2>{{0,1},{2,1}}));
+  }
+  
+  void test_matrix_vector_multiplication() const {
+    auto A = matr2i{{1,0},{1,2}};
+    auto x = math::vector::vector<int, 2>{1,2};
+    TS_ASSERT_EQUALS(A*x, (math::vector::vector<int, 2>{1,5}));
+  }
+  
+//  void test_matrix_composition() const {
+//    using namespace math::matrix;
+//    using vec4 = math::vector::vector<double, 4>;
+//    using vec3 = math::vector::vector<double, 3>;
+//    auto rot = rotation<4>(math::degree{90}, rotate::X_AXIS);
+//    auto mov = translation(vec3{2.0, 2.5, 1.5});
+//    auto scl = scalar(vec3{2.0, math::vector::fill});
+//    vec4 epsilon{0.00001, math::vector::fill};
+//    TS_ASSERT_DELTA((mov * scl * rot * vec4{1,2,3,1}),
+//                     (vec4{4.0,-1.5,-4.5,1.0}),
+//                    epsilon);
+//  }
+  
+  void test_matrix_from_vector() const {
+    using vec3 = math::vector::vector<double, 3>;
+    vec3 v = vec3{1,2,3};
+    math::matrix::matrix<double, 3, 1> m{v};
+    TS_ASSERT_EQUALS(m(0,0), v[0]);
+    TS_ASSERT_EQUALS(m(1,0), v[1]);
+    TS_ASSERT_EQUALS(m(2,0), v[2]);
+  }
+  
+  void test_matrix_init_list_except() const {
+    TS_ASSERT_THROWS((math::matrix::matrix<double, 2, 1>{{1.0}}), std::logic_error);
+  }
+  
+  void test_assign_row() const {
+    matr2i A = math::matrix::identity<int, 2>();
+    matr2i const B = 2 * A;
+    A[0] = B[0];
+    TS_ASSERT_EQUALS(A, (matr2i{{2,0},{0,1}}));
+  }
+private:
+};

+ 266 - 0
matrix.xcodeproj/project.pbxproj

@@ -0,0 +1,266 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CD0E428F1D9B39B1002FFED1 /* matrix_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD0E428C1D9B394E002FFED1 /* matrix_tc.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CD0E42771D9B38A9002FFED1 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		CD0E42791D9B38A9002FFED1 /* matrix_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = matrix_tc; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD0E428B1D9B3943002FFED1 /* matrix.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = matrix.t.h; sourceTree = "<group>"; };
+		CD0E428C1D9B394E002FFED1 /* matrix_tc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = matrix_tc.cpp; sourceTree = "<group>"; };
+		CD0E428D1D9B3955002FFED1 /* matrix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = matrix.hpp; sourceTree = "<group>"; };
+		CD0E428E1D9B3955002FFED1 /* matrix_helpers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = matrix_helpers.hpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CD0E42761D9B38A9002FFED1 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CD0E42701D9B38A9002FFED1 = {
+			isa = PBXGroup;
+			children = (
+				CD0E428B1D9B3943002FFED1 /* matrix.t.h */,
+				CD0E428C1D9B394E002FFED1 /* matrix_tc.cpp */,
+				CD0E428D1D9B3955002FFED1 /* matrix.hpp */,
+				CD0E428E1D9B3955002FFED1 /* matrix_helpers.hpp */,
+				CD0E427A1D9B38A9002FFED1 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CD0E427A1D9B38A9002FFED1 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD0E42791D9B38A9002FFED1 /* matrix_tc */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		CD0E42781D9B38A9002FFED1 /* matrix_tc */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CD0E42801D9B38A9002FFED1 /* Build configuration list for PBXNativeTarget "matrix_tc" */;
+			buildPhases = (
+				CD0E42901D9B39BC002FFED1 /* Run Script */,
+				CD0E42751D9B38A9002FFED1 /* Sources */,
+				CD0E42761D9B38A9002FFED1 /* Frameworks */,
+				CD0E42771D9B38A9002FFED1 /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = matrix_tc;
+			productName = matrix;
+			productReference = CD0E42791D9B38A9002FFED1 /* matrix_tc */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CD0E42711D9B38A9002FFED1 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = "Sam Jaffe";
+				TargetAttributes = {
+					CD0E42781D9B38A9002FFED1 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+				};
+			};
+			buildConfigurationList = CD0E42741D9B38A9002FFED1 /* Build configuration list for PBXProject "matrix" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CD0E42701D9B38A9002FFED1;
+			productRefGroup = CD0E427A1D9B38A9002FFED1 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CD0E42781D9B38A9002FFED1 /* matrix_tc */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		CD0E42901D9B39BC002FFED1 /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/matrix.t.h",
+			);
+			name = "Run Script";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/matrix_tc.cpp",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cxxtestgen --error-printer -o matrix_tc.cpp matrix.t.h";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CD0E42751D9B38A9002FFED1 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD0E428F1D9B39B1002FFED1 /* matrix_tc.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CD0E427E1D9B38A9002FFED1 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		CD0E427F1D9B38A9002FFED1 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		CD0E42811D9B38A9002FFED1 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				HEADER_SEARCH_PATHS = "${HOME}/Documents/Programming/Resources/cxxtest-4.4/";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = ../..;
+			};
+			name = Debug;
+		};
+		CD0E42821D9B38A9002FFED1 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				HEADER_SEARCH_PATHS = "${HOME}/Documents/Programming/Resources/cxxtest-4.4/";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = ../..;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CD0E42741D9B38A9002FFED1 /* Build configuration list for PBXProject "matrix" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD0E427E1D9B38A9002FFED1 /* Debug */,
+				CD0E427F1D9B38A9002FFED1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CD0E42801D9B38A9002FFED1 /* Build configuration list for PBXNativeTarget "matrix_tc" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD0E42811D9B38A9002FFED1 /* Debug */,
+				CD0E42821D9B38A9002FFED1 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CD0E42711D9B38A9002FFED1 /* Project object */;
+}

+ 83 - 0
matrix_helpers.hpp

@@ -0,0 +1,83 @@
+//
+//  matrix_helpers.hpp
+//  math
+//
+//  Created by Sam Jaffe on 8/20/16.
+//
+
+#pragma once
+
+#include "matrix.hpp"
+
+namespace math { namespace matrix {
+  
+  template <typename T, std::size_t N>
+  matrix<T, N, N> identity() {
+    matrix<T, N, N> rval;
+    VECTOR_FOR_EACH(i) { rval.at(i,i) = 1; }
+    return rval;
+  }
+  
+  template <typename T, std::size_t N>
+  matrix<T, N, N> diagonal(vector::vector<T, N> const & vec) {
+    matrix<T, N, N> rval = identity<T, N>();
+    VECTOR_FOR_EACH(i) { rval.at(i,i) = vec[i]; }
+    return rval;
+  }
+  
+  template <typename T, std::size_t N>
+  matrix<T, N+1, N+1> translation(vector::vector<T, N> const & vec) {
+    matrix<T, N+1, N+1> rval = identity<T, N+1>();
+    VECTOR_FOR_EACH(i) { rval.at(i,N) = vec[i]; }
+    return rval;
+  }
+  
+  template <typename T, std::size_t N>
+  matrix<T, N+1, N+1> scalar(vector::vector<T, N> const & vec) {
+    matrix<T, N+1, N+1> rval = identity<T, N+1>();
+    VECTOR_FOR_EACH(i) { rval.at(i,i) = vec[i]; }
+    return rval;
+  }
+  
+  template <size_t N>
+  struct rotation_t {
+    constexpr rotation_t(size_t f, size_t s)
+    : first(f), second(s) {}
+    size_t first, second;
+  };
+  
+  namespace rotate {
+    constexpr rotation_t<3> const X_AXIS{1,2};
+    constexpr rotation_t<3> const Y_AXIS{2,0};
+    constexpr rotation_t<3> const Z_AXIS{0,1};
+    
+    constexpr rotation_t<3> const ROLL{1,2};
+    constexpr rotation_t<3> const PITCH{2,0};
+    constexpr rotation_t<3> const YAW{0,1};
+    
+    constexpr rotation_t<2> const ROT_2D{0,1};
+  }
+  
+  template <typename T, size_t D>
+  auto rotation(T theta, rotation_t<D> r) -> matrix<decltype(sin(theta)), D, D> {
+    static_assert(D >= 2, "cannot rotate with 1D matrix");
+    using G = decltype(sin(theta));
+    using std::sin;
+    using std::cos;
+    matrix<G, D, D> rval = identity<G, D>();
+    G const vsin = sin(theta);
+    G const vcos = cos(theta);
+    rval.at(r.first, r.first) = vcos;
+    rval.at(r.second, r.second) = vcos;
+    rval.at(r.first, r.second) = -vsin;
+    rval.at(r.second, r.first) = vsin;
+    return rval;
+  }
+  
+  template <size_t D, typename T, size_t RD>
+  auto rotation(T theta, rotation_t<RD> r) -> matrix<decltype(sin(theta)), D, D> {
+    using G = decltype(sin(theta));
+    return rotation(theta, r).concat(identity<G, D - RD>(), concat_strategy::diagonal);
+  }
+  
+} }