namespace h3d {
template <unsigned int N> class Matrix;
template <unsigned int N> class Vector;
using FLOAT = double;
class Test;
class TestSet;
class Test1;
class Test2;
class Test3;
};
#include <iostream>
namespace h3d {
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;
/**
* 長さを取得する。
* @return 取得した長さ。
*/
inline double norm() const;
/**
* 正規化して、長さを1にする。
* @return 正規化したベクトル。
*/
inline Vector normalize() 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;
/**
* ゼロベクトルを取得する。
* @return 取得したゼロベクトル。
*/
static constexpr Vector ZERO();
};
/**
* ベクトルを出力する。
* @param N ベクトルの次元数。
* @param out 出力先となるストリーム。
* @param vec 出力するベクトル。
* @return 出力したストリーム。
*/
template <unsigned int N>
inline ostream& operator <<(ostream& out, const Vector<N>& vec);
template <>
constexpr Vector<2> Vector<2>::ZERO() {
return Vector<2>{0.0, 0.0};
}
template <>
constexpr Vector<3> Vector<3>::ZERO() {
return Vector<3>{0.0, 0.0, 0.0};
}
template <>
constexpr Vector<4> Vector<4>::ZERO() {
return Vector<4>{0.0, 0.0, 0.0, 0.0};
}
};
namespace h3d {
/** 原点(Origin PoinT)。 */
constexpr Vector<3> O_PT {0.0, 0.0, 0.0};
/** X軸。 */
constexpr Vector<3> X_AXIS {1.0, 0.0, 0.0};
/** Y軸。 */
constexpr Vector<3> Y_AXIS {0.0, 1.0, 0.0};
/** Z軸。 */
constexpr Vector<3> Z_AXIS {0.0, 0.0, 1.0};
};
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);
/**
* ビュー行列を作る。
* ビュー行列は、ワールド空間からカメラ空間への変換を行う。
* @param eye_pos 目の位置(POSition)。
* @param cent_pt 視界の中央(CENTer)にある点(PoinT)。
* @param up_dir 上方向(UP DIRection)。省略ならY軸。
* @return 作ったビュー行列。
*/
Matrix<4> view(const Vector<3>& eye_pos, const Vector<3>& cent_pt, const Vector<3>& up_dir = Y_AXIS);
/**
* 透視射影(PERSpective PROJection)行列を作る。
* 透視射影行列は、カメラ空間からクリップ空間への変換を行う。
* @param hori_fov 水平視野角(HORIzontal Field Of View)。弧度。
* @param asp_rat アスペクト比(ASPect RATio)。
* @param near_dist 近平面までの距離(DISTance)。
* @param far_dist 遠平面までの距離(DISTance)。
*/
Matrix<4> persProj(const double& hori_fov, const double& asp_rat, const double& near_dist, const double& far_dist);
};
namespace h3d {
/**
* 角度(ANGle)を弧度(RADian)に変換する。
* @param ang 変換する角度(ANGle)。
* @return 変換した弧度。
*/
inline FLOAT ang2rad(const FLOAT& ang);
};
#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];
/**
* 各成分をゼロクリアする。
*/
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;
/**
* 単位行列を取得する。
* @return 単位行列。
*/
static constexpr Matrix IDENTITY();
/**
* 逆行列(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);
template <>
constexpr Matrix<2> Matrix<2>::IDENTITY() {
return Matrix<2>{
1.0, 0.0,
0.0, 1.0,
};
}
template <>
constexpr Matrix<3> Matrix<3>::IDENTITY() {
return Matrix<3>{
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
};
}
template <>
constexpr Matrix<4> Matrix<4>::IDENTITY() {
return Matrix<4>{
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,
};
}
};
namespace h3d {
/**
* 二つの数が近いかどうかを判定する。
* @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 判定する述語(PREDicate)を記述した文字列(STRing)。
* @param pred_res 判定する述語(PREDicate)の結果(RESult)。
* 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 {
using std::tan;
Matrix<3> scaling(const Vector<3>& coef) {
// 主対角成分に係数を配置する。
// |a 0 0|
// |0 b 0|
// |0 0 c|
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) {
// 公式に従って行列を作る。
Vector<3> nmz_axis = axis.normalize();
FLOAT xx = nmz_axis.c[0] * nmz_axis.c[0];
FLOAT xy = nmz_axis.c[0] * nmz_axis.c[1];
FLOAT xz = nmz_axis.c[0] * nmz_axis.c[2];
FLOAT yy = nmz_axis.c[1] * nmz_axis.c[1];
FLOAT yz = nmz_axis.c[1] * nmz_axis.c[2];
FLOAT zz = nmz_axis.c[2] * nmz_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 = nmz_axis.c[2] * s;
FLOAT xzd = xz * d;
FLOAT ys = nmz_axis.c[1] * s;
FLOAT yyd = yy * d;
FLOAT yzd = yz * d;
FLOAT xs = nmz_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) {
// 単位行列の第4列に平行移動量を配置する。。
// |1 0 0 x|
// |0 1 0 y|
// |0 0 1 z|
// |0 0 0 1|
Matrix<4> res(Matrix<4>::IDENTITY());
for (auto i = 0; i < 3; i++) res.c[i][3] = os.c[i];
return res;
}
Matrix<4> view(const Vector<3>& eye_pos, const Vector<3>& cent_pt, const Vector<3>& up_dir) {
// ビュー行列は、目の位置までの平行移動→視線回転→上方向回転の順序で変換を行う。
// 目の位置が原点にくるように平行移動行列を作る。
Matrix<4> eye_pos_trans(translation(-eye_pos));
// 視線についての回転行列を作る。視線が逆Z軸になるように回転する。
Matrix<3> ec_iz_rot(Matrix<3>::IDENTITY());
Vector<3> nmz_ec = (cent_pt - eye_pos).normalize();
// 視線と逆Z軸の法線を計算する。
Vector<3> ec_iz_nml = nmz_ec.outpro(-Z_AXIS);
// 視線と逆Z軸が平行ではないなら、法線まわりに視線と逆Z軸がなす角度で回転する。
if (ec_iz_nml != Vector<3>::ZERO())
ec_iz_rot = rotation(ec_iz_nml, acos(nmz_ec.inpro(-Z_AXIS)));
// 視線がZ軸なら、Y軸まわりに反転する。
else if (nmz_ec == Z_AXIS) ec_iz_rot = rotation(Y_AXIS, M_PI);
// 上方向についての回転行列を作る。上方向がY軸になるように回転する。
Matrix<3> u_y_rot(Matrix<3>::IDENTITY());
Vector<3> nmz_up_dir = up_dir.normalize();
// 上方向とY軸の法線を計算する。
Vector<3> u_y_nml = nmz_up_dir.outpro(Y_AXIS);
// 上方向とY軸が平行ではないなら、法線まわりに上方向とY軸がなす角度で回転する。
if (u_y_nml != Vector<3>::ZERO())
u_y_rot = rotation(u_y_nml, acos(nmz_up_dir.inpro(Y_AXIS)));
// 上方向が逆Y軸なら、Z軸まわりに反転する。
else if (nmz_up_dir == -Y_AXIS) u_y_rot = rotation(Z_AXIS, M_PI);
// 作った3つの行列を連結する。右から順に配置する。
return (u_y_rot * ec_iz_rot).ext() * eye_pos_trans;
}
Matrix<4> persProj(const double& hori_fov, const double& asp_rat, const double& near_dist, const double& far_dist) {
// 焦点距離(FOCal DISTance)。
double foc_dist = 1.0 / tan(hori_fov / 2.0);
// 右平面と近平面が交差するX座標。
double right_x = near_dist / foc_dist;
// 上平面と近平面が交差するX座標。
double top_y = asp_rat * near_dist / foc_dist;
// 公式に従って行列を作る。
double n2 = near_dist + near_dist;
double n2f = n2 * far_dist;
double fsn = far_dist - near_dist;
double fan = far_dist + near_dist;
double wid = right_x + right_x;
double hei = top_y + top_y;
return Matrix<4>{
n2 / wid, 0.0, 0.0, 0.0,
0.0, n2 / hei, 0.0, 0.0,
0.0, 0.0, -fan / fsn, -n2f / fsn,
0.0, 0.0, -1.0, 0.0,
};
}
};
#include <cmath>
namespace h3d {
inline FLOAT ang2rad(const FLOAT& ang) {
return M_PI * ang / 180.0;
}
};
#include <iostream>
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 << "}";
}
};
#include <algorithm>
#include <cmath>
#include <iostream>
namespace h3d {
using std::copy;
using std::ostream;
using std::sqrt;
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 double Vector<N>::norm() const {
return sqrt(this->inpro(*this));
}
template <unsigned int N>
inline Vector<N> Vector<N>::normalize() const {
return *this / norm();
}
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 << "}";
}
};
namespace h3d {
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}))
ASSERT(nearEqual(p.norm(), 3.0))
ASSERT(nearEqual(q.norm(), sqrt(5.0)))
ASSERT(p.normalize() == (Vector<3>{2.0 / 3.0, 2.0 / 3.0, 1.0 / 3.0}))
ASSERT(q.normalize() == (Vector<3>{1.0 / sqrt(5.0), -2.0 / sqrt(5.0), 0.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,
}))
ASSERT((view(
(Vector<3>{0.0, -3.0, 9.0}), (Vector<3>{0.0, -3.0, 0.0})) *
(Vector<3>{3.0, 5.0, 7.0}).ext()).trunc() ==
(Vector<3>{3.0, 8.0, -2.0}))
ASSERT((view(
O_PT, X_AXIS) *
(Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() ==
(Vector<3>{-5.0, 0.0, 0.0}))
ASSERT((view(
O_PT, -Y_AXIS) *
(Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() ==
(Vector<3>{0.0, 5.0, 0.0}))
ASSERT((view(
O_PT, Z_AXIS) *
(Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() ==
(Vector<3>{0.0, 0.0, 5.0}))
ASSERT((view(
O_PT, -Z_AXIS,
(Vector<3>{1.0, 0.0, 0.0})) *
(Vector<3>{3.0, 0.0, 0.0}).ext()).trunc() ==
(Vector<3>{0.0, 3.0, 0.0}))
ASSERT((view(
O_PT, -Z_AXIS,
(Vector<3>{-1.0, 0.0, 0.0})) *
(Vector<3>{0.0, -3.0, 0.0}).ext()).trunc() ==
(Vector<3>{-3.0, 0.0, 0.0}))
ASSERT((view(
O_PT, X_AXIS,
(Vector<3>{1.0, 0.0, 0.0})) *
(Vector<3>{7.0, 8.0, 0.0}).ext()).trunc() ==
(Vector<3>{-8.0, 0.0, -7.0}))
ASSERT(persProj(ang2rad(120.0), 0.75, 1.0, 10.0) == (Matrix<4>{
(1.0 * 2.0) / (sqrt(3.0) * 2.0), 0.0, 0.0, 0.0,
0.0, (1.0 * 2.0) / (0.75 * sqrt(3.0) * 2.0), 0.0, 0.0,
0.0, 0.0, -((10.0 + 1.0) / (10.0 - 1.0)), -((1.0 * 10.0 * 2.0) / (10.0 - 1.0)),
0.0, 0.0, -1.0, 0.0,
}))
ASSERT(persProj(ang2rad(90.0), 0.5, 2.0, 5.0) == (Matrix<4>{
(2.0 * 2.0) / (2.0 * 2.0), 0.0, 0.0, 0.0,
0.0, (2.0 * 2.0) / (0.5 * 2.0 * 2.0), 0.0, 0.0,
0.0, 0.0, -((5.0 + 2.0) / (5.0 - 2.0)), -((2.0 * 5.0 * 2.0) / (5.0 - 2.0)),
0.0, 0.0, -1.0, 0.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;
}
bmFtZXNwYWNlIGgzZCB7CiAgICB0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IGNsYXNzIE1hdHJpeDsKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gY2xhc3MgVmVjdG9yOwoJdXNpbmcgRkxPQVQgPSBkb3VibGU7CgljbGFzcyBUZXN0OwoJY2xhc3MgVGVzdFNldDsKCWNsYXNzIFRlc3QxOwoJY2xhc3MgVGVzdDI7CgljbGFzcyBUZXN0MzsKfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJCgkvKioKCSAqIE7mrKHlhYPjg5njgq/jg4jjg6vjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBOIOODmeOCr+ODiOODq+OBruasoeWFg+aVsOOAggoJICovCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJY2xhc3MgVmVjdG9yIHsKCXB1YmxpYzoKCQkvKiog5oiQ5YiGKENvbXBvbmVudCnjga7phY3liJfjgIJO5YCL44CCICovCgkJRkxPQVQgY1tOXTsKCQkKCQkvKioKCQkgKiDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgdm9pZCBjbGVhcigpOwoJCQoJCS8qKgoJCSAqIOasoeWFg+OCkuS4gOOBpOaLoeW8tShFWFRlbmQp44GZ44KL44CCCgkJICogQHBhcmFtIGMg6L+95Yqg44GZ44KL5oiQ5YiG44CCCgkJICogQHJldHVybiDmi6HlvLXjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4gKyAxPiBleHQoY29uc3QgRkxPQVQmIGMgPSAxLjApIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWGheepjShJTm5lciBQUk9kdWN0KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG044Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/lhoXnqY3jgIIKCQkgKi8KCQlpbmxpbmUgRkxPQVQgaW5wcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOmVt+OBleOCkuWPluW+l+OBmeOCi+OAggoJCSAqIEByZXR1cm4g5Y+W5b6X44GX44Gf6ZW344GV44CCCgkJICovCgkJaW5saW5lIGRvdWJsZSBub3JtKCkgY29uc3Q7CgkJCgkJLyoqCgkJICog5q2j6KaP5YyW44GX44Gm44CB6ZW344GV44KSMeOBq+OBmeOCi+OAggoJCSAqIEByZXR1cm4g5q2j6KaP5YyW44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBub3JtYWxpemUoKSBjb25zdDsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7jg5njgq/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/lpJbnqY3jgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yIG91dHBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog5qyh5YWD44KS5LiA44Gk5YiH44KK57iu44KB44KLKFRSVU5DYXRlKeOAggoJCSAqIEByZXR1cm4g5YiH44KK57iu44KB44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvcjxOIC0gMT4gdHJ1bmMoKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjgrzjg63jg5njgq/jg4jjg6vjgpLlj5blvpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOWPluW+l+OBl+OBn+OCvOODreODmeOCr+ODiOODq+OAggoJCSAqLwoJCXN0YXRpYyBjb25zdGV4cHIgVmVjdG9yIFpFUk8oKTsKCX07CgkKCS8qKgoJICog44OZ44Kv44OI44Or44KS5Ye65Yqb44GZ44KL44CCCgkgKiBAcGFyYW0gTiDjg5njgq/jg4jjg6vjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBvdXQg5Ye65Yqb5YWI44Go44Gq44KL44K544OI44Oq44O844Og44CCCgkgKiBAcGFyYW0gdmVjIOWHuuWKm+OBmeOCi+ODmeOCr+ODiOODq+OAggoJICogQHJldHVybiDlh7rlipvjgZfjgZ/jgrnjg4jjg6rjg7zjg6DjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFZlY3RvcjxOPiYgdmVjKTsKCQoJdGVtcGxhdGUgPD4gCgljb25zdGV4cHIgVmVjdG9yPDI+IFZlY3RvcjwyPjo6WkVSTygpIHsKCQlyZXR1cm4gVmVjdG9yPDI+ezAuMCwgMC4wfTsKCX0KCQoJdGVtcGxhdGUgPD4gCgljb25zdGV4cHIgVmVjdG9yPDM+IFZlY3RvcjwzPjo6WkVSTygpIHsKCQlyZXR1cm4gVmVjdG9yPDM+ezAuMCwgMC4wLCAwLjB9OwoJfQoJCgl0ZW1wbGF0ZSA8PiAKCWNvbnN0ZXhwciBWZWN0b3I8ND4gVmVjdG9yPDQ+OjpaRVJPKCkgewoJCXJldHVybiBWZWN0b3I8ND57MC4wLCAwLjAsIDAuMCwgMC4wfTsKCX0KfTsKCm5hbWVzcGFjZSBoM2QgewoJLyoqIOWOn+eCuShPcmlnaW4gUG9pblQp44CCICovCgljb25zdGV4cHIgVmVjdG9yPDM+IE9fUFQJCXswLjAsCTAuMCwJMC4wfTsKCS8qKiBY6Lu444CCICovCgljb25zdGV4cHIgVmVjdG9yPDM+IFhfQVhJUwkJezEuMCwJMC4wLAkwLjB9OwoJLyoqIFnou7jjgIIgKi8KCWNvbnN0ZXhwciBWZWN0b3I8Mz4gWV9BWElTCQl7MC4wLAkxLjAsCTAuMH07CgkvKiogWui7uOOAgiAqLwoJY29uc3RleHByIFZlY3RvcjwzPiBaX0FYSVMJCXswLjAsCTAuMCwJMS4wfTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJLyoqCgkgKiDmi6HlpKfjg7vnuK7lsI/ooYzliJfjgpLkvZzjgovjgIIKCSAqIEBwYXJhbSBjb2VmIOWQhOW6p+aomeOBruS/guaVsChDT0VGZmljaWVudCnjgIIKCSAqIGNvZWYgPiAxLjAg44Gq44KJ5ouh5aSnLCBjb2VmIDwgMS4wIOOBquOCiee4ruWwj+OAggoJICogQHJldHVybiDkvZzjgaPjgZ/mi6HlpKfjg7vnuK7lsI/ooYzliJfjgIIKCSAqLwoJTWF0cml4PDM+IHNjYWxpbmcoY29uc3QgVmVjdG9yPDM+JiBjb2VmKTsKCQoJLyoqCgkgKiDlm57ou6LooYzliJfjgpLkvZzjgovjgIIKCSAqIEBwYXJhbSBheGlzIOOBk+OBrui7uOOBruWRqOOCiuOCkuWbnui7ouOBmeOCi+OAggoJICogQHBhcmFtIHJhZCDlm57ou6LjgZnjgovlvKfluqYoUkFEaWFuKeOAggoJICogQHJldHVybiDkvZzjgaPjgZ/lm57ou6LooYzliJfjgIIKCSAqLwoJTWF0cml4PDM+IHJvdGF0aW9uKGNvbnN0IFZlY3RvcjwzPiYgYXhpcywgY29uc3QgRkxPQVQmIHJhZCk7CgkKCS8qKgoJICog5bmz6KGM56e75YuV6KGM5YiX44KS5L2c44KL44CCCgkgKiBAcGFyYW0gb3Mg5aSJ5o+b5b6M44Gu5L2N572u44GL44KJ5aSJ5o+b5YmN44Gu5L2N572u44KS5byV44GE44Gf5beu5YiGKE9mZlNldCnjgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf5bmz6KGM56e75YuV6KGM5YiX44CCCgkgKi8KCU1hdHJpeDw0PiB0cmFuc2xhdGlvbihjb25zdCBWZWN0b3I8Mz4mIG9zKTsKCQoJLyoqCgkgKiDjg5Pjg6Xjg7zooYzliJfjgpLkvZzjgovjgIIKCSAqIOODk+ODpeODvOihjOWIl+OBr+OAgeODr+ODvOODq+ODieepuumWk+OBi+OCieOCq+ODoeODqeepuumWk+OBuOOBruWkieaPm+OCkuihjOOBhuOAggoJICogQHBhcmFtIGV5ZV9wb3Mg55uu44Gu5L2N572uKFBPU2l0aW9uKeOAggoJICogQHBhcmFtIGNlbnRfcHQg6KaW55WM44Gu5Lit5aSuKENFTlRlcinjgavjgYLjgovngrkoUG9pblQp44CCCgkgKiBAcGFyYW0gdXBfZGlyIOS4iuaWueWQkShVUCBESVJlY3Rpb24p44CC55yB55Wl44Gq44KJWei7uOOAggoJICogQHJldHVybiDkvZzjgaPjgZ/jg5Pjg6Xjg7zooYzliJfjgIIKCSAqLwoJTWF0cml4PDQ+IHZpZXcoY29uc3QgVmVjdG9yPDM+JiBleWVfcG9zLCBjb25zdCBWZWN0b3I8Mz4mIGNlbnRfcHQsIGNvbnN0IFZlY3RvcjwzPiYgdXBfZGlyID0gWV9BWElTKTsKCQoJLyoqCgkgKiDpgI/oppblsITlvbEoUEVSU3BlY3RpdmUgUFJPSmVjdGlvbinooYzliJfjgpLkvZzjgovjgIIKCSAqIOmAj+imluWwhOW9seihjOWIl+OBr+OAgeOCq+ODoeODqeepuumWk+OBi+OCieOCr+ODquODg+ODl+epuumWk+OBuOOBruWkieaPm+OCkuihjOOBhuOAggoJICogQHBhcmFtIGhvcmlfZm92IOawtOW5s+imlumHjuinkihIT1JJem9udGFsIEZpZWxkIE9mIFZpZXcp44CC5byn5bqm44CCCgkgKiBAcGFyYW0gYXNwX3JhdCDjgqLjgrnjg5rjgq/jg4jmr5QoQVNQZWN0IFJBVGlvKeOAggoJICogQHBhcmFtIG5lYXJfZGlzdCDov5HlubPpnaLjgb7jgafjga7ot53pm6IoRElTVGFuY2Up44CCCgkgKiBAcGFyYW0gZmFyX2Rpc3Qg6YGg5bmz6Z2i44G+44Gn44Gu6Led6ZuiKERJU1RhbmNlKeOAggoJICovCglNYXRyaXg8ND4gcGVyc1Byb2ooY29uc3QgZG91YmxlJiBob3JpX2ZvdiwgY29uc3QgZG91YmxlJiBhc3BfcmF0LCBjb25zdCBkb3VibGUmIG5lYXJfZGlzdCwgY29uc3QgZG91YmxlJiBmYXJfZGlzdCk7Cn07CgpuYW1lc3BhY2UgaDNkIHsKCS8qKgoJICog6KeS5bqmKEFOR2xlKeOCkuW8p+W6pihSQURpYW4p44Gr5aSJ5o+b44GZ44KL44CCCgkgKiBAcGFyYW0gYW5nIOWkieaPm+OBmeOCi+inkuW6pihBTkdsZSnjgIIKCSAqIEByZXR1cm4g5aSJ5o+b44GX44Gf5byn5bqm44CCCgkgKi8KCWlubGluZSBGTE9BVCBhbmcycmFkKGNvbnN0IEZMT0FUJiBhbmcpOwp9OwoKI2luY2x1ZGUgPGlvc3RyZWFtPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6Om9zdHJlYW07Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJLyoqCgkgKiBO5qyh5YWD5q2j5pa56KGM5YiX44Kv44Op44K544CCCgkgKiBAcGFyYW0gTiDooYzliJfjga7mrKHlhYPmlbDjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWNsYXNzIE1hdHJpeCB7CglwdWJsaWM6CgkJLyoqIOaIkOWIhihDb21wb25lbnQp44Gu6YWN5YiX44CCTsOXTuWAi+OAgiAqLwoJCUZMT0FUIGNbTl1bTl07CgkJCgkJLyoqCgkJICog5ZCE5oiQ5YiG44KS44K844Ot44Kv44Oq44Ki44GZ44KL44CCCgkJICovCgkJaW5saW5lIHZvaWQgY2xlYXIoKTsKCQkKCQkvKioKCQkgKiDooYzliJflvI8oREVUZXJtaW5hbnQp44KS6KiI566X44GZ44KL44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/ooYzliJflvI/jgIIKCQkgKi8KCQlpbmxpbmUgRkxPQVQgZGV0KCkgY29uc3Q7CgkJCgkJLyoqCgkJICog5qyh5YWD44KS5LiA44Gk5ouh5by1KEVYVGVuZCnjgZnjgovjgIIKCQkgKiBAcGFyYW0gbWF0IOOBk+OBruihjOWIl+OBi+OCieacgOW+jOOBruihjOOBqOWIl+OBruaIkOWIhuOCkuOCs+ODlOODvOOBmeOCi+OAggoJCSAqIEByZXR1cm4g5ouh5by144GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeDxOICsgMT4gZXh0KGNvbnN0IE1hdHJpeDxOICsgMT4mIG1hdCA9IE1hdHJpeDxOICsgMT46OklERU5USVRZKCkpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWNmOS9jeihjOWIl+OCkuWPluW+l+OBmeOCi+OAggoJCSAqIEByZXR1cm4g5Y2Y5L2N6KGM5YiX44CCCgkJICovCgkJc3RhdGljIGNvbnN0ZXhwciBNYXRyaXggSURFTlRJVFkoKTsKCQkKCQkvKioKCQkgKiDpgIbooYzliJcoSU5WZXJzZSBtYXRyaXgp44KS6KiI566X44GZ44KL44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/pgIbooYzliJfjgIIKCQkgKiBAdGhyb3cgc3RyaW5nIOihjOWIl+OBjOato+WJh+OBp+OBr+OBquOBhOOBquOCieODoeODg+OCu+ODvOOCuOOCkuOCueODreODvOOBmeOCi+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggaW52KCkgY29uc3QgdGhyb3coc3RyaW5nKTsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7ooYzliJfjgYznrYnjgZfjgY/jgarjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g562J44GX44GP44Gq44GE44Gq44KJdHJ1ZSwg562J44GX44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciAhPShjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44Gn5LmX566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKihjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44Go44Gu56mN44KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3I8Tj4gb3BlcmF0b3IgKihjb25zdCBWZWN0b3I8Tj4mIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKihjb25zdCBGTE9BVCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDooYzliJfjgafkuZfnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOS5l+aVsOOBqOOBquOCi+WPs+WBtOOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gm44CB5Luj5YWl44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAqPShjb25zdCBNYXRyaXgmIHJocyk7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgRkxPQVQmIHJocyk7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5Yqg566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDliqDmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKyhjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5Yqg566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDliqDmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKz0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOBmeOBueOBpuOBruaIkOWIhuOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCSAqIEByZXR1cm4g56ym5Y+344KS5Y+N6Lui44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAtKCkgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5rib566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgLShjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5rib566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgLT0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG044Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC8oY29uc3QgRkxPQVQmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn6Zmk566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDpmaTmlbDjgajjgarjgovlj7PlgbTjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOmZpOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgLz0oY29uc3QgRkxPQVQmIHJocyk7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu6KGM5YiX44GM562J44GX44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbTjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOetieOBl+OBhOOBquOCiXRydWUsIOetieOBl+OBj+OBquOBhOOBquOCiWZhbHNl44CCCgkJICovCgkJaW5saW5lIGJvb2wgb3BlcmF0b3IgPT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOOBqOWIl+OCkuWPluOCiumZpOOBhOOBpuOAgeWwj+ihjOWIlyhTVUJtYXRyaXgp44KS5L2c44KL44CCCgkJICogQHBhcmFtIHJvdyDlj5bjgorpmaTjgY/ooYzjgIIKCQkgKiBAcGFyYW0gY29sIOWPluOCiumZpOOBj+WIl+OAggoJCSAqIEByZXR1cm4g5L2c44Gj44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeDxOIC0gMT4gc3ViKGNvbnN0IHVuc2lnbmVkIGludCYgcm93LCBjb25zdCB1bnNpZ25lZCBpbnQmIGNvbCkgY29uc3Q7CgkJCgkJLyoqCgkJICog5qyh5YWD44KS5LiA44Gk5YiH44KK57iu44KB44KLKFRSVU5DYXRlKeOAggoJCSAqIEByZXR1cm4g5YiH44KK57iu44KB44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeDxOIC0gMT4gdHJ1bmMoKSBjb25zdDsKCX07CgkKCS8qKgoJICog6KGM5YiX44KS5Ye65Yqb44GZ44KL44CCCgkgKiBAcGFyYW0gTiDooYzliJfjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBvdXQg5Ye65Yqb5YWI44Go44Gq44KL44K544OI44Oq44O844Og44CCCgkgKiBAcGFyYW0gbWF0IOWHuuWKm+OBmeOCi+ihjOWIl+OAggoJICogQHJldHVybiDlh7rlipvjgZfjgZ/jgrnjg4jjg6rjg7zjg6DjgIIKCSAqLwoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IE1hdHJpeDxOPiYgbWF0KTsKCQoJdGVtcGxhdGUgPD4gCgljb25zdGV4cHIgTWF0cml4PDI+IE1hdHJpeDwyPjo6SURFTlRJVFkoKSB7CgkJcmV0dXJuIE1hdHJpeDwyPnsKCQkJMS4wLCAwLjAsIAoJCQkwLjAsIDEuMCwgCgkJfTsKCX0KCQoJdGVtcGxhdGUgPD4gCgljb25zdGV4cHIgTWF0cml4PDM+IE1hdHJpeDwzPjo6SURFTlRJVFkoKSB7CgkJcmV0dXJuIE1hdHJpeDwzPnsKCQkJMS4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAKCQl9OwoJfQoJCgl0ZW1wbGF0ZSA8PiAKCWNvbnN0ZXhwciBNYXRyaXg8ND4gTWF0cml4PDQ+OjpJREVOVElUWSgpIHsKCQlyZXR1cm4gTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCAwLjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX07Cgl9Cn07CgpuYW1lc3BhY2UgaDNkIHsKCS8qKgoJICog5LqM44Gk44Gu5pWw44GM6L+R44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkgKiBAcGFyYW0geCDkuIDjgaTnm67jga7mlbDjgIIKCSAqIEBwYXJhbSB5IOS6jOOBpOebruOBruaVsOOAggoJICogQHJldHVybiDov5HjgYTjgarjgol0cnVlLCDpgaDjgYTjgarjgolmYWxzZeOAggoJICovCglpbmxpbmUgYm9vbCBuZWFyRXF1YWwoY29uc3QgRkxPQVQmIHgsIGNvbnN0IEZMT0FUJiB5KTsKfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3V0OwoJdXNpbmcgc3RkOjplbmRsOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCSNkZWZpbmUgQVNTRVJUKHByZWQpIGFzc2VydCgjcHJlZCwgKHByZWQpKTsKCQoJI2RlZmluZSBQUklOVCh2YWwpIGNvdXQgPDwgI3ZhbCA8PCAiPSIgPDwgKHZhbCkgPDwgZW5kbDsKCQoJLyoqCgkgKiDjgqLjgrXjg7zjgrfjg6fjg7PjgpLlrp/ooYzjgZnjgovjgIIKCSAqIOaIkOWKn+OBquOCieaomea6luWHuuWKm+OBq+e1kOaenOOCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIHByZWRfc3RyIOWIpOWumuOBmeOCi+i/sOiqnihQUkVEaWNhdGUp44KS6KiY6L+w44GX44Gf5paH5a2X5YiXKFNUUmluZynjgIIKCSAqIEBwYXJhbSBwcmVkX3JlcyDliKTlrprjgZnjgovov7Doqp4oUFJFRGljYXRlKeOBrue1kOaenChSRVN1bHQp44CCCgkgKiB0cnVl44Gq44KJ5oiQ5Yqf77yMZmFsc2XjgarjgonlpLHmlZfjgajliKTlrprjgZnjgovjgIIKCSAqIEB0aHJvdyBzdHJpbmcg5aSx5pWX44Gq44KJ44Oh44OD44K744O844K444KS44K544Ot44O844GZ44KL44CCCgkgKi8KCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJY2xhc3MgVGVzdCB7IHB1YmxpYzogdmlydHVhbCB2b2lkIHJ1bigpIGNvbnN0ID0gMDsgfTsKfTsKCiNpbmNsdWRlIDxtZW1vcnk+CiNpbmNsdWRlIDx2ZWN0b3I+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6c2hhcmVkX3B0cjsKCXVzaW5nIHN0ZDo6dmVjdG9yOwoJCgljbGFzcyBUZXN0U2V0IHsKCXB1YmxpYzoKCQlUZXN0U2V0KCk7CgkJdm9pZCBydW4oKSBjb25zdDsKCXByb3RlY3RlZDoKCQl2ZWN0b3I8c2hhcmVkX3B0cjxUZXN0Pj4gdGVzdHM7Cgl9Owp9OwoKbmFtZXNwYWNlIGgzZCB7CgljbGFzcyBUZXN0MSA6IHB1YmxpYyBUZXN0IHsKCXB1YmxpYzoKCQl2aXJ0dWFsIHZvaWQgcnVuKCkgY29uc3Qgb3ZlcnJpZGU7Cgl9Owp9OwoKbmFtZXNwYWNlIGgzZCB7CgljbGFzcyBUZXN0MiA6IHB1YmxpYyBUZXN0IHsKCXB1YmxpYzoKCQl2aXJ0dWFsIHZvaWQgcnVuKCkgY29uc3Qgb3ZlcnJpZGU7Cgl9Owp9OwoKbmFtZXNwYWNlIGgzZCB7CgljbGFzcyBUZXN0MyA6IHB1YmxpYyBUZXN0IHsKCXB1YmxpYzoKCQl2aXJ0dWFsIHZvaWQgcnVuKCkgY29uc3Qgb3ZlcnJpZGU7Cgl9Owp9OwoKLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8KCiNpbmNsdWRlIDxjbWF0aD4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjp0YW47CgkKCU1hdHJpeDwzPiBzY2FsaW5nKGNvbnN0IFZlY3RvcjwzPiYgY29lZikgewoJCS8vIOS4u+WvvuinkuaIkOWIhuOBq+S/guaVsOOCkumFjee9ruOBmeOCi+OAggoJCS8vIHxhIDAgMHwKCQkvLyB8MCBiIDB8CgkJLy8gfDAgMCBjfAoJCU1hdHJpeDwzPiByZXMoTWF0cml4PDM+OjpJREVOVElUWSgpKTsKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCAzOyBpKyspIHJlcy5jW2ldW2ldID0gY29lZi5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCU1hdHJpeDwzPiByb3RhdGlvbihjb25zdCBWZWN0b3I8Mz4mIGF4aXMsIGNvbnN0IEZMT0FUJiByYWQpIHsKCQkvLyDlhazlvI/jgavlvpPjgaPjgabooYzliJfjgpLkvZzjgovjgIIKCQlWZWN0b3I8Mz4gbm16X2F4aXMgPSBheGlzLm5vcm1hbGl6ZSgpOwoJCUZMT0FUIHh4ID0gbm16X2F4aXMuY1swXSAqIG5tel9heGlzLmNbMF07CgkJRkxPQVQgeHkgPSBubXpfYXhpcy5jWzBdICogbm16X2F4aXMuY1sxXTsKCQlGTE9BVCB4eiA9IG5tel9heGlzLmNbMF0gKiBubXpfYXhpcy5jWzJdOwoJCUZMT0FUIHl5ID0gbm16X2F4aXMuY1sxXSAqIG5tel9heGlzLmNbMV07CgkJRkxPQVQgeXogPSBubXpfYXhpcy5jWzFdICogbm16X2F4aXMuY1syXTsKCQlGTE9BVCB6eiA9IG5tel9heGlzLmNbMl0gKiBubXpfYXhpcy5jWzJdOwoJCUZMT0FUIHMgPSBzaW4ocmFkKTsKCQlGTE9BVCBjID0gY29zKHJhZCk7CgkJRkxPQVQgZCA9IDEuMCAtIGM7CgkJRkxPQVQgeHhkID0geHggKiBkOwoJCUZMT0FUIHh5ZCA9IHh5ICogZDsKCQlGTE9BVCB6cyA9IG5tel9heGlzLmNbMl0gKiBzOwoJCUZMT0FUIHh6ZCA9IHh6ICogZDsKCQlGTE9BVCB5cyA9IG5tel9heGlzLmNbMV0gKiBzOwoJCUZMT0FUIHl5ZCA9IHl5ICogZDsKCQlGTE9BVCB5emQgPSB5eiAqIGQ7CgkJRkxPQVQgeHMgPSBubXpfYXhpcy5jWzBdICogczsKCQlGTE9BVCB6emQgPSB6eiAqIGQ7CgkJcmV0dXJuIE1hdHJpeDwzPnsKCQkJYyArIHh4ZCwgIHh5ZCAtIHpzLCB4emQgKyB5cywgCgkJCXh5ZCArIHpzLCBjICsgeXlkLCAgeXpkIC0geHMsIAoJCQl4emQgLSB5cywgeXpkICsgeHMsIGMgKyB6emQsIAoJCX07Cgl9CgkKCU1hdHJpeDw0PiB0cmFuc2xhdGlvbihjb25zdCBWZWN0b3I8Mz4mIG9zKSB7CgkJLy8g5Y2Y5L2N6KGM5YiX44Gu56ysNOWIl+OBq+W5s+ihjOenu+WLlemHj+OCkumFjee9ruOBmeOCi+OAguOAggoJCS8vIHwxIDAgMCB4fAoJCS8vIHwwIDEgMCB5fAoJCS8vIHwwIDAgMSB6fAoJCS8vIHwwIDAgMCAxfAoJCU1hdHJpeDw0PiByZXMoTWF0cml4PDQ+OjpJREVOVElUWSgpKTsKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCAzOyBpKyspIHJlcy5jW2ldWzNdID0gb3MuY1tpXTsKCQlyZXR1cm4gcmVzOwoJfQoJCglNYXRyaXg8ND4gdmlldyhjb25zdCBWZWN0b3I8Mz4mIGV5ZV9wb3MsIGNvbnN0IFZlY3RvcjwzPiYgY2VudF9wdCwgY29uc3QgVmVjdG9yPDM+JiB1cF9kaXIpIHsKCQkvLyDjg5Pjg6Xjg7zooYzliJfjga/jgIHnm67jga7kvY3nva7jgb7jgafjga7lubPooYznp7vli5XihpLoppbnt5rlm57ou6LihpLkuIrmlrnlkJHlm57ou6Ljga7poIbluo/jgaflpInmj5vjgpLooYzjgYbjgIIKCQkKCQkvLyDnm67jga7kvY3nva7jgYzljp/ngrnjgavjgY/jgovjgojjgYbjgavlubPooYznp7vli5XooYzliJfjgpLkvZzjgovjgIIKCQlNYXRyaXg8ND4gZXllX3Bvc190cmFucyh0cmFuc2xhdGlvbigtZXllX3BvcykpOwoJCQoJCS8vIOimlue3muOBq+OBpOOBhOOBpuOBruWbnui7ouihjOWIl+OCkuS9nOOCi+OAguimlue3muOBjOmAhlrou7jjgavjgarjgovjgojjgYbjgavlm57ou6LjgZnjgovjgIIKCQkKCQlNYXRyaXg8Mz4gZWNfaXpfcm90KE1hdHJpeDwzPjo6SURFTlRJVFkoKSk7CgkJVmVjdG9yPDM+IG5tel9lYyA9IChjZW50X3B0IC0gZXllX3Bvcykubm9ybWFsaXplKCk7CgkJLy8g6KaW57ea44Go6YCGWui7uOOBruazlee3muOCkuioiOeul+OBmeOCi+OAggoJCVZlY3RvcjwzPiBlY19pel9ubWwgPSBubXpfZWMub3V0cHJvKC1aX0FYSVMpOwoJCS8vIOimlue3muOBqOmAhlrou7jjgYzlubPooYzjgafjga/jgarjgYTjgarjgonjgIHms5Xnt5rjgb7jgo/jgorjgavoppbnt5rjgajpgIZa6Lu444GM44Gq44GZ6KeS5bqm44Gn5Zue6Lui44GZ44KL44CCCgkJaWYgKGVjX2l6X25tbCAhPSBWZWN0b3I8Mz46OlpFUk8oKSkgCgkJCWVjX2l6X3JvdCA9IHJvdGF0aW9uKGVjX2l6X25tbCwgYWNvcyhubXpfZWMuaW5wcm8oLVpfQVhJUykpKTsKCQkvLyDoppbnt5rjgYxa6Lu444Gq44KJ44CBWei7uOOBvuOCj+OCiuOBq+WPjei7ouOBmeOCi+OAggoJCWVsc2UgaWYgKG5tel9lYyA9PSBaX0FYSVMpIGVjX2l6X3JvdCA9IHJvdGF0aW9uKFlfQVhJUywgTV9QSSk7CgkJCgkJLy8g5LiK5pa55ZCR44Gr44Gk44GE44Gm44Gu5Zue6Lui6KGM5YiX44KS5L2c44KL44CC5LiK5pa55ZCR44GMWei7uOOBq+OBquOCi+OCiOOBhuOBq+Wbnui7ouOBmeOCi+OAggoJCQoJCU1hdHJpeDwzPiB1X3lfcm90KE1hdHJpeDwzPjo6SURFTlRJVFkoKSk7CgkJVmVjdG9yPDM+IG5tel91cF9kaXIgPSB1cF9kaXIubm9ybWFsaXplKCk7CgkJLy8g5LiK5pa55ZCR44GoWei7uOOBruazlee3muOCkuioiOeul+OBmeOCi+OAggoJCVZlY3RvcjwzPiB1X3lfbm1sID0gbm16X3VwX2Rpci5vdXRwcm8oWV9BWElTKTsKCQkvLyDkuIrmlrnlkJHjgahZ6Lu444GM5bmz6KGM44Gn44Gv44Gq44GE44Gq44KJ44CB5rOV57ea44G+44KP44KK44Gr5LiK5pa55ZCR44GoWei7uOOBjOOBquOBmeinkuW6puOBp+Wbnui7ouOBmeOCi+OAggoJCWlmICh1X3lfbm1sICE9IFZlY3RvcjwzPjo6WkVSTygpKSAKCQkJdV95X3JvdCA9IHJvdGF0aW9uKHVfeV9ubWwsIGFjb3Mobm16X3VwX2Rpci5pbnBybyhZX0FYSVMpKSk7CgkJLy8g5LiK5pa55ZCR44GM6YCGWei7uOOBquOCieOAgVrou7jjgb7jgo/jgorjgavlj43ou6LjgZnjgovjgIIKCQllbHNlIGlmIChubXpfdXBfZGlyID09IC1ZX0FYSVMpIHVfeV9yb3QgPSByb3RhdGlvbihaX0FYSVMsIE1fUEkpOwoJCQoJCS8vIOS9nOOBo+OBnzPjgaTjga7ooYzliJfjgpLpgKPntZDjgZnjgovjgILlj7PjgYvjgonpoIbjgavphY3nva7jgZnjgovjgIIKCQlyZXR1cm4gKHVfeV9yb3QgKiBlY19pel9yb3QpLmV4dCgpICogZXllX3Bvc190cmFuczsKCX0KCQoJTWF0cml4PDQ+IHBlcnNQcm9qKGNvbnN0IGRvdWJsZSYgaG9yaV9mb3YsIGNvbnN0IGRvdWJsZSYgYXNwX3JhdCwgY29uc3QgZG91YmxlJiBuZWFyX2Rpc3QsIGNvbnN0IGRvdWJsZSYgZmFyX2Rpc3QpIHsKCQkvLyDnhKbngrnot53pm6IoRk9DYWwgRElTVGFuY2Up44CCCgkJZG91YmxlIGZvY19kaXN0ID0gMS4wIC8gdGFuKGhvcmlfZm92IC8gMi4wKTsKCQkvLyDlj7PlubPpnaLjgajov5HlubPpnaLjgYzkuqTlt67jgZnjgotY5bqn5qiZ44CCCgkJZG91YmxlIHJpZ2h0X3ggPSBuZWFyX2Rpc3QgLyBmb2NfZGlzdDsKCQkvLyDkuIrlubPpnaLjgajov5HlubPpnaLjgYzkuqTlt67jgZnjgotY5bqn5qiZ44CCCgkJZG91YmxlIHRvcF95ID0gYXNwX3JhdCAqIG5lYXJfZGlzdCAvIGZvY19kaXN0OwoJCQoJCS8vIOWFrOW8j+OBq+W+k+OBo+OBpuihjOWIl+OCkuS9nOOCi+OAggoJCWRvdWJsZSBuMiA9IG5lYXJfZGlzdCArIG5lYXJfZGlzdDsKCQlkb3VibGUgbjJmID0gbjIgKiBmYXJfZGlzdDsKCQlkb3VibGUgZnNuID0gZmFyX2Rpc3QgLSBuZWFyX2Rpc3Q7CgkJZG91YmxlIGZhbiA9IGZhcl9kaXN0ICsgbmVhcl9kaXN0OwoJCWRvdWJsZSB3aWQgPSByaWdodF94ICsgcmlnaHRfeDsKCQlkb3VibGUgaGVpID0gdG9wX3kgKyB0b3BfeTsKCQlyZXR1cm4gTWF0cml4PDQ+ewoJCQluMiAvIHdpZCwgMC4wLCAgICAgIDAuMCwgICAgICAgIDAuMCwgCgkJCTAuMCwgICAgICBuMiAvIGhlaSwgMC4wLCAgICAgICAgMC4wLCAKCQkJMC4wLCAgICAgIDAuMCwgICAgICAtZmFuIC8gZnNuLCAtbjJmIC8gZnNuLCAKCQkJMC4wLCAgICAgIDAuMCwgICAgICAtMS4wLCAgICAgICAwLjAsIAoJCX07Cgl9Cn07CgojaW5jbHVkZSA8Y21hdGg+CgpuYW1lc3BhY2UgaDNkIHsKCWlubGluZSBGTE9BVCBhbmcycmFkKGNvbnN0IEZMT0FUJiBhbmcpIHsKCQlyZXR1cm4gTV9QSSAqIGFuZyAvIDE4MC4wOwoJfQp9OwoKI2luY2x1ZGUgPGlvc3RyZWFtPgoKbmFtZXNwYWNlIGgzZCB7Cgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIHZvaWQgTWF0cml4PE4+OjpjbGVhcigpIHsKCQkvLyDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSAKCQkJCXRoaXMtPmNbcm93XVtjb2xdID0gMC4wOwoJCX0KCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBGTE9BVCBNYXRyaXg8Tj46OmRldCgpIGNvbnN0IHsKCQlGTE9BVCByZXMgPSAwLjA7CgkJLy8gMeihjOebruOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgewoJCQkvLyDmiJDliIbjgavjgIHjgZ3jgozjgajlr77lv5zjgZnjgovlsI/ooYzliJflvI/jgpLkuZfnrpfjgZnjgovjgIIKCQkJRkxPQVQgY29mYWMgPSBjWzBdW2NvbF0gKiBzdWIoMCwgY29sKS5kZXQoKTsKCQkJLy8gKC0xKV5jb2wKCQkJaWYgKChjb2wgJiAweDEpID09IDEpIGNvZmFjID0gLWNvZmFjOwoJCQkvLyDntZDmnpzjgavkvZnlm6DlrZDjgpLliqDnrpfjgZnjgovjgIIKCQkJcmVzICs9IGNvZmFjOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8PiAKCWlubGluZSBGTE9BVCBNYXRyaXg8Mj46OmRldCgpIGNvbnN0IHsKCQkvLyDlho3luLDnmoTjgarooYzliJflvI/oqIjnrpfjga7ntYLnnYDngrnjgIIKCQkvLyAyw5cy44Gu6KGM5YiX5byP44KS6KiI566X44GZ44KL44CCCgkJcmV0dXJuIHRoaXMtPmNbMF1bMF0gKiB0aGlzLT5jWzFdWzFdIC0gdGhpcy0+Y1swXVsxXSAqIHRoaXMtPmNbMV1bMF07Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4gKyAxPiBNYXRyaXg8Tj46OmV4dChjb25zdCBNYXRyaXg8TiArIDE+JiBtYXQpIGNvbnN0IHsKCQlNYXRyaXg8TiArIDE+IHJlczsKCQkvLyDntZDmnpzjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTiArIDE7IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOICsgMTsgY29sKyspIHsKCQkJCS8vIOihjOOBqOWIl+OBjE7ku6XlhoXjgarjgonjgZPjga7ooYzliJcsIOOBneOCjOS7peWkluOBquOCieW8leaVsOOBruihjOWIl+OBi+OCieaIkOWIhuOCkuOCs+ODlOODvOOBmeOCi+OAggoJCQkJaWYgKHJvdyA8IE4gJiYgY29sIDwgTikgcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF07CgkJCQllbHNlIHJlcy5jW3Jvd11bY29sXSA9IG1hdC5jW3Jvd11bY29sXTsKCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46OmludigpIGNvbnN0IHRocm93KHN0cmluZykgewoJCS8vIOOBvuOBmuihjOWIl+W8j+OCkuioiOeul+OBl+OAgeato+WJh+OBp+OBguOCi+OBk+OBqOOCkueiuuOBi+OCgeOCi+OAggoJCUZMT0FUIGRldF9yZXMgPSBkZXQoKTsKCQlpZiAobmVhckVxdWFsKGRldF9yZXMsIDAuMCkpIHRocm93ICLooYzliJfjgYzmraPliYfjgafjga/jgarjgYTjgIIiOwoJCS8vIOihjOWIl+W8j+OBrumAhuaVsOOCkuioiOeul+OBl+OBpuOBiuOBj+OAggoJCUZMT0FUIGludl9kZXQgPSAxLjAgLyBkZXRfcmVzOwoJCU1hdHJpeDxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgewoJCQkJLy8g6KGM5YiX5byP44Gu6YCG5pWw44Gr44CB5a++6KeS44Gu5bCP6KGM5YiX5byP44KS5LmX566X44GZ44KL44CCCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSBpbnZfZGV0ICogc3ViKGNvbCwgcm93KS5kZXQoKTsKCQkJCS8vICgtMSleKHJvdyArIGNvbCkKCQkJCWlmICgoKHJvdyArIGNvbCkgJiAweDEpID09IDEpIAoJCQkJCXJlcy5jW3Jvd11bY29sXSA9IC1yZXMuY1tyb3ddW2NvbF07CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBib29sIE1hdHJpeDxOPjo6b3BlcmF0b3IgIT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gIW9wZXJhdG9yID09KHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKihjb25zdCBNYXRyaXgmIHJocykgY29uc3QgewoJCU1hdHJpeDxOPiByZXM7CgkJLy8g57WQ5p6c44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgewoJCQkJcmVzLmNbcm93XVtjb2xdID0gMC4wOwoJCQkJLy8g5bem5YG044Gv5a++5b+c44GZ44KL6KGM44Gu5ZCE5oiQ5YiGLCDlj7PlgbTjga/lr77lv5zjgZnjgovliJfjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZfjgabjgIHkuZfnrpfjgZnjgovjgIIKCQkJCWZvciAoYXV0byBpID0gMDsgaSA8IE47IGkrKykgCgkJCQkJcmVzLmNbcm93XVtjb2xdICs9IHRoaXMtPmNbcm93XVtpXSAqIHJocy5jW2ldW2NvbF07CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqKGNvbnN0IFZlY3RvcjxOPiYgcmhzKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDjg5njgq/jg4jjg6vjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHsKCQkJcmVzLmNbaV0gPSAwLjA7CgkJCS8vIOW3puWBtOOBruihjOWIl+OBr+OAgeWvvuW/nOOBmeOCi+ihjOOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBl+OBpuOAgeS5l+eul+OBmeOCi+OAggoJCQlmb3IgKGF1dG8gaiA9IDA7IGogPCBOOyBqKyspIAoJCQkJcmVzLmNbaV0gKz0gdGhpcy0+Y1tpXVtqXSAqIHJocy5jW2pdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICooY29uc3QgRkxPQVQmIHJocykgY29uc3QgewoJCU1hdHJpeDxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gr44K544Kr44Op44O85YCk44KS5LmX566X44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSB0aGlzLT5jW3Jvd11bY29sXSAqIHJoczsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqPShjb25zdCBNYXRyaXgmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAqPShjb25zdCBGTE9BVCYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKihyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yICsoY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWvvuW/nOOBmeOCi+aIkOWIhuWQjOWjq+OBp+WKoOeul+OBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF0gKyByaHMuY1tyb3ddW2NvbF07CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgKz0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciArKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4+IE1hdHJpeDxOPjo6b3BlcmF0b3IgLSgpIGNvbnN0IHsKCQlNYXRyaXg8Tj4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gLXRoaXMtPmNbcm93XVtjb2xdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yIC0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gb3BlcmF0b3IgKygtcmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAtPShjb25zdCBNYXRyaXgmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yIC0ocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBNYXRyaXg8Tj4gTWF0cml4PE4+OjpvcGVyYXRvciAvKGNvbnN0IEZMT0FUJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gb3BlcmF0b3IgKigxLjAgLyByaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIE1hdHJpeDxOPiBNYXRyaXg8Tj46Om9wZXJhdG9yIC89KGNvbnN0IEZMT0FUJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAvKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglib29sIE1hdHJpeDxOPjo6b3BlcmF0b3IgPT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlib29sIHJlcyA9IHRydWU7CgkJaWYgKCZyaHMgIT0gdGhpcykgewoJCQkvLyDlkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIHsKCQkJCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgYzov5HjgZHjgozjgbDnrYnjgZfjgYTjgajliKTlrprjgZnjgovjgIIKCQkJCQlpZiAoIW5lYXJFcXVhbCh0aGlzLT5jW3Jvd11bY29sXSwgcmhzLmNbcm93XVtjb2xdKSkgewoJCQkJCQlyZXMgPSBmYWxzZTsKCQkJCQkJYnJlYWs7CgkJCQkJfQoJCQkJfQoJCQkJaWYgKCFyZXMpIGJyZWFrOwoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4gLSAxPiBNYXRyaXg8Tj46OnN1Yihjb25zdCB1bnNpZ25lZCBpbnQmIHJvdywgY29uc3QgdW5zaWduZWQgaW50JiBjb2wpIGNvbnN0IHsKCQlNYXRyaXg8TiAtIDE+IHN1YjsKCQlhdXRvIHN1Yl9yb3cgPSAwOwoJCS8vIOOBk+OBruihjOWIl+OBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCS8vIOWPluOCiumZpOOBj+ihjOOBqOWIl+OBq+OBpOOBhOOBpuOBr+WHpueQhuOCkuOCueOCreODg+ODl+OBmeOCi+OAggoJCWZvciAoYXV0byBzdXBfcm93ID0gMDsgc3VwX3JvdyA8IE47IHN1cF9yb3crKykgewoJCQlpZiAoc3VwX3JvdyA9PSByb3cpIGNvbnRpbnVlOwoJCQlhdXRvIHN1Yl9jb2wgPSAwOwoJCQlmb3IgKGF1dG8gc3VwX2NvbCA9IDA7IHN1cF9jb2wgPCBOOyBzdXBfY29sKyspIHsKCQkJCWlmIChzdXBfY29sID09IGNvbCkgY29udGludWU7CgkJCQkvLyDlr77lv5zjgZnjgovmiJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkJCXN1Yi5jW3N1Yl9yb3ddW3N1Yl9jb2xdID0gdGhpcy0+Y1tzdXBfcm93XVtzdXBfY29sXTsKCQkJCXN1Yl9jb2wrKzsKCQkJfQoJCQlzdWJfcm93Kys7CgkJfQoJCXJldHVybiBzdWI7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgTWF0cml4PE4gLSAxPiBNYXRyaXg8Tj46OnRydW5jKCkgY29uc3QgewoJCU1hdHJpeDxOIC0gMT4gcmVzOwoJCS8vIOe1kOaenOOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOIC0gMTsgcm93KyspIHsKCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOIC0gMTsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF07CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBNYXRyaXg8Tj4mIG1hdCkgewoJCW91dCA8PCAieyI7CgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCW91dCA8PCAieyI7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgb3V0IDw8IG1hdC5jW3Jvd11bY29sXSA8PCAiLCAiOwoJCQlvdXQgPDwgIn0sICI7CgkJfQoJCW91dCA8PCAifSI7Cgl9Cn07CgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxpb3N0cmVhbT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3B5OwoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJdXNpbmcgc3RkOjpzcXJ0OwoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIHZvaWQgVmVjdG9yPE4+OjpjbGVhcigpIHsKCQkvLyDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHRoaXMtPmNbaV0gPSAwLjA7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4gKyAxPiBWZWN0b3I8Tj46OmV4dChjb25zdCBGTE9BVCYgYykgY29uc3QgewoJCVZlY3RvcjxOICsgMT4gcmVzOwoJCS8vIOOBk+OBruODmeOCr+ODiOODq+OBi+OCiU7lgIvjga7miJDliIbjgpLjgrPjg5Tjg7zjgZfjgabjgIHmnIDlvozjga/lvJXmlbDjga7miJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQljb3B5KHRoaXMtPmMsIHRoaXMtPmMgKyBOLCByZXMuYyk7CgkJcmVzLmNbTl0gPSBjOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgRkxPQVQgVmVjdG9yPE4+OjppbnBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCUZMT0FUIHJlcyA9IDA7CgkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44Gu56mN44KS5rGC44KB44Gm44CB5ZCI6KiI44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMgKz0gdGhpcy0+Y1tpXSAqIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgZG91YmxlIFZlY3RvcjxOPjo6bm9ybSgpIGNvbnN0IHsKCQlyZXR1cm4gc3FydCh0aGlzLT5pbnBybygqdGhpcykpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om5vcm1hbGl6ZSgpIGNvbnN0IHsKCQlyZXR1cm4gKnRoaXMgLyBub3JtKCk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgYm9vbCBWZWN0b3I8Tj46Om9wZXJhdG9yICE9KGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuICFvcGVyYXRvciA9PShyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yICooY29uc3QgRkxPQVQmIHJocykgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gr44K544Kr44Op44O85YCk44KS5LmX566X44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMuY1tpXSA9IHRoaXMtPmNbaV0gKiByaHM7CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4mIFZlY3RvcjxOPjo6b3BlcmF0b3IgKj0oY29uc3QgRkxPQVQmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gdGhpcy0+Y1tpXSArIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yICs9KGNvbnN0IFZlY3RvciYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yIC0oKSBjb25zdCB7CgkJVmVjdG9yPE4+IHJlczsKCQkvLyDlkITmiJDliIbjga7nrKblj7fjgpLlj43ou6LjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gLXRoaXMtPmNbaV07CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvcGVyYXRvciAtKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yIC09KGNvbnN0IFZlY3RvciYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLShyaHMpOwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOPiBWZWN0b3I8Tj46Om9wZXJhdG9yIC8oY29uc3QgRkxPQVQmIHJocykgY29uc3QgewoJCXJldHVybiBvcGVyYXRvciAqKDEuMCAvIHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgVmVjdG9yPE4+JiBWZWN0b3I8Tj46Om9wZXJhdG9yIC89KGNvbnN0IEZMT0FUJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAvKHJocyk7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgYm9vbCBWZWN0b3I8Tj46Om9wZXJhdG9yID09KGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GX44Gm44CB5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCWZvciAoYXV0byBpID0gMDsgaSA8IE47IGkrKykgewoJCQkJaWYgKCFuZWFyRXF1YWwodGhpcy0+Y1tpXSwgcmhzLmNbaV0pKSB7CgkJCQkJcmVzID0gZmFsc2U7CgkJCQkJYnJlYWs7CgkJCQl9CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGUgPHVuc2lnbmVkIGludCBOPiAKCWlubGluZSBWZWN0b3I8Tj4gVmVjdG9yPE4+OjpvdXRwcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0IHsKCQkvLyDlkITmiJDliIbjgavjgaTjgYTjgabjgIHlr77lv5zjgZnjgovliJfjgpLpmaTjgYTjgZ8yw5cy44Gu6KGM5YiX5byP44KS6KiI566X44GZ44KL44CCCgkJLy8gUMOXUSA9IDwgUHnjg7tReiAtIFB644O7UXksIFB644O7UXggLSBQeOODu1F6LCBQeOODu1F5IC0gUHnjg7tReCA+CgkJcmV0dXJuIFZlY3RvcjxOPnsKCQkJdGhpcy0+Y1sxXSAqIHJocy5jWzJdIC0gdGhpcy0+Y1syXSAqIHJocy5jWzFdLCAKCQkJdGhpcy0+Y1syXSAqIHJocy5jWzBdIC0gdGhpcy0+Y1swXSAqIHJocy5jWzJdLCAKCQkJdGhpcy0+Y1swXSAqIHJocy5jWzFdIC0gdGhpcy0+Y1sxXSAqIHJocy5jWzBdLCAKCQl9OwoJfQoJCgl0ZW1wbGF0ZSA8dW5zaWduZWQgaW50IE4+IAoJaW5saW5lIFZlY3RvcjxOIC0gMT4gVmVjdG9yPE4+Ojp0cnVuYygpIGNvbnN0IHsKCQlWZWN0b3I8TiAtIDE+IHJlczsKCQljb3B5KHRoaXMtPmMsIHRoaXMtPmMgKyBOIC0gMSwgcmVzLmMpOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlIDx1bnNpZ25lZCBpbnQgTj4gCglpbmxpbmUgb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBWZWN0b3I8Tj4mIHZlYykgewoJCW91dCA8PCAieyI7CgkJZm9yIChhdXRvIGMgOiB2ZWMuYykgb3V0IDw8IGMgPDwgIiwgIjsKCQlvdXQgPDwgIn0iOwoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7CglpbmxpbmUgYm9vbCBuZWFyRXF1YWwoY29uc3QgRkxPQVQmIHgsIGNvbnN0IEZMT0FUJiB5KSB7CgkJc3RhdGljIGNvbnN0IEZMT0FUIFRIUkVTSE9MRCA9IDAuMDAwMDAwMTsKCQlGTE9BVCBkaWYgPSB4IC0geTsKCQlyZXR1cm4gZGlmID4gLVRIUkVTSE9MRCAmJiBkaWYgPCBUSFJFU0hPTEQ7Cgl9Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxzdHJpbmc+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6Y291dDsKCXVzaW5nIHN0ZDo6ZW5kbDsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCglpbmxpbmUgdm9pZCBhc3NlcnQoY29uc3Qgc3RyaW5nJiBwcmVkX3N0ciwgY29uc3QgYm9vbCYgcHJlZF9yZXMpIHRocm93KHN0cmluZykgewoJCWlmIChwcmVkX3JlcykgY291dCA8PCAi44Ki44K144O844OI5oiQ5YqfOiAiIDw8IHByZWRfc3RyIDw8IGVuZGw7CgkJZWxzZSB0aHJvdyAi44Ki44K144O844OI5aSx5pWXOiAiICsgcHJlZF9zdHI7Cgl9Cn07CgojaW5jbHVkZSA8bWVtb3J5PgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnNoYXJlZF9wdHI7CgkKCVRlc3RTZXQ6OlRlc3RTZXQoKSB7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QxKSk7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QyKSk7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QzKSk7Cgl9CgoJdm9pZCBUZXN0U2V0OjpydW4oKSBjb25zdCB7IGZvciAoYXV0byBpdGVyIDogdGhpcy0+dGVzdHMpIGl0ZXItPnJ1bigpOyB9Cn07CgojaW5jbHVkZSA8Y21hdGg+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6c3FydDsKCQoJdm9pZCBUZXN0MTo6cnVuKCkgY29uc3QgewoJCVZlY3RvcjwzPiBwezIuMCwgMi4wLCAxLjB9LCBxezEuMCwgLTIuMCwgMC4wfTsKCQlBU1NFUlQobmVhckVxdWFsKHAuY1swXSwgMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHAuY1sxXSwgMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHAuY1syXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHEuY1swXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKHEuY1sxXSwgLTIuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChxLmNbMl0sIDAuMCkpCgkJCgkJQVNTRVJUKHAuZXh0KCkgPT0gKFZlY3Rvcjw0PnsyLjAsIDIuMCwgMS4wLCAxLjB9KSkKCQlBU1NFUlQocC5leHQoMC4wKSA9PSAoVmVjdG9yPDQ+ezIuMCwgMi4wLCAxLjAsIDAuMH0pKQoJCUFTU0VSVChwLnRydW5jKCkgPT0gKFZlY3RvcjwyPnsyLjAsIDIuMH0pKQoJCQoJCUFTU0VSVChwID09IChWZWN0b3I8Mz57Mi4wLCAyLjAsIDEuMH0pKQoJCUFTU0VSVChxID09IChWZWN0b3I8Mz57MS4wLCAtMi4wLCAwLjB9KSkKCQlBU1NFUlQocCAhPSAoVmVjdG9yPDM+ezEuMCwgLTIuMCwgMC4wfSkpCgkJQVNTRVJUKHEgIT0gKFZlY3RvcjwzPnsyLjAsIDIuMCwgMS4wfSkpCgkJQVNTRVJUKC1wID09IChWZWN0b3I8Mz57LTIuMCwgLTIuMCwgLTEuMH0pKQoJCUFTU0VSVCgtcSA9PSAoVmVjdG9yPDM+ey0xLjAsIDIuMCwgMC4wfSkpCgkJQVNTRVJUKHAgKiAyLjAgPT0gKFZlY3RvcjwzPns0LjAsIDQuMCwgMi4wfSkpCgkJQVNTRVJUKHEgKiAtMy4wID09IChWZWN0b3I8Mz57LTMuMCwgNi4wLCAwLjB9KSkKCQlBU1NFUlQocCAvIDIuMCA9PSAoVmVjdG9yPDM+ezEuMCwgMS4wLCAwLjV9KSkKCQlBU1NFUlQocSAvIC0zLjAgPT0gKFZlY3RvcjwzPnsxLjAgLyAtMy4wLCAtMi4wIC8gLTMuMCwgMC4wfSkpCgkJCgkJQVNTRVJUKHAgKyBxID09IChWZWN0b3I8Mz57My4wLCAwLjAsIDEuMH0pKQoJCUFTU0VSVChxICsgcCA9PSAoVmVjdG9yPDM+ezMuMCwgMC4wLCAxLjB9KSkKCQlBU1NFUlQocCAtIHEgPT0gKFZlY3RvcjwzPnsxLjAsIDQuMCwgMS4wfSkpCgkJQVNTRVJUKHEgLSBwID09IChWZWN0b3I8Mz57LTEuMCwgLTQuMCwgLTEuMH0pKQoJCQoJCVZlY3RvcjwzPiByOwoJCXIgPSBwOwoJCUFTU0VSVChyID09IChWZWN0b3I8Mz57Mi4wLCAyLjAsIDEuMH0pKQoJCUFTU0VSVCgociAqPSAzLjApID09IChWZWN0b3I8Mz57Ni4wLCA2LjAsIDMuMH0pKQoJCUFTU0VSVCgociAvPSAtMi4wKSA9PSAoVmVjdG9yPDM+ey0zLjAsIC0zLjAsIC0xLjV9KSkKCQlBU1NFUlQoKHIgKz0gKFZlY3RvcjwzPns1LjAsIC0xLjAsIDAuMH0pKSA9PSAoVmVjdG9yPDM+ezIuMCwgLTQuMCwgLTEuNX0pKQoJCUFTU0VSVCgociAtPSAoVmVjdG9yPDM+ezAuMCwgLTIuMCwgNC4wfSkpID09IChWZWN0b3I8Mz57Mi4wLCAtMi4wLCAtNS41fSkpCgkJci5jbGVhcigpOwoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoci5jWzJdLCAwLjApKQoJCQoJCUFTU0VSVChuZWFyRXF1YWwocC5pbnBybyhxKSwgLTIuMCkpCgkJQVNTRVJUKHAub3V0cHJvKHEpID09IChWZWN0b3I8Mz57Mi4wLCAxLjAsIC02LjB9KSkKCQkKCQlBU1NFUlQobmVhckVxdWFsKHAubm9ybSgpLCAzLjApKQoJCUFTU0VSVChuZWFyRXF1YWwocS5ub3JtKCksIHNxcnQoNS4wKSkpCgkJCgkJQVNTRVJUKHAubm9ybWFsaXplKCkgPT0gKFZlY3RvcjwzPnsyLjAgLyAzLjAsIDIuMCAvIDMuMCwgMS4wIC8gMy4wfSkpCgkJQVNTRVJUKHEubm9ybWFsaXplKCkgPT0gKFZlY3RvcjwzPnsxLjAgLyBzcXJ0KDUuMCksIC0yLjAgLyBzcXJ0KDUuMCksIDAuMH0pKQoJfQp9OwoKI2luY2x1ZGUgPGNtYXRoPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmNvczsKCXVzaW5nIHN0ZDo6c2luOwoJdXNpbmcgc3RkOjpzcXJ0OwoJCgl2b2lkIFRlc3QyOjpydW4oKSBjb25zdCB7CgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mj46OklERU5USVRZKCkuY1swXVswXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwyPjo6SURFTlRJVFkoKS5jWzBdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWSgpLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mj46OklERU5USVRZKCkuY1sxXVsxXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1swXVsyXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMV1bMV0sIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1sxXVsyXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzJdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMl1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1syXVsyXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1swXVsyXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzBdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1sxXVsxXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzFdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMV1bM10sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1syXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzJdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMl1bMl0sIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1syXVszXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzNdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbM11bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1szXVsyXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzNdWzNdLCAxLjApKQoJCQoJCU1hdHJpeDwyPiBmewoJCQkxLjAsICAtMi4wLCAKCQkJLTMuMCwgNC4wLCAKCQl9LCBnewoJCQktOS4wLCA4LjAsIAoJCQk3LjAsICAtNi4wLCAKCQl9OwoJCQoJCUFTU0VSVChuZWFyRXF1YWwoZi5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZi5jWzBdWzFdLCAtMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGYuY1sxXVswXSwgLTMuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChmLmNbMV1bMV0sIDQuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnLmNbMF1bMF0sIC05LjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZy5jWzBdWzFdLCA4LjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZy5jWzFdWzBdLCA3LjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZy5jWzFdWzFdLCAtNi4wKSkKCQkKCQlBU1NFUlQoZi5leHQoKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsICAtMi4wLCAwLjAsIAoJCQktMy4wLCA0LjAsICAwLjAsIAoJCQkwLjAsICAwLjAsICAxLjAsIAoJCX0pKQoJCUFTU0VSVChmLmV4dCgoTWF0cml4PDM+ewoJCQkzLjAsICAtOC4wLCA1LjAsIAoJCQktMi4wLCA3LjAsICA0LjAsIAoJCQkxLjAsICAtNS4wLCAwLjAsIAoJCX0pKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsICAtMi4wLCA1LjAsIAoJCQktMy4wLCA0LjAsICA0LjAsIAoJCQkxLjAsICAtNS4wLCAwLjAsIAoJCX0pKQoJCUFTU0VSVChmLnRydW5jKCkgPT0gKE1hdHJpeDwxPnsKCQkJMS4wLCAKCQl9KSkKCQkKCQlBU1NFUlQoZiA9PSAoTWF0cml4PDI+ewoJCQkxLjAsICAtMi4wLCAKCQkJLTMuMCwgNC4wLCAKCQl9KSkKCQlBU1NFUlQoZyA9PSAoTWF0cml4PDI+ewoJCQktOS4wLCA4LjAsIAoJCQk3LjAsICAtNi4wLCAKCQl9KSkKCQlBU1NFUlQoZiAhPSAoTWF0cml4PDI+ewoJCQktOS4wLCA4LjAsIAoJCQk3LjAsICAtNi4wLCAKCQl9KSkKCQlBU1NFUlQoZyAhPSAoTWF0cml4PDI+ewoJCQkxLjAsICAtMi4wLCAKCQkJLTMuMCwgNC4wLCAKCQl9KSkKCQkKCQlBU1NFUlQoLWYgPT0gKE1hdHJpeDwyPnsKCQkJLTEuMCwgMi4wLCAKCQkJMy4wLCAgLTQuMCwgCgkJfSkpCgkJQVNTRVJUKC1nID09IChNYXRyaXg8Mj57CgkJCTkuMCwgIC04LjAsIAoJCQktNy4wLCA2LjAsIAoJCX0pKQoJCUFTU0VSVChmICsgZyA9PSAoTWF0cml4PDI+ewoJCQktOC4wLCA2LjAsIAoJCQk0LjAsICAtMi4wLCAKCQl9KSkKCQlBU1NFUlQoZiAtIGcgPT0gKE1hdHJpeDwyPnsKCQkJMTAuMCwgIC0xMC4wLCAKCQkJLTEwLjAsIDEwLjAsIAoJCX0pKQoJCUFTU0VSVChmICogZyA9PSAoTWF0cml4PDI+ewoJCQktMjMuMCwgMjAuMCwgCgkJCTU1LjAsICAtNDguMCwgCgkJfSkpCgkJQVNTRVJUKGcgKiBmID09IChNYXRyaXg8Mj57CgkJCS0zMy4wLCA1MC4wLCAKCQkJMjUuMCwgIC0zOC4wLCAKCQl9KSkKCQlBU1NFUlQoZiAqIE1hdHJpeDwyPjo6SURFTlRJVFkoKSA9PSAoTWF0cml4PDI+ewoJCQkxLjAsICAtMi4wLCAKCQkJLTMuMCwgNC4wLCAKCQl9KSkKCQlBU1NFUlQoTWF0cml4PDI+OjpJREVOVElUWSgpICogZyA9PSAoTWF0cml4PDI+ewoJCQktOS4wLCA4LjAsIAoJCQk3LjAsICAtNi4wLCAKCQl9KSkKCQlBU1NFUlQoZiAqIDMuMCA9PSAoTWF0cml4PDI+ewoJCQkzLjAsICAtNi4wLCAKCQkJLTkuMCwgMTIuMCwgCgkJfSkpCgkJQVNTRVJUKGcgLyAtMi4wID09IChNYXRyaXg8Mj57CgkJCTQuNSwgIC00LjAsIAoJCQktMy41LCAzLjAsIAoJCX0pKQoJCQoJCU1hdHJpeDwyPiBoOwoJCWggPSBmOwoJCUFTU0VSVChoID09IChNYXRyaXg8Mj57CgkJCTEuMCwgIC0yLjAsIAoJCQktMy4wLCA0LjAsIAoJCX0pKQoJCUFTU0VSVCgoaCArPSAoTWF0cml4PDI+ewoJCQkwLjEsICAtMC4yLCAKCQkJLTAuMywgMC40LCAKCQl9KSkgPT0gKE1hdHJpeDwyPnsKCQkJMS4xLCAgLTIuMiwgCgkJCS0zLjMsIDQuNCwgCgkJfSkpCgkJQVNTRVJUKChoIC09IChNYXRyaXg8Mj57CgkJCS0wLjksIDAuOCwgCgkJCTAuNywgIC0wLjYsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQkyLjAsICAtMy4wLCAKCQkJLTQuMCwgNS4wLCAKCQl9KSkKCQlBU1NFUlQoKGggKj0gKE1hdHJpeDwyPnsKCQkJLTQuMCwgMy4wLCAKCQkJLTIuMCwgMS4wLCAKCQl9KSkgPT0gKE1hdHJpeDwyPnsKCQkJLTIuMCwgMy4wLCAKCQkJNi4wLCAgLTcuMCwgCgkJfSkpCgkJQVNTRVJUKChoICo9IDMuMCkgPT0gKE1hdHJpeDwyPnsKCQkJLTYuMCwgOS4wLCAKCQkJMTguMCwgLTIxLjAsIAoJCX0pKQoJCUFTU0VSVCgoaCAvPSAtMi4wKSA9PSAoTWF0cml4PDI+ewoJCQkzLjAsICAtNC41LCAKCQkJLTkuMCwgMTAuNSwgCgkJfSkpCgkJaC5jbGVhcigpOwoJCUFTU0VSVChuZWFyRXF1YWwoaC5jWzBdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoaC5jWzBdWzFdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoaC5jWzFdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoaC5jWzFdWzFdLCAwLjApKQoJCQoJCUFTU0VSVChuZWFyRXF1YWwoKE1hdHJpeDwyPnsKCQkJMi4wLCAgNy4wLCAKCQkJLTMuMCwgMC41LCAKCQl9KS5kZXQoKSwgMjIuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbCgoTWF0cml4PDM+ewoJCQkwLjAsIDAuMCwgMS4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgCgkJCTEuMCwgMC4wLCAwLjAsIAoJCX0pLmRldCgpLCAtMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKChNYXRyaXg8Mz57CgkJCTAuNSwgICAgICAgICAgICAgIHNxcnQoMy4wKSAvIDIuMCwgMC4wLCAKCQkJLXNxcnQoMy4wKSAvIDIuMCwgMC41LCAgICAgICAgICAgICAwLjAsIAoJCQkwLjAsICAgICAgICAgICAgICAwLjAsICAgICAgICAgICAgIDEuMCwgCgkJfSkuZGV0KCksIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbCgoTWF0cml4PDM+ewoJCQk1LCAgNywgIDEsIAoJCQkxNywgMiwgIDY0LCAKCQkJMTAsIDE0LCAyLCAKCQl9KS5kZXQoKSwgMC4wKSkKCQkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMi4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMy4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgNC4wLCAKCQl9KS5pbnYoKSA9PSAoTWF0cml4PDM+ewoJCQkxMi4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgIDguMCwgMC4wLCAKCQkJMC4wLCAgMC4wLCA2LjAsIAoJCX0pIC8gMjQuMCkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMi4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMy4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgNC4wLCAKCQl9KSAqICgoTWF0cml4PDM+ewoJCQkxMi4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgIDguMCwgMC4wLCAKCQkJMC4wLCAgMC4wLCA2LjAsIAoJCX0pIC8gMjQuMCkgPT0gTWF0cml4PDM+OjpJREVOVElUWSgpKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkxLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAyLjAsIDIuMCwgCgkJCTMuMCwgMC4wLCA4LjAsIAoJCX0pLmludigpID09IChNYXRyaXg8Mz57CgkJCTE2LjAsIDAuMCwgMC4wLCAKCQkJNi4wLCAgOC4wLCAtMi4wLCAKCQkJLTYuMCwgMC4wLCAyLjAsIAoJCX0pIC8gMTYuMCkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMS4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMi4wLCAyLjAsIAoJCQkzLjAsIDAuMCwgOC4wLCAKCQl9KSAqICgoTWF0cml4PDM+ewoJCQkxNi4wLCAwLjAsIDAuMCwgCgkJCTYuMCwgIDguMCwgLTIuMCwgCgkJCS02LjAsIDAuMCwgMi4wLCAKCQl9KSAvIDE2LjApID09IE1hdHJpeDwzPjo6SURFTlRJVFkoKSkKCQlGTE9BVCB0aGV0YV9yYWQgPSBhbmcycmFkKDYwLjApOwoJCUZMT0FUIGNvc19yZXMgPSBjb3ModGhldGFfcmFkKTsKCQlGTE9BVCBzaW5fcmVzID0gc2luKHRoZXRhX3JhZCk7CgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCWNvc19yZXMsIDAuMCwgLXNpbl9yZXMsIAoJCQkwLjAsICAgICAxLjAsIDAuMCwgCgkJCXNpbl9yZXMsIDAuMCwgY29zX3JlcywgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJY29zX3JlcywgIDAuMCwgc2luX3JlcywgCgkJCTAuMCwgICAgICAxLjAsIDAuMCwgCgkJCS1zaW5fcmVzLCAwLjAsIGNvc19yZXMsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQljb3NfcmVzLCAwLjAsIC1zaW5fcmVzLCAKCQkJMC4wLCAgICAgMS4wLCAwLjAsIAoJCQlzaW5fcmVzLCAwLjAsIGNvc19yZXMsIAoJCX0pICogKE1hdHJpeDwzPnsKCQkJY29zX3JlcywgIDAuMCwgc2luX3JlcywgCgkJCTAuMCwgICAgICAxLjAsIDAuMCwgCgkJCS1zaW5fcmVzLCAwLjAsIGNvc19yZXMsIAoJCX0pID09IE1hdHJpeDwzPjo6SURFTlRJVFkoKSkKCQlBU1NFUlQoKE1hdHJpeDw0PnsKCQkJMS4wLCAwLjAsIDAuMCwgNC4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgMy4wLCAKCQkJMC4wLCAwLjAsIDEuMCwgNy4wLCAKCQkJMC4wLCAwLjAsIDAuMCwgMS4wLCAKCQl9KS5pbnYoKSA9PSAoTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCAtNC4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgLTMuMCwgCgkJCTAuMCwgMC4wLCAxLjAsIC03LjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCA0LjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAzLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCA3LjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX0pICogKE1hdHJpeDw0PnsKCQkJMS4wLCAwLjAsIDAuMCwgLTQuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIC0zLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAtNy4wLCAKCQkJMC4wLCAwLjAsIDAuMCwgMS4wLCAKCQl9KSA9PSBNYXRyaXg8ND46OklERU5USVRZKCkpCgkJCgkJQVNTRVJUKChNYXRyaXg8Mj57CgkJCTEuMCwgIC0zLjAsIAoJCQktNC4wLCA2LjAsIAoJCX0pICogKFZlY3RvcjwyPnsyLjAsIDUuMH0pID09IChWZWN0b3I8Mj57LTEzLjAsIDIyLjB9KSkKCQlBU1NFUlQoKE1hdHJpeDwyPnsKCQkJMC4wLCAgOC4wLCAKCQkJLTkuMCwgMy4wLCAKCQl9KSAqIChWZWN0b3I8Mj57MS4wLCAtNC4wfSkgPT0gKFZlY3RvcjwyPnstMzIuMCwgLTIxLjB9KSkKCX0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzcXJ0OwoJCgl2b2lkIFRlc3QzOjpydW4oKSBjb25zdCB7CgkJQVNTRVJUKHNjYWxpbmcoKFZlY3RvcjwzPnsxLjAsIC0yLjAsIDAuMH0pKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsIDAuMCwgIDAuMCwgCgkJCTAuMCwgLTIuMCwgMC4wLCAKCQkJMC4wLCAwLjAsICAwLjAsIAoJCX0pKQoJCUFTU0VSVChzY2FsaW5nKChWZWN0b3I8Mz57MC4wLCA5LjAsIC04LjB9KSkgPT0gKE1hdHJpeDwzPnsKCQkJMC4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgOS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgLTguMCwgCgkJfSkpCgkJCgkJQVNTRVJUKHJvdGF0aW9uKChWZWN0b3I8Mz57MS4wLCAwLjAsIDAuMH0pLCBhbmcycmFkKDMwLjApKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAsIDAuMCwgICAgICAgICAgICAgMC4wLCAKCQkJMC4wLCBzcXJ0KDMuMCkgLyAyLjAsIC0wLjUsIAoJCQkwLjAsIDAuNSwgICAgICAgICAgICAgc3FydCgzLjApIC8gMi4wLCAKCQl9KSkKCQlBU1NFUlQocm90YXRpb24oKFZlY3RvcjwzPnswLjAsIDEuMCwgMC4wfSksIGFuZzJyYWQoLTQ1LjApKSA9PSAoTWF0cml4PDM+ewoJCQkxLjAgLyBzcXJ0KDIuMCksIDAuMCwgLTEuMCAvIHNxcnQoMi4wKSwgCgkJCTAuMCwgICAgICAgICAgICAgMS4wLCAwLjAsIAoJCQkxLjAgLyBzcXJ0KDIuMCksIDAuMCwgMS4wIC8gc3FydCgyLjApLCAKCQl9KSkKCQlBU1NFUlQocm90YXRpb24oKFZlY3RvcjwzPnswLjAsIDAuMCwgMS4wfSksIGFuZzJyYWQoNjAuMCkpID09IChNYXRyaXg8Mz57CgkJCTAuNSwgICAgICAgICAgICAgLXNxcnQoMy4wKSAvIDIuMCwgMC4wLCAKCQkJc3FydCgzLjApIC8gMi4wLCAwLjUsICAgICAgICAgICAgICAwLjAsIAoJCQkwLjAsICAgICAgICAgICAgIDAuMCwgICAgICAgICAgICAgIDEuMCwgCgkJfSkpCgkJCgkJQVNTRVJUKHRyYW5zbGF0aW9uKChWZWN0b3I8Mz57MS4wLCAtMi4wLCAwLjB9KSkgPT0gKE1hdHJpeDw0PnsKCQkJMS4wLCAwLjAsIDAuMCwgMS4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgLTIuMCwgCgkJCTAuMCwgMC4wLCAxLjAsIDAuMCwgCgkJCTAuMCwgMC4wLCAwLjAsIDEuMCwgCgkJfSkpCgkJQVNTRVJUKHRyYW5zbGF0aW9uKChWZWN0b3I8Mz57MC4wLCA5LjAsIC04LjB9KSkgPT0gKE1hdHJpeDw0PnsKCQkJMS4wLCAwLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgOS4wLCAKCQkJMC4wLCAwLjAsIDEuMCwgLTguMCwgCgkJCTAuMCwgMC4wLCAwLjAsIDEuMCwgCgkJfSkpCgkJCgkJQVNTRVJUKCh2aWV3KAoJCQkoVmVjdG9yPDM+ezAuMCwgLTMuMCwgOS4wfSksIChWZWN0b3I8Mz57MC4wLCAtMy4wLCAwLjB9KSkgKiAKCQkJKFZlY3RvcjwzPnszLjAsIDUuMCwgIDcuMH0pLmV4dCgpKS50cnVuYygpID09IAoJCQkoVmVjdG9yPDM+ezMuMCwgOC4wLCAgLTIuMH0pKQoJCUFTU0VSVCgodmlldygKCQkJT19QVCwgWF9BWElTKSAqIAoJCQkoVmVjdG9yPDM+ezAuMCwgIDAuMCwgLTUuMH0pLmV4dCgpKS50cnVuYygpID09IAoJCQkoVmVjdG9yPDM+ey01LjAsIDAuMCwgMC4wfSkpCgkJQVNTRVJUKCh2aWV3KAoJCQlPX1BULCAtWV9BWElTKSAqIAoJCQkoVmVjdG9yPDM+ezAuMCwgMC4wLCAtNS4wfSkuZXh0KCkpLnRydW5jKCkgPT0gCgkJCShWZWN0b3I8Mz57MC4wLCA1LjAsIDAuMH0pKQoJCUFTU0VSVCgodmlldygKCQkJT19QVCwgWl9BWElTKSAqIAoJCQkoVmVjdG9yPDM+ezAuMCwgMC4wLCAtNS4wfSkuZXh0KCkpLnRydW5jKCkgPT0gCgkJCShWZWN0b3I8Mz57MC4wLCAwLjAsIDUuMH0pKQoJCUFTU0VSVCgodmlldygKCQkJT19QVCwgLVpfQVhJUywgCgkJCShWZWN0b3I8Mz57MS4wLCAwLjAsIDAuMH0pKSAqIAoJCQkoVmVjdG9yPDM+ezMuMCwgMC4wLCAwLjB9KS5leHQoKSkudHJ1bmMoKSA9PSAKCQkJKFZlY3RvcjwzPnswLjAsIDMuMCwgMC4wfSkpCgkJQVNTRVJUKCh2aWV3KAoJCQlPX1BULCAtWl9BWElTLCAKCQkJKFZlY3RvcjwzPnstMS4wLCAwLjAsICAwLjB9KSkgKiAKCQkJKFZlY3RvcjwzPnswLjAsICAtMy4wLCAwLjB9KS5leHQoKSkudHJ1bmMoKSA9PSAKCQkJKFZlY3RvcjwzPnstMy4wLCAwLjAsICAwLjB9KSkKCQlBU1NFUlQoKHZpZXcoCgkJCU9fUFQsIFhfQVhJUywgCgkJCShWZWN0b3I8Mz57MS4wLCAgMC4wLCAwLjB9KSkgKiAKCQkJKFZlY3RvcjwzPns3LjAsICA4LjAsIDAuMH0pLmV4dCgpKS50cnVuYygpID09IAoJCQkoVmVjdG9yPDM+ey04LjAsIDAuMCwgLTcuMH0pKQoJCQoJCUFTU0VSVChwZXJzUHJvaihhbmcycmFkKDEyMC4wKSwgMC43NSwgMS4wLCAxMC4wKSA9PSAoTWF0cml4PDQ+ewoJCQkoMS4wICogMi4wKSAvIChzcXJ0KDMuMCkgKiAyLjApLCAwLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjAsIAoJCQkwLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoMS4wICogMi4wKSAvICgwLjc1ICogc3FydCgzLjApICogMi4wKSwgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjAsIAoJCQkwLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLSgoMTAuMCArIDEuMCkgLyAoMTAuMCAtIDEuMCkpLCAtKCgxLjAgKiAxMC4wICogMi4wKSAvICgxMC4wIC0gMS4wKSksICAKCQkJMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0xLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAKCQl9KSkKCQlBU1NFUlQocGVyc1Byb2ooYW5nMnJhZCg5MC4wKSwgMC41LCAyLjAsIDUuMCkgPT0gKE1hdHJpeDw0PnsKCQkJKDIuMCAqIDIuMCkgLyAoMi4wICogMi4wKSwgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAKCQkJMC4wLCAgICAgICAgICAgICAgICAgICAgICAgKDIuMCAqIDIuMCkgLyAoMC41ICogMi4wICogMi4wKSwgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAKCQkJMC4wLCAgICAgICAgICAgICAgICAgICAgICAgMC4wLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLSgoNS4wICsgMi4wKSAvICg1LjAgLSAyLjApKSwgLSgoMi4wICogNS4wICogMi4wKSAvICg1LjAgLSAyLjApKSwgIAoJCQkwLjAsICAgICAgICAgICAgICAgICAgICAgICAwLjAsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtMS4wLCAgICAgICAgICAgICAgICAgICAgICAgICAwLjAsIAoJCX0pKQoJfQp9OwoKI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RyaW5nPgoKaW50IG1haW4oKSB7Cgl1c2luZyBzdGQ6OmNlcnI7Cgl1c2luZyBzdGQ6OmVuZGw7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJdHJ5IHsKCQloM2Q6OlRlc3RTZXQoKS5ydW4oKTsKCX0KCWNhdGNoIChjb25zdCBzdHJpbmcmIG1zZykgewoJCWNlcnIgPDwgbXNnIDw8IGVuZGw7CgkJcmV0dXJuIDE7Cgl9CglyZXR1cm4gMDsKfQo=
アサート成功: 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(p.norm(), 3.0)
アサート成功: nearEqual(q.norm(), sqrt(5.0))
アサート成功: p.normalize() == (Vector<3>{2.0 / 3.0, 2.0 / 3.0, 1.0 / 3.0})
アサート成功: q.normalize() == (Vector<3>{1.0 / sqrt(5.0), -2.0 / sqrt(5.0), 0.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, })
アサート成功: (view( (Vector<3>{0.0, -3.0, 9.0}), (Vector<3>{0.0, -3.0, 0.0})) * (Vector<3>{3.0, 5.0, 7.0}).ext()).trunc() == (Vector<3>{3.0, 8.0, -2.0})
アサート成功: (view( O_PT, X_AXIS) * (Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() == (Vector<3>{-5.0, 0.0, 0.0})
アサート成功: (view( O_PT, -Y_AXIS) * (Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() == (Vector<3>{0.0, 5.0, 0.0})
アサート成功: (view( O_PT, Z_AXIS) * (Vector<3>{0.0, 0.0, -5.0}).ext()).trunc() == (Vector<3>{0.0, 0.0, 5.0})
アサート成功: (view( O_PT, -Z_AXIS, (Vector<3>{1.0, 0.0, 0.0})) * (Vector<3>{3.0, 0.0, 0.0}).ext()).trunc() == (Vector<3>{0.0, 3.0, 0.0})
アサート成功: (view( O_PT, -Z_AXIS, (Vector<3>{-1.0, 0.0, 0.0})) * (Vector<3>{0.0, -3.0, 0.0}).ext()).trunc() == (Vector<3>{-3.0, 0.0, 0.0})
アサート成功: (view( O_PT, X_AXIS, (Vector<3>{1.0, 0.0, 0.0})) * (Vector<3>{7.0, 8.0, 0.0}).ext()).trunc() == (Vector<3>{-8.0, 0.0, -7.0})
アサート成功: persProj(ang2rad(120.0), 0.75, 1.0, 10.0) == (Matrix<4>{ (1.0 * 2.0) / (sqrt(3.0) * 2.0), 0.0, 0.0, 0.0, 0.0, (1.0 * 2.0) / (0.75 * sqrt(3.0) * 2.0), 0.0, 0.0, 0.0, 0.0, -((10.0 + 1.0) / (10.0 - 1.0)), -((1.0 * 10.0 * 2.0) / (10.0 - 1.0)), 0.0, 0.0, -1.0, 0.0, })
アサート成功: persProj(ang2rad(90.0), 0.5, 2.0, 5.0) == (Matrix<4>{ (2.0 * 2.0) / (2.0 * 2.0), 0.0, 0.0, 0.0, 0.0, (2.0 * 2.0) / (0.5 * 2.0 * 2.0), 0.0, 0.0, 0.0, 0.0, -((5.0 + 2.0) / (5.0 - 2.0)), -((2.0 * 5.0 * 2.0) / (5.0 - 2.0)), 0.0, 0.0, -1.0, 0.0, })