#include <algorithm>
#include <cmath>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
namespace h3d {
template <unsigned int N> class Matrix;
template <unsigned int N> class Vector;
};
namespace h3d {
using std::ostream;
using std::string;
/**
* N次元正方行列クラス。
* @param N 行列の次元数。
*/
template <unsigned int N>
class Matrix {
public:
/** 成分(Component)の配列。N×N個。 */
double c[N][N];
/** 単位行列。 */
static const Matrix IDENTITY;
/**
* 各成分をゼロクリアする。
*/
void clear();
/**
* 行列式(DETerminant)を計算する。
* @return 計算した行列式。
*/
double det() const;
/**
* 次元を一つ拡張(EXTend)した行列を作る。
* @param mat この行列から最後の行と列の成分をコピーする。
* @return 拡張した行列。
*/
Matrix<N + 1> ext(const Matrix<N + 1>& mat = Matrix<N + 1>::IDENTITY) const;
/**
* 逆行列(INVerse matrix)を計算する。
* @return 計算した逆行列。
* @throw string 行列が正則ではない場合、メッセージをスローする。
*/
Matrix inv() const throw(string);
/**
* 二つの行列が等しくないかどうかを判定する。
* @param rhs 右側の行列。
* @return 等しくないならtrue, 等しいならfalse。
*/
bool operator !=(const Matrix& rhs) const;
/**
* 行列で乗算する。
* @param rhs 乗数となる右側の行列。
* @return 乗算した行列。
*/
Matrix operator *(const Matrix& rhs) const;
/**
* ベクトルとの積を計算する。
* @param rhs 乗数となる右側のベクトル。
* @return 計算したベクトル。
*/
Vector<N> operator *(const Vector<N>& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算した行列。
*/
Matrix operator *(const double& rhs) const;
/**
* 行列で乗算して、代入する。
* @param rhs 乗数となる右側の行列。
* @return 乗算して、代入した行列。
*/
Matrix operator *=(const Matrix& rhs);
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算して、代入した行列。
*/
Matrix operator *=(const double& rhs);
/**
* 行列を加算する。
* @param rhs 加数となる右側の行列。
* @return 加算した行列。
*/
Matrix operator +(const Matrix& rhs) const;
/**
* 行列を加算して、代入する。
* @param rhs 加数となる右側の行列。
* @return 加算して、代入した行列。
*/
Matrix operator +=(const Matrix& rhs);
/**
* すべての成分の符号を反転する。
* @return 符号を反転した行列。
*/
Matrix operator -() const;
/**
* 行列を減算する。
* @param rhs 減数となる右側の行列。
* @return 減算した行列。
*/
Matrix operator -(const Matrix& rhs) const;
/**
* 行列を減算して、代入する。
* @param rhs 減数となる右側の行列。
* @return 減算して、代入した行列。
*/
Matrix operator -=(const Matrix& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算した行列。
*/
Matrix operator /(const double& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算して、代入した行列。
*/
Matrix operator /=(const double& rhs);
/**
* 二つの行列が等しいかどうかを判定する。
* @param rhs 右側の行列。
* @return 等しいならtrue, 等しくないならfalse。
*/
bool operator ==(const Matrix& rhs) const;
/**
* 次元を一つ縮小(SHRink)する。
* @return 縮小した行列。
*/
Matrix<N - 1> shr() const;
/**
* 行と列を取り除いて、小行列(SUBmatrix)を作る。
* @param row 取り除く行。
* @param col 取り除く列。
* @return 作った行列。
*/
Matrix<N - 1> sub(const unsigned int& row, const unsigned int& col) const;
};
template <>
const Matrix<2> Matrix<2>::IDENTITY{
1.0, 0.0,
0.0, 1.0,
};
template <>
const Matrix<3> Matrix<3>::IDENTITY{
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
};
template <>
const Matrix<4> Matrix<4>::IDENTITY{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
};
/**
* 行列を出力する。
* @param out 出力先となるストリーム。
* @param mat 出力する行列。
* @return 出力したストリーム。
*/
template <unsigned int N> ostream& operator <<(ostream& out, const Matrix<N>& mat);
};
namespace h3d {
using std::copy;
using std::ostream;
/**
* N次元ベクトルクラス。
* @param N ベクトルの次元数。
*/
template <unsigned int N>
class Vector {
public:
/** 成分(Component)の配列。N個。 */
double c[N];
/**
* 各成分をゼロクリアする。
*/
void clear();
/**
* 次元を一つ拡張(EXTend)する。
* @param c 追加する成分。
* @return 拡張したベクトル。
*/
Vector<N + 1> ext(const double& c = 1.0) const;
/**
* 内積(INner PROduct)を計算する。
* @param rhs 右側のベクトル。
* @return 計算した内積。
*/
double inpro(const Vector& rhs) const;
/**
* 二つのベクトルが等しくないかどうかを判定する。
* @param rhs 右側のベクトル。
* @return 等しくないならtrue, 等しいならfalse。
*/
bool operator !=(const Vector& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算したベクトル。
*/
Vector operator *(const double& rhs) const;
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算して、代入したベクトル。
*/
Vector& operator *=(const double& rhs);
/**
* ベクトルを加算する。
* @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 rhs 除数となる右側のスカラー値。
* @return 除算したベクトル。
*/
Vector operator /(const double& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算して、代入したベクトル。
*/
Vector& operator /=(const double& rhs);
/**
* 二つのベクトルが等しいかどうかを判定する。
* @param rhs 右側のベクトル
* @return 等しいならtrue, 等しくないならfalse。
*/
bool operator ==(const Vector& rhs) const;
/**
* 基底を直交化(ORTHOgonalize)する。
* @param bases 直交化する基底。
*/
static void ortho(Vector<N>* bases);
/**
* 3次元ベクトルの外積(OUTer PROduct)を計算する。
* @param rhs 右側のベクトル。
* @return 計算した外積。
*/
Vector outpro(const Vector& rhs) const;
/**
* 別のベクトルに対する垂直成分(PERPendicularity)を計算する。
* @param vec このベクトルに対する垂直成分を計算する。
* @return 計算した垂直成分。
*/
Vector perp(const Vector& vec) const;
/**
* 別のベクトルに射影(PRoJect)する。
* @param vec このベクトル上に射影する。
* @return 射影したベクトル。
*/
Vector prj(const Vector& vec) const;
/**
* 次元を一つ縮小(SHRink)する。
* @return 縮小したベクトル。
*/
Vector<N - 1> shr() const;
};
/**
* ベクトルを出力する。
* @param out 出力先となるストリーム。
* @param vec 出力するベクトル。
* @return 出力したストリーム。
*/
template <unsigned int N> ostream& operator <<(ostream& out, const Vector<N>& vec);
};
namespace h3d {
/**
* 二つの数が近いかどうかを判定する。
* @param x 一つ目の数。
* @param y 二つ目の数。
* @return 近いならtrue, 遠いならfalse。
*/
inline bool near(const double& x, const double& y);
};
namespace h3d {
using std::cout;
using std::endl;
using std::string;
#define ASSERT(pred) assert(#pred, (pred));
#define 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 {
using std::shared_ptr;
using std::vector;
class TestSet {
public:
TestSet();
void run() const;
protected:
vector<shared_ptr<Test>> tests;
};
};
namespace h3d {
class Test1 : public Test {
public:
virtual void run() const override;
};
};
namespace h3d {
class Test2 : public Test {
public:
virtual void run() const override;
};
};
////////////////////////////////////////////////////////////////////////////////
namespace h3d {
template <unsigned int N>
void Matrix<N>::clear() {
// 各成分をゼロクリアする。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++)
this->c[row][col] = 0.0;
}
}
template <unsigned int N>
double Matrix<N>::det() const {
double res = 0.0;
// 1行目の各成分ごとに反復する。
for (auto col = 0; col < N; col++) {
// 成分に、それと対応する小行列式を乗算する。
double cofac = c[0][col] * sub(0, col).det();
// (-1)^col
if ((col & 0x1) == 1) cofac = -cofac;
// 結果に余因子を加算する。
res += cofac;
}
return res;
}
template <unsigned int N>
Matrix<N + 1> Matrix<N>::ext(const Matrix<N + 1>& mat) const {
Matrix<N + 1> res;
// 一つ大きい次元の各成分ごとに反復する。
for (auto row = 0; row < N + 1; row++) {
for (auto col = 0; col < N + 1; col++) {
// 行と列がN以内ならこの行列, それ以外なら引数の行列から成分をコピーする。
if (row < N && col < N) res.c[row][col] = this->c[row][col];
else res.c[row][col] = mat.c[row][col];
}
}
return res;
}
template <unsigned int N>
Matrix<N> Matrix<N>::inv() const throw(string) {
// まず行列式を計算し、正則であることを確かめる。
double dett = det();
if (near(dett, 0.0)) throw "行列が正則ではない。";
// 行列式の逆数を計算しておく。
double inv_det = 1.0 / dett;
Matrix<N> res;
// 各成分ごとに反復する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++) {
// 行列式の逆数に、対角の小行列式を乗算する。
res.c[row][col] = inv_det * sub(col, row).det();
// (-1)^(row + col)
if (((row + col) & 0x1) == 1)
res.c[row][col] = -res.c[row][col];
}
}
return res;
}
template <>
double Matrix<2>::det() const {
return this->c[0][0] * this->c[1][1] - this->c[0][1] * this->c[1][0];
}
template <unsigned int N>
bool Matrix<N>::operator !=(const Matrix& rhs) const {
return !operator ==(rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator *(const Matrix& rhs) const {
Matrix<N> res;
// 各成分ごとに反復する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++) {
res.c[row][col] = 0.0;
// 左側は対応する行, 右側は対応する列を反復して、乗算する。
for (auto i = 0; i < N; i++)
res.c[row][col] += this->c[row][i] * rhs.c[i][col];
}
}
return res;
}
template <unsigned int N>
Vector<N> Matrix<N>::operator *(const Vector<N>& rhs) const {
Vector<N> res;
// ベクトルの各成分ごとに反復する。
for (auto i = 0; i < N; i++) {
res.c[i] = 0.0;
// 左側の行列は対応する行を走査して、乗算する。
for (auto j = 0; j < N; j++)
res.c[i] += this->c[i][j] * rhs.c[j];
}
return res;
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator *(const double& rhs) const {
Matrix<N> res;
// 各成分にスカラー値を乗算する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++)
res.c[row][col] = this->c[row][col] * rhs;
}
return res;
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator *=(const Matrix& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator *=(const double& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator +(const Matrix& rhs) const {
Matrix<N> res;
// 対応する成分同士で加算する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++)
res.c[row][col] = this->c[row][col] + rhs.c[row][col];
}
return res;
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator +=(const Matrix& rhs) {
return *this = operator +(rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator -() const {
Matrix<N> res;
// 各成分の符号を反転する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++)
res.c[row][col] = -this->c[row][col];
}
return res;
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator -(const Matrix& rhs) const {
return operator +(-rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator -=(const Matrix& rhs) {
return *this = operator -(rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator /(const double& rhs) const {
return operator *(1.0 / rhs);
}
template <unsigned int N>
Matrix<N> Matrix<N>::operator /=(const double& rhs) {
return *this = operator /(rhs);
}
template <unsigned int N>
bool Matrix<N>::operator ==(const Matrix& rhs) const {
bool res = true;
if (&rhs != this) {
// 各成分ごとに反復する。
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++) {
// 対応する成分同士が近ければ等しいと判定する。
if (!near(this->c[row][col], rhs.c[row][col])) {
res = false;
break;
}
}
if (!res) break;
}
}
return res;
}
template <unsigned int N>
Matrix<N - 1> Matrix<N>::shr() const {
Matrix<N - 1> res;
// 一つ小さい次元の各成分ごとに反復する。
for (auto row = 0; row < N - 1; row++) {
// 対応する成分をコピーする。
for (auto col = 0; col < N - 1; col++)
res.c[row][col] = this->c[row][col];
}
return res;
}
template <unsigned int N>
Matrix<N - 1> Matrix<N>::sub(const unsigned int& row, const unsigned int& col) const {
Matrix<N - 1> sub;
auto sub_row = 0;
// この行列の各成分ごとに反復する。
// 取り除く行と列については処理をスキップする。
for (auto sup_row = 0; sup_row < N; sup_row++) {
if (sup_row == row) continue;
auto sub_col = 0;
for (auto sup_col = 0; sup_col < N; sup_col++) {
if (sup_col == col) continue;
// 対応する成分をコピーする。
sub.c[sub_row][sub_col] = this->c[sup_row][sup_col];
sub_col++;
}
sub_row++;
}
return sub;
}
template <unsigned int N> ostream& operator <<(ostream& out, const Matrix<N>& mat) {
out << "{";
for (auto row = 0; row < N; row++) {
out << "{";
for (auto col = 0; col < N; col++) out << mat.c[row][col] << ", ";
out << "}, ";
}
out << "}";
}
};
namespace h3d {
using std::ostream;
template <unsigned int N>
void Vector<N>::clear() {
// 各成分をゼロクリアする。
for (auto i = 0; i < N; i++) this->c[i] = 0.0;
}
template <unsigned int N>
Vector<N + 1> Vector<N>::ext(const double& c) const {
Vector<N + 1> res;
// このベクトルからN個の成分をコピーして、最後は引数の成分をコピーする。
copy(this->c, this->c + N, res.c);
res.c[N] = c;
return res;
}
template <unsigned int N>
double Vector<N>::inpro(const Vector& rhs) const {
double res = 0;
// 対応する成分同士の積を求めて、合計する。
for (auto i = 0; i < N; i++) res += this->c[i] * rhs.c[i];
return res;
}
template <unsigned int N>
bool Vector<N>::operator !=(const Vector& rhs) const {
return !operator ==(rhs);
}
template <unsigned int N>
Vector<N> Vector<N>::operator *(const double& rhs) const {
Vector<N> res;
// 各成分にスカラー値を乗算する。
for (auto i = 0; i < N; i++) res.c[i] = this->c[i] * rhs;
return res;
}
template <unsigned int N>
Vector<N>& Vector<N>::operator *=(const double& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
Vector<N> Vector<N>::operator +(const Vector& rhs) const {
Vector<N> res;
// 対応する成分同士で加算する。
for (auto i = 0; i < N; i++) res.c[i] = this->c[i] + rhs.c[i];
return res;
}
template <unsigned int N>
Vector<N>& Vector<N>::operator +=(const Vector& rhs) {
return *this = operator +(rhs);
}
template <unsigned int N>
Vector<N> Vector<N>::operator -() const {
Vector<N> res;
// 各成分の符号を反転する。
for (auto i = 0; i < N; i++) res.c[i] = -this->c[i];
return res;
}
template <unsigned int N>
Vector<N> Vector<N>::operator -(const Vector& rhs) const {
return operator +(-rhs);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator -=(const Vector& rhs) {
return *this = operator -(rhs);
}
template <unsigned int N>
Vector<N> Vector<N>::operator /(const double& rhs) const {
return operator *(1.0 / rhs);
}
template <unsigned int N>
Vector<N>& Vector<N>::operator /=(const double& rhs) {
return *this = operator /(rhs);
}
template <unsigned int N>
bool Vector<N>::operator ==(const Vector& rhs) const {
bool res = true;
if (&rhs != this) {
// 各成分ごとに反復する。
for (auto i = 0; i < N; i++) {
// 対応する成分同士が近ければ等しいと判定する。
if (!near(this->c[i], rhs.c[i])) {
res = false;
break;
}
}
}
return res;
}
template <unsigned int N>
void Vector<N>::ortho(Vector<N>* bases) {
// グラム・シュミットの直交化法。
for (auto i = 2; i < N; i++) {
Vector<N> sum;
sum.clear();
for (auto j = 0; j < i; j++) sum += bases[i].prj(bases[j]);
bases[i] -= sum;
}
}
template <unsigned int N>
Vector<N> Vector<N>::outpro(const Vector& rhs) const {
// 各成分について、対応する列を除いた2×2の行列式を計算する。
// P×Q = < Py・Qz - Pz・Qy, Pz・Qx - Px・Qz, Px・Qy - Py・Qx >
return Vector<N>{
this->c[1] * rhs.c[2] - this->c[2] * rhs.c[1],
this->c[2] * rhs.c[0] - this->c[0] * rhs.c[2],
this->c[0] * rhs.c[1] - this->c[1] * rhs.c[0],
};
}
template <unsigned int N>
Vector<N> Vector<N>::perp(const Vector& vec) const {
// 射影したベクトルを引けば、垂直成分が残る。
return *this - prj(vec);
}
template <unsigned int N>
Vector<N> Vector<N>::prj(const Vector& vec) const {
// PをQ上に射影した結果は次式で与えられる。
// P・Q
// -----・Q
// |Q|^2
return vec * inpro(vec) / vec.inpro(vec);
}
template <unsigned int N>
Vector<N - 1> Vector<N>::shr() const {
Vector<N - 1> res;
copy(this->c, this->c + N - 1, res.c);
return res;
}
template <unsigned int N> ostream& operator <<(ostream& out, const Vector<N>& vec) {
out << "{";
for (auto c : vec.c) out << c << ", ";
out << "}";
}
};
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;
}
};
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;
}
};
namespace h3d {
using std::shared_ptr;
TestSet::TestSet() {
this->tests.push_back(shared_ptr<Test>(new Test1));
this->tests.push_back(shared_ptr<Test>(new Test2));
}
void TestSet::run() const { for (auto iter : this->tests) iter->run(); }
};
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.c[0], 2.0))
ASSERT(near(p.c[1], 2.0))
ASSERT(near(p.c[2], 1.0))
ASSERT(near(q.c[0], 1.0))
ASSERT(near(q.c[1], -2.0))
ASSERT(near(q.c[2], 0.0))
ASSERT(p.ext() == (Vector<4>{2.0, 2.0, 1.0, 1.0}))
ASSERT(p.ext(0.0) == (Vector<4>{2.0, 2.0, 1.0, 0.0}))
ASSERT(p.shr() == (Vector<2>{2.0, 2.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;
r = p;
ASSERT(r == (Vector<3>{2.0, 2.0, 1.0}))
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}))
r.clear();
ASSERT(near(r.c[0], 0.0))
ASSERT(near(r.c[1], 0.0))
ASSERT(near(r.c[2], 0.0))
ASSERT(near(p.inpro(q), -2.0))
ASSERT(p.outpro(q) == (Vector<3>{2.0, 1.0, -6.0}))
ASSERT(p.prj(q) == (Vector<3>{-0.4, 0.8, 0.0}))
ASSERT(q.prj(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}))
}
};
namespace h3d {
using std::cos;
using std::sin;
using std::sqrt;
void Test2::run() const {
ASSERT(near(Matrix<2>::IDENTITY.c[0][0], 1.0))
ASSERT(near(Matrix<2>::IDENTITY.c[0][1], 0.0))
ASSERT(near(Matrix<2>::IDENTITY.c[1][0], 0.0))
ASSERT(near(Matrix<2>::IDENTITY.c[1][1], 1.0))
ASSERT(near(Matrix<3>::IDENTITY.c[0][0], 1.0))
ASSERT(near(Matrix<3>::IDENTITY.c[0][1], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[0][2], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[1][0], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[1][1], 1.0))
ASSERT(near(Matrix<3>::IDENTITY.c[1][2], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[2][0], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[2][1], 0.0))
ASSERT(near(Matrix<3>::IDENTITY.c[2][2], 1.0))
ASSERT(near(Matrix<4>::IDENTITY.c[0][0], 1.0))
ASSERT(near(Matrix<4>::IDENTITY.c[0][1], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[0][2], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[0][3], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[1][0], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[1][1], 1.0))
ASSERT(near(Matrix<4>::IDENTITY.c[1][2], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[1][3], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[2][0], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[2][1], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[2][2], 1.0))
ASSERT(near(Matrix<4>::IDENTITY.c[2][3], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[3][0], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[3][1], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[3][2], 0.0))
ASSERT(near(Matrix<4>::IDENTITY.c[3][3], 1.0))
Matrix<2> f{
1.0, -2.0,
-3.0, 4.0,
}, g{
-9.0, 8.0,
7.0, -6.0,
};
ASSERT(near(f.c[0][0], 1.0))
ASSERT(near(f.c[0][1], -2.0))
ASSERT(near(f.c[1][0], -3.0))
ASSERT(near(f.c[1][1], 4.0))
ASSERT(near(g.c[0][0], -9.0))
ASSERT(near(g.c[0][1], 8.0))
ASSERT(near(g.c[1][0], 7.0))
ASSERT(near(g.c[1][1], -6.0))
ASSERT(f.ext() == (Matrix<3>{
1.0, -2.0, 0.0,
-3.0, 4.0, 0.0,
0.0, 0.0, 1.0,
}))
ASSERT(f.ext((Matrix<3>{
3.0, -8.0, 5.0,
-2.0, 7.0, 4.0,
1.0, -5.0, 0.0,
})) == (Matrix<3>{
1.0, -2.0, 5.0,
-3.0, 4.0, 4.0,
1.0, -5.0, 0.0,
}))
ASSERT(f.shr() == (Matrix<1>{
1.0,
}))
ASSERT(f == (Matrix<2>{
1.0, -2.0,
-3.0, 4.0,
}))
ASSERT(g == (Matrix<2>{
-9.0, 8.0,
7.0, -6.0,
}))
ASSERT(f != (Matrix<2>{
-9.0, 8.0,
7.0, -6.0,
}))
ASSERT(g != (Matrix<2>{
1.0, -2.0,
-3.0, 4.0,
}))
ASSERT(-f == (Matrix<2>{
-1.0, 2.0,
3.0, -4.0,
}))
ASSERT(-g == (Matrix<2>{
9.0, -8.0,
-7.0, 6.0,
}))
ASSERT(f + g == (Matrix<2>{
-8.0, 6.0,
4.0, -2.0,
}))
ASSERT(f - g == (Matrix<2>{
10.0, -10.0,
-10.0, 10.0,
}))
ASSERT(f * g == (Matrix<2>{
-23.0, 20.0,
55.0, -48.0,
}))
ASSERT(g * f == (Matrix<2>{
-33.0, 50.0,
25.0, -38.0,
}))
ASSERT(f * Matrix<2>::IDENTITY == (Matrix<2>{
1.0, -2.0,
-3.0, 4.0,
}))
ASSERT(Matrix<2>::IDENTITY * g == (Matrix<2>{
-9.0, 8.0,
7.0, -6.0,
}))
ASSERT(f * 3.0 == (Matrix<2>{
3.0, -6.0,
-9.0, 12.0,
}))
ASSERT(g / -2.0 == (Matrix<2>{
4.5, -4.0,
-3.5, 3.0,
}))
Matrix<2> h;
h = f;
ASSERT(h == (Matrix<2>{
1.0, -2.0,
-3.0, 4.0,
}))
ASSERT((h += (Matrix<2>{
0.1, -0.2,
-0.3, 0.4,
})) == (Matrix<2>{
1.1, -2.2,
-3.3, 4.4,
}))
ASSERT((h -= (Matrix<2>{
-0.9, 0.8,
0.7, -0.6,
})) == (Matrix<2>{
2.0, -3.0,
-4.0, 5.0,
}))
ASSERT((h *= (Matrix<2>{
-4.0, 3.0,
-2.0, 1.0,
})) == (Matrix<2>{
-2.0, 3.0,
6.0, -7.0,
}))
ASSERT((h *= 3.0) == (Matrix<2>{
-6.0, 9.0,
18.0, -21.0,
}))
ASSERT((h /= -2.0) == (Matrix<2>{
3.0, -4.5,
-9.0, 10.5,
}))
h.clear();
ASSERT(near(h.c[0][0], 0.0))
ASSERT(near(h.c[0][1], 0.0))
ASSERT(near(h.c[1][0], 0.0))
ASSERT(near(h.c[1][1], 0.0))
ASSERT(near((Matrix<2>{
2.0, 7.0,
-3.0, 0.5,
}).det(), 22.0))
ASSERT(near((Matrix<3>{
0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
}).det(), -1.0))
ASSERT(near((Matrix<3>{
0.5, sqrt(3.0) / 2.0, 0.0,
-sqrt(3.0) / 2.0, 0.5, 0.0,
0.0, 0.0, 1.0,
}).det(), 1.0))
ASSERT(near((Matrix<3>{
5, 7, 1,
17, 2, 64,
10, 14, 2,
}).det(), 0.0))
ASSERT((Matrix<3>{
2.0, 0.0, 0.0,
0.0, 3.0, 0.0,
0.0, 0.0, 4.0,
}).inv() == (Matrix<3>{
12.0, 0.0, 0.0,
0.0, 8.0, 0.0,
0.0, 0.0, 6.0,
}) / 24.0)
ASSERT((Matrix<3>{
2.0, 0.0, 0.0,
0.0, 3.0, 0.0,
0.0, 0.0, 4.0,
}) * ((Matrix<3>{
12.0, 0.0, 0.0,
0.0, 8.0, 0.0,
0.0, 0.0, 6.0,
}) / 24.0) == Matrix<3>::IDENTITY)
ASSERT((Matrix<3>{
1.0, 0.0, 0.0,
0.0, 2.0, 2.0,
3.0, 0.0, 8.0,
}).inv() == (Matrix<3>{
16.0, 0.0, 0.0,
6.0, 8.0, -2.0,
-6.0, 0.0, 2.0,
}) / 16.0)
ASSERT((Matrix<3>{
1.0, 0.0, 0.0,
0.0, 2.0, 2.0,
3.0, 0.0, 8.0,
}) * ((Matrix<3>{
16.0, 0.0, 0.0,
6.0, 8.0, -2.0,
-6.0, 0.0, 2.0,
}) / 16.0) == Matrix<3>::IDENTITY)
double theta = 60.0;
double theta_rad = M_PI * theta / 180.0;
double coss = cos(theta_rad);
double sinn = sin(theta_rad);
ASSERT((Matrix<3>{
coss, 0.0, -sinn,
0.0, 1.0, 0.0,
sinn, 0.0, coss,
}).inv() == (Matrix<3>{
coss, 0.0, sinn,
0.0, 1.0, 0.0,
-sinn, 0.0, coss,
}))
ASSERT((Matrix<3>{
coss, 0.0, -sinn,
0.0, 1.0, 0.0,
sinn, 0.0, coss,
}) * (Matrix<3>{
coss, 0.0, sinn,
0.0, 1.0, 0.0,
-sinn, 0.0, coss,
}) == Matrix<3>::IDENTITY)
ASSERT((Matrix<4>{
1.0, 0.0, 0.0, 4.0,
0.0, 1.0, 0.0, 3.0,
0.0, 0.0, 1.0, 7.0,
0.0, 0.0, 0.0, 1.0,
}).inv() == (Matrix<4>{
1.0, 0.0, 0.0, -4.0,
0.0, 1.0, 0.0, -3.0,
0.0, 0.0, 1.0, -7.0,
0.0, 0.0, 0.0, 1.0,
}))
ASSERT((Matrix<4>{
1.0, 0.0, 0.0, 4.0,
0.0, 1.0, 0.0, 3.0,
0.0, 0.0, 1.0, 7.0,
0.0, 0.0, 0.0, 1.0,
}) * (Matrix<4>{
1.0, 0.0, 0.0, -4.0,
0.0, 1.0, 0.0, -3.0,
0.0, 0.0, 1.0, -7.0,
0.0, 0.0, 0.0, 1.0,
}) == Matrix<4>::IDENTITY)
ASSERT((Matrix<2>{
1.0, -3.0,
-4.0, 6.0,
}) * (Vector<2>{2.0, 5.0}) == (Vector<2>{-13.0, 22.0}))
ASSERT((Matrix<2>{
0.0, 8.0,
-9.0, 3.0,
}) * (Vector<2>{1.0, -4.0}) == (Vector<2>{-32.0, -21.0}))
}
};
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;
}
I2luY2x1ZGUgPGFsZ29yaXRobT4KI2luY2x1ZGUgPGNtYXRoPgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxtZW1vcnk+CiNpbmNsdWRlIDxzdHJpbmc+CiNpbmNsdWRlIDx2ZWN0b3I+CgpuYW1lc3BhY2UgaDNkIHsKICAgIHRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gY2xhc3MgTWF0cml4OwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiBjbGFzcyBWZWN0b3I7Cn07CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6b3N0cmVhbTsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCgkvKioKCSAqIE7mrKHlhYPmraPmlrnooYzliJfjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBOIOihjOWIl+OBruasoeWFg+aVsOOAggoJICovCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJY2xhc3MgTWF0cml4IHsKCXB1YmxpYzoKCQkvKiog5oiQ5YiGKENvbXBvbmVudCnjga7phY3liJfjgIJOw5dO5YCL44CCICovCgkJZG91YmxlIGNbTl1bTl07CgkJCgkJLyoqIOWNmOS9jeihjOWIl+OAgiAqLwoJCXN0YXRpYyBjb25zdCBNYXRyaXggSURFTlRJVFk7CgkJCgkJLyoqCgkJICog5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJICovCgkJdm9pZCBjbGVhcigpOwoJCQoJCS8qKgoJCSAqIOihjOWIl+W8jyhERVRlcm1pbmFudCnjgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+ihjOWIl+W8j+OAggoJCSAqLwoJCWRvdWJsZSBkZXQoKSBjb25zdDsKCQkKCQkvKioKCQkqIOasoeWFg+OCkuS4gOOBpOaLoeW8tShFWFRlbmQp44GX44Gf6KGM5YiX44KS5L2c44KL44CCCgkJICogQHBhcmFtIG1hdCDjgZPjga7ooYzliJfjgYvjgonmnIDlvozjga7ooYzjgajliJfjga7miJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOaLoeW8teOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCU1hdHJpeDxOICsgMT4gZXh0KGNvbnN0IE1hdHJpeDxOICsgMT4mIG1hdCA9IE1hdHJpeDxOICsgMT46OklERU5USVRZKSBjb25zdDsKCQkKCQkvKioKCQkgKiDpgIbooYzliJcoSU5WZXJzZSBtYXRyaXgp44KS6KiI566X44GZ44KL44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/pgIbooYzliJfjgIIKCQkgKiBAdGhyb3cgc3RyaW5nIOihjOWIl+OBjOato+WJh+OBp+OBr+OBquOBhOWgtOWQiOOAgeODoeODg+OCu+ODvOOCuOOCkuOCueODreODvOOBmeOCi+OAggoJCSAqLwoJCU1hdHJpeCBpbnYoKSBjb25zdCB0aHJvdyhzdHJpbmcpOwoJCQoJCS8qKgoJCSAqIOS6jOOBpOOBruihjOWIl+OBjOetieOBl+OBj+OBquOBhOOBi+OBqeOBhuOBi+OCkuWIpOWumuOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDnrYnjgZfjgY/jgarjgYTjgarjgol0cnVlLCDnrYnjgZfjgYTjgarjgolmYWxzZeOAggoJCSAqLwoJCWJvb2wgb3BlcmF0b3IgIT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OBp+S5l+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgKihjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44Go44Gu56mN44KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvcjxOPiBvcGVyYXRvciAqKGNvbnN0IFZlY3RvcjxOPiYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafkuZfnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOS5l+aVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gf6KGM5YiX44CCCgkJICovCgkJTWF0cml4IG9wZXJhdG9yICooY29uc3QgZG91YmxlJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgZG91YmxlJiByaHMpOwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkuWKoOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDliqDnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgKyhjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5Yqg566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDliqDmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCU1hdHJpeCBvcGVyYXRvciArPShjb25zdCBNYXRyaXgmIHJocyk7CgkJCgkJLyoqCgkJICog44GZ44G544Gm44Gu5oiQ5YiG44Gu56ym5Y+344KS5Y+N6Lui44GZ44KL44CCCgkJICogQHJldHVybiDnrKblj7fjgpLlj43ou6LjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgLSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkua4m+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlNYXRyaXggb3BlcmF0b3IgLShjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5rib566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCU1hdHJpeCBvcGVyYXRvciAtPShjb25zdCBNYXRyaXgmIHJocyk7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn6Zmk566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDpmaTmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOmZpOeul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCU1hdHJpeCBvcGVyYXRvciAvKGNvbnN0IGRvdWJsZSYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g6Zmk566X44GX44Gm44CB5Luj5YWl44GX44Gf6KGM5YiX44CCCgkJICovCgkJTWF0cml4IG9wZXJhdG9yIC89KGNvbnN0IGRvdWJsZSYgcmhzKTsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7ooYzliJfjgYznrYnjgZfjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g562J44GX44GE44Gq44KJdHJ1ZSwg562J44GX44GP44Gq44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlib29sIG9wZXJhdG9yID09KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDmrKHlhYPjgpLkuIDjgaTnuK7lsI8oU0hSaW5rKeOBmeOCi+OAggoJCSAqIEByZXR1cm4g57iu5bCP44GX44Gf6KGM5YiX44CCCgkJICovCgkJTWF0cml4PE4gLSAxPiBzaHIoKSBjb25zdDsKCQkKCQkvKioKCQkgKiDooYzjgajliJfjgpLlj5bjgorpmaTjgYTjgabjgIHlsI/ooYzliJcoU1VCbWF0cml4KeOCkuS9nOOCi+OAggoJCSAqIEBwYXJhbSByb3cg5Y+W44KK6Zmk44GP6KGM44CCCgkJICogQHBhcmFtIGNvbCDlj5bjgorpmaTjgY/liJfjgIIKCQkgKiBAcmV0dXJuIOS9nOOBo+OBn+ihjOWIl+OAggoJCSAqLwoJCU1hdHJpeDxOIC0gMT4gc3ViKGNvbnN0IHVuc2lnbmVkIGludCYgcm93LCBjb25zdCB1bnNpZ25lZCBpbnQmIGNvbCkgY29uc3Q7Cgl9OwoJCgl0ZW1wbGF0ZSA8PiAKCWNvbnN0IE1hdHJpeDwyPiBNYXRyaXg8Mj46OklERU5USVRZewoJCTEuMCwgMC4wLCAKCQkwLjAsIDEuMCwgCgl9OwoJCgl0ZW1wbGF0ZSA8PiAKCWNvbnN0IE1hdHJpeDwzPiBNYXRyaXg8Mz46OklERU5USVRZewoJCTEuMCwgMC4wLCAwLjAsIAoJCTAuMCwgMS4wLCAwLjAsIAoJCTAuMCwgMC4wLCAxLjAsIAoJfTsKCQoJdGVtcGxhdGUgPD4gCgljb25zdCBNYXRyaXg8ND4gTWF0cml4PDQ+OjpJREVOVElUWXsKCQkxLjAsIDAuMCwgMC4wLCAwLjAsIAoJCTAuMCwgMS4wLCAwLjAsIDAuMCwgCgkJMC4wLCAwLjAsIDEuMCwgMC4wLCAKCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJfTsKCQoJLyoqCgkgKiDooYzliJfjgpLlh7rlipvjgZnjgovjgIIKCSAqIEBwYXJhbSBvdXQg5Ye65Yqb5YWI44Go44Gq44KL44K544OI44Oq44O844Og44CCCgkgKiBAcGFyYW0gbWF0IOWHuuWKm+OBmeOCi+ihjOWIl+OAggoJICogQHJldHVybiDlh7rlipvjgZfjgZ/jgrnjg4jjg6rjg7zjg6DjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IE1hdHJpeDxOPiYgbWF0KTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3B5OwoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJCgkvKioKCSAqIE7mrKHlhYPjg5njgq/jg4jjg6vjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBOIOODmeOCr+ODiOODq+OBruasoeWFg+aVsOOAggoJICovCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJY2xhc3MgVmVjdG9yIHsKCXB1YmxpYzoKCQkvKiog5oiQ5YiGKENvbXBvbmVudCnjga7phY3liJfjgIJO5YCL44CCICovCgkJZG91YmxlIGNbTl07CgkJCgkJLyoqCgkJICog5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJICovCgkJdm9pZCBjbGVhcigpOwoJCQoJCS8qKgoJCSAqIOasoeWFg+OCkuS4gOOBpOaLoeW8tShFWFRlbmQp44GZ44KL44CCCgkJICogQHBhcmFtIGMg6L+95Yqg44GZ44KL5oiQ5YiG44CCCgkJICogQHJldHVybiDmi6HlvLXjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlWZWN0b3I8TiArIDE+IGV4dChjb25zdCBkb3VibGUmIGMgPSAxLjApIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWGheepjShJTm5lciBQUk9kdWN0KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/lhoXnqY3jgIIKCQkgKi8KCQlkb3VibGUgaW5wcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOS6jOOBpOOBruODmeOCr+ODiOODq+OBjOetieOBl+OBj+OBquOBhOOBi+OBqeOBhuOBi+OCkuWIpOWumuOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDnrYnjgZfjgY/jgarjgYTjgarjgol0cnVlLCDnrYnjgZfjgYTjgarjgolmYWxzZeOAggoJCSAqLwoJCWJvb2wgb3BlcmF0b3IgIT0oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBmeOCi+OAggoJCSogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvciBvcGVyYXRvciAqKGNvbnN0IGRvdWJsZSYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafkuZfnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOS5l+aVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gm44CB5Luj5YWl44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJVmVjdG9yJiBvcGVyYXRvciAqPShjb25zdCBkb3VibGUmIHJocyk7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44KS5Yqg566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDliqDmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvciBvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjg5njgq/jg4jjg6vjgpLliqDnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWKoOaVsOOBqOOBquOCi+WPs+WBtOOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g5Yqg566X44GX44Gm44CB5Luj5YWl44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJVmVjdG9yJiBvcGVyYXRvciArPShjb25zdCBWZWN0b3ImIHJocyk7CgkJCgkJLyoqCgkJICog5ZCE5oiQ5YiG44Gu56ym5Y+344KS5Y+N6Lui44GZ44KL44CCCgkJICogQHJldHVybiDnrKblj7fjgpLlj43ou6LjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlWZWN0b3Igb3BlcmF0b3IgLSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOODmeOCr+ODiOODq+OCkua4m+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlWZWN0b3Igb3BlcmF0b3IgLShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44KS5rib566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvciYgb3BlcmF0b3IgLT0oY29uc3QgVmVjdG9yJiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlWZWN0b3Igb3BlcmF0b3IgLyhjb25zdCBkb3VibGUmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn6Zmk566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDpmaTmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOmZpOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvciYgb3BlcmF0b3IgLz0oY29uc3QgZG91YmxlJiByaHMpOwoJCQoJCS8qKgoJCSAqIOS6jOOBpOOBruODmeOCr+ODiOODq+OBjOetieOBl+OBhOOBi+OBqeOBhuOBi+OCkuWIpOWumuOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu44OZ44Kv44OI44OrCgkJICogQHJldHVybiDnrYnjgZfjgYTjgarjgol0cnVlLCDnrYnjgZfjgY/jgarjgYTjgarjgolmYWxzZeOAggoJCSAqLwoJCWJvb2wgb3BlcmF0b3IgPT0oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWfuuW6leOCkuebtOS6pOWMlihPUlRIT2dvbmFsaXplKeOBmeOCi+OAggoJCSAqIEBwYXJhbSBiYXNlcyDnm7TkuqTljJbjgZnjgovln7rlupXjgIIKCQkgKi8KCQlzdGF0aWMgdm9pZCBvcnRobyhWZWN0b3I8Tj4qIGJhc2VzKTsKCQkKCQkvKioKCQkgKiAz5qyh5YWD44OZ44Kv44OI44Or44Gu5aSW56mNKE9VVGVyIFBST2R1Y3Qp44KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+WkluepjeOAggoJCSAqLwoJCVZlY3RvciBvdXRwcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSog5Yil44Gu44OZ44Kv44OI44Or44Gr5a++44GZ44KL5Z6C55u05oiQ5YiGKFBFUlBlbmRpY3VsYXJpdHkp44KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHZlYyDjgZPjga7jg5njgq/jg4jjg6vjgavlr77jgZnjgovlnoLnm7TmiJDliIbjgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+WeguebtOaIkOWIhuOAggoJCSAqLwoJCVZlY3RvciBwZXJwKGNvbnN0IFZlY3RvciYgdmVjKSBjb25zdDsKCQkKCQkvKioKCQkgKiDliKXjga7jg5njgq/jg4jjg6vjgavlsITlvbEoUFJvSmVjdCnjgZnjgovjgIIKCQkgKiBAcGFyYW0gdmVjIOOBk+OBruODmeOCr+ODiOODq+S4iuOBq+WwhOW9seOBmeOCi+OAggoJCSAqIEByZXR1cm4g5bCE5b2x44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJVmVjdG9yIHByaihjb25zdCBWZWN0b3ImIHZlYykgY29uc3Q7CgkJCgkJLyoqCgkJICog5qyh5YWD44KS5LiA44Gk57iu5bCPKFNIUmluaynjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOe4ruWwj+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCVZlY3RvcjxOIC0gMT4gc2hyKCkgY29uc3Q7Cgl9OwoJCgkvKioKCSAqIOODmeOCr+ODiOODq+OCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIG91dCDlh7rlipvlhYjjgajjgarjgovjgrnjg4jjg6rjg7zjg6DjgIIKCSAqIEBwYXJhbSB2ZWMg5Ye65Yqb44GZ44KL44OZ44Kv44OI44Or44CCCgkgKiBAcmV0dXJuIOWHuuWKm+OBl+OBn+OCueODiOODquODvOODoOOAggoJICovCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IG9zdHJlYW0mIG9wZXJhdG9yIDw8KG9zdHJlYW0mIG91dCwgY29uc3QgVmVjdG9yPE4+JiB2ZWMpOwp9OwoKbmFtZXNwYWNlIGgzZCB7CgkvKioKCSAqIOS6jOOBpOOBruaVsOOBjOi/keOBhOOBi+OBqeOBhuOBi+OCkuWIpOWumuOBmeOCi+OAggoJICogQHBhcmFtIHgg5LiA44Gk55uu44Gu5pWw44CCCgkgKiBAcGFyYW0geSDkuozjgaTnm67jga7mlbDjgIIKCSAqIEByZXR1cm4g6L+R44GE44Gq44KJdHJ1ZSwg6YGg44GE44Gq44KJZmFsc2XjgIIKCSAqLwoJaW5saW5lIGJvb2wgbmVhcihjb25zdCBkb3VibGUmIHgsIGNvbnN0IGRvdWJsZSYgeSk7Cn07CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6Y291dDsKCXVzaW5nIHN0ZDo6ZW5kbDsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCgkjZGVmaW5lIEFTU0VSVChwcmVkKSBhc3NlcnQoI3ByZWQsIChwcmVkKSk7CgkKCSNkZWZpbmUgUFJJTlQodmFsKSBjb3V0IDw8ICN2YWwgPDwgIj0iIDw8ICh2YWwpIDw8IGVuZGw7CgkKCS8qKgoJICog44Ki44K144O844K344On44Oz44KS5a6f6KGM44GZ44KL44CCCgkgKiDmiJDlip/jgarjgonmqJnmupblh7rlipvjgavntZDmnpzjgpLlh7rlipvjgZnjgovjgIIKCSAqIEBwYXJhbSBwcmVkX3N0ciDliKTlrprjgZnjgovlvI/jgpLooajjgZfjgZ/mloflrZfliJfjgIIKCSAqIEBwYXJhbSBwcmVkX3JlcyDliKTlrprntZDmnpzjgIJ0cnVl44Gq44KJ5oiQ5Yqf77yMZmFsc2XjgarjgonlpLHmlZfjgajliKTlrprjgZnjgovjgIIKCSAqIEB0aHJvdyBzdHJpbmcg5aSx5pWX44Gq44KJ44Oh44OD44K744O844K444KS44K544Ot44O844GZ44KL44CCCgkgKi8KCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJY2xhc3MgVGVzdCB7IHB1YmxpYzogdmlydHVhbCB2b2lkIHJ1bigpIGNvbnN0ID0gMDsgfTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzaGFyZWRfcHRyOwoJdXNpbmcgc3RkOjp2ZWN0b3I7CgkKCWNsYXNzIFRlc3RTZXQgewoJcHVibGljOgoJCVRlc3RTZXQoKTsKCQl2b2lkIHJ1bigpIGNvbnN0OwoJcHJvdGVjdGVkOgoJCXZlY3RvcjxzaGFyZWRfcHRyPFRlc3Q+PiB0ZXN0czsKCX07Cn07CgpuYW1lc3BhY2UgaDNkIHsKCWNsYXNzIFRlc3QxIDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCBvdmVycmlkZTsKCX07Cn07CgpuYW1lc3BhY2UgaDNkIHsKCWNsYXNzIFRlc3QyIDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCBvdmVycmlkZTsKCX07Cn07CgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKbmFtZXNwYWNlIGgzZCB7Cgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJdm9pZCBNYXRyaXg8Tj46OmNsZWFyKCkgewoJCS8vIOWQhOaIkOWIhuOCkuOCvOODreOCr+ODquOCouOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJdGhpcy0+Y1tyb3ddW2NvbF0gPSAwLjA7CgkJfQoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJZG91YmxlIE1hdHJpeDxOPjo6ZGV0KCkgY29uc3QgewoJCWRvdWJsZSByZXMgPSAwLjA7CgkJLy8gMeihjOebruOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgewoJCQkvLyDmiJDliIbjgavjgIHjgZ3jgozjgajlr77lv5zjgZnjgovlsI/ooYzliJflvI/jgpLkuZfnrpfjgZnjgovjgIIKCQkJZG91YmxlIGNvZmFjID0gY1swXVtjb2xdICogc3ViKDAsIGNvbCkuZGV0KCk7CgkJCS8vICgtMSleY29sCgkJCWlmICgoY29sICYgMHgxKSA9PSAxKSBjb2ZhYyA9IC1jb2ZhYzsKCQkJLy8g57WQ5p6c44Gr5L2Z5Zug5a2Q44KS5Yqg566X44GZ44KL44CCCgkJCXJlcyArPSBjb2ZhYzsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOICsgMT4gTWF0cml4PE4+OjpleHQoY29uc3QgTWF0cml4PE4gKyAxPiYgbWF0KSBjb25zdCB7CgkJTWF0cml4PE4gKyAxPiByZXM7CgkJLy8g5LiA44Gk5aSn44GN44GE5qyh5YWD44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE4gKyAxOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTiArIDE7IGNvbCsrKSB7CgkJCQkvLyDooYzjgajliJfjgYxO5Lul5YaF44Gq44KJ44GT44Gu6KGM5YiXLCDjgZ3jgozku6XlpJbjgarjgonlvJXmlbDjga7ooYzliJfjgYvjgonmiJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkJCWlmIChyb3cgPCBOICYmIGNvbCA8IE4pIHJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdOwoJCQkJZWxzZSByZXMuY1tyb3ddW2NvbF0gPSBtYXQuY1tyb3ddW2NvbF07CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOPiBNYXRyaXg8Tj46OmludigpIGNvbnN0IHRocm93KHN0cmluZykgewoJCS8vIOOBvuOBmuihjOWIl+W8j+OCkuioiOeul+OBl+OAgeato+WJh+OBp+OBguOCi+OBk+OBqOOCkueiuuOBi+OCgeOCi+OAggoJCWRvdWJsZSBkZXR0ID0gZGV0KCk7CgkJaWYgKG5lYXIoZGV0dCwgMC4wKSkgdGhyb3cgIuihjOWIl+OBjOato+WJh+OBp+OBr+OBquOBhOOAgiI7CgkJLy8g6KGM5YiX5byP44Gu6YCG5pWw44KS6KiI566X44GX44Gm44GK44GP44CCCgkJZG91YmxlIGludl9kZXQgPSAxLjAgLyBkZXR0OwoJCU1hdHJpeDxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgewoJCQkJLy8g6KGM5YiX5byP44Gu6YCG5pWw44Gr44CB5a++6KeS44Gu5bCP6KGM5YiX5byP44KS5LmX566X44GZ44KL44CCCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSBpbnZfZGV0ICogc3ViKGNvbCwgcm93KS5kZXQoKTsKCQkJCS8vICgtMSleKHJvdyArIGNvbCkKCQkJCWlmICgoKHJvdyArIGNvbCkgJiAweDEpID09IDEpIAoJCQkJCXJlcy5jW3Jvd11bY29sXSA9IC1yZXMuY1tyb3ddW2NvbF07CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPD4gCglkb3VibGUgTWF0cml4PDI+OjpkZXQoKSBjb25zdCB7CgkJcmV0dXJuIHRoaXMtPmNbMF1bMF0gKiB0aGlzLT5jWzFdWzFdIC0gdGhpcy0+Y1swXVsxXSAqIHRoaXMtPmNbMV1bMF07Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglib29sIE1hdHJpeDxOPjo6b3BlcmF0b3IgIT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gIW9wZXJhdG9yID09KHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJTWF0cml4PE4+IHJlczsKCQkvLyDlkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQlyZXMuY1tyb3ddW2NvbF0gPSAwLjA7CgkJCQkvLyDlt6blgbTjga/lr77lv5zjgZnjgovooYwsIOWPs+WBtOOBr+WvvuW/nOOBmeOCi+WIl+OCkuWPjeW+qeOBl+OBpuOAgeS5l+eul+OBmeOCi+OAggoJCQkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSAKCQkJCQlyZXMuY1tyb3ddW2NvbF0gKz0gdGhpcy0+Y1tyb3ddW2ldICogcmhzLmNbaV1bY29sXTsKCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJVmVjdG9yPE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKihjb25zdCBWZWN0b3I8Tj4mIHJocykgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g44OZ44Kv44OI44Or44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSB7CgkJCXJlcy5jW2ldID0gMC4wOwoJCQkvLyDlt6blgbTjga7ooYzliJfjga/lr77lv5zjgZnjgovooYzjgpLotbDmn7vjgZfjgabjgIHkuZfnrpfjgZnjgovjgIIKCQkJZm9yIChhdXRvIGogPSAwOyBqIDwgTjsgaisrKSAKCQkJCXJlcy5jW2ldICs9IHRoaXMtPmNbaV1bal0gKiByaHMuY1tqXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICooY29uc3QgZG91YmxlJiByaHMpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBq+OCueOCq+ODqeODvOWApOOCkuS5l+eul+OBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF0gKiByaHM7CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqPShjb25zdCBNYXRyaXgmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICo9KGNvbnN0IGRvdWJsZSYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKihyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKyhjb25zdCBNYXRyaXgmIHJocykgY29uc3QgewoJCU1hdHJpeDxOPiByZXM7CgkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44Gn5Yqg566X44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSB0aGlzLT5jW3Jvd11bY29sXSArIHJocy5jW3Jvd11bY29sXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICs9KGNvbnN0IE1hdHJpeCYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLSgpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gLXRoaXMtPmNbcm93XVtjb2xdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLShjb25zdCBNYXRyaXgmIHJocykgY29uc3QgewoJCXJldHVybiBvcGVyYXRvciArKC1yaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLT0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAtKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAvKGNvbnN0IGRvdWJsZSYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICooMS4wIC8gcmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yIC89KGNvbnN0IGRvdWJsZSYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJYm9vbCBNYXRyaXg8Tj46Om9wZXJhdG9yID09KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCQkJaWYgKCFuZWFyKHRoaXMtPmNbcm93XVtjb2xdLCByaHMuY1tyb3ddW2NvbF0pKSB7CgkJCQkJCXJlcyA9IGZhbHNlOwoJCQkJCQlicmVhazsKCQkJCQl9CgkJCQl9CgkJCQlpZiAoIXJlcykgYnJlYWs7CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOIC0gMT4gTWF0cml4PE4+OjpzaHIoKSBjb25zdCB7CgkJTWF0cml4PE4gLSAxPiByZXM7CgkJLy8g5LiA44Gk5bCP44GV44GE5qyh5YWD44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE4gLSAxOyByb3crKykgewoJCQkvLyDlr77lv5zjgZnjgovmiJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE4gLSAxOyBjb2wrKykgCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSB0aGlzLT5jW3Jvd11bY29sXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCU1hdHJpeDxOIC0gMT4gTWF0cml4PE4+OjpzdWIoY29uc3QgdW5zaWduZWQgaW50JiByb3csIGNvbnN0IHVuc2lnbmVkIGludCYgY29sKSBjb25zdCB7CgkJTWF0cml4PE4gLSAxPiBzdWI7CgkJYXV0byBzdWJfcm93ID0gMDsKCQkvLyDjgZPjga7ooYzliJfjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkvLyDlj5bjgorpmaTjgY/ooYzjgajliJfjgavjgaTjgYTjgabjga/lh6bnkIbjgpLjgrnjgq3jg4Pjg5fjgZnjgovjgIIKCQlmb3IgKGF1dG8gc3VwX3JvdyA9IDA7IHN1cF9yb3cgPCBOOyBzdXBfcm93KyspIHsKCQkJaWYgKHN1cF9yb3cgPT0gcm93KSBjb250aW51ZTsKCQkJYXV0byBzdWJfY29sID0gMDsKCQkJZm9yIChhdXRvIHN1cF9jb2wgPSAwOyBzdXBfY29sIDwgTjsgc3VwX2NvbCsrKSB7CgkJCQlpZiAoc3VwX2NvbCA9PSBjb2wpIGNvbnRpbnVlOwoJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJCQlzdWIuY1tzdWJfcm93XVtzdWJfY29sXSA9IHRoaXMtPmNbc3VwX3Jvd11bc3VwX2NvbF07CgkJCQlzdWJfY29sKys7CgkJCX0KCQkJc3ViX3JvdysrOwoJCX0KCQlyZXR1cm4gc3ViOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IG9zdHJlYW0mIG9wZXJhdG9yIDw8KG9zdHJlYW0mIG91dCwgY29uc3QgTWF0cml4PE4+JiBtYXQpIHsKCQlvdXQgPDwgInsiOwoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlvdXQgPDwgInsiOwoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIG91dCA8PCBtYXQuY1tyb3ddW2NvbF0gPDwgIiwgIjsKCQkJb3V0IDw8ICJ9LCAiOwoJCX0KCQlvdXQgPDwgIn0iOwoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6Om9zdHJlYW07CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCgl2b2lkIFZlY3RvcjxOPjo6Y2xlYXIoKSB7CgkJLy8g5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSB0aGlzLT5jW2ldID0gMC4wOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJVmVjdG9yPE4gKyAxPiBWZWN0b3I8Tj46OmV4dChjb25zdCBkb3VibGUmIGMpIGNvbnN0IHsKCQlWZWN0b3I8TiArIDE+IHJlczsKCQkvLyDjgZPjga7jg5njgq/jg4jjg6vjgYvjgolO5YCL44Gu5oiQ5YiG44KS44Kz44OU44O844GX44Gm44CB5pyA5b6M44Gv5byV5pWw44Gu5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJY29weSh0aGlzLT5jLCB0aGlzLT5jICsgTiwgcmVzLmMpOwoJCXJlcy5jW05dID0gYzsKCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJZG91YmxlIFZlY3RvcjxOPjo6aW5wcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0IHsKCQlkb3VibGUgcmVzID0gMDsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjga7nqY3jgpLmsYLjgoHjgabjgIHlkIjoqIjjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcyArPSB0aGlzLT5jW2ldICogcmhzLmNbaV07CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWJvb2wgVmVjdG9yPE4+OjpvcGVyYXRvciAhPShjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCXJldHVybiAhb3BlcmF0b3IgPT0ocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCVZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yICooY29uc3QgZG91YmxlJiByaHMpIGNvbnN0IHsKCQlWZWN0b3I8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBq+OCueOCq+ODqeODvOWApOOCkuS5l+eul+OBmeOCi+OAggoJCWZvciAoYXV0byBpID0gMDsgaSA8IE47IGkrKykgcmVzLmNbaV0gPSB0aGlzLT5jW2ldICogcmhzOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4mIFZlY3RvcjxOPjo6b3BlcmF0b3IgKj0oY29uc3QgZG91YmxlJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gdGhpcy0+Y1tpXSArIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4mIFZlY3RvcjxOPjo6b3BlcmF0b3IgKz0oY29uc3QgVmVjdG9yJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciArKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciAtKCkgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gu56ym5Y+344KS5Y+N6Lui44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMuY1tpXSA9IC10aGlzLT5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciAtKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4mIFZlY3RvcjxOPjo6b3BlcmF0b3IgLT0oY29uc3QgVmVjdG9yJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAtKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciAvKGNvbnN0IGRvdWJsZSYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICooMS4wIC8gcmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCVZlY3RvcjxOPiYgVmVjdG9yPE4+OjpvcGVyYXRvciAvPShjb25zdCBkb3VibGUmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yIC8ocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWJvb2wgVmVjdG9yPE4+OjpvcGVyYXRvciA9PShjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCWJvb2wgcmVzID0gdHJ1ZTsKCQlpZiAoJnJocyAhPSB0aGlzKSB7CgkJCS8vIOWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHsKCQkJCS8vIOWvvuW/nOOBmeOCi+aIkOWIhuWQjOWjq+OBjOi/keOBkeOCjOOBsOetieOBl+OBhOOBqOWIpOWumuOBmeOCi+OAggoJCQkJaWYgKCFuZWFyKHRoaXMtPmNbaV0sIHJocy5jW2ldKSkgewoJCQkJCXJlcyA9IGZhbHNlOwoJCQkJCWJyZWFrOwoJCQkJfQoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCgl2b2lkIFZlY3RvcjxOPjo6b3J0aG8oVmVjdG9yPE4+KiBiYXNlcykgewoJCS8vIOOCsOODqeODoOODu+OCt+ODpeODn+ODg+ODiOOBruebtOS6pOWMluazleOAggoJCWZvciAoYXV0byBpID0gMjsgaSA8IE47IGkrKykgewoJCQlWZWN0b3I8Tj4gc3VtOwoJCQlzdW0uY2xlYXIoKTsKCQkJZm9yIChhdXRvIGogPSAwOyBqIDwgaTsgaisrKSBzdW0gKz0gYmFzZXNbaV0ucHJqKGJhc2VzW2pdKTsKCQkJYmFzZXNbaV0gLT0gc3VtOwoJCX0KCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCVZlY3RvcjxOPiBWZWN0b3I8Tj46Om91dHBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCS8vIOWQhOaIkOWIhuOBq+OBpOOBhOOBpuOAgeWvvuW/nOOBmeOCi+WIl+OCkumZpOOBhOOBnzLDlzLjga7ooYzliJflvI/jgpLoqIjnrpfjgZnjgovjgIIKCQkvLyBQw5dRID0gPCBQeeODu1F6IC0gUHrjg7tReSwgUHrjg7tReCAtIFB444O7UXosIFB444O7UXkgLSBQeeODu1F4ID4KCQlyZXR1cm4gVmVjdG9yPE4+ewoJCQl0aGlzLT5jWzFdICogcmhzLmNbMl0gLSB0aGlzLT5jWzJdICogcmhzLmNbMV0sIAoJCQl0aGlzLT5jWzJdICogcmhzLmNbMF0gLSB0aGlzLT5jWzBdICogcmhzLmNbMl0sIAoJCQl0aGlzLT5jWzBdICogcmhzLmNbMV0gLSB0aGlzLT5jWzFdICogcmhzLmNbMF0sIAoJCX07Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpwZXJwKGNvbnN0IFZlY3RvciYgdmVjKSBjb25zdCB7CgkJLy8g5bCE5b2x44GX44Gf44OZ44Kv44OI44Or44KS5byV44GR44Gw44CB5Z6C55u05oiQ5YiG44GM5q6L44KL44CCCgkJcmV0dXJuICp0aGlzIC0gcHJqKHZlYyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglWZWN0b3I8Tj4gVmVjdG9yPE4+OjpwcmooY29uc3QgVmVjdG9yJiB2ZWMpIGNvbnN0IHsKCQkvLyBQ44KSUeS4iuOBq+WwhOW9seOBl+OBn+e1kOaenOOBr+asoeW8j+OBp+S4juOBiOOCieOCjOOCi+OAggoJCS8vIFDjg7tRCgkJLy8gLS0tLS3jg7tRCgkJLy8gfFF8XjIKCQlyZXR1cm4gdmVjICogaW5wcm8odmVjKSAvIHZlYy5pbnBybyh2ZWMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJVmVjdG9yPE4gLSAxPiBWZWN0b3I8Tj46OnNocigpIGNvbnN0IHsKCQlWZWN0b3I8TiAtIDE+IHJlczsKCQljb3B5KHRoaXMtPmMsIHRoaXMtPmMgKyBOIC0gMSwgcmVzLmMpOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBWZWN0b3I8Tj4mIHZlYykgewoJCW91dCA8PCAieyI7CgkJZm9yIChhdXRvIGMgOiB2ZWMuYykgb3V0IDw8IGMgPDwgIiwgIjsKCQlvdXQgPDwgIn0iOwoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7CglpbmxpbmUgYm9vbCBuZWFyKGNvbnN0IGRvdWJsZSYgeCwgY29uc3QgZG91YmxlJiB5KSB7CgkJc3RhdGljIGNvbnN0IGRvdWJsZSBUSFJFU0hPTEQgPSAwLjAwMDAwMDE7CgkJZG91YmxlIGRpZiA9IHggLSB5OwoJCXJldHVybiBkaWYgPiAtVEhSRVNIT0xEICYmIGRpZiA8IFRIUkVTSE9MRDsKCX0KfTsKCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3V0OwoJdXNpbmcgc3RkOjplbmRsOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKSB7CgkJaWYgKHByZWRfcmVzKSBjb3V0IDw8ICLjgqLjgrXjg7zjg4jmiJDlip86ICIgPDwgcHJlZF9zdHIgPDwgZW5kbDsKCQllbHNlIHRocm93ICLjgqLjgrXjg7zjg4jlpLHmlZc6ICIgKyBwcmVkX3N0cjsKCX0KfTsKCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzaGFyZWRfcHRyOwoJCglUZXN0U2V0OjpUZXN0U2V0KCkgewoJCXRoaXMtPnRlc3RzLnB1c2hfYmFjayhzaGFyZWRfcHRyPFRlc3Q+KG5ldyBUZXN0MSkpOwoJCXRoaXMtPnRlc3RzLnB1c2hfYmFjayhzaGFyZWRfcHRyPFRlc3Q+KG5ldyBUZXN0MikpOwoJfQoKCXZvaWQgVGVzdFNldDo6cnVuKCkgY29uc3QgeyBmb3IgKGF1dG8gaXRlciA6IHRoaXMtPnRlc3RzKSBpdGVyLT5ydW4oKTsgfQp9OwoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnNxcnQ7CgkKCXZvaWQgVGVzdDE6OnJ1bigpIGNvbnN0IHsKCQlWZWN0b3I8Mz4gcHsyLjAsIDIuMCwgMS4wfSwgcXsxLjAsIC0yLjAsIDAuMH07CgkJQVNTRVJUKG5lYXIocC5jWzBdLCAyLjApKQoJCUFTU0VSVChuZWFyKHAuY1sxXSwgMi4wKSkKCQlBU1NFUlQobmVhcihwLmNbMl0sIDEuMCkpCgkJQVNTRVJUKG5lYXIocS5jWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyKHEuY1sxXSwgLTIuMCkpCgkJQVNTRVJUKG5lYXIocS5jWzJdLCAwLjApKQoJCQoJCUFTU0VSVChwLmV4dCgpID09IChWZWN0b3I8ND57Mi4wLCAyLjAsIDEuMCwgMS4wfSkpCgkJQVNTRVJUKHAuZXh0KDAuMCkgPT0gKFZlY3Rvcjw0PnsyLjAsIDIuMCwgMS4wLCAwLjB9KSkKCQlBU1NFUlQocC5zaHIoKSA9PSAoVmVjdG9yPDI+ezIuMCwgMi4wfSkpCgkJCgkJQVNTRVJUKHAgPT0gKFZlY3RvcjwzPnsyLjAsIDIuMCwgMS4wfSkpCgkJQVNTRVJUKHEgPT0gKFZlY3RvcjwzPnsxLjAsIC0yLjAsIDAuMH0pKQoJCUFTU0VSVChwICE9IChWZWN0b3I8Mz57MS4wLCAtMi4wLCAwLjB9KSkKCQlBU1NFUlQocSAhPSAoVmVjdG9yPDM+ezIuMCwgMi4wLCAxLjB9KSkKCQlBU1NFUlQoLXAgPT0gKFZlY3RvcjwzPnstMi4wLCAtMi4wLCAtMS4wfSkpCgkJQVNTRVJUKC1xID09IChWZWN0b3I8Mz57LTEuMCwgMi4wLCAwLjB9KSkKCQlBU1NFUlQocCAqIDIuMCA9PSAoVmVjdG9yPDM+ezQuMCwgNC4wLCAyLjB9KSkKCQlBU1NFUlQocSAqIC0zLjAgPT0gKFZlY3RvcjwzPnstMy4wLCA2LjAsIDAuMH0pKQoJCUFTU0VSVChwIC8gMi4wID09IChWZWN0b3I8Mz57MS4wLCAxLjAsIDAuNX0pKQoJCUFTU0VSVChxIC8gLTMuMCA9PSAoVmVjdG9yPDM+ezEuMCAvIC0zLjAsIC0yLjAgLyAtMy4wLCAwLjB9KSkKCQkKCQlBU1NFUlQocCArIHEgPT0gKFZlY3RvcjwzPnszLjAsIDAuMCwgMS4wfSkpCgkJQVNTRVJUKHEgKyBwID09IChWZWN0b3I8Mz57My4wLCAwLjAsIDEuMH0pKQoJCUFTU0VSVChwIC0gcSA9PSAoVmVjdG9yPDM+ezEuMCwgNC4wLCAxLjB9KSkKCQlBU1NFUlQocSAtIHAgPT0gKFZlY3RvcjwzPnstMS4wLCAtNC4wLCAtMS4wfSkpCgkJCgkJVmVjdG9yPDM+IHI7CgkJciA9IHA7CgkJQVNTRVJUKHIgPT0gKFZlY3RvcjwzPnsyLjAsIDIuMCwgMS4wfSkpCgkJQVNTRVJUKChyICo9IDMuMCkgPT0gKFZlY3RvcjwzPns2LjAsIDYuMCwgMy4wfSkpCgkJQVNTRVJUKChyIC89IC0yLjApID09IChWZWN0b3I8Mz57LTMuMCwgLTMuMCwgLTEuNX0pKQoJCUFTU0VSVCgociArPSAoVmVjdG9yPDM+ezUuMCwgLTEuMCwgMC4wfSkpID09IChWZWN0b3I8Mz57Mi4wLCAtNC4wLCAtMS41fSkpCgkJQVNTRVJUKChyIC09IChWZWN0b3I8Mz57MC4wLCAtMi4wLCA0LjB9KSkgPT0gKFZlY3RvcjwzPnsyLjAsIC0yLjAsIC01LjV9KSkKCQlyLmNsZWFyKCk7CgkJQVNTRVJUKG5lYXIoci5jWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyKHIuY1sxXSwgMC4wKSkKCQlBU1NFUlQobmVhcihyLmNbMl0sIDAuMCkpCgkJCgkJQVNTRVJUKG5lYXIocC5pbnBybyhxKSwgLTIuMCkpCgkJQVNTRVJUKHAub3V0cHJvKHEpID09IChWZWN0b3I8Mz57Mi4wLCAxLjAsIC02LjB9KSkKCQlBU1NFUlQocC5wcmoocSkgPT0gKFZlY3RvcjwzPnstMC40LCAwLjgsIDAuMH0pKQoJCUFTU0VSVChxLnByaihwKSA9PSAoVmVjdG9yPDM+ey00LjAgLyA5LjAsIC00LjAgLyA5LjAsIC0yLjAgLyA5LjB9KSkKCQlBU1NFUlQocC5wZXJwKHEpID09IChWZWN0b3I8Mz57Mi40LCAxLjIsIDEuMH0pKQoJCUFTU0VSVChxLnBlcnAocCkgPT0gKFZlY3RvcjwzPnsxLjAgKyA0LjAgLyA5LjAsIC0yLjAgKyA0LjAgLyA5LjAsIDIuMCAvIDkuMH0pKQoJCQoJCVZlY3RvcjwzPiBlWzNdID0gewoJCQl7c3FydCgyLjApIC8gMi4wLCBzcXJ0KDIuMCkgLyAyLjAsIDAuMH0sIAoJCQl7LTEuMCwgMS4wLCAtMS4wfSwgCgkJCXswLjAsIC0yLjAsIC0yLjB9LCAKCQl9OwoJCVZlY3RvcjwzPjo6b3J0aG8oZSk7CgkJQVNTRVJUKGVbMF0gPT0gKFZlY3RvcjwzPntzcXJ0KDIuMCkgLyAyLjAsIHNxcnQoMi4wKSAvIDIuMCwgMC4wfSkpCgkJQVNTRVJUKGVbMV0gPT0gKFZlY3RvcjwzPnstMS4wLCAxLjAsIC0xLjB9KSkKCQlBU1NFUlQoZVsyXSA9PSAoVmVjdG9yPDM+ezEuMCwgLTEuMCwgLTIuMH0pKQoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmNvczsKCXVzaW5nIHN0ZDo6c2luOwoJdXNpbmcgc3RkOjpzcXJ0OwoJCgl2b2lkIFRlc3QyOjpydW4oKSBjb25zdCB7CgkJQVNTRVJUKG5lYXIoTWF0cml4PDI+OjpJREVOVElUWS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDwyPjo6SURFTlRJVFkuY1swXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8Mj46OklERU5USVRZLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDI+OjpJREVOVElUWS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDwzPjo6SURFTlRJVFkuY1swXVswXSwgMS4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8Mz46OklERU5USVRZLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDM+OjpJREVOVElUWS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDwzPjo6SURFTlRJVFkuY1sxXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8Mz46OklERU5USVRZLmNbMV1bMV0sIDEuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDM+OjpJREVOVElUWS5jWzFdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDwzPjo6SURFTlRJVFkuY1syXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8Mz46OklERU5USVRZLmNbMl1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDM+OjpJREVOVElUWS5jWzJdWzJdLCAxLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1swXVswXSwgMS4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8ND46OklERU5USVRZLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDQ+OjpJREVOVElUWS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1swXVszXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8ND46OklERU5USVRZLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDQ+OjpJREVOVElUWS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1sxXVsyXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8ND46OklERU5USVRZLmNbMV1bM10sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1syXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8ND46OklERU5USVRZLmNbMl1bMl0sIDEuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1szXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhcihNYXRyaXg8ND46OklERU5USVRZLmNbM11bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoTWF0cml4PDQ+OjpJREVOVElUWS5jWzNdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyKE1hdHJpeDw0Pjo6SURFTlRJVFkuY1szXVszXSwgMS4wKSkKCQkKCQlNYXRyaXg8Mj4gZnsKCQkJMS4wLAktMi4wLCAKCQkJLTMuMCwJNC4wLCAKCQl9LCBnewoJCQktOS4wLAk4LjAsIAoJCQk3LjAsCS02LjAsIAoJCX07CgkJCgkJQVNTRVJUKG5lYXIoZi5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyKGYuY1swXVsxXSwgLTIuMCkpCgkJQVNTRVJUKG5lYXIoZi5jWzFdWzBdLCAtMy4wKSkKCQlBU1NFUlQobmVhcihmLmNbMV1bMV0sIDQuMCkpCgkJQVNTRVJUKG5lYXIoZy5jWzBdWzBdLCAtOS4wKSkKCQlBU1NFUlQobmVhcihnLmNbMF1bMV0sIDguMCkpCgkJQVNTRVJUKG5lYXIoZy5jWzFdWzBdLCA3LjApKQoJCUFTU0VSVChuZWFyKGcuY1sxXVsxXSwgLTYuMCkpCgkJCgkJQVNTRVJUKGYuZXh0KCkgPT0gKE1hdHJpeDwzPnsKCQkJMS4wLAktMi4wLAkwLjAsIAoJCQktMy4wLAk0LjAsCTAuMCwgCgkJCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJCUFTU0VSVChmLmV4dCgoTWF0cml4PDM+ewoJCQkzLjAsCS04LjAsCTUuMCwgCgkJCS0yLjAsCTcuMCwJNC4wLCAKCQkJMS4wLAktNS4wLAkwLjAsIAoJCX0pKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsCS0yLjAsCTUuMCwgCgkJCS0zLjAsCTQuMCwJNC4wLCAKCQkJMS4wLAktNS4wLAkwLjAsIAoJCX0pKQoJCUFTU0VSVChmLnNocigpID09IChNYXRyaXg8MT57CgkJCTEuMCwgCgkJfSkpCgkJCgkJQVNTRVJUKGYgPT0gKE1hdHJpeDwyPnsKCQkJMS4wLAktMi4wLCAKCQkJLTMuMCwJNC4wLCAKCQl9KSkKCQlBU1NFUlQoZyA9PSAoTWF0cml4PDI+ewoJCQktOS4wLAk4LjAsIAoJCQk3LjAsCS02LjAsIAoJCX0pKQoJCUFTU0VSVChmICE9IChNYXRyaXg8Mj57CgkJCS05LjAsCTguMCwgCgkJCTcuMCwJLTYuMCwgCgkJfSkpCgkJQVNTRVJUKGcgIT0gKE1hdHJpeDwyPnsKCQkJMS4wLAktMi4wLCAKCQkJLTMuMCwJNC4wLCAKCQl9KSkKCQkKCQlBU1NFUlQoLWYgPT0gKE1hdHJpeDwyPnsKCQkJLTEuMCwJMi4wLCAKCQkJMy4wLAktNC4wLCAKCQl9KSkKCQlBU1NFUlQoLWcgPT0gKE1hdHJpeDwyPnsKCQkJOS4wLAktOC4wLCAKCQkJLTcuMCwJNi4wLCAKCQl9KSkKCQlBU1NFUlQoZiArIGcgPT0gKE1hdHJpeDwyPnsKCQkJLTguMCwJNi4wLCAKCQkJNC4wLAktMi4wLCAKCQl9KSkKCQlBU1NFUlQoZiAtIGcgPT0gKE1hdHJpeDwyPnsKCQkJMTAuMCwJLTEwLjAsIAoJCQktMTAuMCwJMTAuMCwgCgkJfSkpCgkJQVNTRVJUKGYgKiBnID09IChNYXRyaXg8Mj57CgkJCS0yMy4wLAkyMC4wLCAKCQkJNTUuMCwJLTQ4LjAsIAoJCX0pKQoJCUFTU0VSVChnICogZiA9PSAoTWF0cml4PDI+ewoJCQktMzMuMCwJNTAuMCwgCgkJCTI1LjAsCS0zOC4wLCAKCQl9KSkKCQlBU1NFUlQoZiAqIE1hdHJpeDwyPjo6SURFTlRJVFkgPT0gKE1hdHJpeDwyPnsKCQkJMS4wLAktMi4wLCAKCQkJLTMuMCwJNC4wLCAKCQl9KSkKCQlBU1NFUlQoTWF0cml4PDI+OjpJREVOVElUWSAqIGcgPT0gKE1hdHJpeDwyPnsKCQkJLTkuMCwJOC4wLCAKCQkJNy4wLAktNi4wLCAKCQl9KSkKCQlBU1NFUlQoZiAqIDMuMCA9PSAoTWF0cml4PDI+ewoJCQkzLjAsCS02LjAsIAoJCQktOS4wLAkxMi4wLCAKCQl9KSkKCQlBU1NFUlQoZyAvIC0yLjAgPT0gKE1hdHJpeDwyPnsKCQkJNC41LAktNC4wLCAKCQkJLTMuNSwJMy4wLCAKCQl9KSkKCQkKCQlNYXRyaXg8Mj4gaDsKCQloID0gZjsKCQlBU1NFUlQoaCA9PSAoTWF0cml4PDI+ewoJCQkxLjAsCS0yLjAsIAoJCQktMy4wLAk0LjAsIAoJCX0pKQoJCUFTU0VSVCgoaCArPSAoTWF0cml4PDI+ewoJCQkwLjEsCS0wLjIsIAoJCQktMC4zLAkwLjQsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQkxLjEsCS0yLjIsIAoJCQktMy4zLAk0LjQsIAoJCX0pKQoJCUFTU0VSVCgoaCAtPSAoTWF0cml4PDI+ewoJCQktMC45LAkwLjgsIAoJCQkwLjcsCS0wLjYsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQkyLjAsCS0zLjAsIAoJCQktNC4wLAk1LjAsIAoJCX0pKQoJCUFTU0VSVCgoaCAqPSAoTWF0cml4PDI+ewoJCQktNC4wLAkzLjAsIAoJCQktMi4wLAkxLjAsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQktMi4wLAkzLjAsIAoJCQk2LjAsCS03LjAsIAoJCX0pKQoJCUFTU0VSVCgoaCAqPSAzLjApID09IChNYXRyaXg8Mj57CgkJCS02LjAsCTkuMCwgCgkJCTE4LjAsCS0yMS4wLCAKCQl9KSkKCQlBU1NFUlQoKGggLz0gLTIuMCkgPT0gKE1hdHJpeDwyPnsKCQkJMy4wLAktNC41LCAKCQkJLTkuMCwJMTAuNSwgCgkJfSkpCgkJaC5jbGVhcigpOwoJCUFTU0VSVChuZWFyKGguY1swXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhcihoLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXIoaC5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyKGguY1sxXVsxXSwgMC4wKSkKCQkKCQlBU1NFUlQobmVhcigoTWF0cml4PDI+ewoJCQkyLjAsCTcuMCwgCgkJCS0zLjAsCTAuNSwgCgkJfSkuZGV0KCksIDIyLjApKQoJCUFTU0VSVChuZWFyKChNYXRyaXg8Mz57CgkJCTAuMCwgMC4wLCAxLjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAKCQkJMS4wLCAwLjAsIDAuMCwgCgkJfSkuZGV0KCksIC0xLjApKQoJCUFTU0VSVChuZWFyKChNYXRyaXg8Mz57CgkJCTAuNSwJCQkJc3FydCgzLjApIC8gMi4wLAkwLjAsIAoJCQktc3FydCgzLjApIC8gMi4wLAkwLjUsCQkJCTAuMCwgCgkJCTAuMCwJCQkJMC4wLAkJCQkxLjAsIAoJCX0pLmRldCgpLCAxLjApKQoJCUFTU0VSVChuZWFyKChNYXRyaXg8Mz57CgkJCTUsCTcsCTEsIAoJCQkxNywJMiwJNjQsIAoJCQkxMCwJMTQsCTIsIAoJCX0pLmRldCgpLCAwLjApKQoJCQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkyLjAsCTAuMCwJMC4wLCAKCQkJMC4wLAkzLjAsCTAuMCwgCgkJCTAuMCwJMC4wLAk0LjAsIAoJCX0pLmludigpID09IChNYXRyaXg8Mz57CgkJCTEyLjAsCTAuMCwJMC4wLCAKCQkJMC4wLAk4LjAsCTAuMCwgCgkJCTAuMCwJMC4wLAk2LjAsIAoJCX0pIC8gMjQuMCkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMi4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJMy4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJNC4wLCAKCQl9KSAqICgoTWF0cml4PDM+ewoJCQkxMi4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJOC4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJNi4wLCAKCQl9KSAvIDI0LjApID09IE1hdHJpeDwzPjo6SURFTlRJVFkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTEuMCwJMC4wLAkwLjAsIAoJCQkwLjAsCTIuMCwJMi4wLCAKCQkJMy4wLAkwLjAsCTguMCwgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJMTYuMCwJMC4wLAkwLjAsIAoJCQk2LjAsCTguMCwJLTIuMCwgCgkJCS02LjAsCTAuMCwJMi4wLCAKCQl9KSAvIDE2LjApCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTEuMCwJMC4wLAkwLjAsIAoJCQkwLjAsCTIuMCwJMi4wLCAKCQkJMy4wLAkwLjAsCTguMCwgCgkJfSkgKiAoKE1hdHJpeDwzPnsKCQkJMTYuMCwJMC4wLAkwLjAsIAoJCQk2LjAsCTguMCwJLTIuMCwgCgkJCS02LjAsCTAuMCwJMi4wLCAKCQl9KSAvIDE2LjApID09IE1hdHJpeDwzPjo6SURFTlRJVFkpCgkJZG91YmxlIHRoZXRhID0gNjAuMDsKCQlkb3VibGUgdGhldGFfcmFkID0gTV9QSSAqIHRoZXRhIC8gMTgwLjA7CgkJZG91YmxlIGNvc3MgPSBjb3ModGhldGFfcmFkKTsKCQlkb3VibGUgc2lubiA9IHNpbih0aGV0YV9yYWQpOwoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQljb3NzLAkwLjAsCS1zaW5uLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCXNpbm4sCTAuMCwJY29zcywgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJY29zcywJMC4wLAlzaW5uLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCS1zaW5uLAkwLjAsCWNvc3MsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQljb3NzLAkwLjAsCS1zaW5uLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCXNpbm4sCTAuMCwJY29zcywgCgkJfSkgKiAoTWF0cml4PDM+ewoJCQljb3NzLAkwLjAsCXNpbm4sIAoJCQkwLjAsCTEuMCwJMC4wLCAKCQkJLXNpbm4sCTAuMCwJY29zcywgCgkJfSkgPT0gTWF0cml4PDM+OjpJREVOVElUWSkKCQlBU1NFUlQoKE1hdHJpeDw0PnsKCQkJMS4wLAkwLjAsCTAuMCwJNC4wLCAKCQkJMC4wLAkxLjAsCTAuMCwJMy4wLCAKCQkJMC4wLAkwLjAsCTEuMCwJNy4wLCAKCQkJMC4wLAkwLjAsCTAuMCwJMS4wLCAKCQl9KS5pbnYoKSA9PSAoTWF0cml4PDQ+ewoJCQkxLjAsCTAuMCwJMC4wLAktNC4wLCAKCQkJMC4wLAkxLjAsCTAuMCwJLTMuMCwgCgkJCTAuMCwJMC4wLAkxLjAsCS03LjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDQ+ewoJCQkxLjAsCTAuMCwJMC4wLAk0LjAsIAoJCQkwLjAsCTEuMCwJMC4wLAkzLjAsIAoJCQkwLjAsCTAuMCwJMS4wLAk3LjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pICogKE1hdHJpeDw0PnsKCQkJMS4wLAkwLjAsCTAuMCwJLTQuMCwgCgkJCTAuMCwJMS4wLAkwLjAsCS0zLjAsIAoJCQkwLjAsCTAuMCwJMS4wLAktNy4wLCAKCQkJMC4wLAkwLjAsCTAuMCwJMS4wLCAKCQl9KSA9PSBNYXRyaXg8ND46OklERU5USVRZKQoJCQoJCUFTU0VSVCgoTWF0cml4PDI+ewoJCQkxLjAsIC0zLjAsIAoJCQktNC4wLCA2LjAsIAoJCX0pICogKFZlY3RvcjwyPnsyLjAsIDUuMH0pID09IChWZWN0b3I8Mj57LTEzLjAsIDIyLjB9KSkKCQlBU1NFUlQoKE1hdHJpeDwyPnsKCQkJMC4wLCA4LjAsIAoJCQktOS4wLCAzLjAsIAoJCX0pICogKFZlY3RvcjwyPnsxLjAsIC00LjB9KSA9PSAoVmVjdG9yPDI+ey0zMi4wLCAtMjEuMH0pKQoJfQp9OwoKaW50IG1haW4oKSB7Cgl1c2luZyBzdGQ6OmNlcnI7Cgl1c2luZyBzdGQ6OmVuZGw7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJdHJ5IHsKCQloM2Q6OlRlc3RTZXQoKS5ydW4oKTsKCX0KCWNhdGNoIChjb25zdCBzdHJpbmcmIG1zZykgewoJCWNlcnIgPDwgbXNnIDw8IGVuZGw7CgkJcmV0dXJuIDE7Cgl9CglyZXR1cm4gMDsKfQo=
アサート成功: near(p.c[0], 2.0)
アサート成功: near(p.c[1], 2.0)
アサート成功: near(p.c[2], 1.0)
アサート成功: near(q.c[0], 1.0)
アサート成功: near(q.c[1], -2.0)
アサート成功: near(q.c[2], 0.0)
アサート成功: p.ext() == (Vector<4>{2.0, 2.0, 1.0, 1.0})
アサート成功: p.ext(0.0) == (Vector<4>{2.0, 2.0, 1.0, 0.0})
アサート成功: p.shr() == (Vector<2>{2.0, 2.0})
アサート成功: p == (Vector<3>{2.0, 2.0, 1.0})
アサート成功: q == (Vector<3>{1.0, -2.0, 0.0})
アサート成功: p != (Vector<3>{1.0, -2.0, 0.0})
アサート成功: q != (Vector<3>{2.0, 2.0, 1.0})
アサート成功: -p == (Vector<3>{-2.0, -2.0, -1.0})
アサート成功: -q == (Vector<3>{-1.0, 2.0, 0.0})
アサート成功: p * 2.0 == (Vector<3>{4.0, 4.0, 2.0})
アサート成功: q * -3.0 == (Vector<3>{-3.0, 6.0, 0.0})
アサート成功: p / 2.0 == (Vector<3>{1.0, 1.0, 0.5})
アサート成功: q / -3.0 == (Vector<3>{1.0 / -3.0, -2.0 / -3.0, 0.0})
アサート成功: p + q == (Vector<3>{3.0, 0.0, 1.0})
アサート成功: q + p == (Vector<3>{3.0, 0.0, 1.0})
アサート成功: p - q == (Vector<3>{1.0, 4.0, 1.0})
アサート成功: q - p == (Vector<3>{-1.0, -4.0, -1.0})
アサート成功: r == (Vector<3>{2.0, 2.0, 1.0})
アサート成功: (r *= 3.0) == (Vector<3>{6.0, 6.0, 3.0})
アサート成功: (r /= -2.0) == (Vector<3>{-3.0, -3.0, -1.5})
アサート成功: (r += (Vector<3>{5.0, -1.0, 0.0})) == (Vector<3>{2.0, -4.0, -1.5})
アサート成功: (r -= (Vector<3>{0.0, -2.0, 4.0})) == (Vector<3>{2.0, -2.0, -5.5})
アサート成功: near(r.c[0], 0.0)
アサート成功: near(r.c[1], 0.0)
アサート成功: near(r.c[2], 0.0)
アサート成功: near(p.inpro(q), -2.0)
アサート成功: p.outpro(q) == (Vector<3>{2.0, 1.0, -6.0})
アサート成功: p.prj(q) == (Vector<3>{-0.4, 0.8, 0.0})
アサート成功: q.prj(p) == (Vector<3>{-4.0 / 9.0, -4.0 / 9.0, -2.0 / 9.0})
アサート成功: p.perp(q) == (Vector<3>{2.4, 1.2, 1.0})
アサート成功: q.perp(p) == (Vector<3>{1.0 + 4.0 / 9.0, -2.0 + 4.0 / 9.0, 2.0 / 9.0})
アサート成功: e[0] == (Vector<3>{sqrt(2.0) / 2.0, sqrt(2.0) / 2.0, 0.0})
アサート成功: e[1] == (Vector<3>{-1.0, 1.0, -1.0})
アサート成功: e[2] == (Vector<3>{1.0, -1.0, -2.0})
アサート成功: near(Matrix<2>::IDENTITY.c[0][0], 1.0)
アサート成功: near(Matrix<2>::IDENTITY.c[0][1], 0.0)
アサート成功: near(Matrix<2>::IDENTITY.c[1][0], 0.0)
アサート成功: near(Matrix<2>::IDENTITY.c[1][1], 1.0)
アサート成功: near(Matrix<3>::IDENTITY.c[0][0], 1.0)
アサート成功: near(Matrix<3>::IDENTITY.c[0][1], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[0][2], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[1][0], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[1][1], 1.0)
アサート成功: near(Matrix<3>::IDENTITY.c[1][2], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[2][0], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[2][1], 0.0)
アサート成功: near(Matrix<3>::IDENTITY.c[2][2], 1.0)
アサート成功: near(Matrix<4>::IDENTITY.c[0][0], 1.0)
アサート成功: near(Matrix<4>::IDENTITY.c[0][1], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[0][2], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[0][3], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[1][0], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[1][1], 1.0)
アサート成功: near(Matrix<4>::IDENTITY.c[1][2], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[1][3], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[2][0], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[2][1], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[2][2], 1.0)
アサート成功: near(Matrix<4>::IDENTITY.c[2][3], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[3][0], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[3][1], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[3][2], 0.0)
アサート成功: near(Matrix<4>::IDENTITY.c[3][3], 1.0)
アサート成功: near(f.c[0][0], 1.0)
アサート成功: near(f.c[0][1], -2.0)
アサート成功: near(f.c[1][0], -3.0)
アサート成功: near(f.c[1][1], 4.0)
アサート成功: near(g.c[0][0], -9.0)
アサート成功: near(g.c[0][1], 8.0)
アサート成功: near(g.c[1][0], 7.0)
アサート成功: near(g.c[1][1], -6.0)
アサート成功: f.ext() == (Matrix<3>{ 1.0, -2.0, 0.0, -3.0, 4.0, 0.0, 0.0, 0.0, 1.0, })
アサート成功: f.ext((Matrix<3>{ 3.0, -8.0, 5.0, -2.0, 7.0, 4.0, 1.0, -5.0, 0.0, })) == (Matrix<3>{ 1.0, -2.0, 5.0, -3.0, 4.0, 4.0, 1.0, -5.0, 0.0, })
アサート成功: f.shr() == (Matrix<1>{ 1.0, })
アサート成功: f == (Matrix<2>{ 1.0, -2.0, -3.0, 4.0, })
アサート成功: g == (Matrix<2>{ -9.0, 8.0, 7.0, -6.0, })
アサート成功: f != (Matrix<2>{ -9.0, 8.0, 7.0, -6.0, })
アサート成功: g != (Matrix<2>{ 1.0, -2.0, -3.0, 4.0, })
アサート成功: -f == (Matrix<2>{ -1.0, 2.0, 3.0, -4.0, })
アサート成功: -g == (Matrix<2>{ 9.0, -8.0, -7.0, 6.0, })
アサート成功: f + g == (Matrix<2>{ -8.0, 6.0, 4.0, -2.0, })
アサート成功: f - g == (Matrix<2>{ 10.0, -10.0, -10.0, 10.0, })
アサート成功: f * g == (Matrix<2>{ -23.0, 20.0, 55.0, -48.0, })
アサート成功: g * f == (Matrix<2>{ -33.0, 50.0, 25.0, -38.0, })
アサート成功: f * Matrix<2>::IDENTITY == (Matrix<2>{ 1.0, -2.0, -3.0, 4.0, })
アサート成功: Matrix<2>::IDENTITY * g == (Matrix<2>{ -9.0, 8.0, 7.0, -6.0, })
アサート成功: f * 3.0 == (Matrix<2>{ 3.0, -6.0, -9.0, 12.0, })
アサート成功: g / -2.0 == (Matrix<2>{ 4.5, -4.0, -3.5, 3.0, })
アサート成功: h == (Matrix<2>{ 1.0, -2.0, -3.0, 4.0, })
アサート成功: (h += (Matrix<2>{ 0.1, -0.2, -0.3, 0.4, })) == (Matrix<2>{ 1.1, -2.2, -3.3, 4.4, })
アサート成功: (h -= (Matrix<2>{ -0.9, 0.8, 0.7, -0.6, })) == (Matrix<2>{ 2.0, -3.0, -4.0, 5.0, })
アサート成功: (h *= (Matrix<2>{ -4.0, 3.0, -2.0, 1.0, })) == (Matrix<2>{ -2.0, 3.0, 6.0, -7.0, })
アサート成功: (h *= 3.0) == (Matrix<2>{ -6.0, 9.0, 18.0, -21.0, })
アサート成功: (h /= -2.0) == (Matrix<2>{ 3.0, -4.5, -9.0, 10.5, })
アサート成功: near(h.c[0][0], 0.0)
アサート成功: near(h.c[0][1], 0.0)
アサート成功: near(h.c[1][0], 0.0)
アサート成功: near(h.c[1][1], 0.0)
アサート成功: near((Matrix<2>{ 2.0, 7.0, -3.0, 0.5, }).det(), 22.0)
アサート成功: near((Matrix<3>{ 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, }).det(), -1.0)
アサート成功: near((Matrix<3>{ 0.5, sqrt(3.0) / 2.0, 0.0, -sqrt(3.0) / 2.0, 0.5, 0.0, 0.0, 0.0, 1.0, }).det(), 1.0)
アサート成功: near((Matrix<3>{ 5, 7, 1, 17, 2, 64, 10, 14, 2, }).det(), 0.0)
アサート成功: (Matrix<3>{ 2.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 4.0, }).inv() == (Matrix<3>{ 12.0, 0.0, 0.0, 0.0, 8.0, 0.0, 0.0, 0.0, 6.0, }) / 24.0
アサート成功: (Matrix<3>{ 2.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 4.0, }) * ((Matrix<3>{ 12.0, 0.0, 0.0, 0.0, 8.0, 0.0, 0.0, 0.0, 6.0, }) / 24.0) == Matrix<3>::IDENTITY
アサート成功: (Matrix<3>{ 1.0, 0.0, 0.0, 0.0, 2.0, 2.0, 3.0, 0.0, 8.0, }).inv() == (Matrix<3>{ 16.0, 0.0, 0.0, 6.0, 8.0, -2.0, -6.0, 0.0, 2.0, }) / 16.0
アサート成功: (Matrix<3>{ 1.0, 0.0, 0.0, 0.0, 2.0, 2.0, 3.0, 0.0, 8.0, }) * ((Matrix<3>{ 16.0, 0.0, 0.0, 6.0, 8.0, -2.0, -6.0, 0.0, 2.0, }) / 16.0) == Matrix<3>::IDENTITY
アサート成功: (Matrix<3>{ coss, 0.0, -sinn, 0.0, 1.0, 0.0, sinn, 0.0, coss, }).inv() == (Matrix<3>{ coss, 0.0, sinn, 0.0, 1.0, 0.0, -sinn, 0.0, coss, })
アサート成功: (Matrix<3>{ coss, 0.0, -sinn, 0.0, 1.0, 0.0, sinn, 0.0, coss, }) * (Matrix<3>{ coss, 0.0, sinn, 0.0, 1.0, 0.0, -sinn, 0.0, coss, }) == Matrix<3>::IDENTITY
アサート成功: (Matrix<4>{ 1.0, 0.0, 0.0, 4.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 7.0, 0.0, 0.0, 0.0, 1.0, }).inv() == (Matrix<4>{ 1.0, 0.0, 0.0, -4.0, 0.0, 1.0, 0.0, -3.0, 0.0, 0.0, 1.0, -7.0, 0.0, 0.0, 0.0, 1.0, })
アサート成功: (Matrix<4>{ 1.0, 0.0, 0.0, 4.0, 0.0, 1.0, 0.0, 3.0, 0.0, 0.0, 1.0, 7.0, 0.0, 0.0, 0.0, 1.0, }) * (Matrix<4>{ 1.0, 0.0, 0.0, -4.0, 0.0, 1.0, 0.0, -3.0, 0.0, 0.0, 1.0, -7.0, 0.0, 0.0, 0.0, 1.0, }) == Matrix<4>::IDENTITY
アサート成功: (Matrix<2>{ 1.0, -3.0, -4.0, 6.0, }) * (Vector<2>{2.0, 5.0}) == (Vector<2>{-13.0, 22.0})
アサート成功: (Matrix<2>{ 0.0, 8.0, -9.0, 3.0, }) * (Vector<2>{1.0, -4.0}) == (Vector<2>{-32.0, -21.0})