namespace h3d {
using FLOAT = double;
template <unsigned int N> class Matrix;
template <unsigned int N> class Vector;
};
namespace h3d {
/**
* 拡大・縮小変換行列を作る。
* @param coef 各座標の係数(COEFficient)。
* coef > 1.0 なら拡大, coef < 1.0 なら縮小。
* @return 作った拡大・縮小変換行列。
*/
Matrix<3> scaling(const Vector<3>& coef);
/**
* 回転変換行列を作る。
* @param axis この軸の周りを回転する。
* @param rad 回転する弧度(RADian)。
* @return 作った回転変換行列。
*/
Matrix<3> rotation(const Vector<3>& axis, const FLOAT& rad);
/**
* 平行移動変換行列を作る。
* @param os 変換後の位置から変換前の位置を引いた差分(OffSet)。
* @return 作った平行移動変換行列。
*/
Matrix<4> translation(const Vector<3>& os);
};
#include <iostream>
namespace h3d {
using std::ostream;
using std::string;
/**
* N次元正方行列クラス。
* @param N 行列の次元数。
*/
template <unsigned int N>
class Matrix {
public:
/** 成分(Component)の配列。N×N個。 */
FLOAT c[N][N];
/** 単位行列。 */
static const Matrix IDENTITY;
/**
* 各成分をゼロクリアする。
*/
inline void clear();
/**
* 行列式(DETerminant)を計算する。
* @return 計算した行列式。
*/
inline FLOAT det() const;
/**
* 次元を一つ拡張(EXTend)する。
* @param mat この行列から最後の行と列の成分をコピーする。
* @return 拡張した行列。
*/
inline Matrix<N + 1> ext(const Matrix<N + 1>& mat = Matrix<N + 1>::IDENTITY) const;
/**
* 逆行列(INVerse matrix)を計算する。
* @return 計算した逆行列。
* @throw string 行列が正則ではないならメッセージをスローする。
*/
inline Matrix inv() const throw(string);
/**
* 二つの行列が等しくないかどうかを判定する。
* @param rhs 右側の行列。
* @return 等しくないならtrue, 等しいならfalse。
*/
inline bool operator !=(const Matrix& rhs) const;
/**
* 行列で乗算する。
* @param rhs 乗数となる右側の行列。
* @return 乗算した行列。
*/
inline Matrix operator *(const Matrix& rhs) const;
/**
* ベクトルとの積を計算する。
* @param rhs 乗数となる右側のベクトル。
* @return 計算したベクトル。
*/
inline Vector<N> operator *(const Vector<N>& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算した行列。
*/
inline Matrix operator *(const FLOAT& rhs) const;
/**
* 行列で乗算して、代入する。
* @param rhs 乗数となる右側の行列。
* @return 乗算して、代入した行列。
*/
inline Matrix operator *=(const Matrix& rhs);
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算して、代入した行列。
*/
inline Matrix operator *=(const FLOAT& rhs);
/**
* 行列を加算する。
* @param rhs 加数となる右側の行列。
* @return 加算した行列。
*/
inline Matrix operator +(const Matrix& rhs) const;
/**
* 行列を加算して、代入する。
* @param rhs 加数となる右側の行列。
* @return 加算して、代入した行列。
*/
inline Matrix operator +=(const Matrix& rhs);
/**
* すべての成分の符号を反転する。
* @return 符号を反転した行列。
*/
inline Matrix operator -() const;
/**
* 行列を減算する。
* @param rhs 減数となる右側の行列。
* @return 減算した行列。
*/
inline Matrix operator -(const Matrix& rhs) const;
/**
* 行列を減算して、代入する。
* @param rhs 減数となる右側の行列。
* @return 減算して、代入した行列。
*/
inline Matrix operator -=(const Matrix& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算した行列。
*/
inline Matrix operator /(const FLOAT& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算して、代入した行列。
*/
inline Matrix operator /=(const FLOAT& rhs);
/**
* 二つの行列が等しいかどうかを判定する。
* @param rhs 右側の行列。
* @return 等しいならtrue, 等しくないならfalse。
*/
inline bool operator ==(const Matrix& rhs) const;
/**
* 行と列を取り除いて、小行列(SUBmatrix)を作る。
* @param row 取り除く行。
* @param col 取り除く列。
* @return 作った行列。
*/
inline Matrix<N - 1> sub(const unsigned int& row, const unsigned int& col) const;
/**
* 次元を一つ切り縮める(TRUNCate)。
* @return 切り縮めた行列。
*/
inline Matrix<N - 1> trunc() const;
};
/**
* 行列を出力する。
* @param N 行列の次元数。
* @param out 出力先となるストリーム。
* @param mat 出力する行列。
* @return 出力したストリーム。
*/
template <unsigned int N>
inline ostream& operator <<(ostream& out, const Matrix<N>& mat);
};
#include <iostream>
namespace h3d {
using std::copy;
using std::ostream;
/**
* N次元ベクトルクラス。
* @param N ベクトルの次元数。
*/
template <unsigned int N>
class Vector {
public:
/** 成分(Component)の配列。N個。 */
FLOAT c[N];
/**
* 各成分をゼロクリアする。
*/
inline void clear();
/**
* 次元を一つ拡張(EXTend)する。
* @param c 追加する成分。
* @return 拡張したベクトル。
*/
inline Vector<N + 1> ext(const FLOAT& c = 1.0) const;
/**
* 内積(INner PROduct)を計算する。
* @param rhs 右側のベクトル。
* @return 計算した内積。
*/
inline FLOAT inpro(const Vector& rhs) const;
/**
* 二つのベクトルが等しくないかどうかを判定する。
* @param rhs 右側のベクトル。
* @return 等しくないならtrue, 等しいならfalse。
*/
inline bool operator !=(const Vector& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算したベクトル。
*/
inline Vector operator *(const FLOAT& rhs) const;
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側のスカラー値。
* @return 乗算して、代入したベクトル。
*/
inline Vector& operator *=(const FLOAT& rhs);
/**
* ベクトルを加算する。
* @param rhs 加数となる右側のベクトル。
* @return 加算したベクトル。
*/
inline Vector operator +(const Vector& rhs) const;
/**
* ベクトルを加算して、代入する。
* @param rhs 加数となる右側のベクトル。
* @return 加算して、代入したベクトル。
*/
inline Vector& operator +=(const Vector& rhs);
/**
* 各成分の符号を反転する。
* @return 符号を反転したベクトル。
*/
inline Vector operator -() const;
/**
* ベクトルを減算する。
* @param rhs 減数となる右側のベクトル。
* @return 減算したベクトル。
*/
inline Vector operator -(const Vector& rhs) const;
/**
* ベクトルを減算して、代入する。
* @param rhs 減数となる右側のベクトル。
* @return 減算して、代入したベクトル。
*/
inline Vector& operator -=(const Vector& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算したベクトル。
*/
inline Vector operator /(const FLOAT& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側のスカラー値。
* @return 除算して、代入したベクトル。
*/
inline Vector& operator /=(const FLOAT& rhs);
/**
* 二つのベクトルが等しいかどうかを判定する。
* @param rhs 右側のベクトル
* @return 等しいならtrue, 等しくないならfalse。
*/
inline bool operator ==(const Vector& rhs) const;
/**
* 3次元ベクトルの外積(OUTer PROduct)を計算する。
* @param rhs 右側のベクトル。
* @return 計算した外積。
*/
inline Vector outpro(const Vector& rhs) const;
/**
* 次元を一つ切り縮める(TRUNCate)。
* @return 切り縮めたベクトル。
*/
inline Vector<N - 1> trunc() const;
};
/**
* ベクトルを出力する。
* @param N ベクトルの次元数。
* @param out 出力先となるストリーム。
* @param vec 出力するベクトル。
* @return 出力したストリーム。
*/
template <unsigned int N>
inline ostream& operator <<(ostream& out, const Vector<N>& vec);
};
namespace h3d {
/**
* 角度(ANGle)を弧度(RADian)に変換する。
* @param ang 変換する角度(ANGle)。
* @return 変換した弧度。
*/
inline FLOAT ang2rad(const FLOAT& ang);
/**
* 二つの数が近いかどうかを判定する。
* @param x 一つ目の数。
* @param y 二つ目の数。
* @return 近いならtrue, 遠いならfalse。
*/
inline bool nearEqual(const FLOAT& x, const FLOAT& y);
};
#include <iostream>
#include <string>
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; };
};
#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;
};
};
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 {
class Test3 : public Test {
public:
virtual void run() const override;
};
};
////////////////////////////////////////////////////////////////////////////////
#include <cmath>
namespace h3d {
Matrix<3> scaling(const Vector<3>& coef) {
Matrix<3> res(Matrix<3>::IDENTITY);
for (auto i = 0; i < 3; i++) res.c[i][i] = coef.c[i];
return res;
}
Matrix<3> rotation(const Vector<3>& axis, const FLOAT& rad) {
FLOAT xx = axis.c[0] * axis.c[0];
FLOAT xy = axis.c[0] * axis.c[1];
FLOAT xz = axis.c[0] * axis.c[2];
FLOAT yy = axis.c[1] * axis.c[1];
FLOAT yz = axis.c[1] * axis.c[2];
FLOAT zz = axis.c[2] * axis.c[2];
FLOAT s = sin(rad);
FLOAT c = cos(rad);
FLOAT d = 1.0 - c;
FLOAT xxd = xx * d;
FLOAT xyd = xy * d;
FLOAT zs = axis.c[2] * s;
FLOAT xzd = xz * d;
FLOAT ys = axis.c[1] * s;
FLOAT yyd = yy * d;
FLOAT yzd = yz * d;
FLOAT xs = axis.c[0] * s;
FLOAT zzd = zz * d;
return Matrix<3>{
c + xxd, xyd - zs, xzd + ys,
xyd + zs, c + yyd, yzd - xs,
xzd - ys, yzd + xs, c + zzd,
};
}
Matrix<4> translation(const Vector<3>& os) {
Matrix<4> res(Matrix<4>::IDENTITY);
for (auto i = 0; i < 3; i++) res.c[i][3] = os.c[i];
return res;
}
};
namespace h3d {
template <unsigned int N>
inline 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>
inline FLOAT Matrix<N>::det() const {
FLOAT res = 0.0;
// 1行目の各成分ごとに反復する。
for (auto col = 0; col < N; col++) {
// 成分に、それと対応する小行列式を乗算する。
FLOAT cofac = c[0][col] * sub(0, col).det();
// (-1)^col
if ((col & 0x1) == 1) cofac = -cofac;
// 結果に余因子を加算する。
res += cofac;
}
return res;
}
template <>
inline FLOAT Matrix<2>::det() const {
// 再帰的な行列式計算の終着点。
// 2×2の行列式を計算する。
return this->c[0][0] * this->c[1][1] - this->c[0][1] * this->c[1][0];
}
template <unsigned int N>
inline 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>
inline Matrix<N> Matrix<N>::inv() const throw(string) {
// まず行列式を計算し、正則であることを確かめる。
FLOAT det_res = det();
if (nearEqual(det_res, 0.0)) throw "行列が正則ではない。";
// 行列式の逆数を計算しておく。
FLOAT inv_det = 1.0 / det_res;
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 <unsigned int N>
inline bool Matrix<N>::operator !=(const Matrix& rhs) const {
return !operator ==(rhs);
}
template <unsigned int N>
inline 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>
inline 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>
inline Matrix<N> Matrix<N>::operator *(const FLOAT& 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>
inline Matrix<N> Matrix<N>::operator *=(const Matrix& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
inline Matrix<N> Matrix<N>::operator *=(const FLOAT& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
inline 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>
inline Matrix<N> Matrix<N>::operator +=(const Matrix& rhs) {
return *this = operator +(rhs);
}
template <unsigned int N>
inline 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>
inline Matrix<N> Matrix<N>::operator -(const Matrix& rhs) const {
return operator +(-rhs);
}
template <unsigned int N>
inline Matrix<N> Matrix<N>::operator -=(const Matrix& rhs) {
return *this = operator -(rhs);
}
template <unsigned int N>
inline Matrix<N> Matrix<N>::operator /(const FLOAT& rhs) const {
return operator *(1.0 / rhs);
}
template <unsigned int N>
inline Matrix<N> Matrix<N>::operator /=(const FLOAT& 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 (!nearEqual(this->c[row][col], rhs.c[row][col])) {
res = false;
break;
}
}
if (!res) break;
}
}
return res;
}
template <unsigned int N>
inline 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>
inline Matrix<N - 1> Matrix<N>::trunc() 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>
inline 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 {
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,
};
};
#include <algorithm>
#include <iostream>
namespace h3d {
using std::ostream;
template <unsigned int N>
inline void Vector<N>::clear() {
// 各成分をゼロクリアする。
for (auto i = 0; i < N; i++) this->c[i] = 0.0;
}
template <unsigned int N>
inline Vector<N + 1> Vector<N>::ext(const FLOAT& 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>
inline FLOAT Vector<N>::inpro(const Vector& rhs) const {
FLOAT res = 0;
// 対応する成分同士の積を求めて、合計する。
for (auto i = 0; i < N; i++) res += this->c[i] * rhs.c[i];
return res;
}
template <unsigned int N>
inline bool Vector<N>::operator !=(const Vector& rhs) const {
return !operator ==(rhs);
}
template <unsigned int N>
inline Vector<N> Vector<N>::operator *(const FLOAT& 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>
inline Vector<N>& Vector<N>::operator *=(const FLOAT& rhs) {
return *this = operator *(rhs);
}
template <unsigned int N>
inline 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>
inline Vector<N>& Vector<N>::operator +=(const Vector& rhs) {
return *this = operator +(rhs);
}
template <unsigned int N>
inline 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>
inline Vector<N> Vector<N>::operator -(const Vector& rhs) const {
return operator +(-rhs);
}
template <unsigned int N>
inline Vector<N>& Vector<N>::operator -=(const Vector& rhs) {
return *this = operator -(rhs);
}
template <unsigned int N>
inline Vector<N> Vector<N>::operator /(const FLOAT& rhs) const {
return operator *(1.0 / rhs);
}
template <unsigned int N>
inline Vector<N>& Vector<N>::operator /=(const FLOAT& rhs) {
return *this = operator /(rhs);
}
template <unsigned int N>
inline bool Vector<N>::operator ==(const Vector& rhs) const {
bool res = true;
if (&rhs != this) {
// 各成分ごとに反復する。
for (auto i = 0; i < N; i++) {
// 対応する成分同士が近ければ等しいと判定する。
if (!nearEqual(this->c[i], rhs.c[i])) {
res = false;
break;
}
}
}
return res;
}
template <unsigned int N>
inline 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>
inline Vector<N - 1> Vector<N>::trunc() const {
Vector<N - 1> res;
copy(this->c, this->c + N - 1, res.c);
return res;
}
template <unsigned int N>
inline ostream& operator <<(ostream& out, const Vector<N>& vec) {
out << "{";
for (auto c : vec.c) out << c << ", ";
out << "}";
}
};
#include <cmath>
namespace h3d {
inline FLOAT ang2rad(const FLOAT& ang) {
return M_PI * ang / 180.0;
}
inline bool nearEqual(const FLOAT& x, const FLOAT& y) {
static const FLOAT THRESHOLD = 0.0000001;
FLOAT 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 <memory>
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));
this->tests.push_back(shared_ptr<Test>(new Test3));
}
void TestSet::run() const { for (auto iter : this->tests) iter->run(); }
};
#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(nearEqual(p.c[0], 2.0))
ASSERT(nearEqual(p.c[1], 2.0))
ASSERT(nearEqual(p.c[2], 1.0))
ASSERT(nearEqual(q.c[0], 1.0))
ASSERT(nearEqual(q.c[1], -2.0))
ASSERT(nearEqual(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.trunc() == (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(nearEqual(r.c[0], 0.0))
ASSERT(nearEqual(r.c[1], 0.0))
ASSERT(nearEqual(r.c[2], 0.0))
ASSERT(nearEqual(p.inpro(q), -2.0))
ASSERT(p.outpro(q) == (Vector<3>{2.0, 1.0, -6.0}))
}
};
#include <cmath>
namespace h3d {
using std::cos;
using std::sin;
using std::sqrt;
void Test2::run() const {
ASSERT(nearEqual(Matrix<2>::IDENTITY.c[0][0], 1.0))
ASSERT(nearEqual(Matrix<2>::IDENTITY.c[0][1], 0.0))
ASSERT(nearEqual(Matrix<2>::IDENTITY.c[1][0], 0.0))
ASSERT(nearEqual(Matrix<2>::IDENTITY.c[1][1], 1.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[0][0], 1.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[0][1], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[0][2], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[1][0], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[1][1], 1.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[1][2], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[2][0], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[2][1], 0.0))
ASSERT(nearEqual(Matrix<3>::IDENTITY.c[2][2], 1.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[0][0], 1.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[0][1], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[0][2], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[0][3], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[1][0], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[1][1], 1.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[1][2], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[1][3], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[2][0], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[2][1], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[2][2], 1.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[2][3], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[3][0], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[3][1], 0.0))
ASSERT(nearEqual(Matrix<4>::IDENTITY.c[3][2], 0.0))
ASSERT(nearEqual(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(nearEqual(f.c[0][0], 1.0))
ASSERT(nearEqual(f.c[0][1], -2.0))
ASSERT(nearEqual(f.c[1][0], -3.0))
ASSERT(nearEqual(f.c[1][1], 4.0))
ASSERT(nearEqual(g.c[0][0], -9.0))
ASSERT(nearEqual(g.c[0][1], 8.0))
ASSERT(nearEqual(g.c[1][0], 7.0))
ASSERT(nearEqual(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.trunc() == (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(nearEqual(h.c[0][0], 0.0))
ASSERT(nearEqual(h.c[0][1], 0.0))
ASSERT(nearEqual(h.c[1][0], 0.0))
ASSERT(nearEqual(h.c[1][1], 0.0))
ASSERT(nearEqual((Matrix<2>{
2.0, 7.0,
-3.0, 0.5,
}).det(), 22.0))
ASSERT(nearEqual((Matrix<3>{
0.0, 0.0, 1.0,
0.0, 1.0, 0.0,
1.0, 0.0, 0.0,
}).det(), -1.0))
ASSERT(nearEqual((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(nearEqual((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)
FLOAT theta_rad = ang2rad(60.0);
FLOAT cos_res = cos(theta_rad);
FLOAT sin_res = sin(theta_rad);
ASSERT((Matrix<3>{
cos_res, 0.0, -sin_res,
0.0, 1.0, 0.0,
sin_res, 0.0, cos_res,
}).inv() == (Matrix<3>{
cos_res, 0.0, sin_res,
0.0, 1.0, 0.0,
-sin_res, 0.0, cos_res,
}))
ASSERT((Matrix<3>{
cos_res, 0.0, -sin_res,
0.0, 1.0, 0.0,
sin_res, 0.0, cos_res,
}) * (Matrix<3>{
cos_res, 0.0, sin_res,
0.0, 1.0, 0.0,
-sin_res, 0.0, cos_res,
}) == 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}))
}
};
#include <cmath>
namespace h3d {
using std::sqrt;
void Test3::run() const {
ASSERT(scaling((Vector<3>{1.0, -2.0, 0.0})) == (Matrix<3>{
1.0, 0.0, 0.0,
0.0, -2.0, 0.0,
0.0, 0.0, 0.0,
}))
ASSERT(scaling((Vector<3>{0.0, 9.0, -8.0})) == (Matrix<3>{
0.0, 0.0, 0.0,
0.0, 9.0, 0.0,
0.0, 0.0, -8.0,
}))
ASSERT(rotation((Vector<3>{1.0, 0.0, 0.0}), ang2rad(30.0)) == (Matrix<3>{
1.0, 0.0, 0.0,
0.0, sqrt(3.0) / 2.0, -0.5,
0.0, 0.5, sqrt(3.0) / 2.0,
}))
ASSERT(rotation((Vector<3>{0.0, 1.0, 0.0}), ang2rad(-45.0)) == (Matrix<3>{
1.0 / sqrt(2.0), 0.0, -1.0 / sqrt(2.0),
0.0, 1.0, 0.0,
1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0),
}))
ASSERT(rotation((Vector<3>{0.0, 0.0, 1.0}), ang2rad(60.0)) == (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,
}))
ASSERT(translation((Vector<3>{1.0, -2.0, 0.0})) == (Matrix<4>{
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, -2.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}))
ASSERT(translation((Vector<3>{0.0, 9.0, -8.0})) == (Matrix<4>{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 9.0,
0.0, 0.0, 1.0, -8.0,
0.0, 0.0, 0.0, 1.0,
}))
}
};
#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;
}
bmFtZXNwYWNlIGgzZCB7CiAgICB1c2luZyBGTE9BVCA9IGRvdWJsZTsKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gY2xhc3MgTWF0cml4OwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiBjbGFzcyBWZWN0b3I7Cn07CgpuYW1lc3BhY2UgaDNkIHsKCS8qKgoJICog5ouh5aSn44O757iu5bCP5aSJ5o+b6KGM5YiX44KS5L2c44KL44CCCgkgKiBAcGFyYW0gY29lZiDlkITluqfmqJnjga7kv4LmlbAoQ09FRmZpY2llbnQp44CCCgkgKiBjb2VmID4gMS4wIOOBquOCieaLoeWkpywgY29lZiA8IDEuMCDjgarjgonnuK7lsI/jgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf5ouh5aSn44O757iu5bCP5aSJ5o+b6KGM5YiX44CCCgkgKi8KCU1hdHJpeDwzPiBzY2FsaW5nKGNvbnN0IFZlY3RvcjwzPiYgY29lZik7CgkKCS8qKgoJICog5Zue6Lui5aSJ5o+b6KGM5YiX44KS5L2c44KL44CCCgkgKiBAcGFyYW0gYXhpcyDjgZPjga7ou7jjga7lkajjgorjgpLlm57ou6LjgZnjgovjgIIKCSAqIEBwYXJhbSByYWQg5Zue6Lui44GZ44KL5byn5bqmKFJBRGlhbinjgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf5Zue6Lui5aSJ5o+b6KGM5YiX44CCCgkgKi8KCU1hdHJpeDwzPiByb3RhdGlvbihjb25zdCBWZWN0b3I8Mz4mIGF4aXMsIGNvbnN0IEZMT0FUJiByYWQpOwoJCgkvKioKCSAqIOW5s+ihjOenu+WLleWkieaPm+ihjOWIl+OCkuS9nOOCi+OAggoJICogQHBhcmFtIG9zIOWkieaPm+W+jOOBruS9jee9ruOBi+OCieWkieaPm+WJjeOBruS9jee9ruOCkuW8leOBhOOBn+W3ruWIhihPZmZTZXQp44CCCgkgKiBAcmV0dXJuIOS9nOOBo+OBn+W5s+ihjOenu+WLleWkieaPm+ihjOWIl+OAggoJICovCglNYXRyaXg8ND4gdHJhbnNsYXRpb24oY29uc3QgVmVjdG9yPDM+JiBvcyk7Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6b3N0cmVhbTsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCgkvKioKCSAqIE7mrKHlhYPmraPmlrnooYzliJfjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBOIOihjOWIl+OBruasoeWFg+aVsOOAggoJICovCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJY2xhc3MgTWF0cml4IHsKCXB1YmxpYzoKCQkvKiog5oiQ5YiGKENvbXBvbmVudCnjga7phY3liJfjgIJOw5dO5YCL44CCICovCgkJRkxPQVQgY1tOXVtOXTsKCQkKCQkvKiog5Y2Y5L2N6KGM5YiX44CCICovCgkJc3RhdGljIGNvbnN0IE1hdHJpeCBJREVOVElUWTsKCQkKCQkvKioKCQkgKiDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgdm9pZCBjbGVhcigpOwoJCQoJCS8qKgoJCSAqIOihjOWIl+W8jyhERVRlcm1pbmFudCnjgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+ihjOWIl+W8j+OAggoJCSAqLwoJCWlubGluZSBGTE9BVCBkZXQoKSBjb25zdDsKCQkKCQkvKioKCQkqIOasoeWFg+OCkuS4gOOBpOaLoeW8tShFWFRlbmQp44GZ44KL44CCCgkJICogQHBhcmFtIG1hdCDjgZPjga7ooYzliJfjgYvjgonmnIDlvozjga7ooYzjgajliJfjga7miJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOaLoeW8teOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXg8TiArIDE+IGV4dChjb25zdCBNYXRyaXg8TiArIDE+JiBtYXQgPSBNYXRyaXg8TiArIDE+OjpJREVOVElUWSkgY29uc3Q7CgkJCgkJLyoqCgkJICog6YCG6KGM5YiXKElOVmVyc2UgbWF0cml4KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEByZXR1cm4g6KiI566X44GX44Gf6YCG6KGM5YiX44CCCgkJICogQHRocm93IHN0cmluZyDooYzliJfjgYzmraPliYfjgafjga/jgarjgYTjgarjgonjg6Hjg4Pjgrvjg7zjgrjjgpLjgrnjg63jg7zjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IGludigpIGNvbnN0IHRocm93KHN0cmluZyk7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu6KGM5YiX44GM562J44GX44GP44Gq44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOetieOBl+OBj+OBquOBhOOBquOCiXRydWUsIOetieOBl+OBhOOBquOCiWZhbHNl44CCCgkJICovCgkJaW5saW5lIGJvb2wgb3BlcmF0b3IgIT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OBp+S5l+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICooY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOODmeOCr+ODiOODq+OBqOOBruepjeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4+IG9wZXJhdG9yICooY29uc3QgVmVjdG9yPE4+JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICooY29uc3QgRkxPQVQmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44Gn5LmX566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICo9KGNvbnN0IEZMT0FUJiByaHMpOwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkuWKoOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDliqDnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICsoY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkuWKoOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDliqDnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICs9KGNvbnN0IE1hdHJpeCYgcmhzKTsKCQkKCQkvKioKCQkgKiDjgZnjgbnjgabjga7miJDliIbjga7nrKblj7fjgpLlj43ou6LjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOespuWPt+OCkuWPjei7ouOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgLSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkua4m+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkua4m+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC09KGNvbnN0IE1hdHJpeCYgcmhzKTsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g6Zmk566X44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAvKGNvbnN0IEZMT0FUJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC89KGNvbnN0IEZMT0FUJiByaHMpOwoJCQoJCS8qKgoJCSAqIOS6jOOBpOOBruihjOWIl+OBjOetieOBl+OBhOOBi+OBqeOBhuOBi+OCkuWIpOWumuOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu6KGM5YiX44CCCgkJICogQHJldHVybiDnrYnjgZfjgYTjgarjgol0cnVlLCDnrYnjgZfjgY/jgarjgYTjgarjgolmYWxzZeOAggoJCSAqLwoJCWlubGluZSBib29sIG9wZXJhdG9yID09KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDooYzjgajliJfjgpLlj5bjgorpmaTjgYTjgabjgIHlsI/ooYzliJcoU1VCbWF0cml4KeOCkuS9nOOCi+OAggoJCSAqIEBwYXJhbSByb3cg5Y+W44KK6Zmk44GP6KGM44CCCgkJICogQHBhcmFtIGNvbCDlj5bjgorpmaTjgY/liJfjgIIKCQkgKiBAcmV0dXJuIOS9nOOBo+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXg8TiAtIDE+IHN1Yihjb25zdCB1bnNpZ25lZCBpbnQmIHJvdywgY29uc3QgdW5zaWduZWQgaW50JiBjb2wpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOasoeWFg+OCkuS4gOOBpOWIh+OCiue4ruOCgeOCiyhUUlVOQ2F0ZSnjgIIKCQkgKiBAcmV0dXJuIOWIh+OCiue4ruOCgeOBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXg8TiAtIDE+IHRydW5jKCkgY29uc3Q7Cgl9OwoJCgkvKioKCSAqIOihjOWIl+OCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIE4g6KGM5YiX44Gu5qyh5YWD5pWw44CCCgkgKiBAcGFyYW0gb3V0IOWHuuWKm+WFiOOBqOOBquOCi+OCueODiOODquODvOODoOOAggoJICogQHBhcmFtIG1hdCDlh7rlipvjgZnjgovooYzliJfjgIIKCSAqIEByZXR1cm4g5Ye65Yqb44GX44Gf44K544OI44Oq44O844Og44CCCgkgKi8KCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBNYXRyaXg8Tj4mIG1hdCk7Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6Y29weTsKCXVzaW5nIHN0ZDo6b3N0cmVhbTsKCQoJLyoqCgkgKiBO5qyh5YWD44OZ44Kv44OI44Or44Kv44Op44K544CCCgkgKiBAcGFyYW0gTiDjg5njgq/jg4jjg6vjga7mrKHlhYPmlbDjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWNsYXNzIFZlY3RvciB7CglwdWJsaWM6CgkJLyoqIOaIkOWIhihDb21wb25lbnQp44Gu6YWN5YiX44CCTuWAi+OAgiAqLwoJCUZMT0FUIGNbTl07CgkJCgkJLyoqCgkJICog5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJICovCgkJaW5saW5lIHZvaWQgY2xlYXIoKTsKCQkKCQkvKioKCQkgKiDmrKHlhYPjgpLkuIDjgaTmi6HlvLUoRVhUZW5kKeOBmeOCi+OAggoJCSAqIEBwYXJhbSBjIOi/veWKoOOBmeOCi+aIkOWIhuOAggoJCSAqIEByZXR1cm4g5ouh5by144GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvcjxOICsgMT4gZXh0KGNvbnN0IEZMT0FUJiBjID0gMS4wKSBjb25zdDsKCQkKCQkvKioKCQkgKiDlhoXnqY0oSU5uZXIgUFJPZHVjdCnjgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g6KiI566X44GX44Gf5YaF56mN44CCCgkJICovCgkJaW5saW5lIEZMT0FUIGlucHJvKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7jg5njgq/jg4jjg6vjgYznrYnjgZfjgY/jgarjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g562J44GX44GP44Gq44GE44Gq44KJdHJ1ZSwg562J44GX44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciAhPShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GZ44KL44CCCgkJKiBAcGFyYW0gcmhzIOS5l+aVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBvcGVyYXRvciAqKGNvbnN0IEZMT0FUJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDkuZfnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yJiBvcGVyYXRvciAqPShjb25zdCBGTE9BVCYgcmhzKTsKCQkKCQkvKioKCQkgKiDjg5njgq/jg4jjg6vjgpLliqDnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWKoOaVsOOBqOOBquOCi+WPs+WBtOOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g5Yqg566X44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjg5njgq/jg4jjg6vjgpLliqDnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWKoOaVsOOBqOOBquOCi+WPs+WBtOOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g5Yqg566X44GX44Gm44CB5Luj5YWl44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciYgb3BlcmF0b3IgKz0oY29uc3QgVmVjdG9yJiByaHMpOwoJCQoJCS8qKgoJCSAqIOWQhOaIkOWIhuOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCSAqIEByZXR1cm4g56ym5Y+344KS5Y+N6Lui44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBvcGVyYXRvciAtKCkgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44KS5rib566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igb3BlcmF0b3IgLShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44KS5rib566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3ImIG9wZXJhdG9yIC09KGNvbnN0IFZlY3RvciYgcmhzKTsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtOOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g6Zmk566X44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBvcGVyYXRvciAvKGNvbnN0IEZMT0FUJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yJiBvcGVyYXRvciAvPShjb25zdCBGTE9BVCYgcmhzKTsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7jg5njgq/jg4jjg6vjgYznrYnjgZfjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruODmeOCr+ODiOODqwoJCSAqIEByZXR1cm4g562J44GX44GE44Gq44KJdHJ1ZSwg562J44GX44GP44Gq44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciA9PShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICogM+asoeWFg+ODmeOCr+ODiOODq+OBruWkluepjShPVVRlciBQUk9kdWN0KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/lpJbnqY3jgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yIG91dHBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog5qyh5YWD44KS5LiA44Gk5YiH44KK57iu44KB44KLKFRSVU5DYXRlKeOAggoJCSAqIEByZXR1cm4g5YiH44KK57iu44KB44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvcjxOIC0gMT4gdHJ1bmMoKSBjb25zdDsKCX07CgkKCS8qKgoJICog44OZ44Kv44OI44Or44KS5Ye65Yqb44GZ44KL44CCCgkgKiBAcGFyYW0gTiDjg5njgq/jg4jjg6vjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBvdXQg5Ye65Yqb5YWI44Go44Gq44KL44K544OI44Oq44O844Og44CCCgkgKiBAcGFyYW0gdmVjIOWHuuWKm+OBmeOCi+ODmeOCr+ODiOODq+OAggoJICogQHJldHVybiDlh7rlipvjgZfjgZ/jgrnjg4jjg6rjg7zjg6DjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFZlY3RvcjxOPiYgdmVjKTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJLyoqCgkgKiDop5LluqYoQU5HbGUp44KS5byn5bqmKFJBRGlhbinjgavlpInmj5vjgZnjgovjgIIKCSAqIEBwYXJhbSBhbmcg5aSJ5o+b44GZ44KL6KeS5bqmKEFOR2xlKeOAggoJICogQHJldHVybiDlpInmj5vjgZfjgZ/lvKfluqbjgIIKCSAqLwoJaW5saW5lIEZMT0FUIGFuZzJyYWQoY29uc3QgRkxPQVQmIGFuZyk7CgkKCS8qKgoJICog5LqM44Gk44Gu5pWw44GM6L+R44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkgKiBAcGFyYW0geCDkuIDjgaTnm67jga7mlbDjgIIKCSAqIEBwYXJhbSB5IOS6jOOBpOebruOBruaVsOOAggoJICogQHJldHVybiDov5HjgYTjgarjgol0cnVlLCDpgaDjgYTjgarjgolmYWxzZeOAggoJICovCglpbmxpbmUgYm9vbCBuZWFyRXF1YWwoY29uc3QgRkxPQVQmIHgsIGNvbnN0IEZMT0FUJiB5KTsKfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3V0OwoJdXNpbmcgc3RkOjplbmRsOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCSNkZWZpbmUgQVNTRVJUKHByZWQpIGFzc2VydCgjcHJlZCwgKHByZWQpKTsKCQoJI2RlZmluZSBQUklOVCh2YWwpIGNvdXQgPDwgI3ZhbCA8PCAiPSIgPDwgKHZhbCkgPDwgZW5kbDsKCQoJLyoqCgkgKiDjgqLjgrXjg7zjgrfjg6fjg7PjgpLlrp/ooYzjgZnjgovjgIIKCSAqIOaIkOWKn+OBquOCieaomea6luWHuuWKm+OBq+e1kOaenOOCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIHByZWRfc3RyIOWIpOWumuOBmeOCi+W8j+OCkuihqOOBl+OBn+aWh+Wtl+WIl+OAggoJICogQHBhcmFtIHByZWRfcmVzIOWIpOWumue1kOaenOOAgnRydWXjgarjgonmiJDlip/vvIxmYWxzZeOBquOCieWkseaVl+OBqOWIpOWumuOBmeOCi+OAggoJICogQHRocm93IHN0cmluZyDlpLHmlZfjgarjgonjg6Hjg4Pjgrvjg7zjgrjjgpLjgrnjg63jg7zjgZnjgovjgIIKCSAqLwoJaW5saW5lIHZvaWQgYXNzZXJ0KGNvbnN0IHN0cmluZyYgcHJlZF9zdHIsIGNvbnN0IGJvb2wmIHByZWRfcmVzKSB0aHJvdyhzdHJpbmcpOwp9OwoKbmFtZXNwYWNlIGgzZCB7CgljbGFzcyBUZXN0IHsgcHVibGljOiB2aXJ0dWFsIHZvaWQgcnVuKCkgY29uc3QgPSAwOyB9Owp9OwoKI2luY2x1ZGUgPG1lbW9yeT4KI2luY2x1ZGUgPHZlY3Rvcj4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzaGFyZWRfcHRyOwoJdXNpbmcgc3RkOjp2ZWN0b3I7CgkKCWNsYXNzIFRlc3RTZXQgewoJcHVibGljOgoJCVRlc3RTZXQoKTsKCQl2b2lkIHJ1bigpIGNvbnN0OwoJcHJvdGVjdGVkOgoJCXZlY3RvcjxzaGFyZWRfcHRyPFRlc3Q+PiB0ZXN0czsKCX07Cn07CgpuYW1lc3BhY2UgaDNkIHsKCWNsYXNzIFRlc3QxIDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCBvdmVycmlkZTsKCX07Cn07CgpuYW1lc3BhY2UgaDNkIHsKCWNsYXNzIFRlc3QyIDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCBvdmVycmlkZTsKCX07Cn07CgpuYW1lc3BhY2UgaDNkIHsKCWNsYXNzIFRlc3QzIDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCBvdmVycmlkZTsKCX07Cn07CgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKI2luY2x1ZGUgPGNtYXRoPgoKbmFtZXNwYWNlIGgzZCB7CglNYXRyaXg8Mz4gc2NhbGluZyhjb25zdCBWZWN0b3I8Mz4mIGNvZWYpIHsKCQlNYXRyaXg8Mz4gcmVzKE1hdHJpeDwzPjo6SURFTlRJVFkpOwoJCWZvciAoYXV0byBpID0gMDsgaSA8IDM7IGkrKykgcmVzLmNbaV1baV0gPSBjb2VmLmNbaV07CgkJcmV0dXJuIHJlczsKCX0KCQoJTWF0cml4PDM+IHJvdGF0aW9uKGNvbnN0IFZlY3RvcjwzPiYgYXhpcywgY29uc3QgRkxPQVQmIHJhZCkgewoJCUZMT0FUIHh4ID0gYXhpcy5jWzBdICogYXhpcy5jWzBdOwoJCUZMT0FUIHh5ID0gYXhpcy5jWzBdICogYXhpcy5jWzFdOwoJCUZMT0FUIHh6ID0gYXhpcy5jWzBdICogYXhpcy5jWzJdOwoJCUZMT0FUIHl5ID0gYXhpcy5jWzFdICogYXhpcy5jWzFdOwoJCUZMT0FUIHl6ID0gYXhpcy5jWzFdICogYXhpcy5jWzJdOwoJCUZMT0FUIHp6ID0gYXhpcy5jWzJdICogYXhpcy5jWzJdOwoJCUZMT0FUIHMgPSBzaW4ocmFkKTsKCQlGTE9BVCBjID0gY29zKHJhZCk7CgkJRkxPQVQgZCA9IDEuMCAtIGM7CgkJRkxPQVQgeHhkID0geHggKiBkOwoJCUZMT0FUIHh5ZCA9IHh5ICogZDsKCQlGTE9BVCB6cyA9IGF4aXMuY1syXSAqIHM7CgkJRkxPQVQgeHpkID0geHogKiBkOwoJCUZMT0FUIHlzID0gYXhpcy5jWzFdICogczsKCQlGTE9BVCB5eWQgPSB5eSAqIGQ7CgkJRkxPQVQgeXpkID0geXogKiBkOwoJCUZMT0FUIHhzID0gYXhpcy5jWzBdICogczsKCQlGTE9BVCB6emQgPSB6eiAqIGQ7CgkJcmV0dXJuIE1hdHJpeDwzPnsKCQkJYyArIHh4ZCwJeHlkIC0genMsCXh6ZCArIHlzLCAKCQkJeHlkICsgenMsCWMgKyB5eWQsCXl6ZCAtIHhzLCAKCQkJeHpkIC0geXMsCXl6ZCArIHhzLAljICsgenpkLCAKCQl9OwoJfQoJCglNYXRyaXg8ND4gdHJhbnNsYXRpb24oY29uc3QgVmVjdG9yPDM+JiBvcykgewoJCU1hdHJpeDw0PiByZXMoTWF0cml4PDQ+OjpJREVOVElUWSk7CgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgMzsgaSsrKSByZXMuY1tpXVszXSA9IG9zLmNbaV07CgkJcmV0dXJuIHJlczsKCX0KfTsKCm5hbWVzcGFjZSBoM2QgewoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSB2b2lkIE1hdHJpeDxOPjo6Y2xlYXIoKSB7CgkJLy8g5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgCgkJCQl0aGlzLT5jW3Jvd11bY29sXSA9IDAuMDsKCQl9Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgRkxPQVQgTWF0cml4PE4+OjpkZXQoKSBjb25zdCB7CgkJRkxPQVQgcmVzID0gMC4wOwoJCS8vIDHooYznm67jga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIHsKCQkJLy8g5oiQ5YiG44Gr44CB44Gd44KM44Go5a++5b+c44GZ44KL5bCP6KGM5YiX5byP44KS5LmX566X44GZ44KL44CCCgkJCUZMT0FUIGNvZmFjID0gY1swXVtjb2xdICogc3ViKDAsIGNvbCkuZGV0KCk7CgkJCS8vICgtMSleY29sCgkJCWlmICgoY29sICYgMHgxKSA9PSAxKSBjb2ZhYyA9IC1jb2ZhYzsKCQkJLy8g57WQ5p6c44Gr5L2Z5Zug5a2Q44KS5Yqg566X44GZ44KL44CCCgkJCXJlcyArPSBjb2ZhYzsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPD4gCglpbmxpbmUgRkxPQVQgTWF0cml4PDI+OjpkZXQoKSBjb25zdCB7CgkJLy8g5YaN5biw55qE44Gq6KGM5YiX5byP6KiI566X44Gu57WC552A54K544CCCgkJLy8gMsOXMuOBruihjOWIl+W8j+OCkuioiOeul+OBmeOCi+OAggoJCXJldHVybiB0aGlzLT5jWzBdWzBdICogdGhpcy0+Y1sxXVsxXSAtIHRoaXMtPmNbMF1bMV0gKiB0aGlzLT5jWzFdWzBdOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOICsgMT4gTWF0cml4PE4+OjpleHQoY29uc3QgTWF0cml4PE4gKyAxPiYgbWF0KSBjb25zdCB7CgkJTWF0cml4PE4gKyAxPiByZXM7CgkJLy8g57WQ5p6c44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE4gKyAxOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTiArIDE7IGNvbCsrKSB7CgkJCQkvLyDooYzjgajliJfjgYxO5Lul5YaF44Gq44KJ44GT44Gu6KGM5YiXLCDjgZ3jgozku6XlpJbjgarjgonlvJXmlbDjga7ooYzliJfjgYvjgonmiJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkJCWlmIChyb3cgPCBOICYmIGNvbCA8IE4pIHJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdOwoJCQkJZWxzZSByZXMuY1tyb3ddW2NvbF0gPSBtYXQuY1tyb3ddW2NvbF07CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjppbnYoKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQkvLyDjgb7jgZrooYzliJflvI/jgpLoqIjnrpfjgZfjgIHmraPliYfjgafjgYLjgovjgZPjgajjgpLnorrjgYvjgoHjgovjgIIKCQlGTE9BVCBkZXRfcmVzID0gZGV0KCk7CgkJaWYgKG5lYXJFcXVhbChkZXRfcmVzLCAwLjApKSB0aHJvdyAi6KGM5YiX44GM5q2j5YmH44Gn44Gv44Gq44GE44CCIjsKCQkvLyDooYzliJflvI/jga7pgIbmlbDjgpLoqIjnrpfjgZfjgabjgYrjgY/jgIIKCQlGTE9BVCBpbnZfZGV0ID0gMS4wIC8gZGV0X3JlczsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIHsKCQkJCS8vIOihjOWIl+W8j+OBrumAhuaVsOOBq+OAgeWvvuinkuOBruWwj+ihjOWIl+W8j+OCkuS5l+eul+OBmeOCi+OAggoJCQkJcmVzLmNbcm93XVtjb2xdID0gaW52X2RldCAqIHN1Yihjb2wsIHJvdykuZGV0KCk7CgkJCQkvLyAoLTEpXihyb3cgKyBjb2wpCgkJCQlpZiAoKChyb3cgKyBjb2wpICYgMHgxKSA9PSAxKSAKCQkJCQlyZXMuY1tyb3ddW2NvbF0gPSAtcmVzLmNbcm93XVtjb2xdOwoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgYm9vbCBNYXRyaXg8Tj46Om9wZXJhdG9yICE9KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJcmV0dXJuICFvcGVyYXRvciA9PShyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICooY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOe1kOaenOOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIHsKCQkJCXJlcy5jW3Jvd11bY29sXSA9IDAuMDsKCQkJCS8vIOW3puWBtOOBr+WvvuW/nOOBmeOCi+ihjOOBruWQhOaIkOWIhiwg5Y+z5YG044Gv5a++5b+c44GZ44KL5YiX44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GX44Gm44CB5LmX566X44GZ44KL44CCCgkJCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIAoJCQkJCXJlcy5jW3Jvd11bY29sXSArPSB0aGlzLT5jW3Jvd11baV0gKiByaHMuY1tpXVtjb2xdOwoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKihjb25zdCBWZWN0b3I8Tj4mIHJocykgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g44OZ44Kv44OI44Or44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSB7CgkJCXJlcy5jW2ldID0gMC4wOwoJCQkvLyDlt6blgbTjga7ooYzliJfjga/jgIHlr77lv5zjgZnjgovooYzjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZfjgabjgIHkuZfnrpfjgZnjgovjgIIKCQkJZm9yIChhdXRvIGogPSAwOyBqIDwgTjsgaisrKSAKCQkJCXJlcy5jW2ldICs9IHRoaXMtPmNbaV1bal0gKiByaHMuY1tqXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqKGNvbnN0IEZMT0FUJiByaHMpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBq+OCueOCq+ODqeODvOWApOOCkuS5l+eul+OBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF0gKiByaHM7CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKj0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKj0oY29uc3QgRkxPQVQmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciArKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJTWF0cml4PE4+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSAKCQkJCXJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdICsgcmhzLmNbcm93XVtjb2xdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICs9KGNvbnN0IE1hdHJpeCYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yIC0oKSBjb25zdCB7CgkJTWF0cml4PE4+IHJlczsKCQkvLyDlkITmiJDliIbjga7nrKblj7fjgpLlj43ou6LjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSAKCQkJCXJlcy5jW3Jvd11bY29sXSA9IC10aGlzLT5jW3Jvd11bY29sXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAtKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLT0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAtKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLyhjb25zdCBGTE9BVCYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICooMS4wIC8gcmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAvPShjb25zdCBGTE9BVCYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJYm9vbCBNYXRyaXg8Tj46Om9wZXJhdG9yID09KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCQkJaWYgKCFuZWFyRXF1YWwodGhpcy0+Y1tyb3ddW2NvbF0sIHJocy5jW3Jvd11bY29sXSkpIHsKCQkJCQkJcmVzID0gZmFsc2U7CgkJCQkJCWJyZWFrOwoJCQkJCX0KCQkJCX0KCQkJCWlmICghcmVzKSBicmVhazsKCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOIC0gMT4gTWF0cml4PE4+OjpzdWIoY29uc3QgdW5zaWduZWQgaW50JiByb3csIGNvbnN0IHVuc2lnbmVkIGludCYgY29sKSBjb25zdCB7CgkJTWF0cml4PE4gLSAxPiBzdWI7CgkJYXV0byBzdWJfcm93ID0gMDsKCQkvLyDjgZPjga7ooYzliJfjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkvLyDlj5bjgorpmaTjgY/ooYzjgajliJfjgavjgaTjgYTjgabjga/lh6bnkIbjgpLjgrnjgq3jg4Pjg5fjgZnjgovjgIIKCQlmb3IgKGF1dG8gc3VwX3JvdyA9IDA7IHN1cF9yb3cgPCBOOyBzdXBfcm93KyspIHsKCQkJaWYgKHN1cF9yb3cgPT0gcm93KSBjb250aW51ZTsKCQkJYXV0byBzdWJfY29sID0gMDsKCQkJZm9yIChhdXRvIHN1cF9jb2wgPSAwOyBzdXBfY29sIDwgTjsgc3VwX2NvbCsrKSB7CgkJCQlpZiAoc3VwX2NvbCA9PSBjb2wpIGNvbnRpbnVlOwoJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJCQlzdWIuY1tzdWJfcm93XVtzdWJfY29sXSA9IHRoaXMtPmNbc3VwX3Jvd11bc3VwX2NvbF07CgkJCQlzdWJfY29sKys7CgkJCX0KCQkJc3ViX3JvdysrOwoJCX0KCQlyZXR1cm4gc3ViOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOIC0gMT4gTWF0cml4PE4+Ojp0cnVuYygpIGNvbnN0IHsKCQlNYXRyaXg8TiAtIDE+IHJlczsKCQkvLyDntZDmnpzjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTiAtIDE7IHJvdysrKSB7CgkJCS8vIOWvvuW/nOOBmeOCi+aIkOWIhuOCkuOCs+ODlOODvOOBmeOCi+OAggoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTiAtIDE7IGNvbCsrKSAKCQkJCXJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIG9zdHJlYW0mIG9wZXJhdG9yIDw8KG9zdHJlYW0mIG91dCwgY29uc3QgTWF0cml4PE4+JiBtYXQpIHsKCQlvdXQgPDwgInsiOwoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlvdXQgPDwgInsiOwoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIG91dCA8PCBtYXQuY1tyb3ddW2NvbF0gPDwgIiwgIjsKCQkJb3V0IDw8ICJ9LCAiOwoJCX0KCQlvdXQgPDwgIn0iOwoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7Cgl0ZW1wbGF0ZSA8PiAKCWNvbnN0IE1hdHJpeDwyPiBNYXRyaXg8Mj46OklERU5USVRZewoJCTEuMCwgMC4wLCAKCQkwLjAsIDEuMCwgCgl9OwoJCgl0ZW1wbGF0ZSA8PiAKCWNvbnN0IE1hdHJpeDwzPiBNYXRyaXg8Mz46OklERU5USVRZewoJCTEuMCwgMC4wLCAwLjAsIAoJCTAuMCwgMS4wLCAwLjAsIAoJCTAuMCwgMC4wLCAxLjAsIAoJfTsKCQoJdGVtcGxhdGUgPD4gCgljb25zdCBNYXRyaXg8ND4gTWF0cml4PDQ+OjpJREVOVElUWXsKCQkxLjAsIDAuMCwgMC4wLCAwLjAsIAoJCTAuMCwgMS4wLCAwLjAsIDAuMCwgCgkJMC4wLCAwLjAsIDEuMCwgMC4wLCAKCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJfTsKfTsKCiNpbmNsdWRlIDxhbGdvcml0aG0+CiNpbmNsdWRlIDxpb3N0cmVhbT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIHZvaWQgVmVjdG9yPE4+OjpjbGVhcigpIHsKCQkvLyDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHRoaXMtPmNbaV0gPSAwLjA7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4gKyAxPiBWZWN0b3I8Tj46OmV4dChjb25zdCBGTE9BVCYgYykgY29uc3QgewoJCVZlY3RvcjxOICsgMT4gcmVzOwoJCS8vIOOBk+OBruODmeOCr+ODiOODq+OBi+OCiU7lgIvjga7miJDliIbjgpLjgrPjg5Tjg7zjgZfjgabjgIHmnIDlvozjga/lvJXmlbDjga7miJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQljb3B5KHRoaXMtPmMsIHRoaXMtPmMgKyBOLCByZXMuYyk7CgkJcmVzLmNbTl0gPSBjOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgRkxPQVQgVmVjdG9yPE4+OjppbnBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCUZMT0FUIHJlcyA9IDA7CgkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44Gu56mN44KS5rGC44KB44Gm44CB5ZCI6KiI44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMgKz0gdGhpcy0+Y1tpXSAqIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgYm9vbCBWZWN0b3I8Tj46Om9wZXJhdG9yICE9KGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuICFvcGVyYXRvciA9PShyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yICooY29uc3QgRkxPQVQmIHJocykgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gr44K544Kr44Op44O85YCk44KS5LmX566X44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMuY1tpXSA9IHRoaXMtPmNbaV0gKiByaHM7CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4mIFZlY3RvcjxOPjo6b3BlcmF0b3IgKj0oY29uc3QgRkxPQVQmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gdGhpcy0+Y1tpXSArIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yICs9KGNvbnN0IFZlY3RvciYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yIC0oKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDlkITmiJDliIbjga7nrKblj7fjgpLlj43ou6LjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gLXRoaXMtPmNbaV07CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciAtKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yIC09KGNvbnN0IFZlY3RvciYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLShyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yIC8oY29uc3QgRkxPQVQmIHJocykgY29uc3QgewoJCXJldHVybiBvcGVyYXRvciAqKDEuMCAvIHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yIC89KGNvbnN0IEZMT0FUJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAvKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgYm9vbCBWZWN0b3I8Tj46Om9wZXJhdG9yID09KGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJCWZvciAoYXV0byBpID0gMDsgaSA8IE47IGkrKykgewoJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCQlpZiAoIW5lYXJFcXVhbCh0aGlzLT5jW2ldLCByaHMuY1tpXSkpIHsKCQkJCQlyZXMgPSBmYWxzZTsKCQkJCQlicmVhazsKCQkJCX0KCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om91dHBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCS8vIOWQhOaIkOWIhuOBq+OBpOOBhOOBpuOAgeWvvuW/nOOBmeOCi+WIl+OCkumZpOOBhOOBnzLDlzLjga7ooYzliJflvI/jgpLoqIjnrpfjgZnjgovjgIIKCQkvLyBQw5dRID0gPCBQeeODu1F6IC0gUHrjg7tReSwgUHrjg7tReCAtIFB444O7UXosIFB444O7UXkgLSBQeeODu1F4ID4KCQlyZXR1cm4gVmVjdG9yPE4+ewoJCQl0aGlzLT5jWzFdICogcmhzLmNbMl0gLSB0aGlzLT5jWzJdICogcmhzLmNbMV0sIAoJCQl0aGlzLT5jWzJdICogcmhzLmNbMF0gLSB0aGlzLT5jWzBdICogcmhzLmNbMl0sIAoJCQl0aGlzLT5jWzBdICogcmhzLmNbMV0gLSB0aGlzLT5jWzFdICogcmhzLmNbMF0sIAoJCX07Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4gLSAxPiBWZWN0b3I8Tj46OnRydW5jKCkgY29uc3QgewoJCVZlY3RvcjxOIC0gMT4gcmVzOwoJCWNvcHkodGhpcy0+YywgdGhpcy0+YyArIE4gLSAxLCByZXMuYyk7CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFZlY3RvcjxOPiYgdmVjKSB7CgkJb3V0IDw8ICJ7IjsKCQlmb3IgKGF1dG8gYyA6IHZlYy5jKSBvdXQgPDwgYyA8PCAiLCAiOwoJCW91dCA8PCAifSI7Cgl9Cn07CgojaW5jbHVkZSA8Y21hdGg+CgpuYW1lc3BhY2UgaDNkIHsKCWlubGluZSBGTE9BVCBhbmcycmFkKGNvbnN0IEZMT0FUJiBhbmcpIHsKCQlyZXR1cm4gTV9QSSAqIGFuZyAvIDE4MC4wOwoJfQoJCglpbmxpbmUgYm9vbCBuZWFyRXF1YWwoY29uc3QgRkxPQVQmIHgsIGNvbnN0IEZMT0FUJiB5KSB7CgkJc3RhdGljIGNvbnN0IEZMT0FUIFRIUkVTSE9MRCA9IDAuMDAwMDAwMTsKCQlGTE9BVCBkaWYgPSB4IC0geTsKCQlyZXR1cm4gZGlmID4gLVRIUkVTSE9MRCAmJiBkaWYgPCBUSFJFU0hPTEQ7Cgl9Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxzdHJpbmc+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6Y291dDsKCXVzaW5nIHN0ZDo6ZW5kbDsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCglpbmxpbmUgdm9pZCBhc3NlcnQoY29uc3Qgc3RyaW5nJiBwcmVkX3N0ciwgY29uc3QgYm9vbCYgcHJlZF9yZXMpIHRocm93KHN0cmluZykgewoJCWlmIChwcmVkX3JlcykgY291dCA8PCAi44Ki44K144O844OI5oiQ5YqfOiAiIDw8IHByZWRfc3RyIDw8IGVuZGw7CgkJZWxzZSB0aHJvdyAi44Ki44K144O844OI5aSx5pWXOiAiICsgcHJlZF9zdHI7Cgl9Cn07CgojaW5jbHVkZSA8bWVtb3J5PgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnNoYXJlZF9wdHI7CgkKCVRlc3RTZXQ6OlRlc3RTZXQoKSB7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QxKSk7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QyKSk7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QzKSk7Cgl9CgoJdm9pZCBUZXN0U2V0OjpydW4oKSBjb25zdCB7IGZvciAoYXV0byBpdGVyIDogdGhpcy0+dGVzdHMpIGl0ZXItPnJ1bigpOyB9Cn07CgojaW5jbHVkZSA8Y21hdGg+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6c3FydDsKCQoJdm9pZCBUZXN0MTo6cnVuKCkgY29uc3QgewoJCVZlY3RvcjwzPiBwezIuMCwgMi4wLCAxLjB9LCBxezEuMCwgLTIuMCwgMC4wfTsKCQlBU1NFUlQobmVhckVxdWFsKHAuY1swXSwgMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHAuY1sxXSwgMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHAuY1syXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHEuY1swXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHEuY1sxXSwgLTIuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChxLmNbMl0sIDAuMCkpCgkJCgkJQVNTRVJUKHAuZXh0KCkgPT0gKFZlY3Rvcjw0PnsyLjAsIDIuMCwgMS4wLCAxLjB9KSkKCQlBU1NFUlQocC5leHQoMC4wKSA9PSAoVmVjdG9yPDQ+ezIuMCwgMi4wLCAxLjAsIDAuMH0pKQoJCUFTU0VSVChwLnRydW5jKCkgPT0gKFZlY3RvcjwyPnsyLjAsIDIuMH0pKQoJCQoJCUFTU0VSVChwID09IChWZWN0b3I8Mz57Mi4wLCAyLjAsIDEuMH0pKQoJCUFTU0VSVChxID09IChWZWN0b3I8Mz57MS4wLCAtMi4wLCAwLjB9KSkKCQlBU1NFUlQocCAhPSAoVmVjdG9yPDM+ezEuMCwgLTIuMCwgMC4wfSkpCgkJQVNTRVJUKHEgIT0gKFZlY3RvcjwzPnsyLjAsIDIuMCwgMS4wfSkpCgkJQVNTRVJUKC1wID09IChWZWN0b3I8Mz57LTIuMCwgLTIuMCwgLTEuMH0pKQoJCUFTU0VSVCgtcSA9PSAoVmVjdG9yPDM+ey0xLjAsIDIuMCwgMC4wfSkpCgkJQVNTRVJUKHAgKiAyLjAgPT0gKFZlY3RvcjwzPns0LjAsIDQuMCwgMi4wfSkpCgkJQVNTRVJUKHEgKiAtMy4wID09IChWZWN0b3I8Mz57LTMuMCwgNi4wLCAwLjB9KSkKCQlBU1NFUlQocCAvIDIuMCA9PSAoVmVjdG9yPDM+ezEuMCwgMS4wLCAwLjV9KSkKCQlBU1NFUlQocSAvIC0zLjAgPT0gKFZlY3RvcjwzPnsxLjAgLyAtMy4wLCAtMi4wIC8gLTMuMCwgMC4wfSkpCgkJCgkJQVNTRVJUKHAgKyBxID09IChWZWN0b3I8Mz57My4wLCAwLjAsIDEuMH0pKQoJCUFTU0VSVChxICsgcCA9PSAoVmVjdG9yPDM+ezMuMCwgMC4wLCAxLjB9KSkKCQlBU1NFUlQocCAtIHEgPT0gKFZlY3RvcjwzPnsxLjAsIDQuMCwgMS4wfSkpCgkJQVNTRVJUKHEgLSBwID09IChWZWN0b3I8Mz57LTEuMCwgLTQuMCwgLTEuMH0pKQoJCQoJCVZlY3RvcjwzPiByOwoJCXIgPSBwOwoJCUFTU0VSVChyID09IChWZWN0b3I8Mz57Mi4wLCAyLjAsIDEuMH0pKQoJCUFTU0VSVCgociAqPSAzLjApID09IChWZWN0b3I8Mz57Ni4wLCA2LjAsIDMuMH0pKQoJCUFTU0VSVCgociAvPSAtMi4wKSA9PSAoVmVjdG9yPDM+ey0zLjAsIC0zLjAsIC0xLjV9KSkKCQlBU1NFUlQoKHIgKz0gKFZlY3RvcjwzPns1LjAsIC0xLjAsIDAuMH0pKSA9PSAoVmVjdG9yPDM+ezIuMCwgLTQuMCwgLTEuNX0pKQoJCUFTU0VSVCgociAtPSAoVmVjdG9yPDM+ezAuMCwgLTIuMCwgNC4wfSkpID09IChWZWN0b3I8Mz57Mi4wLCAtMi4wLCAtNS41fSkpCgkJci5jbGVhcigpOwoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzJdLCAwLjApKQoJCQoJCUFTU0VSVChuZWFyRXF1YWwocC5pbnBybyhxKSwgLTIuMCkpCgkJQVNTRVJUKHAub3V0cHJvKHEpID09IChWZWN0b3I8Mz57Mi4wLCAxLjAsIC02LjB9KSkKCX0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3M7Cgl1c2luZyBzdGQ6OnNpbjsKCXVzaW5nIHN0ZDo6c3FydDsKCQoJdm9pZCBUZXN0Mjo6cnVuKCkgY29uc3QgewoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWS5jWzBdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWS5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzBdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzFdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzJdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzJdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWS5jWzJdWzJdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzBdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzBdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzFdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzFdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzJdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzJdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzNdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzNdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzNdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWS5jWzNdWzNdLCAxLjApKQoJCQoJCU1hdHJpeDwyPiBmewoJCQkxLjAsCS0yLjAsIAoJCQktMy4wLAk0LjAsIAoJCX0sIGd7CgkJCS05LjAsCTguMCwgCgkJCTcuMCwJLTYuMCwgCgkJfTsKCQkKCQlBU1NFUlQobmVhckVxdWFsKGYuY1swXVswXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGYuY1swXVsxXSwgLTIuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChmLmNbMV1bMF0sIC0zLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZi5jWzFdWzFdLCA0LjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZy5jWzBdWzBdLCAtOS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGcuY1swXVsxXSwgOC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGcuY1sxXVswXSwgNy4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGcuY1sxXVsxXSwgLTYuMCkpCgkJCgkJQVNTRVJUKGYuZXh0KCkgPT0gKE1hdHJpeDwzPnsKCQkJMS4wLAktMi4wLAkwLjAsIAoJCQktMy4wLAk0LjAsCTAuMCwgCgkJCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJCUFTU0VSVChmLmV4dCgoTWF0cml4PDM+ewoJCQkzLjAsCS04LjAsCTUuMCwgCgkJCS0yLjAsCTcuMCwJNC4wLCAKCQkJMS4wLAktNS4wLAkwLjAsIAoJCX0pKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsCS0yLjAsCTUuMCwgCgkJCS0zLjAsCTQuMCwJNC4wLCAKCQkJMS4wLAktNS4wLAkwLjAsIAoJCX0pKQoJCUFTU0VSVChmLnRydW5jKCkgPT0gKE1hdHJpeDwxPnsKCQkJMS4wLCAKCQl9KSkKCQkKCQlBU1NFUlQoZiA9PSAoTWF0cml4PDI+ewoJCQkxLjAsCS0yLjAsIAoJCQktMy4wLAk0LjAsIAoJCX0pKQoJCUFTU0VSVChnID09IChNYXRyaXg8Mj57CgkJCS05LjAsCTguMCwgCgkJCTcuMCwJLTYuMCwgCgkJfSkpCgkJQVNTRVJUKGYgIT0gKE1hdHJpeDwyPnsKCQkJLTkuMCwJOC4wLCAKCQkJNy4wLAktNi4wLCAKCQl9KSkKCQlBU1NFUlQoZyAhPSAoTWF0cml4PDI+ewoJCQkxLjAsCS0yLjAsIAoJCQktMy4wLAk0LjAsIAoJCX0pKQoJCQoJCUFTU0VSVCgtZiA9PSAoTWF0cml4PDI+ewoJCQktMS4wLAkyLjAsIAoJCQkzLjAsCS00LjAsIAoJCX0pKQoJCUFTU0VSVCgtZyA9PSAoTWF0cml4PDI+ewoJCQk5LjAsCS04LjAsIAoJCQktNy4wLAk2LjAsIAoJCX0pKQoJCUFTU0VSVChmICsgZyA9PSAoTWF0cml4PDI+ewoJCQktOC4wLAk2LjAsIAoJCQk0LjAsCS0yLjAsIAoJCX0pKQoJCUFTU0VSVChmIC0gZyA9PSAoTWF0cml4PDI+ewoJCQkxMC4wLAktMTAuMCwgCgkJCS0xMC4wLAkxMC4wLCAKCQl9KSkKCQlBU1NFUlQoZiAqIGcgPT0gKE1hdHJpeDwyPnsKCQkJLTIzLjAsCTIwLjAsIAoJCQk1NS4wLAktNDguMCwgCgkJfSkpCgkJQVNTRVJUKGcgKiBmID09IChNYXRyaXg8Mj57CgkJCS0zMy4wLAk1MC4wLCAKCQkJMjUuMCwJLTM4LjAsIAoJCX0pKQoJCUFTU0VSVChmICogTWF0cml4PDI+OjpJREVOVElUWSA9PSAoTWF0cml4PDI+ewoJCQkxLjAsCS0yLjAsIAoJCQktMy4wLAk0LjAsIAoJCX0pKQoJCUFTU0VSVChNYXRyaXg8Mj46OklERU5USVRZICogZyA9PSAoTWF0cml4PDI+ewoJCQktOS4wLAk4LjAsIAoJCQk3LjAsCS02LjAsIAoJCX0pKQoJCUFTU0VSVChmICogMy4wID09IChNYXRyaXg8Mj57CgkJCTMuMCwJLTYuMCwgCgkJCS05LjAsCTEyLjAsIAoJCX0pKQoJCUFTU0VSVChnIC8gLTIuMCA9PSAoTWF0cml4PDI+ewoJCQk0LjUsCS00LjAsIAoJCQktMy41LAkzLjAsIAoJCX0pKQoJCQoJCU1hdHJpeDwyPiBoOwoJCWggPSBmOwoJCUFTU0VSVChoID09IChNYXRyaXg8Mj57CgkJCTEuMCwJLTIuMCwgCgkJCS0zLjAsCTQuMCwgCgkJfSkpCgkJQVNTRVJUKChoICs9IChNYXRyaXg8Mj57CgkJCTAuMSwJLTAuMiwgCgkJCS0wLjMsCTAuNCwgCgkJfSkpID09IChNYXRyaXg8Mj57CgkJCTEuMSwJLTIuMiwgCgkJCS0zLjMsCTQuNCwgCgkJfSkpCgkJQVNTRVJUKChoIC09IChNYXRyaXg8Mj57CgkJCS0wLjksCTAuOCwgCgkJCTAuNywJLTAuNiwgCgkJfSkpID09IChNYXRyaXg8Mj57CgkJCTIuMCwJLTMuMCwgCgkJCS00LjAsCTUuMCwgCgkJfSkpCgkJQVNTRVJUKChoICo9IChNYXRyaXg8Mj57CgkJCS00LjAsCTMuMCwgCgkJCS0yLjAsCTEuMCwgCgkJfSkpID09IChNYXRyaXg8Mj57CgkJCS0yLjAsCTMuMCwgCgkJCTYuMCwJLTcuMCwgCgkJfSkpCgkJQVNTRVJUKChoICo9IDMuMCkgPT0gKE1hdHJpeDwyPnsKCQkJLTYuMCwJOS4wLCAKCQkJMTguMCwJLTIxLjAsIAoJCX0pKQoJCUFTU0VSVCgoaCAvPSAtMi4wKSA9PSAoTWF0cml4PDI+ewoJCQkzLjAsCS00LjUsIAoJCQktOS4wLAkxMC41LCAKCQl9KSkKCQloLmNsZWFyKCk7CgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMF1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMV1bMV0sIDAuMCkpCgkJCgkJQVNTRVJUKG5lYXJFcXVhbCgoTWF0cml4PDI+ewoJCQkyLjAsCTcuMCwgCgkJCS0zLjAsCTAuNSwgCgkJfSkuZGV0KCksIDIyLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoKE1hdHJpeDwzPnsKCQkJMC4wLCAwLjAsIDEuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIAoJCQkxLjAsIDAuMCwgMC4wLCAKCQl9KS5kZXQoKSwgLTEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbCgoTWF0cml4PDM+ewoJCQkwLjUsCQkJCXNxcnQoMy4wKSAvIDIuMCwJMC4wLCAKCQkJLXNxcnQoMy4wKSAvIDIuMCwJMC41LAkJCQkwLjAsIAoJCQkwLjAsCQkJCTAuMCwJCQkJMS4wLCAKCQl9KS5kZXQoKSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKChNYXRyaXg8Mz57CgkJCTUsCTcsCTEsIAoJCQkxNywJMiwJNjQsIAoJCQkxMCwJMTQsCTIsIAoJCX0pLmRldCgpLCAwLjApKQoJCQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkyLjAsCTAuMCwJMC4wLCAKCQkJMC4wLAkzLjAsCTAuMCwgCgkJCTAuMCwJMC4wLAk0LjAsIAoJCX0pLmludigpID09IChNYXRyaXg8Mz57CgkJCTEyLjAsCTAuMCwJMC4wLCAKCQkJMC4wLAk4LjAsCTAuMCwgCgkJCTAuMCwJMC4wLAk2LjAsIAoJCX0pIC8gMjQuMCkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMi4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJMy4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJNC4wLCAKCQl9KSAqICgoTWF0cml4PDM+ewoJCQkxMi4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJOC4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJNi4wLCAKCQl9KSAvIDI0LjApID09IE1hdHJpeDwzPjo6SURFTlRJVFkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTEuMCwJMC4wLAkwLjAsIAoJCQkwLjAsCTIuMCwJMi4wLCAKCQkJMy4wLAkwLjAsCTguMCwgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJMTYuMCwJMC4wLAkwLjAsIAoJCQk2LjAsCTguMCwJLTIuMCwgCgkJCS02LjAsCTAuMCwJMi4wLCAKCQl9KSAvIDE2LjApCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTEuMCwJMC4wLAkwLjAsIAoJCQkwLjAsCTIuMCwJMi4wLCAKCQkJMy4wLAkwLjAsCTguMCwgCgkJfSkgKiAoKE1hdHJpeDwzPnsKCQkJMTYuMCwJMC4wLAkwLjAsIAoJCQk2LjAsCTguMCwJLTIuMCwgCgkJCS02LjAsCTAuMCwJMi4wLCAKCQl9KSAvIDE2LjApID09IE1hdHJpeDwzPjo6SURFTlRJVFkpCgkJRkxPQVQgdGhldGFfcmFkID0gYW5nMnJhZCg2MC4wKTsKCQlGTE9BVCBjb3NfcmVzID0gY29zKHRoZXRhX3JhZCk7CgkJRkxPQVQgc2luX3JlcyA9IHNpbih0aGV0YV9yYWQpOwoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQljb3NfcmVzLAkwLjAsCS1zaW5fcmVzLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCXNpbl9yZXMsCTAuMCwJY29zX3JlcywgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJY29zX3JlcywJMC4wLAlzaW5fcmVzLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCS1zaW5fcmVzLAkwLjAsCWNvc19yZXMsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQljb3NfcmVzLAkwLjAsCS1zaW5fcmVzLCAKCQkJMC4wLAkxLjAsCTAuMCwgCgkJCXNpbl9yZXMsCTAuMCwJY29zX3JlcywgCgkJfSkgKiAoTWF0cml4PDM+ewoJCQljb3NfcmVzLAkwLjAsCXNpbl9yZXMsIAoJCQkwLjAsCTEuMCwJMC4wLCAKCQkJLXNpbl9yZXMsCTAuMCwJY29zX3JlcywgCgkJfSkgPT0gTWF0cml4PDM+OjpJREVOVElUWSkKCQlBU1NFUlQoKE1hdHJpeDw0PnsKCQkJMS4wLAkwLjAsCTAuMCwJNC4wLCAKCQkJMC4wLAkxLjAsCTAuMCwJMy4wLCAKCQkJMC4wLAkwLjAsCTEuMCwJNy4wLCAKCQkJMC4wLAkwLjAsCTAuMCwJMS4wLCAKCQl9KS5pbnYoKSA9PSAoTWF0cml4PDQ+ewoJCQkxLjAsCTAuMCwJMC4wLAktNC4wLCAKCQkJMC4wLAkxLjAsCTAuMCwJLTMuMCwgCgkJCTAuMCwJMC4wLAkxLjAsCS03LjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDQ+ewoJCQkxLjAsCTAuMCwJMC4wLAk0LjAsIAoJCQkwLjAsCTEuMCwJMC4wLAkzLjAsIAoJCQkwLjAsCTAuMCwJMS4wLAk3LjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pICogKE1hdHJpeDw0PnsKCQkJMS4wLAkwLjAsCTAuMCwJLTQuMCwgCgkJCTAuMCwJMS4wLAkwLjAsCS0zLjAsIAoJCQkwLjAsCTAuMCwJMS4wLAktNy4wLCAKCQkJMC4wLAkwLjAsCTAuMCwJMS4wLCAKCQl9KSA9PSBNYXRyaXg8ND46OklERU5USVRZKQoJCQoJCUFTU0VSVCgoTWF0cml4PDI+ewoJCQkxLjAsIC0zLjAsIAoJCQktNC4wLCA2LjAsIAoJCX0pICogKFZlY3RvcjwyPnsyLjAsIDUuMH0pID09IChWZWN0b3I8Mj57LTEzLjAsIDIyLjB9KSkKCQlBU1NFUlQoKE1hdHJpeDwyPnsKCQkJMC4wLCA4LjAsIAoJCQktOS4wLCAzLjAsIAoJCX0pICogKFZlY3RvcjwyPnsxLjAsIC00LjB9KSA9PSAoVmVjdG9yPDI+ey0zMi4wLCAtMjEuMH0pKQoJfQp9OwoKI2luY2x1ZGUgPGNtYXRoPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnNxcnQ7CgkKCXZvaWQgVGVzdDM6OnJ1bigpIGNvbnN0IHsKCQlBU1NFUlQoc2NhbGluZygoVmVjdG9yPDM+ezEuMCwgLTIuMCwgMC4wfSkpID09IChNYXRyaXg8Mz57CgkJCTEuMCwJMC4wLAkwLjAsIAoJCQkwLjAsCS0yLjAsCTAuMCwgCgkJCTAuMCwJMC4wLAkwLjAsIAoJCX0pKQoJCUFTU0VSVChzY2FsaW5nKChWZWN0b3I8Mz57MC4wLCA5LjAsIC04LjB9KSkgPT0gKE1hdHJpeDwzPnsKCQkJMC4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJOS4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJLTguMCwgCgkJfSkpCgkJCgkJQVNTRVJUKHJvdGF0aW9uKChWZWN0b3I8Mz57MS4wLCAwLjAsIDAuMH0pLCBhbmcycmFkKDMwLjApKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsCQkJCTAuMCwJCQkJMC4wLCAKCQkJMC4wLAkJCQlzcXJ0KDMuMCkgLyAyLjAsCS0wLjUsIAoJCQkwLjAsCQkJCTAuNSwJCQkJc3FydCgzLjApIC8gMi4wLCAKCQl9KSkKCQlBU1NFUlQocm90YXRpb24oKFZlY3RvcjwzPnswLjAsIDEuMCwgMC4wfSksIGFuZzJyYWQoLTQ1LjApKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAgLyBzcXJ0KDIuMCksCTAuMCwJCQkJLTEuMCAvIHNxcnQoMi4wKSwgCgkJCTAuMCwJCQkJMS4wLAkJCQkwLjAsIAoJCQkxLjAgLyBzcXJ0KDIuMCksCTAuMCwJCQkJMS4wIC8gc3FydCgyLjApLCAKCQl9KSkKCQlBU1NFUlQocm90YXRpb24oKFZlY3RvcjwzPnswLjAsIDAuMCwgMS4wfSksIGFuZzJyYWQoNjAuMCkpID09IChNYXRyaXg8Mz57CgkJCTAuNSwJCQkJLXNxcnQoMy4wKSAvIDIuMCwJMC4wLCAKCQkJc3FydCgzLjApIC8gMi4wLAkwLjUsCQkJCTAuMCwgCgkJCTAuMCwJCQkJMC4wLAkJCQkxLjAsIAoJCX0pKQoJCQoJCUFTU0VSVCh0cmFuc2xhdGlvbigoVmVjdG9yPDM+ezEuMCwgLTIuMCwgMC4wfSkpID09IChNYXRyaXg8ND57CgkJCTEuMCwJMC4wLAkwLjAsCTEuMCwgCgkJCTAuMCwJMS4wLAkwLjAsCS0yLjAsIAoJCQkwLjAsCTAuMCwJMS4wLAkwLjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJCUFTU0VSVCh0cmFuc2xhdGlvbigoVmVjdG9yPDM+ezAuMCwgOS4wLCAtOC4wfSkpID09IChNYXRyaXg8ND57CgkJCTEuMCwJMC4wLAkwLjAsCTAuMCwgCgkJCTAuMCwJMS4wLAkwLjAsCTkuMCwgCgkJCTAuMCwJMC4wLAkxLjAsCS04LjAsIAoJCQkwLjAsCTAuMCwJMC4wLAkxLjAsIAoJCX0pKQoJfQp9OwoKI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RyaW5nPgoKaW50IG1haW4oKSB7Cgl1c2luZyBzdGQ6OmNlcnI7Cgl1c2luZyBzdGQ6OmVuZGw7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJdHJ5IHsKCQloM2Q6OlRlc3RTZXQoKS5ydW4oKTsKCX0KCWNhdGNoIChjb25zdCBzdHJpbmcmIG1zZykgewoJCWNlcnIgPDwgbXNnIDw8IGVuZGw7CgkJcmV0dXJuIDE7Cgl9CglyZXR1cm4gMDsKfQo=
アサート成功: nearEqual(p.c[0], 2.0)
アサート成功: nearEqual(p.c[1], 2.0)
アサート成功: nearEqual(p.c[2], 1.0)
アサート成功: nearEqual(q.c[0], 1.0)
アサート成功: nearEqual(q.c[1], -2.0)
アサート成功: nearEqual(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.trunc() == (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})
アサート成功: nearEqual(r.c[0], 0.0)
アサート成功: nearEqual(r.c[1], 0.0)
アサート成功: nearEqual(r.c[2], 0.0)
アサート成功: nearEqual(p.inpro(q), -2.0)
アサート成功: p.outpro(q) == (Vector<3>{2.0, 1.0, -6.0})
アサート成功: nearEqual(Matrix<2>::IDENTITY.c[0][0], 1.0)
アサート成功: nearEqual(Matrix<2>::IDENTITY.c[0][1], 0.0)
アサート成功: nearEqual(Matrix<2>::IDENTITY.c[1][0], 0.0)
アサート成功: nearEqual(Matrix<2>::IDENTITY.c[1][1], 1.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[0][0], 1.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[0][1], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[0][2], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[1][0], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[1][1], 1.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[1][2], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[2][0], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[2][1], 0.0)
アサート成功: nearEqual(Matrix<3>::IDENTITY.c[2][2], 1.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[0][0], 1.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[0][1], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[0][2], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[0][3], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[1][0], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[1][1], 1.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[1][2], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[1][3], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[2][0], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[2][1], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[2][2], 1.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[2][3], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[3][0], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[3][1], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[3][2], 0.0)
アサート成功: nearEqual(Matrix<4>::IDENTITY.c[3][3], 1.0)
アサート成功: nearEqual(f.c[0][0], 1.0)
アサート成功: nearEqual(f.c[0][1], -2.0)
アサート成功: nearEqual(f.c[1][0], -3.0)
アサート成功: nearEqual(f.c[1][1], 4.0)
アサート成功: nearEqual(g.c[0][0], -9.0)
アサート成功: nearEqual(g.c[0][1], 8.0)
アサート成功: nearEqual(g.c[1][0], 7.0)
アサート成功: nearEqual(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.trunc() == (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, })
アサート成功: nearEqual(h.c[0][0], 0.0)
アサート成功: nearEqual(h.c[0][1], 0.0)
アサート成功: nearEqual(h.c[1][0], 0.0)
アサート成功: nearEqual(h.c[1][1], 0.0)
アサート成功: nearEqual((Matrix<2>{ 2.0, 7.0, -3.0, 0.5, }).det(), 22.0)
アサート成功: nearEqual((Matrix<3>{ 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, }).det(), -1.0)
アサート成功: nearEqual((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)
アサート成功: nearEqual((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>{ cos_res, 0.0, -sin_res, 0.0, 1.0, 0.0, sin_res, 0.0, cos_res, }).inv() == (Matrix<3>{ cos_res, 0.0, sin_res, 0.0, 1.0, 0.0, -sin_res, 0.0, cos_res, })
アサート成功: (Matrix<3>{ cos_res, 0.0, -sin_res, 0.0, 1.0, 0.0, sin_res, 0.0, cos_res, }) * (Matrix<3>{ cos_res, 0.0, sin_res, 0.0, 1.0, 0.0, -sin_res, 0.0, cos_res, }) == 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})
アサート成功: scaling((Vector<3>{1.0, -2.0, 0.0})) == (Matrix<3>{ 1.0, 0.0, 0.0, 0.0, -2.0, 0.0, 0.0, 0.0, 0.0, })
アサート成功: scaling((Vector<3>{0.0, 9.0, -8.0})) == (Matrix<3>{ 0.0, 0.0, 0.0, 0.0, 9.0, 0.0, 0.0, 0.0, -8.0, })
アサート成功: rotation((Vector<3>{1.0, 0.0, 0.0}), ang2rad(30.0)) == (Matrix<3>{ 1.0, 0.0, 0.0, 0.0, sqrt(3.0) / 2.0, -0.5, 0.0, 0.5, sqrt(3.0) / 2.0, })
アサート成功: rotation((Vector<3>{0.0, 1.0, 0.0}), ang2rad(-45.0)) == (Matrix<3>{ 1.0 / sqrt(2.0), 0.0, -1.0 / sqrt(2.0), 0.0, 1.0, 0.0, 1.0 / sqrt(2.0), 0.0, 1.0 / sqrt(2.0), })
アサート成功: rotation((Vector<3>{0.0, 0.0, 1.0}), ang2rad(60.0)) == (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, })
アサート成功: translation((Vector<3>{1.0, -2.0, 0.0})) == (Matrix<4>{ 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, -2.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, })
アサート成功: translation((Vector<3>{0.0, 9.0, -8.0})) == (Matrix<4>{ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 9.0, 0.0, 0.0, 1.0, -8.0, 0.0, 0.0, 0.0, 1.0, })