namespace h3d {
/**
* N次元ベクトルクラス。
* @param N ベクトルの次元数。
*/
template <unsigned int N>
class Vector {
public:
/** 成分の配列。N個。 */
double f[N];
/**
* すべての成分をゼロクリアする。
*/
void clear();
/**
* 二つのベクトルの内積を計算する。
* @param vec 右側のベクトル。
* @return 計算した内積。
*/
double inpro(const Vector& rhs) const;
/**
* 二つのベクトルが不等価かどうかを判定する。
* @param vec 別のベクトル。
* @return 不等価ならtrue, 不等価ではないならfalse。
*/
bool operator !=(const Vector& vec) const;
/**
* スカラー値で乗算する。
* @param sca 乗数となるスカラー値。
* @return 乗算したベクトル。
*/
Vector operator *(const double& sca) const;
/**
* スカラー値で乗算して、代入する。
* @param sca 乗数となるスカラー値。
* @return 代入したベクトル。
*/
Vector& operator *=(const double& sca);
/**
* 二つのベクトルを加算する。
* @param rhs 右側のベクトル。
* @return 加算したベクトル。
*/
Vector operator +(const Vector& rhs) const;
/**
* ベクトルを加算して、代入する。
* @param rhs 右側のベクトル。
* @return 代入したベクトル。
*/
Vector& operator +=(const Vector& rhs);
/**
* すべての成分の符号を反転する。
* @return 符号を反転したベクトル。
*/
Vector operator -() const;
/**
* 二つのベクトルを減算する。
* @param rhs 右側のベクトル。
* @return 減算したベクトル。
*/
Vector operator -(const Vector& rhs) const;
/**
* ベクトルを減算して、代入する。
* @param rhs 右側のベクトル。
* @return 代入したベクトル。
*/
Vector& operator -=(const Vector& rhs);
/**
* スカラー値で除算する。
* @param sca 除数となるスカラー値。
* @return 除算したベクトル。
*/
Vector operator /(const double& sca) const;
/**
* スカラー値で除算して、代入する。
* @param sca 除数となるスカラー値。
* @return 代入したベクトル。
*/
Vector& operator /=(const double& sca);
/**
* 二つのベクトルが等価かどうかを判定する。
* @param vec 別のベクトル。
* @return 等価ならtrue, 等価ではないならfalse。
*/
bool operator ==(const Vector& vec) const;
/**
* 基底を直交化する。
* @param vecs 直交化する基底。
*/
static void ortho(Vector<N>* vecs);
/**
* 二つのベクトルの外積を計算する。
* @param vec 右側のベクトル。
* @return 計算した外積。
*/
Vector outpro(const Vector& rhs) const;
/**
* 別のベクトルに対する垂直成分を計算する。
* @param vec このベクトルに対する垂直成分を計算する。
* @return 計算した垂直成分。
*/
Vector perp(const Vector& vec) const;
/**
* 別のベクトルに射影する。
* @param vec このベクトル上に射影する。
* @return 射影したベクトル。
*/
Vector project(const Vector& vec) const;
};
/**
* 二つの3次元ベクトルの外積を計算する。3次元ベクトル専用。
* @param rhs 右側のベクトル。
* @return 計算した外積。
*/
template <>
Vector<3> Vector<3>::outpro(const Vector<3>& rhs) const;
};
namespace h3d {
/**
* 二つの数が近いかどうかを判定する。
* @param x 一つ目の数。
* @param y 二つ目の数。
* @return 近いならtrue, 遠いならfalse。
*/
inline bool near(const double& x, const double& y);
};
#include <iostream>
#include <string>
namespace h3d {
using std::cout;
using std::endl;
using std::string;
#define ASSERT(pred) assert(#pred, (pred));
#define DEBUG_PRINT(val) cout << #val << "=" << (val) << endl;
/**
* アサーションを実行する。
* 成功なら標準出力に結果を出力する。
* @param pred_str 判定する式を表した文字列。
* @param pred_res 判定結果。trueなら成功,falseなら失敗と判定する。
* @throw string 失敗ならメッセージをスローする。
*/
inline void assert(const string& pred_str, const bool& pred_res) throw(string);
};
namespace h3d {
class Test { public: virtual void run() const = 0; };
};
namespace h3d {
class Test1 : public Test {
public:
virtual void run() const override;
};
};
#include <memory>
#include <vector>
namespace h3d {
using std::shared_ptr;
using std::vector;
class TestSet {
public:
TestSet();
void run() const;
protected:
vector<shared_ptr<Test>> tests;
};
};
////////////////////////////////////////////////////////////////////////////////
#include <cmath>
namespace h3d {
using std::sqrt;
void Test1::run() const {
Vector<3> p{2.0, 2.0, 1.0}, q{1.0, -2.0, 0.0};
ASSERT(near(p.f[0], 2.0))
ASSERT(near(p.f[1], 2.0))
ASSERT(near(p.f[2], 1.0))
ASSERT(near(q.f[0], 1.0))
ASSERT(near(q.f[1], -2.0))
ASSERT(near(q.f[2], 0.0))
ASSERT(p == (Vector<3>{2.0, 2.0, 1.0}))
ASSERT(q == (Vector<3>{1.0, -2.0, 0.0}))
ASSERT(p != (Vector<3>{1.0, -2.0, 0.0}))
ASSERT(q != (Vector<3>{2.0, 2.0, 1.0}))
ASSERT(-p == (Vector<3>{-2.0, -2.0, -1.0}))
ASSERT(-q == (Vector<3>{-1.0, 2.0, 0.0}))
ASSERT(p * 2.0 == (Vector<3>{4.0, 4.0, 2.0}))
ASSERT(q * -3.0 == (Vector<3>{-3.0, 6.0, 0.0}))
ASSERT(p / 2.0 == (Vector<3>{1.0, 1.0, 0.5}))
ASSERT(q / -3.0 == (Vector<3>{1.0 / -3.0, -2.0 / -3.0, 0.0}))
ASSERT(p + q == (Vector<3>{3.0, 0.0, 1.0}))
ASSERT(q + p == (Vector<3>{3.0, 0.0, 1.0}))
ASSERT(p - q == (Vector<3>{1.0, 4.0, 1.0}))
ASSERT(q - p == (Vector<3>{-1.0, -4.0, -1.0}))
Vector<3> r = p;
ASSERT((r *= 3.0) == (Vector<3>{6.0, 6.0, 3.0}))
ASSERT((r /= -2.0) == (Vector<3>{-3.0, -3.0, -1.5}))
ASSERT((r += (Vector<3>{5.0, -1.0, 0.0})) == (Vector<3>{2.0, -4.0, -1.5}))
ASSERT((r -= (Vector<3>{0.0, -2.0, 4.0})) == (Vector<3>{2.0, -2.0, -5.5}))
ASSERT(near(p.inpro(q), -2.0))
ASSERT(p.outpro(q) == (Vector<3>{2.0, 1.0, -6.0}))
ASSERT(p.project(q) == (Vector<3>{-0.4, 0.8, 0.0}))
ASSERT(q.project(p) == (Vector<3>{-4.0 / 9.0, -4.0 / 9.0, -2.0 / 9.0}))
ASSERT(p.perp(q) == (Vector<3>{2.4, 1.2, 1.0}))
ASSERT(q.perp(p) == (Vector<3>{1.0 + 4.0 / 9.0, -2.0 + 4.0 / 9.0, 2.0 / 9.0}))
Vector<3> e[3] = {
{sqrt(2.0) / 2.0, sqrt(2.0) / 2.0, 0.0},
{-1.0, 1.0, -1.0},
{0.0, -2.0, -2.0},
};
Vector<3>::ortho(e);
ASSERT(e[0] == (Vector<3>{sqrt(2.0) / 2.0, sqrt(2.0) / 2.0, 0.0}))
ASSERT(e[1] == (Vector<3>{-1.0, 1.0, -1.0}))
ASSERT(e[2] == (Vector<3>{1.0, -1.0, -2.0}))
}
};
#include <memory>
namespace h3d {
using std::shared_ptr;
TestSet::TestSet() {
this->tests.push_back(shared_ptr<Test>(new Test1));
}
void TestSet::run() const { for (auto iter : this->tests) iter->run(); }
};
#include <utility>
namespace h3d {
using std::move;
template <unsigned int N>
void Vector<N>::clear() {
for (auto i = 0; i < N; i++) this->f[i] = 0.0;
}
template <unsigned int N>
double Vector<N>::inpro(const Vector& rhs) const {
double res = 0;
// 対応する成分同士の積を求めて、合計する。
for (auto i = 0; i < N; i++) res += this->f[i] * rhs.f[i];
return res;
}
template <unsigned int N>
bool Vector<N>::operator !=(const Vector& vec) const {
return !operator ==(vec);
}
template <unsigned int N>
Vector<N> Vector<N>::operator *(const double& sca) const {
Vector<N> res;
// それぞれの成分にスカラー値を掛ける。
for (auto i = 0; i < N; i++) res.f[i] = this->f[i] * sca;
return move(res);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator *=(const double& sca) {
// それぞれの成分にスカラー値を掛ける。
for (auto i = 0; i < N; i++) this->f[i] *= sca;
return *this;
}
template <unsigned int N>
Vector<N> Vector<N>::operator +(const Vector& rhs) const {
Vector<N> res;
// 対応する成分同士で加算する。
for (auto i = 0; i < N; i++) res.f[i] = this->f[i] + rhs.f[i];
return move(res);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator +=(const Vector& rhs) {
// 対応する成分同士で加算する。
for (auto i = 0; i < N; i++) this->f[i] += rhs.f[i];
return *this;
}
template <unsigned int N>
Vector<N> Vector<N>::operator -() const {
Vector<N> res;
// すべての成分の符号を反転する。
for (auto i = 0; i < N; i++) res.f[i] = -this->f[i];
return move(res);
}
template <unsigned int N>
Vector<N> Vector<N>::operator -(const Vector& rhs) const {
Vector<N> res;
// 対応する成分同士で減算する。
for (auto i = 0; i < N; i++) res.f[i] = this->f[i] - rhs.f[i];
return move(res);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator -=(const Vector& rhs) {
// 対応する成分同士で減算する。
for (auto i = 0; i < N; i++) this->f[i] -= rhs.f[i];
return *this;
}
template <unsigned int N>
Vector<N> Vector<N>::operator /(const double& sca) const {
Vector<N> res;
// それぞれの成分をスカラー値で割る。
for (auto i = 0; i < N; i++) res.f[i] = this->f[i] / sca;
return move(res);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator /=(const double& sca) {
// それぞれの成分をスカラー値で割る。
for (auto i = 0; i < N; i++) this->f[i] /= sca;
return *this;
}
template <unsigned int N>
bool Vector<N>::operator ==(const Vector& vec) const {
bool res = true;
if (&vec != this) {
// すべての対応する成分が近ければ等価と判定する。
for (auto i = 0; i < N; i++) {
if (!near(this->f[i], vec.f[i])) {
res = false;
break;
}
}
}
return res;
}
template <unsigned int N>
void Vector<N>::ortho(Vector<N>* vecs) {
// グラム・シュミットの直交化法。
for (auto i = 2; i < N; i++) {
Vector<N> sum;
sum.clear();
for (auto j = 0; j < i; j++) sum += vecs[i].project(vecs[j]);
vecs[i] -= sum;
}
}
template <>
Vector<3> Vector<3>::outpro(const Vector<3>& rhs) const {
// それぞれの成分について、対応する列を除いた2×2の行列式を計算する。
// P×Q = < Py・Qz - Pz・Qy, Pz・Qx - Px・Qz, Px・Qy - Py・Qx >
return move(Vector<3>{
this->f[1] * rhs.f[2] - this->f[2] * rhs.f[1],
this->f[2] * rhs.f[0] - this->f[0] * rhs.f[2],
this->f[0] * rhs.f[1] - this->f[1] * rhs.f[0],
});
}
template <unsigned int N>
Vector<N> Vector<N>::perp(const Vector& vec) const {
// 元のベクトルから、投影したベクトルを引けば、垂直成分が残る。
return move(*this - project(vec));
}
template <unsigned int N>
Vector<N> Vector<N>::project(const Vector& vec) const {
// PをQ上に射影した結果は次式で与えられる。
// P・Q
// -----・Q
// |Q|^2
return move(vec * inpro(vec) / vec.inpro(vec));
}
};
namespace h3d {
inline bool near(const double& x, const double& y) {
static const double THRESHOLD = 0.0000001;
double dif = x - y;
return dif > -THRESHOLD && dif < THRESHOLD;
}
};
#include <iostream>
#include <string>
namespace h3d {
using std::cout;
using std::endl;
using std::string;
inline void assert(const string& pred_str, const bool& pred_res) throw(string) {
if (pred_res) cout << "アサート成功: " << pred_str << endl;
else throw "アサート失敗: " + pred_str;
}
};
#include <iostream>
#include <string>
int main() {
using std::cerr;
using std::endl;
using std::string;
try {
h3d::TestSet().run();
}
catch (const string& msg) {
cerr << msg << endl;
return 1;
}
return 0;
}