namespace h3d {
/** 使用する浮動小数点数型。 */
using FLOAT = double;
/**
* 二つの数が近いかどうかを判定する。
* @param lhs 左側(Left Hand Side)の数。
* @param rhs 右側(Right Hand Side)の数。
* @return 近いならtrue, 遠いならfalse。
*/
template<typename X>
inline bool nearEqual(const X& lhs, const X& rhs);
};
#include <iostream>
namespace h3d {
using std::ostream;
/**
* N次元ベクトルクラス。
* @param N ベクトルの次元数。
* @param C 成分(Component)の型。省略ならFLOAT。
* この型で実体化したnearEqualを使う。
*/
template<unsigned int N, typename C = FLOAT>
class Vector {
public:
/** 成分(Component)の配列。N個。 */
C c[N];
/**
* 各成分をゼロクリアする。
*/
inline void clear();
/**
* 次元を一つ拡張(EXTend)する。
* @param ext_comp 追加する成分(COMPonent)。
* @return 拡張したベクトル。
*/
inline Vector<N + 1, C> ext(const C& ext_comp = 1.0) const;
/**
* 内積(INner PROduct)を計算する。
* @param rhs 右側(Right Hand Side)のベクトル。
* @return 計算した内積。
*/
inline C inpro(const Vector& rhs) const;
/**
* 長さを取得する。
* @return 取得した長さ。
*/
inline double norm() const;
/**
* 正規化し、長さを1にする。
* @return 正規化したベクトル。
*/
inline Vector normalize() const;
/**
* 二つのベクトルが等しくないかどうかを判定する。
* @param rhs 右側(Right Hand Side)のベクトル。
* @return 等しくないならtrue, 等しいならfalse。
*/
inline bool operator !=(const Vector& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算したベクトル。
*/
inline Vector operator *(const C& rhs) const;
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算して、代入したベクトル。
*/
inline Vector& operator *=(const C& rhs);
/**
* ベクトルを加算する。
* @param rhs 加数となる右側(Right Hand Side)のベクトル。
* @return 加算したベクトル。
*/
inline Vector operator +(const Vector& rhs) const;
/**
* ベクトルを加算して、代入する。
* @param rhs 加数となる右側(Right Hand Side)のベクトル。
* @return 加算して、代入したベクトル。
*/
inline Vector& operator +=(const Vector& rhs);
/**
* 各成分の符号を反転する。
* @return 符号を反転したベクトル。
*/
inline Vector operator -() const;
/**
* ベクトルを減算する。
* @param rhs 減数となる右側(Right Hand Side)のベクトル。
* @return 減算したベクトル。
*/
inline Vector operator -(const Vector& rhs) const;
/**
* ベクトルを減算して、代入する。
* @param rhs 減数となる右側(Right Hand Side)のベクトル。
* @return 減算して、代入したベクトル。
*/
inline Vector& operator -=(const Vector& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算したベクトル。
*/
inline Vector operator /(const C& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算して、代入したベクトル。
*/
inline Vector& operator /=(const C& rhs);
/**
* 二つのベクトルが等しいかどうかを判定する。
* @param rhs 右側(Right Hand Side)のベクトル
* @return 等しいならtrue, 等しくないならfalse。
*/
inline bool operator ==(const Vector& rhs) const;
/**
* 3次元ベクトルの外積(OUTer PROduct)を計算する。
* @param rhs 右側(Right Hand Side)のベクトル。
* @return 計算した外積。
*/
inline Vector outpro(const Vector& rhs) const;
/**
* 次元を一つ切り縮める(TRUNCate)。
* @return 切り縮めたベクトル。
*/
inline Vector<N - 1, C> trunc() const;
/**
* ゼロベクトルを取得する。
* @return 取得したゼロベクトル。
*/
static constexpr Vector ZERO();
};
/**
* N次元ベクトルを出力する。
* @param N ベクトルの次元数。
* @param C 成分(Component)の型。
* @param out 出力先となるストリーム。
* @param vec 出力するベクトル。
* @return 出力したストリーム。
*/
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const Vector<N, C>& vec);
/** 浮動小数点数の2次元ゼロベクトル。 */
template<>
constexpr Vector<2> Vector<2>::ZERO() {
return Vector<2>{0.0, 0.0};
}
/** 浮動小数点数の3次元ゼロベクトル。 */
template<>
constexpr Vector<3> Vector<3>::ZERO() {
return Vector<3>{0.0, 0.0, 0.0};
}
/** 浮動小数点数の4次元ゼロベクトル。 */
template<>
constexpr Vector<4> Vector<4>::ZERO() {
return Vector<4>{0.0, 0.0, 0.0, 0.0};
}
};
#include <iostream>
#include <string>
#include <tuple>
namespace h3d {
using std::ostream;
using std::string;
using std::tuple;
/**
* N次多項式クラス。
* 0次(定数項)からN次までのN + 1個の係数を格納する。
* 例) 3x^2 - 2x + 4 → c[0] = 4, c[1] = -2, c[2] = 3
* @param N 多項式の次数。項の数より一つ小さい。
* @param C 係数(Coefficient)の型。省略ならFLOAT。
* この型で実体化したnearEqualを使う。
*/
template<unsigned int N, typename C = FLOAT>
class PolyExp {
public:
/** 各項の係数(Coefficient)を格納する N + 1 次元ベクトル。成分のインデックスが次数に対応する。 */
Vector<N + 1, C> c;
/**
* 多項式を作る。
*/
inline PolyExp() = default;
/**
* 係数ベクトルから多項式を作る。
* @param coef_vec 係数ベクトル(COEFficient VECtor)。
* 定数項以外の係数はゼロクリアする。
*/
inline explicit PolyExp(const Vector<N + 1, C>& coef_vec);
/**
* 定数項の係数から多項式を作ることで、CからPolyExpへの型変換を行う。
* @param const_coef 定数項の係数(COEFficient)。
* 定数項以外の係数はゼロクリアする。
*/
inline PolyExp(const C& const_coef);
/**
* 二つの多項式が等しくないかどうかを判定する。
* @param rhs 右側(Right Hand Side)の多項式。
* @return 等しくないならtrue, 等しいならfalse。
*/
inline bool operator !=(const PolyExp& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算した多項式。
*/
inline PolyExp operator *(const C& rhs) const;
/**
* 多項式で乗算する。
* @param rhs 乗数となる右側(Right Hand Side)の多項式。
* @return 乗算した多項式。
*/
inline PolyExp operator *(const PolyExp& rhs) const throw(string);
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算して、代入した多項式。
*/
inline PolyExp& operator *=(const C& rhs);
/**
* 多項式で乗算して、代入する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算して、代入した多項式。
*/
inline PolyExp& operator *=(const PolyExp& rhs) throw(string);
/**
* 多項式を加算する。
* @param rhs 加数となる右側(Right Hand Side)の多項式。
* @return 加算した多項式。
*/
inline PolyExp operator +(const PolyExp& rhs) const;
/**
* 多項式を加算して、代入する。
* @param rhs 加数となる右側(Right Hand Side)の多項式。
* @return 加算して、代入した多項式。
*/
inline PolyExp& operator +=(const PolyExp& rhs);
/**
* 各係数の符号を反転する。
* @return 符号を反転した多項式。
*/
inline PolyExp operator -() const;
/**
* 多項式を減算する。
* @param rhs 減数となる右側(Right Hand Side)の多項式。
* @return 減算した多項式。
*/
inline PolyExp operator -(const PolyExp& rhs) const;
/**
* 多項式を減算して、代入する。
* @param rhs 減数となる右側(Right Hand Side)の多項式。
* @return 減算して、代入した多項式。
*/
inline PolyExp& operator -=(const PolyExp& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算した多項式。
*/
inline PolyExp operator /(const C& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算して、代入した多項式。
*/
inline PolyExp& operator /=(const C& rhs);
/**
* 二つの多項式が等しいかどうかを判定する。
* @param rhs 右側(Right Hand Side)の多項式
* @return 等しいならtrue, 等しくないならfalse。
*/
inline bool operator ==(const PolyExp& rhs) const;
/**
* 実数根(RooTS)を計算する。
* @return 計算した実数根。昇順にソートされている。
* 一つ目が実数根の個数。二つ目が実数根を格納したベクトル。
*/
inline tuple<unsigned int, Vector<N, C>> rts() const;
};
/**
* 多項式の実数根を計算するクラス。
* @param N 多項式の次数。
* @param C 係数(Coefficient)の型。
*/
template<unsigned int N, typename C>
class PolyExpRoots {
public:
/** インスタンスを作ることはできない。 */
PolyExpRoots() = delete;
/**
* 多項式の実数根を計算する。
* @param poly_exp 計算する多項式(POLYnominal EXPression)。
* @return 計算した実数根。昇順にソートされている。
* 一つ目が実数根の個数。二つ目が実数根を格納したベクトル。
*/
inline static tuple<unsigned int, Vector<N, C>> calculate(const PolyExp<N, C>& poly_exp);
};
/**
* 2次多項式の実数根を計算するクラス。
* @param C 係数(Coefficient)の型。
*/
template<typename C>
class PolyExpRoots<2, C> {
public:
/** インスタンスを作ることはできない。 */
PolyExpRoots() = delete;
/**
* 2次多項式の実数根を計算する。
* @param poly_exp 計算する2次多項式(POLYnominal EXPression)。
* @return 計算した実数根。昇順にソートされている。
* 一つ目が実数根の個数。二つ目が実数根を格納したベクトル。
*/
inline static tuple<unsigned int, Vector<2, C>> calculate(const PolyExp<2, C>& poly_exp);
};
/**
* 3次多項式の実数根を計算するクラス。
* @param C 係数(Coefficient)の型。
*/
template<typename C>
class PolyExpRoots<3, C> {
public:
/** インスタンスを作ることはできない。 */
PolyExpRoots() = delete;
/**
* 3次多項式の実数根を計算する。
* @param poly_exp 計算する3次多項式(POLYnominal EXPression)。
* @return 計算した実数根。昇順にソートされている。
* 一つ目が実数根の個数。二つ目が実数根を格納したベクトル。
*/
inline static tuple<unsigned int, Vector<3, C>> calculate(const PolyExp<3, C>& poly_exp);
};
/**
* N次多項式を出力する。
* @param N 多項式の次元数。
* @param out 出力先となるストリーム。
* @param poly_exp 出力する多項式。
* @return 出力したストリーム。
*/
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const PolyExp<N, C>& poly_exp);
};
namespace h3d {
/**
* polygonToTrianglesのラムダの説明用の関数型。
* @param tri_vert1_ind 分割した三角形の一つ目の頂点インデックス。
* @param tri_vert2_ind 分割した三角形の二つ目の頂点インデックス。
* @param tri_vert3_ind 分割した三角形の三つ目の頂点インデックス。
*/
using POLY2TRI_LAMBDA = void (*)(const unsigned int& tri_vert1_ind, const unsigned int& tri_vert2_ind, const unsigned int& tri_vert3_ind);
/** 原点。 */
constexpr Vector<3> O_POINT{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};
/**
* 角度を弧度に変換する。
* @param ang 変換する角度(ANGle)。
* @return 変換した弧度。
*/
inline FLOAT angleToRadian(const FLOAT& ang);
/**
* 多角形を三角形に分割する。
* @param poly_begin 多角形の始端の頂点インデックスを指す反復子。
* @param poly_end 多角形の終端の頂点インデックスを指す反復子。
* @param lambda 分割した三角形ごとに呼び出す{@link h3d::POLY2TRI_LAMBDA ラムダ}。
*/
template<typename PI, typename L>
inline void polygonToTriangles(PI poly_begin, PI poly_end, L lambda);
};
#include <iostream>
#include <string>
#include <tuple>
namespace h3d {
using std::ostream;
using std::string;
using std::tuple;
/**
* N次元正方行列クラス。
* @param N 行列の次元数。
* @param C 成分(Component)の型。省略ならFLOAT。
* この型で実体化したnearEqualを使う。
*/
template<unsigned int N, typename C = FLOAT>
class Matrix {
public:
/** 成分(Component)の配列。N×N個。 */
C c[N][N];
/**
* 各成分をゼロクリアする。
*/
inline void clear();
/**
* 行列式(DETerminant)を計算する。
* @return 計算した行列式。
*/
inline C det() const;
/**
* 固有値(EIGenVALueS)を計算する。
* @return 計算した固有値を格納したベクトル。昇順にソートされている。
* @throw string 行列が非正則ならメッセージをスローする。
*/
inline Vector<N, C> eigvals() const throw(string);
/**
* 固有ベクトル(EIGenVECtorS)を計算する。
* @return 計算した固有ベクトルを格納したベクトル。
* 各固有ベクトルは正規化されている。
* @throw string 行列が非正則ならメッセージをスローする。
*/
inline Vector<N, Vector<N, C>> eigvecs() const throw(string);
/**
* 次元を一つ拡張(EXTend)する。
* @param ext_mat この行列(MATrix)から最後の行と列の成分をコピーする。
* @return 拡張した行列。
*/
inline Matrix<N + 1, C> ext(const Matrix<N + 1, C>& ext_mat = Matrix<N + 1, C>::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 乗数となる右側(Right Hand Side)の行列。
* @return 乗算した行列。
*/
inline Matrix operator *(const Matrix& rhs) const;
/**
* ベクトルで乗算する。
* @param rhs 乗数となる右側(Right Hand Side)のベクトル。
* @return 計算したベクトル。
*/
inline Vector<N> operator *(const Vector<N>& rhs) const;
/**
* スカラー値で乗算する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算した行列。
*/
inline Matrix operator *(const C& rhs) const;
/**
* 行列で乗算して、代入する。
* @param rhs 乗数となる右側(Right Hand Side)の行列。
* @return 乗算して、代入した行列。
*/
inline Matrix operator *=(const Matrix& rhs);
/**
* スカラー値で乗算して、代入する。
* @param rhs 乗数となる右側(Right Hand Side)のスカラー値。
* @return 乗算して、代入した行列。
*/
inline Matrix operator *=(const C& rhs);
/**
* 行列を加算する。
* @param rhs 加数となる右側(Right Hand Side)の行列。
* @return 加算した行列。
*/
inline Matrix operator +(const Matrix& rhs) const;
/**
* 行列を加算して、代入する。
* @param rhs 加数となる右側(Right Hand Side)の行列。
* @return 加算して、代入した行列。
*/
inline Matrix operator +=(const Matrix& rhs);
/**
* すべての成分の符号を反転する。
* @return 符号を反転した行列。
*/
inline Matrix operator -() const;
/**
* 行列を減算する。
* @param rhs 減数となる右側(Right Hand Side)の行列。
* @return 減算した行列。
*/
inline Matrix operator -(const Matrix& rhs) const;
/**
* 行列を減算して、代入する。
* @param rhs 減数となる右側(Right Hand Side)の行列。
* @return 減算して、代入した行列。
*/
inline Matrix operator -=(const Matrix& rhs);
/**
* スカラー値で除算する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算した行列。
*/
inline Matrix operator /(const C& rhs) const;
/**
* スカラー値で除算して、代入する。
* @param rhs 除数となる右側(Right Hand Side)のスカラー値。
* @return 除算して、代入した行列。
*/
inline Matrix operator /=(const C& rhs);
/**
* 二つの行列が等しいかどうかを判定する。
* @param rhs 右側(Right Hand Side)の行列。
* @return 等しいならtrue, 等しくないならfalse。
*/
inline bool operator ==(const Matrix& rhs) const;
/**
* 被約形にする。連立1次方程式の解を計算するときに使う。
* @param con_col 定数ベクトル。省略ならゼロベクトル。
* @return 被約系にした行列と定数ベクトル。一つ目は行列, 二つ目は定数ベクトル。
*/
inline tuple<Matrix, Vector<N, C>> reduce(const Vector<N, C>& con_col = Vector<N, C>::ZERO()) const;
/**
* 行と列を取り除いて、小行列(SUBmatrix)を作る。
* @param row 取り除く行。
* @param col 取り除く列。
* @return 作った行列。
*/
inline Matrix<N - 1, C> sub(const unsigned int& row, const unsigned int& col) const;
/**
* 次元を一つ切り縮める(TRUNCate)。
* @return 切り縮めた行列。
*/
inline Matrix<N - 1, C> trunc() const;
};
/**
* N次元正方行列式計算クラス。
* @param N 行列の次元数。
* @param C 成分(Component)の型。
*/
template<unsigned int N, typename C>
class MatrixDeterminant {
public:
/** インスタンスを作ることはできない。 */
MatrixDeterminant() = delete;
/**
* 行列式を計算する。
* @param mat 計算する行列。
* @return 計算した行列式。
*/
inline static C calculate(const Matrix<N, C>& mat);
};
/**
* 2次元正方行列式計算クラス。
* @param C 成分(Component)の型。
*/
template<typename C>
class MatrixDeterminant<2, C> {
public:
/** インスタンスを作ることはできない。 */
MatrixDeterminant() = delete;
/**
* 2次元正方行列式を計算する。
* @param mat 計算する2次元行列。
* @return 計算した行列式。
*/
inline static C calculate(const Matrix<2, C>& mat);
};
/**
* N次元正方行列を出力する。
* @param N 行列の次元数。
* @param C 成分(Component)の型。
* @param out 出力先となるストリーム。
* @param mat 出力する行列。
* @return 出力したストリーム。
*/
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const Matrix<N, C>& mat);
/** 浮動小数点数の2次元単位行列。 */
template<>
constexpr Matrix<2> Matrix<2>::IDENTITY() {
return Matrix<2>{
1.0, 0.0,
0.0, 1.0,
};
}
/** 浮動小数点数の3次元単位行列。 */
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,
};
}
/** 浮動小数点数の4次元単位行列。 */
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 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);
/**
* 透視射影行列を作る。
* 透視射影行列はカメラ空間からクリップ空間への変換を行う。
* @param hori_fov 水平視野角(HORIzontal Field Of View)。弧度。
* @param asp_rat アスペクト比(ASPect RATio)。
* @param near_dist 近平面までの距離(DISTance)。
* @param far_dist 遠平面までの距離(DISTance)。
*/
Matrix<4> perspectiveProjection(const double& hori_fov, const double& asp_rat, const double& near_dist, const double& far_dist);
};
#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);
};
#include <string>
namespace h3d {
using std::string;
class Test { public: virtual void run() const throw(string) = 0; };
};
#include <memory>
#include <string>
#include <vector>
namespace h3d {
using std::shared_ptr;
using std::string;
using std::vector;
class TestSet {
public:
TestSet();
void run() const throw(string);
protected:
vector<shared_ptr<Test>> tests;
};
};
#include <string>
namespace h3d {
using std::string;
class Test2 : public Test {
public:
virtual void run() const throw(string) override;
};
};
#include <string>
namespace h3d {
using std::string;
class Test5 : public Test {
public:
virtual void run() const throw(string) override;
};
};
////////////////////////////////////////////////////////////////////////////////
#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;
}
};
namespace h3d {
template<typename X>
inline bool nearEqual(const X& lhs, const X& rhs) {
return lhs == rhs;
}
template<>
inline bool nearEqual<FLOAT>(const FLOAT& lhs, const FLOAT& rhs) {
static const FLOAT THRESHOLD = 0.00000000000001;
FLOAT dif = lhs - rhs;
return dif > -THRESHOLD && dif < THRESHOLD;
}
};
#include <cmath>
namespace h3d {
inline FLOAT angleToRadian(const FLOAT& ang) { return M_PI * ang / 180.0; }
template<typename PI, typename L>
inline void polygonToTriangles(PI poly_begin, PI poly_end, L lambda) {
// アルゴリズムを示す。
// 1. 始端から始める。
// 2. 現在位置から二つ先が終端以上なら終了する。
// 3. 始端なら、現在位置のものを一つ目の頂点インデックスとして、5に飛ぶ。
// 4. 始端ではないなら、一つ前の位置のものを一つ目の頂点インデックスとする。
// 5. 一つ次の位置に進んで、現在位置のものを二つ目の頂点インデックスとする。
// 6. 一つ次の位置に進んで、現在位置のものを三つ目の頂点インデックスとする。
// 7. ラムダを呼ぶ。
// 8. 一つ前の位置に戻って、2に飛ぶ。
for (auto iter = poly_begin; iter + 2 < poly_end;) {
unsigned int tri_vert1_ind, tri_vert2_ind, tri_vert3_ind;
if (iter == poly_begin) tri_vert1_ind = *iter;
else tri_vert1_ind = *(iter - 1);
tri_vert2_ind = *++iter;
tri_vert3_ind = *(++iter)--;
lambda(tri_vert1_ind, tri_vert2_ind, tri_vert3_ind);
}
}
};
#include <algorithm>
#include <iostream>
#include <tuple>
#include <utility>
namespace h3d {
using std::fill;
using std::get;
using std::make_tuple;
using std::swap;
using std::tuple;
template<unsigned int N, typename C>
inline void Matrix<N, C>::clear() {
// 各成分をゼロクリアする。
fill((C*)this->c, (C*)this->c + N * N, 0.0);
}
template<unsigned int N, typename C>
inline C Matrix<N, C>::det() const {
return MatrixDeterminant<N, C>::calculate(*this);
}
template<unsigned int N, typename C>
inline Vector<N, C> Matrix<N, C>::eigvals() const throw(string) {
// 特性多項式を作る。
Matrix<N, PolyExp<N, C>> ch_mat;
for (auto row = 0; row < N; row++) {
for (auto col = 0; col < N; col++) {
PolyExp<N, C> comp(this->c[row][col]);
if (row == col) comp.c.c[1] = -1.0;
ch_mat.c[row][col] = comp;
}
}
PolyExp<N, C> ch_poly_exp(ch_mat.det());
// 特性多項式の実数根を計算する。
tuple<unsigned int, Vector<N, C>> rts = ch_poly_exp.rts();
// 行列が非正則なら、N個の実数根を持たない(?)。
if (get<0>(rts) < N) throw string("行列が非正則。");
return get<1>(rts);
}
template<unsigned int N, typename C>
inline Vector<N, Vector<N, C>> Matrix<N, C>::eigvecs() const throw(string) {
Vector<N, Vector<N, C>> res;
// 固有値を計算する。
Vector<N, C> eigvals_res(eigvals());
// 固有値ごとに反復する。
for (auto i = 0; i < N; i++) {
// 被約係数行列を作る。
Matrix rdc_coef_mat(*this);
for (auto j = 0; j < N; j++) rdc_coef_mat.c[j][j] -= eigvals_res.c[i];
rdc_coef_mat = get<0>(rdc_coef_mat.reduce());
// (M - λI)V = 0 を解く。
// 主対角成分ごとに反復する。
for (auto j = 0; j < N; j++) {
// 先導成分を含むならスキップする。
if (!nearEqual(rdc_coef_mat.c[j][j], 0.0)) continue;
// 固有ベクトルの成分ごとに反復する。
for (auto k = 0; k < N; k++) {
// 主対角に対応する成分は1、それ以外は対応する行の成分の符号を反転したもの。
if (k == j) res.c[i].c[k] = 1.0;
else res.c[i].c[k] = -rdc_coef_mat.c[k][j];
}
break;
}
// 正規化する。
res.c[i] = res.c[i].normalize();
}
return res;
}
template<unsigned int N, typename C>
inline Matrix<N + 1, C> Matrix<N, C>::ext(const Matrix<N + 1, C>& ext_mat) const {
Matrix<N + 1, C> 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] = ext_mat.c[row][col];
}
}
return res;
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::inv() const throw(string) {
// まず行列式を計算し、正則であることを確かめる。
C det_res = det();
if (nearEqual(det_res, 0.0)) throw string("行列が非正則。");
// 行列式の逆数を計算しておく。
C inv_det = 1.0 / det_res;
Matrix<N, C> 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, typename C>
inline bool Matrix<N, C>::operator !=(const Matrix& rhs) const {
return !operator ==(rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator *(const Matrix& rhs) const {
Matrix<N, C> 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, typename C>
inline Vector<N> Matrix<N, C>::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, typename C>
inline Matrix<N, C> Matrix<N, C>::operator *(const C& rhs) const {
Matrix<N, C> 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, typename C>
inline Matrix<N, C> Matrix<N, C>::operator *=(const Matrix& rhs) {
return *this = operator *(rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator *=(const C& rhs) {
return *this = operator *(rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator +(const Matrix& rhs) const {
Matrix<N, C> 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, typename C>
inline Matrix<N, C> Matrix<N, C>::operator +=(const Matrix& rhs) {
return *this = operator +(rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator -() const {
Matrix<N, C> 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, typename C>
inline Matrix<N, C> Matrix<N, C>::operator -(const Matrix& rhs) const {
return operator +(-rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator -=(const Matrix& rhs) {
return *this = operator -(rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator /(const C& rhs) const {
return operator *(1.0 / rhs);
}
template<unsigned int N, typename C>
inline Matrix<N, C> Matrix<N, C>::operator /=(const C& rhs) {
return *this = operator /(rhs);
}
template<unsigned int N, typename C>
bool Matrix<N, C>::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, typename C>
inline tuple<Matrix<N, C>, Vector<N, C>> Matrix<N, C>::reduce(const Vector<N, C>& con_col) const {
Matrix<N, C> res_mat(*this);
Vector<N, C> res_con_col(con_col);
// カレント行インデックスを初期化する。
auto i = 0;
// 各列ごとに反復する。
for (auto j = 0; j < N; j++) {
// ゼロではない成分を探索する。
auto k = i;
for (; k < N; k++) {
if (!nearEqual(res_mat.c[k][j], 0.0)) break;
}
// ゼロではない成分が見つからなければ次の列に移る。
if (k >= N) continue;
// 見つかった行がカレント行でなければ、二つの行を入れ替える。
// 例) ↓カレント列
// | 0 5|5| ←カレント行
// |*2 4|6| ←見つかった行
// ↓二つの行を入れ替える。
// |*2 4|6| ←カレント行
// | 0 5|5|
if (k != i) {
for (auto s = 0; s < N; s++)
swap(res_mat.c[k][s], res_mat.c[i][s]);
swap(res_con_col.c[k], res_con_col.c[i]);
}
// カレント行の先導成分を1にするために、先導成分の逆数を各成分に掛ける。
// 例) ↓カレント列
// |*2 4|6| ←カレント行
// | 2 6|9|
// ↓先導成分2の逆数である1/2を各成分に掛ける。
// |*1 2|3| ←カレント行
// | 2 6|9|
C inv_ij_comp = 1.0 / res_mat.c[i][j];
for (auto s = j; s < N; s++)
res_mat.c[i][s] *= inv_ij_comp;
res_con_col.c[i] *= inv_ij_comp;
// カレント列の各成分が、カレント行以外はゼロになるようにする。
// 例) ↓カレント列
// | 1 2|3| ←カレント行
// |*2 6|9| ←ゼロにしたい行
// ↓ゼロにしたい行にカレント行×-2を加える。
// | 1 2|3| ←カレント行
// |*0 2|3| ←ゼロにしたい行
// 各行ごとに反復する。
for (auto r = 0; r < N; r++) {
// カレント行ならスキップする。
if (r == i) continue;
// 係数を計算しておく。ゼロならスキップ。
C coef = -res_mat.c[r][j];
if (nearEqual(coef, 0.0)) continue;
// 各列ごとに反復して、ゼロにしたい行の成分にカレント行の成分×係数を加える。
for (auto s = j; s < N; s++)
res_mat.c[r][s] += res_mat.c[i][s] * coef;
res_con_col.c[r] += res_con_col.c[i] * coef;
}
i++;
}
return make_tuple(res_mat, res_con_col);
}
template<unsigned int N, typename C>
inline Matrix<N - 1, C> Matrix<N, C>::sub(const unsigned int& row, const unsigned int& col) const {
Matrix<N - 1, C> 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, typename C>
inline Matrix<N - 1, C> Matrix<N, C>::trunc() const {
Matrix<N - 1, C> 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, typename C>
inline C MatrixDeterminant<N, C>::calculate(const Matrix<N, C>& mat) {
C res = 0.0;
// 1行目の各成分ごとに反復する。
for (auto col = 0; col < N; col++) {
// 成分に、それと対応する小行列式を乗算する。
C cofac = mat.c[0][col] * mat.sub(0, col).det();
// (-1)^col
if ((col & 0x1) == 1) cofac = -cofac;
// 結果に余因子を加算する。
res += cofac;
}
return res;
}
template<typename C>
inline C MatrixDeterminant<2, C>::calculate(const Matrix<2, C>& mat) {
// 再帰的な行列式計算の終着点。
// 2×2の行列式を計算する。
return mat.c[0][0] * mat.c[1][1] - mat.c[0][1] * mat.c[1][0];
}
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const Matrix<N, C>& 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>
#include <tuple>
namespace h3d {
using std::acos;
using std::cbrt;
using std::cos;
using std::make_tuple;
using std::ostream;
using std::pow;
using std::sort;
using std::tuple;
template<unsigned int N, typename C>
inline PolyExp<N, C>::PolyExp(const Vector<N + 1, C>& coef_vec) : c(coef_vec) {}
template<unsigned int N, typename C>
inline PolyExp<N, C>::PolyExp(const C& const_coef) {
this->c.clear();
this->c.c[0] = const_coef;
}
template<unsigned int N, typename C>
inline bool PolyExp<N, C>::operator !=(const PolyExp& rhs) const {
return !operator ==(rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator *(const C& rhs) const {
return PolyExp(this->c * rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator *(const PolyExp& rhs) const throw(string) {
PolyExp<N, C> res;
res.c.clear();
// 左側の各係数と右側の各係数の組み合わせごとに反復して、乗算する。
for (auto i = 0; i < N + 1; i++) {
if (!nearEqual(this->c.c[i], 0.0)) {
for (auto j = 0; j < N + 1; j++) {
if (!nearEqual(rhs.c.c[j], 0.0)) {
if (i + j >= N + 1) throw string("多項式の次数が足りない。");
res.c.c[i + j] += this->c.c[i] * rhs.c.c[j];
}
}
}
}
return res;
}
template<unsigned int N, typename C>
inline PolyExp<N, C>& PolyExp<N, C>::operator *=(const C& rhs) {
return *this = operator *(rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C>& PolyExp<N, C>::operator *=(const PolyExp& rhs) throw(string) {
return *this = operator *(rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator +(const PolyExp& rhs) const {
return PolyExp(this->c + rhs.c);
}
template<unsigned int N, typename C>
inline PolyExp<N, C>& PolyExp<N, C>::operator +=(const PolyExp& rhs) {
return *this = operator +(rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator -() const {
return PolyExp(-this->c);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator -(const PolyExp& rhs) const {
return operator +(-rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C>& PolyExp<N, C>::operator -=(const PolyExp& rhs) {
return *this = operator -(rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C> PolyExp<N, C>::operator /(const C& rhs) const {
return operator *(1.0 / rhs);
}
template<unsigned int N, typename C>
inline PolyExp<N, C>& PolyExp<N, C>::operator /=(const C& rhs) {
return *this = operator /(rhs);
}
template<unsigned int N, typename C>
inline bool PolyExp<N, C>::operator ==(const PolyExp& rhs) const {
return this->c == rhs.c;
}
template<unsigned int N, typename C>
inline tuple<unsigned int, Vector<N, C>> PolyExp<N, C>::rts() const {
return PolyExpRoots<N, C>::calculate(*this);
}
template<typename C>
inline tuple<unsigned int, Vector<2, C>> PolyExpRoots<2, C>::calculate(const PolyExp<2, C>& poly_exp) {
unsigned int res_rts_cnt = 0;
Vector<2, C> res_rts;
// 各係数をわかりやすい名前の変数に代入する。
// ax^2 + bx + c
C a = poly_exp.c.c[2], b = poly_exp.c.c[1], c = poly_exp.c.c[0];
// 判別式を計算する。
// d = b^2 - 4ac
C d = b * b - 4.0 * a * c;
// 1 / 2a
C inv_a2 = 1.0 / (a + a);
// d = 0 なら重根。正なら二つの実数根を持つ。
if (nearEqual(d, 0.0)) {
res_rts_cnt = 1;
res_rts.c[0] = -b * inv_a2;
}
else if (d > 0.0) {
res_rts_cnt = 2;
res_rts.c[0] = (-b + sqrt(d)) * inv_a2;
res_rts.c[1] = (-b - sqrt(d)) * inv_a2;
sort(res_rts.c, res_rts.c + 2);
}
return make_tuple(res_rts_cnt, res_rts);
}
template<typename C>
inline tuple<unsigned int, Vector<3, C>> PolyExpRoots<3, C>::calculate(const PolyExp<3, C>& poly_exp) {
unsigned int res_rts_cnt = 0;
Vector<3, C> res_rts;
// 3次の係数が1になるように除算を行う。
PolyExp<3, C> poly_exp2(poly_exp / poly_exp.c.c[3]);
// 各係数をわかりやすい名前の変数に代入する。
// t^3 + at^2 + bt + c
C a = poly_exp2.c.c[2], b = poly_exp2.c.c[1], c = poly_exp2.c.c[0];
// a
// t = x - ---
// 3
// とすると、
// x^3 + px + q
// が得られる。ただし、
// 1
// p = - ---a^2 + b
// 3
// 2 1
// q = ----a^3 - ---ab + c
// 27 3
// である。
// xの根を求められたら、a/3を引けば、tの根を得られる。
C a2p = a * a, a3p = a2p * a;
C p = -a2p / 3.0 + b, q = a3p * 2.0 / 27.0 - a * b / 3.0 + c;
C pd = p / 3.0, qd = q / 2.0;
C pd3p = pd * pd * pd, qd2p = qd * qd;
C dd = pd3p + qd2p;
if (nearEqual(dd, 0.0) || dd > 0) {
C r = cbrt(-qd + sqrt(dd));
if (nearEqual(dd, 0.0)) {
res_rts_cnt = 2;
res_rts.c[0] = r + r;
res_rts.c[1] = -r;
if (nearEqual(res_rts.c[0], res_rts.c[1])) res_rts_cnt = 1;
else sort(res_rts.c, res_rts.c + 2);
}
else {
C s = cbrt(-qd - sqrt(dd));
res_rts_cnt = 1;
res_rts.c[0] = r + s;
}
}
else {
res_rts_cnt = 3;
C theta = acos(-qd / sqrt(-pd3p)) / 3.0;
C u = sqrt(-pd), u2m = u + u;
res_rts.c[0] = cos(theta) * u2m;
res_rts.c[1] = cos(theta + M_PI * 2.0 / 3.0) * u2m;
res_rts.c[2] = cos(theta - M_PI * 2.0 / 3.0) * u2m;
sort(res_rts.c, res_rts.c + 3);
}
C a3d = a / 3.0;
for (auto i = 0; i < res_rts_cnt; i++) res_rts.c[i] -= a3d;
return make_tuple(res_rts_cnt, res_rts);
}
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const PolyExp<N, C>& poly_exp) {
out << "{";
for (auto c : poly_exp.c) out << c << ", ";
out << "}";
}
};
#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_trl(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_trl;
}
Matrix<4> perspectiveProjection(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;
// 上平面と近平面が交差するY座標。
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 <algorithm>
#include <cmath>
#include <iostream>
namespace h3d {
using std::copy;
using std::fill;
using std::ostream;
using std::sqrt;
template<unsigned int N, typename C>
inline void Vector<N, C>::clear() {
// 各成分をゼロクリアする。
fill(this->c, this->c + N, 0.0);
}
template<unsigned int N, typename C>
inline Vector<N + 1, C> Vector<N, C>::ext(const C& ext_comp) const {
Vector<N + 1, C> res;
// このベクトルからN個の成分をコピーして、最後は引数の成分をコピーする。
copy(this->c, this->c + N, res.c);
res.c[N] = ext_comp;
return res;
}
template<unsigned int N, typename C>
inline C Vector<N, C>::inpro(const Vector& rhs) const {
C res = 0.0;
// 対応する成分同士の積を求めて、合計する。
for (auto i = 0; i < N; i++) res += this->c[i] * rhs.c[i];
return res;
}
template<unsigned int N, typename C>
inline double Vector<N, C>::norm() const {
return sqrt(this->inpro(*this));
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::normalize() const {
return *this / norm();
}
template<unsigned int N, typename C>
inline bool Vector<N, C>::operator !=(const Vector& rhs) const {
return !operator ==(rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::operator *(const C& rhs) const {
Vector<N, C> res;
// 各成分にスカラー値を乗算する。
for (auto i = 0; i < N; i++) res.c[i] = this->c[i] * rhs;
return res;
}
template<unsigned int N, typename C>
inline Vector<N, C>& Vector<N, C>::operator *=(const C& rhs) {
return *this = operator *(rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::operator +(const Vector& rhs) const {
Vector<N, C> res;
// 対応する成分同士で加算する。
for (auto i = 0; i < N; i++) res.c[i] = this->c[i] + rhs.c[i];
return res;
}
template<unsigned int N, typename C>
inline Vector<N, C>& Vector<N, C>::operator +=(const Vector& rhs) {
return *this = operator +(rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::operator -() const {
Vector<N, C> res;
// 各成分の符号を反転する。
for (auto i = 0; i < N; i++) res.c[i] = -this->c[i];
return res;
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::operator -(const Vector& rhs) const {
return operator +(-rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C>& Vector<N, C>::operator -=(const Vector& rhs) {
return *this = operator -(rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C> Vector<N, C>::operator /(const C& rhs) const {
return operator *(1.0 / rhs);
}
template<unsigned int N, typename C>
inline Vector<N, C>& Vector<N, C>::operator /=(const C& rhs) {
return *this = operator /(rhs);
}
template<unsigned int N, typename C>
inline bool Vector<N, C>::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, typename C>
inline Vector<N, C> Vector<N, C>::outpro(const Vector& rhs) const {
// 各成分について、対応する列を除いた2×2の行列式を計算する。
// P×Q = < Py・Qz - Pz・Qy, Pz・Qx - Px・Qz, Px・Qy - Py・Qx >
return Vector<N, C>{
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, typename C>
inline Vector<N - 1, C> Vector<N, C>::trunc() const {
Vector<N - 1, C> res;
copy(this->c, this->c + N - 1, res.c);
return res;
}
template<unsigned int N, typename C>
inline ostream& operator <<(ostream& out, const Vector<N, C>& vec) {
out << "{";
for (auto c : vec.c) out << c << ", ";
out << "}";
}
};
#include <memory>
#include <string>
namespace h3d {
using std::shared_ptr;
using std::string;
TestSet::TestSet() {
this->tests.push_back(shared_ptr<Test>(new Test2));
this->tests.push_back(shared_ptr<Test>(new Test5));
}
void TestSet::run() const throw(string) { for (auto iter : this->tests) iter->run(); }
};
#include <cmath>
#include <tuple>
namespace h3d {
using std::cos;
using std::get;
using std::make_tuple;
using std::sin;
using std::sqrt;
using std::tuple;
void Test2::run() const throw(string) {
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 = angleToRadian(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}))
ASSERT((Matrix<3>{
3.0, 2.0, -3.0,
4.0, -3.0, 6.0,
1.0, 0.0, -1.0,
}).reduce((Vector<3>{
5.0,
1.0,
3.0,
})) == make_tuple((Matrix<3>{
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
}), (Vector<3>{
13.0 / 10.0,
-2.0,
-17.0 / 10.0,
})))
ASSERT((Matrix<3>{
2.0, 1.0, 3.0,
0.0, 1.0, -1.0,
1.0, 3.0, -1.0,
}).reduce() == make_tuple((Matrix<3>{
1.0, 0.0, 2.0,
0.0, 1.0, -1.0,
0.0, 0.0, 0.0,
}), (Vector<3>{
0.0,
0.0,
0.0,
})))
ASSERT((Matrix<3>{
4.0, 3.0, 2.0,
1.0, -1.0, -3.0,
2.0, 3.0, 4.0,
}).reduce() == make_tuple((Matrix<3>{
1.0, 0.0, -1.0,
0.0, 1.0, 2.0,
0.0, 0.0, 0.0,
}), (Vector<3>{
0.0,
0.0,
0.0,
})))
ASSERT((Matrix<2>{
1.0, 1.0,
3.0, -1.0,
}).eigvals() == (Vector<2>{-2.0, 2.0}))
ASSERT((Matrix<3>{
2.0, 1.0, 0.0,
1.0, 1.0, 0.0,
0.0, 0.0, -1.0,
}).eigvals() == (Vector<3>{-1.0, (3.0 - sqrt(5.0)) / 2.0, (3.0 + sqrt(5.0)) / 2.0}))
ASSERT((Matrix<3>{
2.0, 0.0, 0.0,
5.0, 2.0, 3.0,
-4.0, 3.0, 2.0,
}).eigvals() == (Vector<3>{-1.0, 2.0, 5.0}))
ASSERT((Matrix<2>{
1.0, 1.0,
3.0, -1.0,
}).eigvecs() == (Vector<2, Vector<2>>{
(Vector<2>{-1.0 / 3.0 / (sqrt(10.0) / 3.0), 1.0 / (sqrt(10.0) / 3.0)}),
(Vector<2>{1.0 / sqrt(2.0), 1.0 / sqrt(2.0)}),
}))
ASSERT((Matrix<3>{
2.0, 0.0, 0.0,
5.0, 2.0, 3.0,
-4.0, 3.0, 2.0,
}).eigvecs() == (Vector<3, Vector<3>>{
(Vector<3>{0.0 / sqrt(2.0), -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)}),
(Vector<3>{-3.0 / 5.0 / sqrt(2.0), -4.0 / 5.0 / sqrt(2.0), 1.0 / sqrt(2.0)}),
(Vector<3>{0.0 / sqrt(2.0), 1.0 / sqrt(2.0), 1.0 / sqrt(2.0)}),
}))
}
};
#include <cmath>
#include <tuple>
namespace h3d {
using std::get;
using std::tuple;
void Test5::run() const throw(string) {
PolyExp<2> a{Vector<3>{1.0, -2.0, 3.0}};
ASSERT((PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}).c ==
(Vector<3>{3.0, -2.0, 1.0}))
ASSERT(-(PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}) ==
(PolyExp<2>{Vector<3>{-3.0, 2.0, -1.0}}))
ASSERT((PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}) * 2.0 ==
(PolyExp<2>{Vector<3>{6.0, -4.0, 2.0}}))
ASSERT((PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}) / 2.0 ==
(PolyExp<2>{Vector<3>{1.5, -1.0, 0.5}}))
ASSERT((PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}) +
(PolyExp<2>{Vector<3>{-5.0, 1.0, 0.0}}) ==
(PolyExp<2>{Vector<3>{-2.0, -1.0, 1.0}}))
ASSERT((PolyExp<2>{Vector<3>{3.0, -2.0, 1.0}}) -
(PolyExp<2>{Vector<3>{-5.0, 1.0, 0.0}}) ==
(PolyExp<2>{Vector<3>{8.0, -3.0, 1.0}}))
ASSERT((PolyExp<2>{Vector<3>{4.0, 2.0, 0.0}}) *
(PolyExp<2>{Vector<3>{-3.0, 1.0, 0.0}}) ==
(PolyExp<2>{Vector<3>{-12.0, -2.0, 2.0}}))
tuple<unsigned int, Vector<2>> rts_res2;
rts_res2 = (PolyExp<2>{Vector<3>{2.0, -4.0, 2.0}}).rts();
ASSERT(get<0>(rts_res2) == 1)
ASSERT(nearEqual(get<1>(rts_res2).c[0], 1.0))
rts_res2 = (PolyExp<2>{Vector<3>{-3.0, -6.0, -3.0}}).rts();
ASSERT(get<0>(rts_res2) == 1)
ASSERT(nearEqual(get<1>(rts_res2).c[0], -1.0))
rts_res2 = (PolyExp<2>{Vector<3>{-2.0, 0.0, 2.0}}).rts();
ASSERT(get<0>(rts_res2) == 2)
ASSERT(nearEqual(get<1>(rts_res2).c[0], -1.0))
ASSERT(nearEqual(get<1>(rts_res2).c[1], 1.0))
rts_res2 = (PolyExp<2>{Vector<3>{18.0, -3.0, -3.0}}).rts();
ASSERT(get<0>(rts_res2) == 2)
ASSERT(nearEqual(get<1>(rts_res2).c[0], -3.0))
ASSERT(nearEqual(get<1>(rts_res2).c[1], 2.0))
tuple<unsigned int, Vector<3>> rts_res3;
rts_res3 = (PolyExp<3>{Vector<4>{-2.0, 6.0, -6.0, 2.0}}).rts();
ASSERT(get<0>(rts_res3) == 1)
ASSERT(nearEqual(get<1>(rts_res3).c[0], 1.0))
rts_res3 = (PolyExp<3>{Vector<4>{2.0, 3.0, 0.0, -1.0}}).rts();
ASSERT(get<0>(rts_res3) == 2)
ASSERT(nearEqual(get<1>(rts_res3).c[0], -1.0))
ASSERT(nearEqual(get<1>(rts_res3).c[1], 2.0))
rts_res3 = (PolyExp<3>{Vector<4>{180.0, 51.0, -12.0, -3.0}}).rts();
ASSERT(get<0>(rts_res3) == 3)
ASSERT(nearEqual(get<1>(rts_res3).c[0], -5.0))
ASSERT(nearEqual(get<1>(rts_res3).c[1], -3.0))
ASSERT(nearEqual(get<1>(rts_res3).c[2], 4.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;
}
bmFtZXNwYWNlIGgzZCB7CiAgICAvKiog5L2/55So44GZ44KL5rWu5YuV5bCP5pWw54K55pWw5Z6L44CCICovCgl1c2luZyBGTE9BVCA9IGRvdWJsZTsKCQoJLyoqCgkgKiDkuozjgaTjga7mlbDjgYzov5HjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCSAqIEBwYXJhbSBsaHMg5bem5YG0KExlZnQgSGFuZCBTaWRlKeOBruaVsOOAggoJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruaVsOOAggoJICogQHJldHVybiDov5HjgYTjgarjgol0cnVlLCDpgaDjgYTjgarjgolmYWxzZeOAggoJICovCgl0ZW1wbGF0ZTx0eXBlbmFtZSBYPiAKCWlubGluZSBib29sIG5lYXJFcXVhbChjb25zdCBYJiBsaHMsIGNvbnN0IFgmIHJocyk7Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6b3N0cmVhbTsKCQoJLyoqCgkgKiBO5qyh5YWD44OZ44Kv44OI44Or44Kv44Op44K544CCCgkgKiBAcGFyYW0gTiDjg5njgq/jg4jjg6vjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBDIOaIkOWIhihDb21wb25lbnQp44Gu5Z6L44CC55yB55Wl44Gq44KJRkxPQVTjgIIKCSAqIOOBk+OBruWei+OBp+Wun+S9k+WMluOBl+OBn25lYXJFcXVhbOOCkuS9v+OBhuOAggoJICovCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQyA9IEZMT0FUPiAKCWNsYXNzIFZlY3RvciB7CglwdWJsaWM6CgkJLyoqIOaIkOWIhihDb21wb25lbnQp44Gu6YWN5YiX44CCTuWAi+OAgiAqLwoJCUMgY1tOXTsKCQkKCQkvKioKCQkgKiDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgdm9pZCBjbGVhcigpOwoJCQoJCS8qKgoJCSAqIOasoeWFg+OCkuS4gOOBpOaLoeW8tShFWFRlbmQp44GZ44KL44CCCgkJICogQHBhcmFtIGV4dF9jb21wIOi/veWKoOOBmeOCi+aIkOWIhihDT01Qb25lbnQp44CCCgkJICogQHJldHVybiDmi6HlvLXjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4gKyAxLCBDPiBleHQoY29uc3QgQyYgZXh0X2NvbXAgPSAxLjApIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWGheepjShJTm5lciBQUk9kdWN0KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+WGheepjeOAggoJCSAqLwoJCWlubGluZSBDIGlucHJvKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDplbfjgZXjgpLlj5blvpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOWPluW+l+OBl+OBn+mVt+OBleOAggoJCSAqLwoJCWlubGluZSBkb3VibGUgbm9ybSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOato+imj+WMluOBl+OAgemVt+OBleOCkjHjgavjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOato+imj+WMluOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igbm9ybWFsaXplKCkgY29uc3Q7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu44OZ44Kv44OI44Or44GM562J44GX44GP44Gq44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g562J44GX44GP44Gq44GE44Gq44KJdHJ1ZSwg562J44GX44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciAhPShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciBvcGVyYXRvciAqKGNvbnN0IEMmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gm44CB5Luj5YWl44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciYgb3BlcmF0b3IgKj0oY29uc3QgQyYgcmhzKTsKCQkKCQkvKioKCQkgKiDjg5njgq/jg4jjg6vjgpLliqDnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWKoOaVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDliqDnrpfjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yIG9wZXJhdG9yICsoY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOODmeOCr+ODiOODq+OCkuWKoOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3ImIG9wZXJhdG9yICs9KGNvbnN0IFZlY3RvciYgcmhzKTsKCQkKCQkvKioKCQkgKiDlkITmiJDliIbjga7nrKblj7fjgpLlj43ou6LjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOespuWPt+OCkuWPjei7ouOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igb3BlcmF0b3IgLSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOODmeOCr+ODiOODq+OCkua4m+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igb3BlcmF0b3IgLShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44OZ44Kv44OI44Or44KS5rib566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g5rib566X44GX44Gm44CB5Luj5YWl44GX44Gf44OZ44Kv44OI44Or44CCCgkJICovCgkJaW5saW5lIFZlY3RvciYgb3BlcmF0b3IgLT0oY29uc3QgVmVjdG9yJiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOmZpOeul+OBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igb3BlcmF0b3IgLyhjb25zdCBDJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+mZpOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg6Zmk5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOmZpOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3ImIG9wZXJhdG9yIC89KGNvbnN0IEMmIHJocyk7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu44OZ44Kv44OI44Or44GM562J44GX44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruODmeOCr+ODiOODqwoJCSAqIEByZXR1cm4g562J44GX44GE44Gq44KJdHJ1ZSwg562J44GX44GP44Gq44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciA9PShjb25zdCBWZWN0b3ImIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICogM+asoeWFg+ODmeOCr+ODiOODq+OBruWkluepjShPVVRlciBQUk9kdWN0KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jg5njgq/jg4jjg6vjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+WkluepjeOAggoJCSAqLwoJCWlubGluZSBWZWN0b3Igb3V0cHJvKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDmrKHlhYPjgpLkuIDjgaTliIfjgornuK7jgoHjgosoVFJVTkNhdGUp44CCCgkJICogQHJldHVybiDliIfjgornuK7jgoHjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4gLSAxLCBDPiB0cnVuYygpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCvOODreODmeOCr+ODiOODq+OCkuWPluW+l+OBmeOCi+OAggoJCSAqIEByZXR1cm4g5Y+W5b6X44GX44Gf44K844Ot44OZ44Kv44OI44Or44CCCgkJICovCgkJc3RhdGljIGNvbnN0ZXhwciBWZWN0b3IgWkVSTygpOwoJfTsKCQoJLyoqCgkgKiBO5qyh5YWD44OZ44Kv44OI44Or44KS5Ye65Yqb44GZ44KL44CCCgkgKiBAcGFyYW0gTiDjg5njgq/jg4jjg6vjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBDIOaIkOWIhihDb21wb25lbnQp44Gu5Z6L44CCCgkgKiBAcGFyYW0gb3V0IOWHuuWKm+WFiOOBqOOBquOCi+OCueODiOODquODvOODoOOAggoJICogQHBhcmFtIHZlYyDlh7rlipvjgZnjgovjg5njgq/jg4jjg6vjgIIKCSAqIEByZXR1cm4g5Ye65Yqb44GX44Gf44K544OI44Oq44O844Og44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFZlY3RvcjxOLCBDPiYgdmVjKTsKCQoJLyoqIOa1ruWLleWwj+aVsOeCueaVsOOBrjLmrKHlhYPjgrzjg63jg5njgq/jg4jjg6vjgIIgKi8KCXRlbXBsYXRlPD4gCgljb25zdGV4cHIgVmVjdG9yPDI+IFZlY3RvcjwyPjo6WkVSTygpIHsKCQlyZXR1cm4gVmVjdG9yPDI+ezAuMCwgMC4wfTsKCX0KCQoJLyoqIOa1ruWLleWwj+aVsOeCueaVsOOBrjPmrKHlhYPjgrzjg63jg5njgq/jg4jjg6vjgIIgKi8KCXRlbXBsYXRlPD4gCgljb25zdGV4cHIgVmVjdG9yPDM+IFZlY3RvcjwzPjo6WkVSTygpIHsKCQlyZXR1cm4gVmVjdG9yPDM+ezAuMCwgMC4wLCAwLjB9OwoJfQoJCgkvKiog5rWu5YuV5bCP5pWw54K55pWw44GuNOasoeWFg+OCvOODreODmeOCr+ODiOODq+OAgiAqLwoJdGVtcGxhdGU8PiAKCWNvbnN0ZXhwciBWZWN0b3I8ND4gVmVjdG9yPDQ+OjpaRVJPKCkgewoJCXJldHVybiBWZWN0b3I8ND57MC4wLCAwLjAsIDAuMCwgMC4wfTsKCX0KfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHN0cmluZz4KI2luY2x1ZGUgPHR1cGxlPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6Om9zdHJlYW07Cgl1c2luZyBzdGQ6OnN0cmluZzsKCXVzaW5nIHN0ZDo6dHVwbGU7CgkKCS8qKgoJICogTuasoeWkmumgheW8j+OCr+ODqeOCueOAggoJICogMOasoSjlrprmlbDpoIUp44GL44KJTuasoeOBvuOBp+OBrk4gKyAx5YCL44Gu5L+C5pWw44KS5qC857SN44GZ44KL44CCCgkgKiDkvospIDN4XjIgLSAyeCArIDQg4oaSIGNbMF0gPSA0LCBjWzFdID0gLTIsIGNbMl0gPSAzCgkgKiBAcGFyYW0gTiDlpJrpoIXlvI/jga7mrKHmlbDjgILpoIXjga7mlbDjgojjgorkuIDjgaTlsI/jgZXjgYTjgIIKCSAqIEBwYXJhbSBDIOS/guaVsChDb2VmZmljaWVudCnjga7lnovjgILnnIHnlaXjgarjgolGTE9BVOOAggoJICog44GT44Gu5Z6L44Gn5a6f5L2T5YyW44GX44GfbmVhckVxdWFs44KS5L2/44GG44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDID0gRkxPQVQ+IAoJY2xhc3MgUG9seUV4cCB7CglwdWJsaWM6CgkJLyoqIOWQhOmgheOBruS/guaVsChDb2VmZmljaWVudCnjgpLmoLzntI3jgZnjgosgTiArIDEg5qyh5YWD44OZ44Kv44OI44Or44CC5oiQ5YiG44Gu44Kk44Oz44OH44OD44Kv44K544GM5qyh5pWw44Gr5a++5b+c44GZ44KL44CCICovCgkJVmVjdG9yPE4gKyAxLCBDPiBjOwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OCkuS9nOOCi+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwKCkgPSBkZWZhdWx0OwoJCQoJCS8qKgoJCSAqIOS/guaVsOODmeOCr+ODiOODq+OBi+OCieWkmumgheW8j+OCkuS9nOOCi+OAggoJCSAqIEBwYXJhbSBjb2VmX3ZlYyDkv4LmlbDjg5njgq/jg4jjg6soQ09FRmZpY2llbnQgVkVDdG9yKeOAggoJCSAqIOWumuaVsOmgheS7peWkluOBruS/guaVsOOBr+OCvOODreOCr+ODquOCouOBmeOCi+OAggoJCSAqLwoJCWlubGluZSBleHBsaWNpdCBQb2x5RXhwKGNvbnN0IFZlY3RvcjxOICsgMSwgQz4mIGNvZWZfdmVjKTsKCQkKCQkvKioKCQkgKiDlrprmlbDpoIXjga7kv4LmlbDjgYvjgonlpJrpoIXlvI/jgpLkvZzjgovjgZPjgajjgafjgIFD44GL44KJUG9seUV4cOOBuOOBruWei+WkieaPm+OCkuihjOOBhuOAggoJCSAqIEBwYXJhbSBjb25zdF9jb2VmIOWumuaVsOmgheOBruS/guaVsChDT0VGZmljaWVudCnjgIIKCQkgKiDlrprmlbDpoIXku6XlpJbjga7kv4LmlbDjga/jgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgUG9seUV4cChjb25zdCBDJiBjb25zdF9jb2VmKTsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7lpJrpoIXlvI/jgYznrYnjgZfjgY/jgarjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu5aSa6aCF5byP44CCCgkJICogQHJldHVybiDnrYnjgZfjgY/jgarjgYTjgarjgol0cnVlLCDnrYnjgZfjgYTjgarjgolmYWxzZeOAggoJCSAqLwoJCWlubGluZSBib29sIG9wZXJhdG9yICE9KGNvbnN0IFBvbHlFeHAmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn5LmX566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gf5aSa6aCF5byP44CCCgkJICovCgkJaW5saW5lIFBvbHlFeHAgb3BlcmF0b3IgKihjb25zdCBDJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OBp+S5l+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7lpJrpoIXlvI/jgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBn+WkmumgheW8j+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwIG9wZXJhdG9yICooY29uc3QgUG9seUV4cCYgcmhzKSBjb25zdCB0aHJvdyhzdHJpbmcpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+WkmumgheW8j+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwJiBvcGVyYXRvciAqPShjb25zdCBDJiByaHMpOwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+WkmumgheW8j+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwJiBvcGVyYXRvciAqPShjb25zdCBQb2x5RXhwJiByaHMpIHRocm93KHN0cmluZyk7CgkJCgkJLyoqCgkJICog5aSa6aCF5byP44KS5Yqg566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDliqDmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruWkmumgheW8j+OAggoJCSAqIEByZXR1cm4g5Yqg566X44GX44Gf5aSa6aCF5byP44CCCgkJICovCgkJaW5saW5lIFBvbHlFeHAgb3BlcmF0b3IgKyhjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OCkuWKoOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7lpJrpoIXlvI/jgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+WkmumgheW8j+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwJiBvcGVyYXRvciArPShjb25zdCBQb2x5RXhwJiByaHMpOwoJCQoJCS8qKgoJCSAqIOWQhOS/guaVsOOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCSAqIEByZXR1cm4g56ym5Y+344KS5Y+N6Lui44GX44Gf5aSa6aCF5byP44CCCgkJICovCgkJaW5saW5lIFBvbHlFeHAgb3BlcmF0b3IgLSgpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OCkua4m+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5rib5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7lpJrpoIXlvI/jgIIKCQkgKiBAcmV0dXJuIOa4m+eul+OBl+OBn+WkmumgheW8j+OAggoJCSAqLwoJCWlubGluZSBQb2x5RXhwIG9wZXJhdG9yIC0oY29uc3QgUG9seUV4cCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDlpJrpoIXlvI/jgpLmuJvnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOa4m+aVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu5aSa6aCF5byP44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/lpJrpoIXlvI/jgIIKCQkgKi8KCQlpbmxpbmUgUG9seUV4cCYgb3BlcmF0b3IgLT0oY29uc3QgUG9seUV4cCYgcmhzKTsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgZ/lpJrpoIXlvI/jgIIKCQkgKi8KCQlpbmxpbmUgUG9seUV4cCBvcGVyYXRvciAvKGNvbnN0IEMmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog44K544Kr44Op44O85YCk44Gn6Zmk566X44GX44Gm44CB5Luj5YWl44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDpmaTmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruOCueOCq+ODqeODvOWApOOAggoJCSAqIEByZXR1cm4g6Zmk566X44GX44Gm44CB5Luj5YWl44GX44Gf5aSa6aCF5byP44CCCgkJICovCgkJaW5saW5lIFBvbHlFeHAmIG9wZXJhdG9yIC89KGNvbnN0IEMmIHJocyk7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu5aSa6aCF5byP44GM562J44GX44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruWkmumgheW8jwoJCSAqIEByZXR1cm4g562J44GX44GE44Gq44KJdHJ1ZSwg562J44GX44GP44Gq44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciA9PShjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWun+aVsOaguShSb29UUynjgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+Wun+aVsOagueOAguaYh+mghuOBq+OCveODvOODiOOBleOCjOOBpuOBhOOCi+OAggoJCSAqIOS4gOOBpOebruOBjOWun+aVsOagueOBruWAi+aVsOOAguS6jOOBpOebruOBjOWun+aVsOagueOCkuagvOe0jeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSB0dXBsZTx1bnNpZ25lZCBpbnQsIFZlY3RvcjxOLCBDPj4gcnRzKCkgY29uc3Q7Cgl9OwoJCgkvKioKCSAqIOWkmumgheW8j+OBruWun+aVsOagueOCkuioiOeul+OBmeOCi+OCr+ODqeOCueOAggoJICogQHBhcmFtIE4g5aSa6aCF5byP44Gu5qyh5pWw44CCCgkgKiBAcGFyYW0gQyDkv4LmlbAoQ29lZmZpY2llbnQp44Gu5Z6L44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWNsYXNzIFBvbHlFeHBSb290cyB7CglwdWJsaWM6CgkJLyoqIOOCpOODs+OCueOCv+ODs+OCueOCkuS9nOOCi+OBk+OBqOOBr+OBp+OBjeOBquOBhOOAgiAqLwoJCVBvbHlFeHBSb290cygpID0gZGVsZXRlOwoJCQoJCS8qKgoJCSAqIOWkmumgheW8j+OBruWun+aVsOagueOCkuioiOeul+OBmeOCi+OAggoJCSAqIEBwYXJhbSBwb2x5X2V4cCDoqIjnrpfjgZnjgovlpJrpoIXlvI8oUE9MWW5vbWluYWwgRVhQcmVzc2lvbinjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+Wun+aVsOagueOAguaYh+mghuOBq+OCveODvOODiOOBleOCjOOBpuOBhOOCi+OAggoJCSAqIOS4gOOBpOebruOBjOWun+aVsOagueOBruWAi+aVsOOAguS6jOOBpOebruOBjOWun+aVsOagueOCkuagvOe0jeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBzdGF0aWMgdHVwbGU8dW5zaWduZWQgaW50LCBWZWN0b3I8TiwgQz4+IGNhbGN1bGF0ZShjb25zdCBQb2x5RXhwPE4sIEM+JiBwb2x5X2V4cCk7Cgl9OwoJCgkvKioKCSAqIDLmrKHlpJrpoIXlvI/jga7lrp/mlbDmoLnjgpLoqIjnrpfjgZnjgovjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBDIOS/guaVsChDb2VmZmljaWVudCnjga7lnovjgIIKCSAqLwoJdGVtcGxhdGU8dHlwZW5hbWUgQz4gCgljbGFzcyBQb2x5RXhwUm9vdHM8MiwgQz4gewoJcHVibGljOgoJCS8qKiDjgqTjg7Pjgrnjgr/jg7PjgrnjgpLkvZzjgovjgZPjgajjga/jgafjgY3jgarjgYTjgIIgKi8KCQlQb2x5RXhwUm9vdHMoKSA9IGRlbGV0ZTsKCQkKCQkvKioKCQkgKiAy5qyh5aSa6aCF5byP44Gu5a6f5pWw5qC544KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHBvbHlfZXhwIOioiOeul+OBmeOCizLmrKHlpJrpoIXlvI8oUE9MWW5vbWluYWwgRVhQcmVzc2lvbinjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+Wun+aVsOagueOAguaYh+mghuOBq+OCveODvOODiOOBleOCjOOBpuOBhOOCi+OAggoJCSAqIOS4gOOBpOebruOBjOWun+aVsOagueOBruWAi+aVsOOAguS6jOOBpOebruOBjOWun+aVsOagueOCkuagvOe0jeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBzdGF0aWMgdHVwbGU8dW5zaWduZWQgaW50LCBWZWN0b3I8MiwgQz4+IGNhbGN1bGF0ZShjb25zdCBQb2x5RXhwPDIsIEM+JiBwb2x5X2V4cCk7Cgl9OwoJCgkvKioKCSAqIDPmrKHlpJrpoIXlvI/jga7lrp/mlbDmoLnjgpLoqIjnrpfjgZnjgovjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBDIOS/guaVsChDb2VmZmljaWVudCnjga7lnovjgIIKCSAqLwoJdGVtcGxhdGU8dHlwZW5hbWUgQz4gCgljbGFzcyBQb2x5RXhwUm9vdHM8MywgQz4gewoJcHVibGljOgoJCS8qKiDjgqTjg7Pjgrnjgr/jg7PjgrnjgpLkvZzjgovjgZPjgajjga/jgafjgY3jgarjgYTjgIIgKi8KCQlQb2x5RXhwUm9vdHMoKSA9IGRlbGV0ZTsKCQkKCQkvKioKCQkgKiAz5qyh5aSa6aCF5byP44Gu5a6f5pWw5qC544KS6KiI566X44GZ44KL44CCCgkJICogQHBhcmFtIHBvbHlfZXhwIOioiOeul+OBmeOCizPmrKHlpJrpoIXlvI8oUE9MWW5vbWluYWwgRVhQcmVzc2lvbinjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+Wun+aVsOagueOAguaYh+mghuOBq+OCveODvOODiOOBleOCjOOBpuOBhOOCi+OAggoJCSAqIOS4gOOBpOebruOBjOWun+aVsOagueOBruWAi+aVsOOAguS6jOOBpOebruOBjOWun+aVsOagueOCkuagvOe0jeOBl+OBn+ODmeOCr+ODiOODq+OAggoJCSAqLwoJCWlubGluZSBzdGF0aWMgdHVwbGU8dW5zaWduZWQgaW50LCBWZWN0b3I8MywgQz4+IGNhbGN1bGF0ZShjb25zdCBQb2x5RXhwPDMsIEM+JiBwb2x5X2V4cCk7Cgl9OwoJCgkvKioKCSAqIE7mrKHlpJrpoIXlvI/jgpLlh7rlipvjgZnjgovjgIIKCSAqIEBwYXJhbSBOIOWkmumgheW8j+OBruasoeWFg+aVsOOAggoJICogQHBhcmFtIG91dCDlh7rlipvlhYjjgajjgarjgovjgrnjg4jjg6rjg7zjg6DjgIIKCSAqIEBwYXJhbSBwb2x5X2V4cCDlh7rlipvjgZnjgovlpJrpoIXlvI/jgIIKCSAqIEByZXR1cm4g5Ye65Yqb44GX44Gf44K544OI44Oq44O844Og44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFBvbHlFeHA8TiwgQz4mIHBvbHlfZXhwKTsKfTsKCm5hbWVzcGFjZSBoM2QgewoJLyoqCgkgKiBwb2x5Z29uVG9UcmlhbmdsZXPjga7jg6njg6Djg4Djga7oqqzmmI7nlKjjga7plqLmlbDlnovjgIIKCSAqIEBwYXJhbSB0cmlfdmVydDFfaW5kIOWIhuWJsuOBl+OBn+S4ieinkuW9ouOBruS4gOOBpOebruOBrumggueCueOCpOODs+ODh+ODg+OCr+OCueOAggoJICogQHBhcmFtIHRyaV92ZXJ0Ml9pbmQg5YiG5Ymy44GX44Gf5LiJ6KeS5b2i44Gu5LqM44Gk55uu44Gu6aCC54K544Kk44Oz44OH44OD44Kv44K544CCCgkgKiBAcGFyYW0gdHJpX3ZlcnQzX2luZCDliIblibLjgZfjgZ/kuInop5LlvaLjga7kuInjgaTnm67jga7poILngrnjgqTjg7Pjg4fjg4Pjgq/jgrnjgIIKCSAqLwoJdXNpbmcgUE9MWTJUUklfTEFNQkRBID0gdm9pZCAoKikoY29uc3QgdW5zaWduZWQgaW50JiB0cmlfdmVydDFfaW5kLCBjb25zdCB1bnNpZ25lZCBpbnQmIHRyaV92ZXJ0Ml9pbmQsIGNvbnN0IHVuc2lnbmVkIGludCYgdHJpX3ZlcnQzX2luZCk7CgkKCS8qKiDljp/ngrnjgIIgKi8KCWNvbnN0ZXhwciBWZWN0b3I8Mz4gT19QT0lOVHswLjAsIDAuMCwgMC4wfTsKCS8qKiBY6Lu444CCICovCgljb25zdGV4cHIgVmVjdG9yPDM+IFhfQVhJUyB7MS4wLCAwLjAsIDAuMH07CgkvKiogWei7uOOAgiAqLwoJY29uc3RleHByIFZlY3RvcjwzPiBZX0FYSVMgezAuMCwgMS4wLCAwLjB9OwoJLyoqIFrou7jjgIIgKi8KCWNvbnN0ZXhwciBWZWN0b3I8Mz4gWl9BWElTIHswLjAsIDAuMCwgMS4wfTsKCQoJLyoqCgkgKiDop5LluqbjgpLlvKfluqbjgavlpInmj5vjgZnjgovjgIIKCSAqIEBwYXJhbSBhbmcg5aSJ5o+b44GZ44KL6KeS5bqmKEFOR2xlKeOAggoJICogQHJldHVybiDlpInmj5vjgZfjgZ/lvKfluqbjgIIKCSAqLwoJaW5saW5lIEZMT0FUIGFuZ2xlVG9SYWRpYW4oY29uc3QgRkxPQVQmIGFuZyk7CgkKCS8qKgoJICog5aSa6KeS5b2i44KS5LiJ6KeS5b2i44Gr5YiG5Ymy44GZ44KL44CCCgkgKiBAcGFyYW0gcG9seV9iZWdpbiDlpJrop5LlvaLjga7lp4vnq6/jga7poILngrnjgqTjg7Pjg4fjg4Pjgq/jgrnjgpLmjIfjgZnlj43lvqnlrZDjgIIKCSAqIEBwYXJhbSBwb2x5X2VuZCDlpJrop5LlvaLjga7ntYLnq6/jga7poILngrnjgqTjg7Pjg4fjg4Pjgq/jgrnjgpLmjIfjgZnlj43lvqnlrZDjgIIKCSAqIEBwYXJhbSBsYW1iZGEg5YiG5Ymy44GX44Gf5LiJ6KeS5b2i44GU44Go44Gr5ZG844Gz5Ye644GZe0BsaW5rIGgzZDo6UE9MWTJUUklfTEFNQkRBIOODqeODoOODgH3jgIIKCSAqLwoJdGVtcGxhdGU8dHlwZW5hbWUgUEksIHR5cGVuYW1lIEw+IAoJaW5saW5lIHZvaWQgcG9seWdvblRvVHJpYW5nbGVzKFBJIHBvbHlfYmVnaW4sIFBJIHBvbHlfZW5kLCBMIGxhbWJkYSk7Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxzdHJpbmc+CiNpbmNsdWRlIDx0dXBsZT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJdXNpbmcgc3RkOjpzdHJpbmc7Cgl1c2luZyBzdGQ6OnR1cGxlOwoJCgkvKioKCSAqIE7mrKHlhYPmraPmlrnooYzliJfjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBOIOihjOWIl+OBruasoeWFg+aVsOOAggoJICogQHBhcmFtIEMg5oiQ5YiGKENvbXBvbmVudCnjga7lnovjgILnnIHnlaXjgarjgolGTE9BVOOAggoJICog44GT44Gu5Z6L44Gn5a6f5L2T5YyW44GX44GfbmVhckVxdWFs44KS5L2/44GG44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDID0gRkxPQVQ+IAoJY2xhc3MgTWF0cml4IHsKCXB1YmxpYzoKCQkvKiog5oiQ5YiGKENvbXBvbmVudCnjga7phY3liJfjgIJOw5dO5YCL44CCICovCgkJQyBjW05dW05dOwoJCQoJCS8qKgoJCSAqIOWQhOaIkOWIhuOCkuOCvOODreOCr+ODquOCouOBmeOCi+OAggoJCSAqLwoJCWlubGluZSB2b2lkIGNsZWFyKCk7CgkJCgkJLyoqCgkJICog6KGM5YiX5byPKERFVGVybWluYW50KeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEByZXR1cm4g6KiI566X44GX44Gf6KGM5YiX5byP44CCCgkJICovCgkJaW5saW5lIEMgZGV0KCkgY29uc3Q7CgkJCgkJLyoqCgkJICog5Zu65pyJ5YCkKEVJR2VuVkFMdWVTKeOCkuioiOeul+OBmeOCi+OAggoJCSAqIEByZXR1cm4g6KiI566X44GX44Gf5Zu65pyJ5YCk44KS5qC857SN44GX44Gf44OZ44Kv44OI44Or44CC5piH6aCG44Gr44K944O844OI44GV44KM44Gm44GE44KL44CCCgkJICogQHRocm93IHN0cmluZyDooYzliJfjgYzpnZ7mraPliYfjgarjgonjg6Hjg4Pjgrvjg7zjgrjjgpLjgrnjg63jg7zjgZnjgovjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4sIEM+IGVpZ3ZhbHMoKSBjb25zdCB0aHJvdyhzdHJpbmcpOwoJCQoJCS8qKgoJCSAqIOWbuuacieODmeOCr+ODiOODqyhFSUdlblZFQ3RvclMp44KS6KiI566X44GZ44KL44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/lm7rmnInjg5njgq/jg4jjg6vjgpLmoLzntI3jgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKiDlkITlm7rmnInjg5njgq/jg4jjg6vjga/mraPopo/ljJbjgZXjgozjgabjgYTjgovjgIIKCQkgKiBAdGhyb3cgc3RyaW5nIOihjOWIl+OBjOmdnuato+WJh+OBquOCieODoeODg+OCu+ODvOOCuOOCkuOCueODreODvOOBmeOCi+OAggoJCSAqLwoJCWlubGluZSBWZWN0b3I8TiwgVmVjdG9yPE4sIEM+PiBlaWd2ZWNzKCkgY29uc3QgdGhyb3coc3RyaW5nKTsKCQkKCQkvKioKCQkgKiDmrKHlhYPjgpLkuIDjgaTmi6HlvLUoRVhUZW5kKeOBmeOCi+OAggoJCSAqIEBwYXJhbSBleHRfbWF0IOOBk+OBruihjOWIlyhNQVRyaXgp44GL44KJ5pyA5b6M44Gu6KGM44Go5YiX44Gu5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJICogQHJldHVybiDmi6HlvLXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4PE4gKyAxLCBDPiBleHQoY29uc3QgTWF0cml4PE4gKyAxLCBDPiYgZXh0X21hdCA9IE1hdHJpeDxOICsgMSwgQz46OklERU5USVRZKCkpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOWNmOS9jeihjOWIl+OCkuWPluW+l+OBmeOCi+OAggoJCSAqIEByZXR1cm4g5Y2Y5L2N6KGM5YiX44CCCgkJICovCgkJc3RhdGljIGNvbnN0ZXhwciBNYXRyaXggSURFTlRJVFkoKTsKCQkKCQkvKioKCQkgKiDpgIbooYzliJcoSU5WZXJzZSBtYXRyaXgp44KS6KiI566X44GZ44KL44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/pgIbooYzliJfjgIIKCQkgKiBAdGhyb3cgc3RyaW5nIOihjOWIl+OBjOmdnuato+WJh+OBquOCieODoeODg+OCu+ODvOOCuOOCkuOCueODreODvOOBmeOCi+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggaW52KCkgY29uc3QgdGhyb3coc3RyaW5nKTsKCQkKCQkvKioKCQkgKiDkuozjgaTjga7ooYzliJfjgYznrYnjgZfjgY/jgarjgYTjgYvjganjgYbjgYvjgpLliKTlrprjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWPs+WBtOOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g562J44GX44GP44Gq44GE44Gq44KJdHJ1ZSwg562J44GX44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciAhPShjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44Gn5LmX566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDkuZfmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g5LmX566X44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAqKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjg5njgq/jg4jjg6vjgafkuZfnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOS5l+aVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu44OZ44Kv44OI44Or44CCCgkJICogQHJldHVybiDoqIjnrpfjgZfjgZ/jg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgVmVjdG9yPE4+IG9wZXJhdG9yICooY29uc3QgVmVjdG9yPE4+JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKihjb25zdCBDJiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOCueOCq+ODqeODvOWApOOBp+S5l+eul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5LmX5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7jgrnjgqvjg6njg7zlgKTjgIIKCQkgKiBAcmV0dXJuIOS5l+eul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKj0oY29uc3QgQyYgcmhzKTsKCQkKCQkvKioKCQkgKiDooYzliJfjgpLliqDnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOWKoOaVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu6KGM5YiX44CCCgkJICogQHJldHVybiDliqDnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yICsoY29uc3QgTWF0cml4JiByaHMpIGNvbnN0OwoJCQoJCS8qKgoJCSAqIOihjOWIl+OCkuWKoOeul+OBl+OBpuOAgeS7o+WFpeOBmeOCi+OAggoJCSAqIEBwYXJhbSByaHMg5Yqg5pWw44Go44Gq44KL5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7ooYzliJfjgIIKCQkgKiBAcmV0dXJuIOWKoOeul+OBl+OBpuOAgeS7o+WFpeOBl+OBn+ihjOWIl+OAggoJCSAqLwoJCWlubGluZSBNYXRyaXggb3BlcmF0b3IgKz0oY29uc3QgTWF0cml4JiByaHMpOwoJCQoJCS8qKgoJCSAqIOOBmeOBueOBpuOBruaIkOWIhuOBruespuWPt+OCkuWPjei7ouOBmeOCi+OAggoJCSAqIEByZXR1cm4g56ym5Y+344KS5Y+N6Lui44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAtKCkgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM5YiX44KS5rib566X44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDmuJvmlbDjgajjgarjgovlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g5rib566X44GX44Gf6KGM5YiX44CCCgkJICovCgkJaW5saW5lIE1hdHJpeCBvcGVyYXRvciAtKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDooYzliJfjgpLmuJvnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOa4m+aVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu6KGM5YiX44CCCgkJICogQHJldHVybiDmuJvnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC09KGNvbnN0IE1hdHJpeCYgcmhzKTsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC8oY29uc3QgQyYgcmhzKSBjb25zdDsKCQkKCQkvKioKCQkgKiDjgrnjgqvjg6njg7zlgKTjgafpmaTnrpfjgZfjgabjgIHku6PlhaXjgZnjgovjgIIKCQkgKiBAcGFyYW0gcmhzIOmZpOaVsOOBqOOBquOCi+WPs+WBtChSaWdodCBIYW5kIFNpZGUp44Gu44K544Kr44Op44O85YCk44CCCgkJICogQHJldHVybiDpmaTnrpfjgZfjgabjgIHku6PlhaXjgZfjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4IG9wZXJhdG9yIC89KGNvbnN0IEMmIHJocyk7CgkJCgkJLyoqCgkJICog5LqM44Gk44Gu6KGM5YiX44GM562J44GX44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruihjOWIl+OAggoJCSAqIEByZXR1cm4g562J44GX44GE44Gq44KJdHJ1ZSwg562J44GX44GP44Gq44GE44Gq44KJZmFsc2XjgIIKCQkgKi8KCQlpbmxpbmUgYm9vbCBvcGVyYXRvciA9PShjb25zdCBNYXRyaXgmIHJocykgY29uc3Q7CgkJCgkJLyoqCgkJICog6KKr57SE5b2i44Gr44GZ44KL44CC6YCj56uLMeasoeaWueeoi+W8j+OBruino+OCkuioiOeul+OBmeOCi+OBqOOBjeOBq+S9v+OBhuOAggoJICAgICAqIEBwYXJhbSBjb25fY29sIOWumuaVsOODmeOCr+ODiOODq+OAguecgeeVpeOBquOCieOCvOODreODmeOCr+ODiOODq+OAggoJCSAqIEByZXR1cm4g6KKr57SE57O744Gr44GX44Gf6KGM5YiX44Go5a6a5pWw44OZ44Kv44OI44Or44CC5LiA44Gk55uu44Gv6KGM5YiXLCDkuozjgaTnm67jga/lrprmlbDjg5njgq/jg4jjg6vjgIIKCQkgKi8KCQlpbmxpbmUgdHVwbGU8TWF0cml4LCBWZWN0b3I8TiwgQz4+IHJlZHVjZShjb25zdCBWZWN0b3I8TiwgQz4mIGNvbl9jb2wgPSBWZWN0b3I8TiwgQz46OlpFUk8oKSkgY29uc3Q7CgkJCgkJLyoqCgkJICog6KGM44Go5YiX44KS5Y+W44KK6Zmk44GE44Gm44CB5bCP6KGM5YiXKFNVQm1hdHJpeCnjgpLkvZzjgovjgIIKCQkgKiBAcGFyYW0gcm93IOWPluOCiumZpOOBj+ihjOOAggoJCSAqIEBwYXJhbSBjb2wg5Y+W44KK6Zmk44GP5YiX44CCCgkJICogQHJldHVybiDkvZzjgaPjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4PE4gLSAxLCBDPiBzdWIoY29uc3QgdW5zaWduZWQgaW50JiByb3csIGNvbnN0IHVuc2lnbmVkIGludCYgY29sKSBjb25zdDsKCQkKCQkvKioKCQkgKiDmrKHlhYPjgpLkuIDjgaTliIfjgornuK7jgoHjgosoVFJVTkNhdGUp44CCCgkJICogQHJldHVybiDliIfjgornuK7jgoHjgZ/ooYzliJfjgIIKCQkgKi8KCQlpbmxpbmUgTWF0cml4PE4gLSAxLCBDPiB0cnVuYygpIGNvbnN0OwoJfTsKCQoJLyoqCgkgKiBO5qyh5YWD5q2j5pa56KGM5YiX5byP6KiI566X44Kv44Op44K544CCCgkgKiBAcGFyYW0gTiDooYzliJfjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBDIOaIkOWIhihDb21wb25lbnQp44Gu5Z6L44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWNsYXNzIE1hdHJpeERldGVybWluYW50IHsKCXB1YmxpYzoKCQkvKiog44Kk44Oz44K544K/44Oz44K544KS5L2c44KL44GT44Go44Gv44Gn44GN44Gq44GE44CCICovCgkJTWF0cml4RGV0ZXJtaW5hbnQoKSA9IGRlbGV0ZTsKCQkKCQkvKioKCQkgKiDooYzliJflvI/jgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gbWF0IOioiOeul+OBmeOCi+ihjOWIl+OAggoJCSAqIEByZXR1cm4g6KiI566X44GX44Gf6KGM5YiX5byP44CCCgkJICovCgkJaW5saW5lIHN0YXRpYyBDIGNhbGN1bGF0ZShjb25zdCBNYXRyaXg8TiwgQz4mIG1hdCk7Cgl9OwoJCgkvKioKCSAqIDLmrKHlhYPmraPmlrnooYzliJflvI/oqIjnrpfjgq/jg6njgrnjgIIKCSAqIEBwYXJhbSBDIOaIkOWIhihDb21wb25lbnQp44Gu5Z6L44CCCgkgKi8KCXRlbXBsYXRlPHR5cGVuYW1lIEM+IAoJY2xhc3MgTWF0cml4RGV0ZXJtaW5hbnQ8MiwgQz4gewoJcHVibGljOgoJCS8qKiDjgqTjg7Pjgrnjgr/jg7PjgrnjgpLkvZzjgovjgZPjgajjga/jgafjgY3jgarjgYTjgIIgKi8KCQlNYXRyaXhEZXRlcm1pbmFudCgpID0gZGVsZXRlOwoJCQoJCS8qKgoJCSAqIDLmrKHlhYPmraPmlrnooYzliJflvI/jgpLoqIjnrpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gbWF0IOioiOeul+OBmeOCizLmrKHlhYPooYzliJfjgIIKCQkgKiBAcmV0dXJuIOioiOeul+OBl+OBn+ihjOWIl+W8j+OAggoJCSAqLwoJCWlubGluZSBzdGF0aWMgQyBjYWxjdWxhdGUoY29uc3QgTWF0cml4PDIsIEM+JiBtYXQpOwoJfTsKCQoJLyoqCgkgKiBO5qyh5YWD5q2j5pa56KGM5YiX44KS5Ye65Yqb44GZ44KL44CCCgkgKiBAcGFyYW0gTiDooYzliJfjga7mrKHlhYPmlbDjgIIKCSAqIEBwYXJhbSBDIOaIkOWIhihDb21wb25lbnQp44Gu5Z6L44CCCgkgKiBAcGFyYW0gb3V0IOWHuuWKm+WFiOOBqOOBquOCi+OCueODiOODquODvOODoOOAggoJICogQHBhcmFtIG1hdCDlh7rlipvjgZnjgovooYzliJfjgIIKCSAqIEByZXR1cm4g5Ye65Yqb44GX44Gf44K544OI44Oq44O844Og44CCCgkgKi8KCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IE1hdHJpeDxOLCBDPiYgbWF0KTsKCQoJLyoqIOa1ruWLleWwj+aVsOeCueaVsOOBrjLmrKHlhYPljZjkvY3ooYzliJfjgIIgKi8KCXRlbXBsYXRlPD4gCgljb25zdGV4cHIgTWF0cml4PDI+IE1hdHJpeDwyPjo6SURFTlRJVFkoKSB7CgkJcmV0dXJuIE1hdHJpeDwyPnsKCQkJMS4wLCAwLjAsIAoJCQkwLjAsIDEuMCwgCgkJfTsKCX0KCQoJLyoqIOa1ruWLleWwj+aVsOeCueaVsOOBrjPmrKHlhYPljZjkvY3ooYzliJfjgIIgKi8KCXRlbXBsYXRlPD4gCgljb25zdGV4cHIgTWF0cml4PDM+IE1hdHJpeDwzPjo6SURFTlRJVFkoKSB7CgkJcmV0dXJuIE1hdHJpeDwzPnsKCQkJMS4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAKCQl9OwoJfQoJCgkvKiog5rWu5YuV5bCP5pWw54K55pWw44GuNOasoeWFg+WNmOS9jeihjOWIl+OAgiAqLwoJdGVtcGxhdGU8PiAKCWNvbnN0ZXhwciBNYXRyaXg8ND4gTWF0cml4PDQ+OjpJREVOVElUWSgpIHsKCQlyZXR1cm4gTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCAwLjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX07Cgl9Cn07CgpuYW1lc3BhY2UgaDNkIHsKCS8qKgoJICog5ouh5aSn44O757iu5bCP6KGM5YiX44KS5L2c44KL44CCCgkgKiBAcGFyYW0gY29lZiDlkITluqfmqJnjga7kv4LmlbAoQ09FRmZpY2llbnQp44CCCgkgKiBjb2VmID4gMS4wIOOBquOCieaLoeWkpywgY29lZiA8IDEuMCDjgarjgonnuK7lsI/jgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf5ouh5aSn44O757iu5bCP6KGM5YiX44CCCgkgKi8KCU1hdHJpeDwzPiBzY2FsaW5nKGNvbnN0IFZlY3RvcjwzPiYgY29lZik7CgkKCS8qKgoJICog5Zue6Lui6KGM5YiX44KS5L2c44KL44CCCgkgKiBAcGFyYW0gYXhpcyDjgZPjga7ou7jjga7lkajjgorjgpLlm57ou6LjgZnjgovjgIIKCSAqIEBwYXJhbSByYWQg5Zue6Lui44GZ44KL5byn5bqmKFJBRGlhbinjgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf5Zue6Lui6KGM5YiX44CCCgkgKi8KCU1hdHJpeDwzPiByb3RhdGlvbihjb25zdCBWZWN0b3I8Mz4mIGF4aXMsIGNvbnN0IEZMT0FUJiByYWQpOwoJCgkvKioKCSAqIOW5s+ihjOenu+WLleihjOWIl+OCkuS9nOOCi+OAggoJICogQHBhcmFtIG9zIOWkieaPm+W+jOOBruS9jee9ruOBi+OCieWkieaPm+WJjeOBruS9jee9ruOCkuW8leOBhOOBn+W3ruWIhihPZmZTZXQp44CCCgkgKiBAcmV0dXJuIOS9nOOBo+OBn+W5s+ihjOenu+WLleihjOWIl+OAggoJICovCglNYXRyaXg8ND4gdHJhbnNsYXRpb24oY29uc3QgVmVjdG9yPDM+JiBvcyk7CgkKCS8qKgoJICog44OT44Ol44O86KGM5YiX44KS5L2c44KL44CCCgkgKiDjg5Pjg6Xjg7zooYzliJfjga/jg6/jg7zjg6vjg4nnqbrplpPjgYvjgonjgqvjg6Hjg6nnqbrplpPjgbjjga7lpInmj5vjgpLooYzjgYbjgIIKCSAqIEBwYXJhbSBleWVfcG9zIOebruOBruS9jee9rihQT1NpdGlvbinjgIIKCSAqIEBwYXJhbSBjZW50X3B0IOimlueVjOOBruS4reWkrihDRU5UZXIp44Gr44GC44KL54K5KFBvaW5UKeOAggoJICogQHBhcmFtIHVwX2RpciDkuIrmlrnlkJEoVVAgRElSZWN0aW9uKeOAguecgeeVpeOBquOCiVnou7jjgIIKCSAqIEByZXR1cm4g5L2c44Gj44Gf44OT44Ol44O86KGM5YiX44CCCgkgKi8KCU1hdHJpeDw0PiB2aWV3KGNvbnN0IFZlY3RvcjwzPiYgZXllX3BvcywgY29uc3QgVmVjdG9yPDM+JiBjZW50X3B0LCBjb25zdCBWZWN0b3I8Mz4mIHVwX2RpciA9IFlfQVhJUyk7CgkKCS8qKgoJICog6YCP6KaW5bCE5b2x6KGM5YiX44KS5L2c44KL44CCCgkgKiDpgI/oppblsITlvbHooYzliJfjga/jgqvjg6Hjg6nnqbrplpPjgYvjgonjgq/jg6rjg4Pjg5fnqbrplpPjgbjjga7lpInmj5vjgpLooYzjgYbjgIIKCSAqIEBwYXJhbSBob3JpX2ZvdiDmsLTlubPoppbph47op5IoSE9SSXpvbnRhbCBGaWVsZCBPZiBWaWV3KeOAguW8p+W6puOAggoJICogQHBhcmFtIGFzcF9yYXQg44Ki44K544Oa44Kv44OI5q+UKEFTUGVjdCBSQVRpbynjgIIKCSAqIEBwYXJhbSBuZWFyX2Rpc3Qg6L+R5bmz6Z2i44G+44Gn44Gu6Led6ZuiKERJU1RhbmNlKeOAggoJICogQHBhcmFtIGZhcl9kaXN0IOmBoOW5s+mdouOBvuOBp+OBrui3nemboihESVNUYW5jZSnjgIIKCSAqLwoJTWF0cml4PDQ+IHBlcnNwZWN0aXZlUHJvamVjdGlvbihjb25zdCBkb3VibGUmIGhvcmlfZm92LCBjb25zdCBkb3VibGUmIGFzcF9yYXQsIGNvbnN0IGRvdWJsZSYgbmVhcl9kaXN0LCBjb25zdCBkb3VibGUmIGZhcl9kaXN0KTsKfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3V0OwoJdXNpbmcgc3RkOjplbmRsOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCSNkZWZpbmUgQVNTRVJUKHByZWQpIGFzc2VydCgjcHJlZCwgKHByZWQpKTsKCQoJI2RlZmluZSBQUklOVCh2YWwpIGNvdXQgPDwgI3ZhbCA8PCAiPSIgPDwgKHZhbCkgPDwgZW5kbDsKCQoJLyoqCgkgKiDjgqLjgrXjg7zjgrfjg6fjg7PjgpLlrp/ooYzjgZnjgovjgIIKCSAqIOaIkOWKn+OBquOCieaomea6luWHuuWKm+OBq+e1kOaenOOCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIHByZWRfc3RyIOWIpOWumuOBmeOCi+i/sOiqnihQUkVEaWNhdGUp44KS6KiY6L+w44GX44Gf5paH5a2X5YiXKFNUUmluZynjgIIKCSAqIEBwYXJhbSBwcmVkX3JlcyDliKTlrprjgZnjgovov7Doqp4oUFJFRGljYXRlKeOBrue1kOaenChSRVN1bHQp44CCCgkgKiB0cnVl44Gq44KJ5oiQ5Yqf77yMZmFsc2XjgarjgonlpLHmlZfjgajliKTlrprjgZnjgovjgIIKCSAqIEB0aHJvdyBzdHJpbmcg5aSx5pWX44Gq44KJ44Oh44OD44K744O844K444KS44K544Ot44O844GZ44KL44CCCgkgKi8KCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKTsKfTsKCiNpbmNsdWRlIDxzdHJpbmc+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCgljbGFzcyBUZXN0IHsgcHVibGljOiB2aXJ0dWFsIHZvaWQgcnVuKCkgY29uc3QgdGhyb3coc3RyaW5nKSA9IDA7IH07Cn07CgojaW5jbHVkZSA8bWVtb3J5PgojaW5jbHVkZSA8c3RyaW5nPgojaW5jbHVkZSA8dmVjdG9yPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnNoYXJlZF9wdHI7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCXVzaW5nIHN0ZDo6dmVjdG9yOwoJCgljbGFzcyBUZXN0U2V0IHsKCXB1YmxpYzoKCQlUZXN0U2V0KCk7CgkJdm9pZCBydW4oKSBjb25zdCB0aHJvdyhzdHJpbmcpOwoJcHJvdGVjdGVkOgoJCXZlY3RvcjxzaGFyZWRfcHRyPFRlc3Q+PiB0ZXN0czsKCX07Cn07CgojaW5jbHVkZSA8c3RyaW5nPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJY2xhc3MgVGVzdDIgOiBwdWJsaWMgVGVzdCB7CglwdWJsaWM6CgkJdmlydHVhbCB2b2lkIHJ1bigpIGNvbnN0IHRocm93KHN0cmluZykgb3ZlcnJpZGU7Cgl9Owp9OwoKI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCWNsYXNzIFRlc3Q1IDogcHVibGljIFRlc3QgewoJcHVibGljOgoJCXZpcnR1YWwgdm9pZCBydW4oKSBjb25zdCB0aHJvdyhzdHJpbmcpIG92ZXJyaWRlOwoJfTsKfTsKCgovLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLwoKI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RyaW5nPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmNvdXQ7Cgl1c2luZyBzdGQ6OmVuZGw7Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJaW5saW5lIHZvaWQgYXNzZXJ0KGNvbnN0IHN0cmluZyYgcHJlZF9zdHIsIGNvbnN0IGJvb2wmIHByZWRfcmVzKSB0aHJvdyhzdHJpbmcpIHsKCQlpZiAocHJlZF9yZXMpIGNvdXQgPDwgIuOCouOCteODvOODiOaIkOWKnzogIiA8PCBwcmVkX3N0ciA8PCBlbmRsOwoJCWVsc2UgdGhyb3cgIuOCouOCteODvOODiOWkseaVlzogIiArIHByZWRfc3RyOwoJfQp9OwoKbmFtZXNwYWNlIGgzZCB7Cgl0ZW1wbGF0ZTx0eXBlbmFtZSBYPiAKCWlubGluZSBib29sIG5lYXJFcXVhbChjb25zdCBYJiBsaHMsIGNvbnN0IFgmIHJocykgewoJCXJldHVybiBsaHMgPT0gcmhzOwoJfQoJCgl0ZW1wbGF0ZTw+IAoJaW5saW5lIGJvb2wgbmVhckVxdWFsPEZMT0FUPihjb25zdCBGTE9BVCYgbGhzLCBjb25zdCBGTE9BVCYgcmhzKSB7CgkJc3RhdGljIGNvbnN0IEZMT0FUIFRIUkVTSE9MRCA9IDAuMDAwMDAwMDAwMDAwMDE7CgkJRkxPQVQgZGlmID0gbGhzIC0gcmhzOwoJCXJldHVybiBkaWYgPiAtVEhSRVNIT0xEICYmIGRpZiA8IFRIUkVTSE9MRDsKCX0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KCm5hbWVzcGFjZSBoM2QgewoJaW5saW5lIEZMT0FUIGFuZ2xlVG9SYWRpYW4oY29uc3QgRkxPQVQmIGFuZykgeyByZXR1cm4gTV9QSSAqIGFuZyAvIDE4MC4wOyB9CgkKCXRlbXBsYXRlPHR5cGVuYW1lIFBJLCB0eXBlbmFtZSBMPiAKCWlubGluZSB2b2lkIHBvbHlnb25Ub1RyaWFuZ2xlcyhQSSBwb2x5X2JlZ2luLCBQSSBwb2x5X2VuZCwgTCBsYW1iZGEpIHsKCQkvLyDjgqLjg6vjgrTjg6rjgrrjg6DjgpLnpLrjgZnjgIIKCQkvLyAxLiDlp4vnq6/jgYvjgonlp4vjgoHjgovjgIIKCQkvLyAyLiDnj77lnKjkvY3nva7jgYvjgonkuozjgaTlhYjjgYzntYLnq6/ku6XkuIrjgarjgonntYLkuobjgZnjgovjgIIKCQkvLyAzLiDlp4vnq6/jgarjgonjgIHnj77lnKjkvY3nva7jga7jgoLjga7jgpLkuIDjgaTnm67jga7poILngrnjgqTjg7Pjg4fjg4Pjgq/jgrnjgajjgZfjgabjgIE144Gr6aOb44G244CCCgkJLy8gNC4g5aeL56uv44Gn44Gv44Gq44GE44Gq44KJ44CB5LiA44Gk5YmN44Gu5L2N572u44Gu44KC44Gu44KS5LiA44Gk55uu44Gu6aCC54K544Kk44Oz44OH44OD44Kv44K544Go44GZ44KL44CCCgkJLy8gNS4g5LiA44Gk5qyh44Gu5L2N572u44Gr6YCy44KT44Gn44CB54++5Zyo5L2N572u44Gu44KC44Gu44KS5LqM44Gk55uu44Gu6aCC54K544Kk44Oz44OH44OD44Kv44K544Go44GZ44KL44CCCgkJLy8gNi4g5LiA44Gk5qyh44Gu5L2N572u44Gr6YCy44KT44Gn44CB54++5Zyo5L2N572u44Gu44KC44Gu44KS5LiJ44Gk55uu44Gu6aCC54K544Kk44Oz44OH44OD44Kv44K544Go44GZ44KL44CCCgkJLy8gNy4g44Op44Og44OA44KS5ZG844G244CCCgkJLy8gOC4g5LiA44Gk5YmN44Gu5L2N572u44Gr5oi744Gj44Gm44CBMuOBq+mjm+OBtuOAggoJCWZvciAoYXV0byBpdGVyID0gcG9seV9iZWdpbjsgaXRlciArIDIgPCBwb2x5X2VuZDspIHsKCQkJdW5zaWduZWQgaW50IHRyaV92ZXJ0MV9pbmQsIHRyaV92ZXJ0Ml9pbmQsIHRyaV92ZXJ0M19pbmQ7CgkJCWlmIChpdGVyID09IHBvbHlfYmVnaW4pIHRyaV92ZXJ0MV9pbmQgPSAqaXRlcjsKCQkJZWxzZSB0cmlfdmVydDFfaW5kID0gKihpdGVyIC0gMSk7CgkJCXRyaV92ZXJ0Ml9pbmQgPSAqKytpdGVyOwoJCQl0cmlfdmVydDNfaW5kID0gKigrK2l0ZXIpLS07CgkJCWxhbWJkYSh0cmlfdmVydDFfaW5kLCB0cmlfdmVydDJfaW5kLCB0cmlfdmVydDNfaW5kKTsKCQl9Cgl9Cn07CgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDx0dXBsZT4KI2luY2x1ZGUgPHV0aWxpdHk+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6ZmlsbDsKCXVzaW5nIHN0ZDo6Z2V0OwoJdXNpbmcgc3RkOjptYWtlX3R1cGxlOwoJdXNpbmcgc3RkOjpzd2FwOwoJdXNpbmcgc3RkOjp0dXBsZTsKCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIHZvaWQgTWF0cml4PE4sIEM+OjpjbGVhcigpIHsKCQkvLyDlkITmiJDliIbjgpLjgrzjg63jgq/jg6rjgqLjgZnjgovjgIIKCQlmaWxsKChDKil0aGlzLT5jLCAoQyopdGhpcy0+YyArIE4gKiBOLCAwLjApOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgQyBNYXRyaXg8TiwgQz46OmRldCgpIGNvbnN0IHsKCQlyZXR1cm4gTWF0cml4RGV0ZXJtaW5hbnQ8TiwgQz46OmNhbGN1bGF0ZSgqdGhpcyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gTWF0cml4PE4sIEM+OjplaWd2YWxzKCkgY29uc3QgdGhyb3coc3RyaW5nKSB7CgkJLy8g54m55oCn5aSa6aCF5byP44KS5L2c44KL44CCCgkJTWF0cml4PE4sIFBvbHlFeHA8TiwgQz4+IGNoX21hdDsKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQlQb2x5RXhwPE4sIEM+IGNvbXAodGhpcy0+Y1tyb3ddW2NvbF0pOwoJCQkJaWYgKHJvdyA9PSBjb2wpIGNvbXAuYy5jWzFdID0gLTEuMDsKCQkJCWNoX21hdC5jW3Jvd11bY29sXSA9IGNvbXA7CgkJCX0KCQl9CgkJUG9seUV4cDxOLCBDPiBjaF9wb2x5X2V4cChjaF9tYXQuZGV0KCkpOwoJCS8vIOeJueaAp+WkmumgheW8j+OBruWun+aVsOagueOCkuioiOeul+OBmeOCi+OAggoJCXR1cGxlPHVuc2lnbmVkIGludCwgVmVjdG9yPE4sIEM+PiBydHMgPSBjaF9wb2x5X2V4cC5ydHMoKTsKCQkvLyDooYzliJfjgYzpnZ7mraPliYfjgarjgonjgIFO5YCL44Gu5a6f5pWw5qC544KS5oyB44Gf44Gq44GEKD8p44CCCgkJaWYgKGdldDwwPihydHMpIDwgTikgdGhyb3cgc3RyaW5nKCLooYzliJfjgYzpnZ7mraPliYfjgIIiKTsKCQlyZXR1cm4gZ2V0PDE+KHJ0cyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgVmVjdG9yPE4sIEM+PiBNYXRyaXg8TiwgQz46OmVpZ3ZlY3MoKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQlWZWN0b3I8TiwgVmVjdG9yPE4sIEM+PiByZXM7CgkJLy8g5Zu65pyJ5YCk44KS6KiI566X44GZ44KL44CCCgkJVmVjdG9yPE4sIEM+IGVpZ3ZhbHNfcmVzKGVpZ3ZhbHMoKSk7CgkJLy8g5Zu65pyJ5YCk44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSB7CgkJCS8vIOiiq+e0hOS/guaVsOihjOWIl+OCkuS9nOOCi+OAggoJCQlNYXRyaXggcmRjX2NvZWZfbWF0KCp0aGlzKTsKCQkJZm9yIChhdXRvIGogPSAwOyBqIDwgTjsgaisrKSByZGNfY29lZl9tYXQuY1tqXVtqXSAtPSBlaWd2YWxzX3Jlcy5jW2ldOwoJCQlyZGNfY29lZl9tYXQgPSBnZXQ8MD4ocmRjX2NvZWZfbWF0LnJlZHVjZSgpKTsKCQkJCgkJCS8vIChNIC0gzrtJKVYgPSAwIOOCkuino+OBj+OAggoJCQkKCQkJLy8g5Li75a++6KeS5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJCWZvciAoYXV0byBqID0gMDsgaiA8IE47IGorKykgewoJCQkJLy8g5YWI5bCO5oiQ5YiG44KS5ZCr44KA44Gq44KJ44K544Kt44OD44OX44GZ44KL44CCCgkJCQlpZiAoIW5lYXJFcXVhbChyZGNfY29lZl9tYXQuY1tqXVtqXSwgMC4wKSkgY29udGludWU7CgkJCQkvLyDlm7rmnInjg5njgq/jg4jjg6vjga7miJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkJCWZvciAoYXV0byBrID0gMDsgayA8IE47IGsrKykgewoJCQkJCS8vIOS4u+WvvuinkuOBq+WvvuW/nOOBmeOCi+aIkOWIhuOBrzHjgIHjgZ3jgozku6XlpJbjga/lr77lv5zjgZnjgovooYzjga7miJDliIbjga7nrKblj7fjgpLlj43ou6LjgZfjgZ/jgoLjga7jgIIKCQkJCQlpZiAoayA9PSBqKSByZXMuY1tpXS5jW2tdID0gMS4wOwoJCQkJCWVsc2UgcmVzLmNbaV0uY1trXSA9IC1yZGNfY29lZl9tYXQuY1trXVtqXTsKCQkJCX0KCQkJCWJyZWFrOwoJCQl9CgkJCS8vIOato+imj+WMluOBmeOCi+OAggoJCQlyZXMuY1tpXSA9IHJlcy5jW2ldLm5vcm1hbGl6ZSgpOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgTWF0cml4PE4gKyAxLCBDPiBNYXRyaXg8TiwgQz46OmV4dChjb25zdCBNYXRyaXg8TiArIDEsIEM+JiBleHRfbWF0KSBjb25zdCB7CgkJTWF0cml4PE4gKyAxLCBDPiByZXM7CgkJLy8g57WQ5p6c44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE4gKyAxOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTiArIDE7IGNvbCsrKSB7CgkJCQkvLyDooYzjgajliJfjgYxO5Lul5YaF44Gq44KJ44GT44Gu6KGM5YiXLCDjgZ3jgozku6XlpJbjgarjgonlvJXmlbDjga7ooYzliJfjgYvjgonmiJDliIbjgpLjgrPjg5Tjg7zjgZnjgovjgIIKCQkJCWlmIChyb3cgPCBOICYmIGNvbCA8IE4pIHJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdOwoJCQkJZWxzZSByZXMuY1tyb3ddW2NvbF0gPSBleHRfbWF0LmNbcm93XVtjb2xdOwoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjppbnYoKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQkvLyDjgb7jgZrooYzliJflvI/jgpLoqIjnrpfjgZfjgIHmraPliYfjgafjgYLjgovjgZPjgajjgpLnorrjgYvjgoHjgovjgIIKCQlDIGRldF9yZXMgPSBkZXQoKTsKCQlpZiAobmVhckVxdWFsKGRldF9yZXMsIDAuMCkpIHRocm93IHN0cmluZygi6KGM5YiX44GM6Z2e5q2j5YmH44CCIik7CgkJLy8g6KGM5YiX5byP44Gu6YCG5pWw44KS6KiI566X44GX44Gm44GK44GP44CCCgkJQyBpbnZfZGV0ID0gMS4wIC8gZGV0X3JlczsKCQlNYXRyaXg8TiwgQz4gcmVzOwoJCS8vIOWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQlmb3IgKGF1dG8gY29sID0gMDsgY29sIDwgTjsgY29sKyspIHsKCQkJCS8vIOihjOWIl+W8j+OBrumAhuaVsOOBq+OAgeWvvuinkuOBruWwj+ihjOWIl+W8j+OCkuS5l+eul+OBmeOCi+OAggoJCQkJcmVzLmNbcm93XVtjb2xdID0gaW52X2RldCAqIHN1Yihjb2wsIHJvdykuZGV0KCk7CgkJCQkvLyAoLTEpXihyb3cgKyBjb2wpCgkJCQlpZiAoKChyb3cgKyBjb2wpICYgMHgxKSA9PSAxKSAKCQkJCQlyZXMuY1tyb3ddW2NvbF0gPSAtcmVzLmNbcm93XVtjb2xdOwoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBib29sIE1hdHJpeDxOLCBDPjo6b3BlcmF0b3IgIT0oY29uc3QgTWF0cml4JiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gIW9wZXJhdG9yID09KHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciAqKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJTWF0cml4PE4sIEM+IHJlczsKCQkvLyDntZDmnpzjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQlyZXMuY1tyb3ddW2NvbF0gPSAwLjA7CgkJCQkvLyDlt6blgbTjga/lr77lv5zjgZnjgovooYzjga7lkITmiJDliIYsIOWPs+WBtOOBr+WvvuW/nOOBmeOCi+WIl+OBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBl+OBpuOAgeS5l+eul+OBmeOCi+OAggoJCQkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSAKCQkJCQlyZXMuY1tyb3ddW2NvbF0gKz0gdGhpcy0+Y1tyb3ddW2ldICogcmhzLmNbaV1bY29sXTsKCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgVmVjdG9yPE4+IE1hdHJpeDxOLCBDPjo6b3BlcmF0b3IgKihjb25zdCBWZWN0b3I8Tj4mIHJocykgY29uc3QgewoJCVZlY3RvcjxOPiByZXM7CgkJLy8g44OZ44Kv44OI44Or44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSB7CgkJCXJlcy5jW2ldID0gMC4wOwoJCQkvLyDlt6blgbTjga7ooYzliJfjga/jgIHlr77lv5zjgZnjgovooYzjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZfjgabjgIHkuZfnrpfjgZnjgovjgIIKCQkJZm9yIChhdXRvIGogPSAwOyBqIDwgTjsgaisrKSAKCQkJCXJlcy5jW2ldICs9IHRoaXMtPmNbaV1bal0gKiByaHMuY1tqXTsKCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIE1hdHJpeDxOLCBDPiBNYXRyaXg8TiwgQz46Om9wZXJhdG9yICooY29uc3QgQyYgcmhzKSBjb25zdCB7CgkJTWF0cml4PE4sIEM+IHJlczsKCQkvLyDlkITmiJDliIbjgavjgrnjgqvjg6njg7zlgKTjgpLkuZfnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSAKCQkJCXJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdICogcmhzOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgTWF0cml4PE4sIEM+IE1hdHJpeDxOLCBDPjo6b3BlcmF0b3IgKj0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciAqPShjb25zdCBDJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciArKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJTWF0cml4PE4sIEM+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gcm93ID0gMDsgcm93IDwgTjsgcm93KyspIHsKCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSAKCQkJCXJlcy5jW3Jvd11bY29sXSA9IHRoaXMtPmNbcm93XVtjb2xdICsgcmhzLmNbcm93XVtjb2xdOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgTWF0cml4PE4sIEM+IE1hdHJpeDxOLCBDPjo6b3BlcmF0b3IgKz0oY29uc3QgTWF0cml4JiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciArKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciAtKCkgY29uc3QgewoJCU1hdHJpeDxOLCBDPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gu56ym5Y+344KS5Y+N6Lui44GZ44KL44CCCgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgCgkJCQlyZXMuY1tyb3ddW2NvbF0gPSAtdGhpcy0+Y1tyb3ddW2NvbF07CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciAtKGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBNYXRyaXg8TiwgQz4gTWF0cml4PE4sIEM+OjpvcGVyYXRvciAtPShjb25zdCBNYXRyaXgmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yIC0ocmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIE1hdHJpeDxOLCBDPiBNYXRyaXg8TiwgQz46Om9wZXJhdG9yIC8oY29uc3QgQyYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICooMS4wIC8gcmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIE1hdHJpeDxOLCBDPiBNYXRyaXg8TiwgQz46Om9wZXJhdG9yIC89KGNvbnN0IEMmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yIC8ocmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJYm9vbCBNYXRyaXg8TiwgQz46Om9wZXJhdG9yID09KGNvbnN0IE1hdHJpeCYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOOyByb3crKykgewoJCQkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCQkJaWYgKCFuZWFyRXF1YWwodGhpcy0+Y1tyb3ddW2NvbF0sIHJocy5jW3Jvd11bY29sXSkpIHsKCQkJCQkJcmVzID0gZmFsc2U7CgkJCQkJCWJyZWFrOwoJCQkJCX0KCQkJCX0KCQkJCWlmICghcmVzKSBicmVhazsKCQkJfQoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgdHVwbGU8TWF0cml4PE4sIEM+LCBWZWN0b3I8TiwgQz4+IE1hdHJpeDxOLCBDPjo6cmVkdWNlKGNvbnN0IFZlY3RvcjxOLCBDPiYgY29uX2NvbCkgY29uc3QgewoJCU1hdHJpeDxOLCBDPiByZXNfbWF0KCp0aGlzKTsKCQlWZWN0b3I8TiwgQz4gcmVzX2Nvbl9jb2woY29uX2NvbCk7CgkJLy8g44Kr44Os44Oz44OI6KGM44Kk44Oz44OH44OD44Kv44K544KS5Yid5pyf5YyW44GZ44KL44CCCgkJYXV0byBpID0gMDsKCQkvLyDlkITliJfjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQlmb3IgKGF1dG8gaiA9IDA7IGogPCBOOyBqKyspIHsKCQkJLy8g44K844Ot44Gn44Gv44Gq44GE5oiQ5YiG44KS5o6i57Si44GZ44KL44CCCgkJCWF1dG8gayA9IGk7CgkJCWZvciAoOyBrIDwgTjsgaysrKSB7CgkJCQlpZiAoIW5lYXJFcXVhbChyZXNfbWF0LmNba11bal0sIDAuMCkpIGJyZWFrOwoJCQl9CgkJCS8vIOOCvOODreOBp+OBr+OBquOBhOaIkOWIhuOBjOimi+OBpOOBi+OCieOBquOBkeOCjOOBsOasoeOBruWIl+OBq+enu+OCi+OAggoJCQlpZiAoayA+PSBOKSBjb250aW51ZTsKCQkJLy8g6KaL44Gk44GL44Gj44Gf6KGM44GM44Kr44Os44Oz44OI6KGM44Gn44Gq44GR44KM44Gw44CB5LqM44Gk44Gu6KGM44KS5YWl44KM5pu/44GI44KL44CCCgkJCS8vIOS+iykgIOKGk+OCq+ODrOODs+ODiOWIlwoJCQkvLyAgICAgfCAwIDV8NXwg4oaQ44Kr44Os44Oz44OI6KGMCgkJCS8vICAgICB8KjIgNHw2fCDihpDopovjgaTjgYvjgaPjgZ/ooYwKCQkJLy8gICAgICAgIOKGk+S6jOOBpOOBruihjOOCkuWFpeOCjOabv+OBiOOCi+OAggoJCQkvLyAgICAgfCoyIDR8Nnwg4oaQ44Kr44Os44Oz44OI6KGMCgkJCS8vICAgICB8IDAgNXw1fAoJCQlpZiAoayAhPSBpKSB7CgkJCQlmb3IgKGF1dG8gcyA9IDA7IHMgPCBOOyBzKyspIAoJCQkJCXN3YXAocmVzX21hdC5jW2tdW3NdLCByZXNfbWF0LmNbaV1bc10pOwoJCQkJc3dhcChyZXNfY29uX2NvbC5jW2tdLCByZXNfY29uX2NvbC5jW2ldKTsKCQkJfQoJCQkvLyDjgqvjg6zjg7Pjg4jooYzjga7lhYjlsI7miJDliIbjgpIx44Gr44GZ44KL44Gf44KB44Gr44CB5YWI5bCO5oiQ5YiG44Gu6YCG5pWw44KS5ZCE5oiQ5YiG44Gr5o6b44GR44KL44CCCgkJCS8vIOS+iykgIOKGk+OCq+ODrOODs+ODiOWIlwoJCQkvLyAgICAgfCoyIDR8Nnwg4oaQ44Kr44Os44Oz44OI6KGMCgkJCS8vICAgICB8IDIgNnw5fAoJCQkvLyAgICAgICAg4oaT5YWI5bCO5oiQ5YiGMuOBrumAhuaVsOOBp+OBguOCizEvMuOCkuWQhOaIkOWIhuOBq+aOm+OBkeOCi+OAggoJCQkvLyAgICAgfCoxIDJ8M3wg4oaQ44Kr44Os44Oz44OI6KGMCgkJCS8vICAgICB8IDIgNnw5fAoJCQlDIGludl9pal9jb21wID0gMS4wIC8gcmVzX21hdC5jW2ldW2pdOwoJCQlmb3IgKGF1dG8gcyA9IGo7IHMgPCBOOyBzKyspIAoJCQkJcmVzX21hdC5jW2ldW3NdICo9IGludl9pal9jb21wOwoJCQlyZXNfY29uX2NvbC5jW2ldICo9IGludl9pal9jb21wOwoJCQkKCQkJLy8g44Kr44Os44Oz44OI5YiX44Gu5ZCE5oiQ5YiG44GM44CB44Kr44Os44Oz44OI6KGM5Lul5aSW44Gv44K844Ot44Gr44Gq44KL44KI44GG44Gr44GZ44KL44CCCgkJCS8vIOS+iykgIOKGk+OCq+ODrOODs+ODiOWIlwoJCQkvLyAgICAgfCAxIDJ8M3wg4oaQ44Kr44Os44Oz44OI6KGMCgkJCS8vICAgICB8KjIgNnw5fCDihpDjgrzjg63jgavjgZfjgZ/jgYTooYwKCQkJLy8gICAgICAgIOKGk+OCvOODreOBq+OBl+OBn+OBhOihjOOBq+OCq+ODrOODs+ODiOihjMOXLTLjgpLliqDjgYjjgovjgIIKCQkJLy8gICAgIHwgMSAyfDN8IOKGkOOCq+ODrOODs+ODiOihjAoJCQkvLyAgICAgfCowIDJ8M3wg4oaQ44K844Ot44Gr44GX44Gf44GE6KGMCgkJCQoJCQkvLyDlkITooYzjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkJZm9yIChhdXRvIHIgPSAwOyByIDwgTjsgcisrKSB7CgkJCQkvLyDjgqvjg6zjg7Pjg4jooYzjgarjgonjgrnjgq3jg4Pjg5fjgZnjgovjgIIKCQkJCWlmIChyID09IGkpIGNvbnRpbnVlOwoJCQkJLy8g5L+C5pWw44KS6KiI566X44GX44Gm44GK44GP44CC44K844Ot44Gq44KJ44K544Kt44OD44OX44CCCgkJCQlDIGNvZWYgPSAtcmVzX21hdC5jW3JdW2pdOwoJCQkJaWYgKG5lYXJFcXVhbChjb2VmLCAwLjApKSBjb250aW51ZTsKCQkJCS8vIOWQhOWIl+OBlOOBqOOBq+WPjeW+qeOBl+OBpuOAgeOCvOODreOBq+OBl+OBn+OBhOihjOOBruaIkOWIhuOBq+OCq+ODrOODs+ODiOihjOOBruaIkOWIhsOX5L+C5pWw44KS5Yqg44GI44KL44CCCgkJCQlmb3IgKGF1dG8gcyA9IGo7IHMgPCBOOyBzKyspIAoJCQkJCXJlc19tYXQuY1tyXVtzXSArPSByZXNfbWF0LmNbaV1bc10gKiBjb2VmOwoJCQkJcmVzX2Nvbl9jb2wuY1tyXSArPSByZXNfY29uX2NvbC5jW2ldICogY29lZjsKCQkJfQoJCQlpKys7CgkJfQoJCXJldHVybiBtYWtlX3R1cGxlKHJlc19tYXQsIHJlc19jb25fY29sKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIE1hdHJpeDxOIC0gMSwgQz4gTWF0cml4PE4sIEM+OjpzdWIoY29uc3QgdW5zaWduZWQgaW50JiByb3csIGNvbnN0IHVuc2lnbmVkIGludCYgY29sKSBjb25zdCB7CgkJTWF0cml4PE4gLSAxLCBDPiBzdWI7CgkJYXV0byBzdWJfcm93ID0gMDsKCQkvLyDjgZPjga7ooYzliJfjga7lkITmiJDliIbjgZTjgajjgavlj43lvqnjgZnjgovjgIIKCQkvLyDlj5bjgorpmaTjgY/ooYzjgajliJfjgavjgaTjgYTjgabjga/lh6bnkIbjgpLjgrnjgq3jg4Pjg5fjgZnjgovjgIIKCQlmb3IgKGF1dG8gc3VwX3JvdyA9IDA7IHN1cF9yb3cgPCBOOyBzdXBfcm93KyspIHsKCQkJaWYgKHN1cF9yb3cgPT0gcm93KSBjb250aW51ZTsKCQkJYXV0byBzdWJfY29sID0gMDsKCQkJZm9yIChhdXRvIHN1cF9jb2wgPSAwOyBzdXBfY29sIDwgTjsgc3VwX2NvbCsrKSB7CgkJCQlpZiAoc3VwX2NvbCA9PSBjb2wpIGNvbnRpbnVlOwoJCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJCQlzdWIuY1tzdWJfcm93XVtzdWJfY29sXSA9IHRoaXMtPmNbc3VwX3Jvd11bc3VwX2NvbF07CgkJCQlzdWJfY29sKys7CgkJCX0KCQkJc3ViX3JvdysrOwoJCX0KCQlyZXR1cm4gc3ViOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgTWF0cml4PE4gLSAxLCBDPiBNYXRyaXg8TiwgQz46OnRydW5jKCkgY29uc3QgewoJCU1hdHJpeDxOIC0gMSwgQz4gcmVzOwoJCS8vIOe1kOaenOOBruWQhOaIkOWIhuOBlOOBqOOBq+WPjeW+qeOBmeOCi+OAggoJCWZvciAoYXV0byByb3cgPSAwOyByb3cgPCBOIC0gMTsgcm93KyspIHsKCQkJLy8g5a++5b+c44GZ44KL5oiQ5YiG44KS44Kz44OU44O844GZ44KL44CCCgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOIC0gMTsgY29sKyspIAoJCQkJcmVzLmNbcm93XVtjb2xdID0gdGhpcy0+Y1tyb3ddW2NvbF07CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBDIE1hdHJpeERldGVybWluYW50PE4sIEM+OjpjYWxjdWxhdGUoY29uc3QgTWF0cml4PE4sIEM+JiBtYXQpIHsKCQlDIHJlcyA9IDAuMDsKCQkvLyAx6KGM55uu44Gu5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GZ44KL44CCCgkJZm9yIChhdXRvIGNvbCA9IDA7IGNvbCA8IE47IGNvbCsrKSB7CgkJCS8vIOaIkOWIhuOBq+OAgeOBneOCjOOBqOWvvuW/nOOBmeOCi+Wwj+ihjOWIl+W8j+OCkuS5l+eul+OBmeOCi+OAggoJCQlDIGNvZmFjID0gbWF0LmNbMF1bY29sXSAqIG1hdC5zdWIoMCwgY29sKS5kZXQoKTsKCQkJLy8gKC0xKV5jb2wKCQkJaWYgKChjb2wgJiAweDEpID09IDEpIGNvZmFjID0gLWNvZmFjOwoJCQkvLyDntZDmnpzjgavkvZnlm6DlrZDjgpLliqDnrpfjgZnjgovjgIIKCQkJcmVzICs9IGNvZmFjOwoJCX0KCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx0eXBlbmFtZSBDPiAKCWlubGluZSBDIE1hdHJpeERldGVybWluYW50PDIsIEM+OjpjYWxjdWxhdGUoY29uc3QgTWF0cml4PDIsIEM+JiBtYXQpIHsKCQkvLyDlho3luLDnmoTjgarooYzliJflvI/oqIjnrpfjga7ntYLnnYDngrnjgIIKCQkvLyAyw5cy44Gu6KGM5YiX5byP44KS6KiI566X44GZ44KL44CCCgkJcmV0dXJuIG1hdC5jWzBdWzBdICogbWF0LmNbMV1bMV0gLSBtYXQuY1swXVsxXSAqIG1hdC5jWzFdWzBdOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBNYXRyaXg8TiwgQz4mIG1hdCkgewoJCW91dCA8PCAieyI7CgkJZm9yIChhdXRvIHJvdyA9IDA7IHJvdyA8IE47IHJvdysrKSB7CgkJCW91dCA8PCAieyI7CgkJCWZvciAoYXV0byBjb2wgPSAwOyBjb2wgPCBOOyBjb2wrKykgb3V0IDw8IG1hdC5jW3Jvd11bY29sXSA8PCAiLCAiOwoJCQlvdXQgPDwgIn0sICI7CgkJfQoJCW91dCA8PCAifSI7Cgl9Cn07CgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHR1cGxlPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmFjb3M7Cgl1c2luZyBzdGQ6OmNicnQ7Cgl1c2luZyBzdGQ6OmNvczsKCXVzaW5nIHN0ZDo6bWFrZV90dXBsZTsKCXVzaW5nIHN0ZDo6b3N0cmVhbTsKCXVzaW5nIHN0ZDo6cG93OwoJdXNpbmcgc3RkOjpzb3J0OwoJdXNpbmcgc3RkOjp0dXBsZTsKCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz46OlBvbHlFeHAoY29uc3QgVmVjdG9yPE4gKyAxLCBDPiYgY29lZl92ZWMpIDogYyhjb2VmX3ZlYykge30KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz46OlBvbHlFeHAoY29uc3QgQyYgY29uc3RfY29lZikgewoJCXRoaXMtPmMuY2xlYXIoKTsKCQl0aGlzLT5jLmNbMF0gPSBjb25zdF9jb2VmOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgYm9vbCBQb2x5RXhwPE4sIEM+OjpvcGVyYXRvciAhPShjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gIW9wZXJhdG9yID09KHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBQb2x5RXhwPE4sIEM+IFBvbHlFeHA8TiwgQz46Om9wZXJhdG9yICooY29uc3QgQyYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIFBvbHlFeHAodGhpcy0+YyAqIHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBQb2x5RXhwPE4sIEM+IFBvbHlFeHA8TiwgQz46Om9wZXJhdG9yICooY29uc3QgUG9seUV4cCYgcmhzKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQlQb2x5RXhwPE4sIEM+IHJlczsKCQlyZXMuYy5jbGVhcigpOwoJCS8vIOW3puWBtOOBruWQhOS/guaVsOOBqOWPs+WBtOOBruWQhOS/guaVsOOBrue1hOOBv+WQiOOCj+OBm+OBlOOBqOOBq+WPjeW+qeOBl+OBpuOAgeS5l+eul+OBmeOCi+OAggoJCWZvciAoYXV0byBpID0gMDsgaSA8IE4gKyAxOyBpKyspIHsKCQkJaWYgKCFuZWFyRXF1YWwodGhpcy0+Yy5jW2ldLCAwLjApKSB7CgkJCQlmb3IgKGF1dG8gaiA9IDA7IGogPCBOICsgMTsgaisrKSB7CgkJCQkJaWYgKCFuZWFyRXF1YWwocmhzLmMuY1tqXSwgMC4wKSkgewoJCQkJCQlpZiAoaSArIGogPj0gTiArIDEpIHRocm93IHN0cmluZygi5aSa6aCF5byP44Gu5qyh5pWw44GM6Laz44KK44Gq44GE44CCIik7CgkJCQkJCXJlcy5jLmNbaSArIGpdICs9IHRoaXMtPmMuY1tpXSAqIHJocy5jLmNbal07CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBQb2x5RXhwPE4sIEM+JiBQb2x5RXhwPE4sIEM+OjpvcGVyYXRvciAqPShjb25zdCBDJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBQb2x5RXhwPE4sIEM+JiBQb2x5RXhwPE4sIEM+OjpvcGVyYXRvciAqPShjb25zdCBQb2x5RXhwJiByaHMpIHRocm93KHN0cmluZykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yICoocmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz4gUG9seUV4cDxOLCBDPjo6b3BlcmF0b3IgKyhjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gUG9seUV4cCh0aGlzLT5jICsgcmhzLmMpOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgUG9seUV4cDxOLCBDPiYgUG9seUV4cDxOLCBDPjo6b3BlcmF0b3IgKz0oY29uc3QgUG9seUV4cCYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgKyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgUG9seUV4cDxOLCBDPiBQb2x5RXhwPE4sIEM+OjpvcGVyYXRvciAtKCkgY29uc3QgewoJCXJldHVybiBQb2x5RXhwKC10aGlzLT5jKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz4gUG9seUV4cDxOLCBDPjo6b3BlcmF0b3IgLShjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gb3BlcmF0b3IgKygtcmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz4mIFBvbHlFeHA8TiwgQz46Om9wZXJhdG9yIC09KGNvbnN0IFBvbHlFeHAmIHJocykgewoJCXJldHVybiAqdGhpcyA9IG9wZXJhdG9yIC0ocmhzKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFBvbHlFeHA8TiwgQz4gUG9seUV4cDxOLCBDPjo6b3BlcmF0b3IgLyhjb25zdCBDJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gb3BlcmF0b3IgKigxLjAgLyByaHMpOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgUG9seUV4cDxOLCBDPiYgUG9seUV4cDxOLCBDPjo6b3BlcmF0b3IgLz0oY29uc3QgQyYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgYm9vbCBQb2x5RXhwPE4sIEM+OjpvcGVyYXRvciA9PShjb25zdCBQb2x5RXhwJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gdGhpcy0+YyA9PSByaHMuYzsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIHR1cGxlPHVuc2lnbmVkIGludCwgVmVjdG9yPE4sIEM+PiBQb2x5RXhwPE4sIEM+OjpydHMoKSBjb25zdCB7CgkJcmV0dXJuIFBvbHlFeHBSb290czxOLCBDPjo6Y2FsY3VsYXRlKCp0aGlzKTsKCX0KCQoJdGVtcGxhdGU8dHlwZW5hbWUgQz4gCglpbmxpbmUgdHVwbGU8dW5zaWduZWQgaW50LCBWZWN0b3I8MiwgQz4+IFBvbHlFeHBSb290czwyLCBDPjo6Y2FsY3VsYXRlKGNvbnN0IFBvbHlFeHA8MiwgQz4mIHBvbHlfZXhwKSB7CgkJdW5zaWduZWQgaW50IHJlc19ydHNfY250ID0gMDsKCQlWZWN0b3I8MiwgQz4gcmVzX3J0czsKCQkvLyDlkITkv4LmlbDjgpLjgo/jgYvjgorjgoTjgZnjgYTlkI3liY3jga7lpInmlbDjgavku6PlhaXjgZnjgovjgIIKCQkvLyBheF4yICsgYnggKyBjCgkJQyBhID0gcG9seV9leHAuYy5jWzJdLCBiID0gcG9seV9leHAuYy5jWzFdLCBjID0gcG9seV9leHAuYy5jWzBdOwoJCS8vIOWIpOWIpeW8j+OCkuioiOeul+OBmeOCi+OAggoJCS8vIGQgPSBiXjIgLSA0YWMKCQlDIGQgPSBiICogYiAtIDQuMCAqIGEgKiBjOwoJCS8vIDEgLyAyYQoJCUMgaW52X2EyID0gMS4wIC8gKGEgKyBhKTsKCQkvLyBkID0gMCDjgarjgonph43moLnjgILmraPjgarjgonkuozjgaTjga7lrp/mlbDmoLnjgpLmjIHjgaTjgIIKCQlpZiAobmVhckVxdWFsKGQsIDAuMCkpIHsKCQkJcmVzX3J0c19jbnQgPSAxOwoJCQlyZXNfcnRzLmNbMF0gPSAtYiAqIGludl9hMjsKCQl9CgkJZWxzZSBpZiAoZCA+IDAuMCkgewoJCQlyZXNfcnRzX2NudCA9IDI7CgkJCXJlc19ydHMuY1swXSA9ICgtYiArIHNxcnQoZCkpICogaW52X2EyOwoJCQlyZXNfcnRzLmNbMV0gPSAoLWIgLSBzcXJ0KGQpKSAqIGludl9hMjsKCQkJc29ydChyZXNfcnRzLmMsIHJlc19ydHMuYyArIDIpOwoJCX0KCQlyZXR1cm4gbWFrZV90dXBsZShyZXNfcnRzX2NudCwgcmVzX3J0cyk7Cgl9CgkKCXRlbXBsYXRlPHR5cGVuYW1lIEM+IAoJaW5saW5lIHR1cGxlPHVuc2lnbmVkIGludCwgVmVjdG9yPDMsIEM+PiBQb2x5RXhwUm9vdHM8MywgQz46OmNhbGN1bGF0ZShjb25zdCBQb2x5RXhwPDMsIEM+JiBwb2x5X2V4cCkgewoJCXVuc2lnbmVkIGludCByZXNfcnRzX2NudCA9IDA7CgkJVmVjdG9yPDMsIEM+IHJlc19ydHM7CgkJLy8gM+asoeOBruS/guaVsOOBjDHjgavjgarjgovjgojjgYbjgavpmaTnrpfjgpLooYzjgYbjgIIKCQlQb2x5RXhwPDMsIEM+IHBvbHlfZXhwMihwb2x5X2V4cCAvIHBvbHlfZXhwLmMuY1szXSk7CgkJLy8g5ZCE5L+C5pWw44KS44KP44GL44KK44KE44GZ44GE5ZCN5YmN44Gu5aSJ5pWw44Gr5Luj5YWl44GZ44KL44CCCgkJLy8gdF4zICsgYXReMiArIGJ0ICsgYwoJCUMgYSA9IHBvbHlfZXhwMi5jLmNbMl0sIGIgPSBwb2x5X2V4cDIuYy5jWzFdLCBjID0gcG9seV9leHAyLmMuY1swXTsKCQkvLyAgICAgICAgICBhCgkJLy8gdCA9IHggLSAtLS0KCQkvLyAgICAgICAgICAzCgkJLy8g44Go44GZ44KL44Go44CBCgkJLy8geF4zICsgcHggKyBxCgkJLy8g44GM5b6X44KJ44KM44KL44CC44Gf44Gg44GX44CBCgkJLy8gICAgICAgIDEKCQkvLyBwID0gLSAtLS1hXjIgKyBiCgkJLy8gICAgICAgIDMKCQkvLyAgICAgIDIgICAgICAgICAxCgkJLy8gcSA9IC0tLS1hXjMgLSAtLS1hYiArIGMKCQkvLyAgICAgIDI3ICAgICAgICAzCgkJLy8g44Gn44GC44KL44CCCgkJLy8geOOBruagueOCkuaxguOCgeOCieOCjOOBn+OCieOAgWEvM+OCkuW8leOBkeOBsOOAgXTjga7moLnjgpLlvpfjgonjgozjgovjgIIKCQlDIGEycCA9IGEgKiBhLCBhM3AgPSBhMnAgKiBhOwoJCUMgcCA9IC1hMnAgLyAzLjAgKyBiLCBxID0gYTNwICogMi4wIC8gMjcuMCAtIGEgKiBiIC8gMy4wICsgYzsKCQlDIHBkID0gcCAvIDMuMCwgcWQgPSBxIC8gMi4wOwoJCUMgcGQzcCA9IHBkICogcGQgKiBwZCwgcWQycCA9IHFkICogcWQ7CgkJQyBkZCA9IHBkM3AgKyBxZDJwOwoJCWlmIChuZWFyRXF1YWwoZGQsIDAuMCkgfHwgZGQgPiAwKSB7CgkJCUMgciA9IGNicnQoLXFkICsgc3FydChkZCkpOwoJCQlpZiAobmVhckVxdWFsKGRkLCAwLjApKSB7CgkJCQlyZXNfcnRzX2NudCA9IDI7CgkJCQlyZXNfcnRzLmNbMF0gPSByICsgcjsKCQkJCXJlc19ydHMuY1sxXSA9IC1yOwoJCQkJaWYgKG5lYXJFcXVhbChyZXNfcnRzLmNbMF0sIHJlc19ydHMuY1sxXSkpIHJlc19ydHNfY250ID0gMTsKCQkJCWVsc2Ugc29ydChyZXNfcnRzLmMsIHJlc19ydHMuYyArIDIpOwoJCQl9CgkJCWVsc2UgewoJCQkJQyBzID0gY2JydCgtcWQgLSBzcXJ0KGRkKSk7CgkJCQlyZXNfcnRzX2NudCA9IDE7CgkJCQlyZXNfcnRzLmNbMF0gPSByICsgczsKCQkJfQoJCX0KCQllbHNlIHsKCQkJcmVzX3J0c19jbnQgPSAzOwoJCQlDIHRoZXRhID0gYWNvcygtcWQgLyBzcXJ0KC1wZDNwKSkgLyAzLjA7CgkJCUMgdSA9IHNxcnQoLXBkKSwgdTJtID0gdSArIHU7CgkJCXJlc19ydHMuY1swXSA9IGNvcyh0aGV0YSkgKiB1Mm07CgkJCXJlc19ydHMuY1sxXSA9IGNvcyh0aGV0YSArIE1fUEkgKiAyLjAgLyAzLjApICogdTJtOwoJCQlyZXNfcnRzLmNbMl0gPSBjb3ModGhldGEgLSBNX1BJICogMi4wIC8gMy4wKSAqIHUybTsKCQkJc29ydChyZXNfcnRzLmMsIHJlc19ydHMuYyArIDMpOwoJCX0KCQlDIGEzZCA9IGEgLyAzLjA7CgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgcmVzX3J0c19jbnQ7IGkrKykgcmVzX3J0cy5jW2ldIC09IGEzZDsKCQlyZXR1cm4gbWFrZV90dXBsZShyZXNfcnRzX2NudCwgcmVzX3J0cyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBvc3RyZWFtJiBvcGVyYXRvciA8PChvc3RyZWFtJiBvdXQsIGNvbnN0IFBvbHlFeHA8TiwgQz4mIHBvbHlfZXhwKSB7CgkJb3V0IDw8ICJ7IjsKCQlmb3IgKGF1dG8gYyA6IHBvbHlfZXhwLmMpIG91dCA8PCBjIDw8ICIsICI7CgkJb3V0IDw8ICJ9IjsKCX0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjp0YW47CgkKCU1hdHJpeDwzPiBzY2FsaW5nKGNvbnN0IFZlY3RvcjwzPiYgY29lZikgewoJCS8vIOS4u+WvvuinkuaIkOWIhuOBq+S/guaVsOOCkumFjee9ruOBmeOCi+OAggoJCS8vIHxhIDAgMHwKCQkvLyB8MCBiIDB8CgkJLy8gfDAgMCBjfAoJCU1hdHJpeDwzPiByZXMoTWF0cml4PDM+OjpJREVOVElUWSgpKTsKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCAzOyBpKyspIHJlcy5jW2ldW2ldID0gY29lZi5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCU1hdHJpeDwzPiByb3RhdGlvbihjb25zdCBWZWN0b3I8Mz4mIGF4aXMsIGNvbnN0IEZMT0FUJiByYWQpIHsKCQkvLyDlhazlvI/jgavlvpPjgaPjgabooYzliJfjgpLkvZzjgovjgIIKCQlWZWN0b3I8Mz4gbm16X2F4aXMgPSBheGlzLm5vcm1hbGl6ZSgpOwoJCUZMT0FUIHh4ID0gbm16X2F4aXMuY1swXSAqIG5tel9heGlzLmNbMF07CgkJRkxPQVQgeHkgPSBubXpfYXhpcy5jWzBdICogbm16X2F4aXMuY1sxXTsKCQlGTE9BVCB4eiA9IG5tel9heGlzLmNbMF0gKiBubXpfYXhpcy5jWzJdOwoJCUZMT0FUIHl5ID0gbm16X2F4aXMuY1sxXSAqIG5tel9heGlzLmNbMV07CgkJRkxPQVQgeXogPSBubXpfYXhpcy5jWzFdICogbm16X2F4aXMuY1syXTsKCQlGTE9BVCB6eiA9IG5tel9heGlzLmNbMl0gKiBubXpfYXhpcy5jWzJdOwoJCUZMT0FUIHMgPSBzaW4ocmFkKTsKCQlGTE9BVCBjID0gY29zKHJhZCk7CgkJRkxPQVQgZCA9IDEuMCAtIGM7CgkJRkxPQVQgeHhkID0geHggKiBkOwoJCUZMT0FUIHh5ZCA9IHh5ICogZDsKCQlGTE9BVCB6cyA9IG5tel9heGlzLmNbMl0gKiBzOwoJCUZMT0FUIHh6ZCA9IHh6ICogZDsKCQlGTE9BVCB5cyA9IG5tel9heGlzLmNbMV0gKiBzOwoJCUZMT0FUIHl5ZCA9IHl5ICogZDsKCQlGTE9BVCB5emQgPSB5eiAqIGQ7CgkJRkxPQVQgeHMgPSBubXpfYXhpcy5jWzBdICogczsKCQlGTE9BVCB6emQgPSB6eiAqIGQ7CgkJcmV0dXJuIE1hdHJpeDwzPnsKCQkJYyArIHh4ZCwgIHh5ZCAtIHpzLCB4emQgKyB5cywgCgkJCXh5ZCArIHpzLCBjICsgeXlkLCAgeXpkIC0geHMsIAoJCQl4emQgLSB5cywgeXpkICsgeHMsIGMgKyB6emQsIAoJCX07Cgl9CgkKCU1hdHJpeDw0PiB0cmFuc2xhdGlvbihjb25zdCBWZWN0b3I8Mz4mIG9zKSB7CgkJLy8g5Y2Y5L2N6KGM5YiX44Gu56ysNOWIl+OBq+W5s+ihjOenu+WLlemHj+OCkumFjee9ruOBmeOCi+OAggoJCS8vIHwxIDAgMCB4fAoJCS8vIHwwIDEgMCB5fAoJCS8vIHwwIDAgMSB6fAoJCS8vIHwwIDAgMCAxfAoJCU1hdHJpeDw0PiByZXMoTWF0cml4PDQ+OjpJREVOVElUWSgpKTsKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCAzOyBpKyspIHJlcy5jW2ldWzNdID0gb3MuY1tpXTsKCQlyZXR1cm4gcmVzOwoJfQoJCglNYXRyaXg8ND4gdmlldyhjb25zdCBWZWN0b3I8Mz4mIGV5ZV9wb3MsIGNvbnN0IFZlY3RvcjwzPiYgY2VudF9wdCwgY29uc3QgVmVjdG9yPDM+JiB1cF9kaXIpIHsKCQkvLyDjg5Pjg6Xjg7zooYzliJfjga/jgIHnm67jga7kvY3nva7jgb7jgafjga7lubPooYznp7vli5XihpLoppbnt5rlm57ou6LihpLkuIrmlrnlkJHlm57ou6Ljga7poIbluo/jgaflpInmj5vjgpLooYzjgYbjgIIKCQkKCQkvLyDnm67jga7kvY3nva7jgYzljp/ngrnjgavjgY/jgovjgojjgYbjgavlubPooYznp7vli5XooYzliJfjgpLkvZzjgovjgIIKCQlNYXRyaXg8ND4gZXllX3Bvc190cmwodHJhbnNsYXRpb24oLWV5ZV9wb3MpKTsKCQkKCQkvLyDoppbnt5rjgavjgaTjgYTjgabjga7lm57ou6LooYzliJfjgpLkvZzjgovjgILoppbnt5rjgYzpgIZa6Lu444Gr44Gq44KL44KI44GG44Gr5Zue6Lui44GZ44KL44CCCgkJCgkJTWF0cml4PDM+IGVjX2l6X3JvdChNYXRyaXg8Mz46OklERU5USVRZKCkpOwoJCVZlY3RvcjwzPiBubXpfZWMgPSAoY2VudF9wdCAtIGV5ZV9wb3MpLm5vcm1hbGl6ZSgpOwoJCS8vIOimlue3muOBqOmAhlrou7jjga7ms5Xnt5rjgpLoqIjnrpfjgZnjgovjgIIKCQlWZWN0b3I8Mz4gZWNfaXpfbm1sID0gbm16X2VjLm91dHBybygtWl9BWElTKTsKCQkvLyDoppbnt5rjgajpgIZa6Lu444GM5bmz6KGM44Gn44Gv44Gq44GE44Gq44KJ44CB5rOV57ea44G+44KP44KK44Gr6KaW57ea44Go6YCGWui7uOOBjOOBquOBmeinkuW6puOBp+Wbnui7ouOBmeOCi+OAggoJCWlmIChlY19pel9ubWwgIT0gVmVjdG9yPDM+OjpaRVJPKCkpIAoJCQllY19pel9yb3QgPSByb3RhdGlvbihlY19pel9ubWwsIGFjb3Mobm16X2VjLmlucHJvKC1aX0FYSVMpKSk7CgkJLy8g6KaW57ea44GMWui7uOOBquOCieOAgVnou7jjgb7jgo/jgorjgavlj43ou6LjgZnjgovjgIIKCQllbHNlIGlmIChubXpfZWMgPT0gWl9BWElTKSBlY19pel9yb3QgPSByb3RhdGlvbihZX0FYSVMsIE1fUEkpOwoJCQoJCS8vIOS4iuaWueWQkeOBq+OBpOOBhOOBpuOBruWbnui7ouihjOWIl+OCkuS9nOOCi+OAguS4iuaWueWQkeOBjFnou7jjgavjgarjgovjgojjgYbjgavlm57ou6LjgZnjgovjgIIKCQkKCQlNYXRyaXg8Mz4gdV95X3JvdChNYXRyaXg8Mz46OklERU5USVRZKCkpOwoJCVZlY3RvcjwzPiBubXpfdXBfZGlyID0gdXBfZGlyLm5vcm1hbGl6ZSgpOwoJCS8vIOS4iuaWueWQkeOBqFnou7jjga7ms5Xnt5rjgpLoqIjnrpfjgZnjgovjgIIKCQlWZWN0b3I8Mz4gdV95X25tbCA9IG5tel91cF9kaXIub3V0cHJvKFlfQVhJUyk7CgkJLy8g5LiK5pa55ZCR44GoWei7uOOBjOW5s+ihjOOBp+OBr+OBquOBhOOBquOCieOAgeazlee3muOBvuOCj+OCiuOBq+S4iuaWueWQkeOBqFnou7jjgYzjgarjgZnop5Lluqbjgaflm57ou6LjgZnjgovjgIIKCQlpZiAodV95X25tbCAhPSBWZWN0b3I8Mz46OlpFUk8oKSkgCgkJCXVfeV9yb3QgPSByb3RhdGlvbih1X3lfbm1sLCBhY29zKG5tel91cF9kaXIuaW5wcm8oWV9BWElTKSkpOwoJCS8vIOS4iuaWueWQkeOBjOmAhlnou7jjgarjgonjgIFa6Lu444G+44KP44KK44Gr5Y+N6Lui44GZ44KL44CCCgkJZWxzZSBpZiAobm16X3VwX2RpciA9PSAtWV9BWElTKSB1X3lfcm90ID0gcm90YXRpb24oWl9BWElTLCBNX1BJKTsKCQkKCQkvLyDkvZzjgaPjgZ8z44Gk44Gu6KGM5YiX44KS6YCj57WQ44GZ44KL44CC5Y+z44GL44KJ6aCG44Gr6YWN572u44GZ44KL44CCCgkJcmV0dXJuICh1X3lfcm90ICogZWNfaXpfcm90KS5leHQoKSAqIGV5ZV9wb3NfdHJsOwoJfQoJCglNYXRyaXg8ND4gcGVyc3BlY3RpdmVQcm9qZWN0aW9uKGNvbnN0IGRvdWJsZSYgaG9yaV9mb3YsIGNvbnN0IGRvdWJsZSYgYXNwX3JhdCwgY29uc3QgZG91YmxlJiBuZWFyX2Rpc3QsIGNvbnN0IGRvdWJsZSYgZmFyX2Rpc3QpIHsKCQkvLyDnhKbngrnot53pm6IoRk9DYWwgRElTVGFuY2Up44CCCgkJZG91YmxlIGZvY19kaXN0ID0gMS4wIC8gdGFuKGhvcmlfZm92IC8gMi4wKTsKCQkvLyDlj7PlubPpnaLjgajov5HlubPpnaLjgYzkuqTlt67jgZnjgotY5bqn5qiZ44CCCgkJZG91YmxlIHJpZ2h0X3ggPSBuZWFyX2Rpc3QgLyBmb2NfZGlzdDsKCQkvLyDkuIrlubPpnaLjgajov5HlubPpnaLjgYzkuqTlt67jgZnjgotZ5bqn5qiZ44CCCgkJZG91YmxlIHRvcF95ID0gYXNwX3JhdCAqIG5lYXJfZGlzdCAvIGZvY19kaXN0OwoJCQoJCS8vIOWFrOW8j+OBq+W+k+OBo+OBpuihjOWIl+OCkuS9nOOCi+OAggoJCWRvdWJsZSBuMiA9IG5lYXJfZGlzdCArIG5lYXJfZGlzdDsKCQlkb3VibGUgbjJmID0gbjIgKiBmYXJfZGlzdDsKCQlkb3VibGUgZnNuID0gZmFyX2Rpc3QgLSBuZWFyX2Rpc3Q7CgkJZG91YmxlIGZhbiA9IGZhcl9kaXN0ICsgbmVhcl9kaXN0OwoJCWRvdWJsZSB3aWQgPSByaWdodF94ICsgcmlnaHRfeDsKCQlkb3VibGUgaGVpID0gdG9wX3kgKyB0b3BfeTsKCQlyZXR1cm4gTWF0cml4PDQ+ewoJCQluMiAvIHdpZCwgMC4wLCAgICAgIDAuMCwgICAgICAgIDAuMCwgCgkJCTAuMCwgICAgICBuMiAvIGhlaSwgMC4wLCAgICAgICAgMC4wLCAKCQkJMC4wLCAgICAgIDAuMCwgICAgICAtZmFuIC8gZnNuLCAtbjJmIC8gZnNuLCAKCQkJMC4wLCAgICAgIDAuMCwgICAgICAtMS4wLCAgICAgICAwLjAsIAoJCX07Cgl9Cn07CgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxpb3N0cmVhbT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3B5OwoJdXNpbmcgc3RkOjpmaWxsOwoJdXNpbmcgc3RkOjpvc3RyZWFtOwoJdXNpbmcgc3RkOjpzcXJ0OwoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgdm9pZCBWZWN0b3I8TiwgQz46OmNsZWFyKCkgewoJCS8vIOWQhOaIkOWIhuOCkuOCvOODreOCr+ODquOCouOBmeOCi+OAggoJCWZpbGwodGhpcy0+YywgdGhpcy0+YyArIE4sIDAuMCk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiArIDEsIEM+IFZlY3RvcjxOLCBDPjo6ZXh0KGNvbnN0IEMmIGV4dF9jb21wKSBjb25zdCB7CgkJVmVjdG9yPE4gKyAxLCBDPiByZXM7CgkJLy8g44GT44Gu44OZ44Kv44OI44Or44GL44KJTuWAi+OBruaIkOWIhuOCkuOCs+ODlOODvOOBl+OBpuOAgeacgOW+jOOBr+W8leaVsOOBruaIkOWIhuOCkuOCs+ODlOODvOOBmeOCi+OAggoJCWNvcHkodGhpcy0+YywgdGhpcy0+YyArIE4sIHJlcy5jKTsKCQlyZXMuY1tOXSA9IGV4dF9jb21wOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBDIFZlY3RvcjxOLCBDPjo6aW5wcm8oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0IHsKCQlDIHJlcyA9IDAuMDsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjga7nqY3jgpLmsYLjgoHjgabjgIHlkIjoqIjjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcyArPSB0aGlzLT5jW2ldICogcmhzLmNbaV07CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIGRvdWJsZSBWZWN0b3I8TiwgQz46Om5vcm0oKSBjb25zdCB7CgkJcmV0dXJuIHNxcnQodGhpcy0+aW5wcm8oKnRoaXMpKTsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFZlY3RvcjxOLCBDPiBWZWN0b3I8TiwgQz46Om5vcm1hbGl6ZSgpIGNvbnN0IHsKCQlyZXR1cm4gKnRoaXMgLyBub3JtKCk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBib29sIFZlY3RvcjxOLCBDPjo6b3BlcmF0b3IgIT0oY29uc3QgVmVjdG9yJiByaHMpIGNvbnN0IHsKCQlyZXR1cm4gIW9wZXJhdG9yID09KHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gVmVjdG9yPE4sIEM+OjpvcGVyYXRvciAqKGNvbnN0IEMmIHJocykgY29uc3QgewoJCVZlY3RvcjxOLCBDPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gr44K544Kr44Op44O85YCk44KS5LmX566X44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMuY1tpXSA9IHRoaXMtPmNbaV0gKiByaHM7CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFZlY3RvcjxOLCBDPiYgVmVjdG9yPE4sIEM+OjpvcGVyYXRvciAqPShjb25zdCBDJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAqKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gVmVjdG9yPE4sIEM+OjpvcGVyYXRvciArKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJVmVjdG9yPE4sIEM+IHJlczsKCQkvLyDlr77lv5zjgZnjgovmiJDliIblkIzlo6vjgafliqDnrpfjgZnjgovjgIIKCQlmb3IgKGF1dG8gaSA9IDA7IGkgPCBOOyBpKyspIHJlcy5jW2ldID0gdGhpcy0+Y1tpXSArIHJocy5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4mIFZlY3RvcjxOLCBDPjo6b3BlcmF0b3IgKz0oY29uc3QgVmVjdG9yJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciArKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gVmVjdG9yPE4sIEM+OjpvcGVyYXRvciAtKCkgY29uc3QgewoJCVZlY3RvcjxOLCBDPiByZXM7CgkJLy8g5ZCE5oiQ5YiG44Gu56ym5Y+344KS5Y+N6Lui44GZ44KL44CCCgkJZm9yIChhdXRvIGkgPSAwOyBpIDwgTjsgaSsrKSByZXMuY1tpXSA9IC10aGlzLT5jW2ldOwoJCXJldHVybiByZXM7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gVmVjdG9yPE4sIEM+OjpvcGVyYXRvciAtKGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJcmV0dXJuIG9wZXJhdG9yICsoLXJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4mIFZlY3RvcjxOLCBDPjo6b3BlcmF0b3IgLT0oY29uc3QgVmVjdG9yJiByaHMpIHsKCQlyZXR1cm4gKnRoaXMgPSBvcGVyYXRvciAtKHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4gVmVjdG9yPE4sIEM+OjpvcGVyYXRvciAvKGNvbnN0IEMmIHJocykgY29uc3QgewoJCXJldHVybiBvcGVyYXRvciAqKDEuMCAvIHJocyk7Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiwgQz4mIFZlY3RvcjxOLCBDPjo6b3BlcmF0b3IgLz0oY29uc3QgQyYgcmhzKSB7CgkJcmV0dXJuICp0aGlzID0gb3BlcmF0b3IgLyhyaHMpOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgYm9vbCBWZWN0b3I8TiwgQz46Om9wZXJhdG9yID09KGNvbnN0IFZlY3RvciYgcmhzKSBjb25zdCB7CgkJYm9vbCByZXMgPSB0cnVlOwoJCWlmICgmcmhzICE9IHRoaXMpIHsKCQkJLy8g5ZCE5oiQ5YiG44GU44Go44Gr5Y+N5b6p44GX44Gm44CB5a++5b+c44GZ44KL5oiQ5YiG5ZCM5aOr44GM6L+R44GR44KM44Gw562J44GX44GE44Go5Yik5a6a44GZ44KL44CCCgkJCWZvciAoYXV0byBpID0gMDsgaSA8IE47IGkrKykgewoJCQkJaWYgKCFuZWFyRXF1YWwodGhpcy0+Y1tpXSwgcmhzLmNbaV0pKSB7CgkJCQkJcmVzID0gZmFsc2U7CgkJCQkJYnJlYWs7CgkJCQl9CgkJCX0KCQl9CgkJcmV0dXJuIHJlczsKCX0KCQoJdGVtcGxhdGU8dW5zaWduZWQgaW50IE4sIHR5cGVuYW1lIEM+IAoJaW5saW5lIFZlY3RvcjxOLCBDPiBWZWN0b3I8TiwgQz46Om91dHBybyhjb25zdCBWZWN0b3ImIHJocykgY29uc3QgewoJCS8vIOWQhOaIkOWIhuOBq+OBpOOBhOOBpuOAgeWvvuW/nOOBmeOCi+WIl+OCkumZpOOBhOOBnzLDlzLjga7ooYzliJflvI/jgpLoqIjnrpfjgZnjgovjgIIKCQkvLyBQw5dRID0gPCBQeeODu1F6IC0gUHrjg7tReSwgUHrjg7tReCAtIFB444O7UXosIFB444O7UXkgLSBQeeODu1F4ID4KCQlyZXR1cm4gVmVjdG9yPE4sIEM+ewoJCQl0aGlzLT5jWzFdICogcmhzLmNbMl0gLSB0aGlzLT5jWzJdICogcmhzLmNbMV0sIAoJCQl0aGlzLT5jWzJdICogcmhzLmNbMF0gLSB0aGlzLT5jWzBdICogcmhzLmNbMl0sIAoJCQl0aGlzLT5jWzBdICogcmhzLmNbMV0gLSB0aGlzLT5jWzFdICogcmhzLmNbMF0sIAoJCX07Cgl9CgkKCXRlbXBsYXRlPHVuc2lnbmVkIGludCBOLCB0eXBlbmFtZSBDPiAKCWlubGluZSBWZWN0b3I8TiAtIDEsIEM+IFZlY3RvcjxOLCBDPjo6dHJ1bmMoKSBjb25zdCB7CgkJVmVjdG9yPE4gLSAxLCBDPiByZXM7CgkJY29weSh0aGlzLT5jLCB0aGlzLT5jICsgTiAtIDEsIHJlcy5jKTsKCQlyZXR1cm4gcmVzOwoJfQoJCgl0ZW1wbGF0ZTx1bnNpZ25lZCBpbnQgTiwgdHlwZW5hbWUgQz4gCglpbmxpbmUgb3N0cmVhbSYgb3BlcmF0b3IgPDwob3N0cmVhbSYgb3V0LCBjb25zdCBWZWN0b3I8TiwgQz4mIHZlYykgewoJCW91dCA8PCAieyI7CgkJZm9yIChhdXRvIGMgOiB2ZWMuYykgb3V0IDw8IGMgPDwgIiwgIjsKCQlvdXQgPDwgIn0iOwoJfQp9OwoKI2luY2x1ZGUgPG1lbW9yeT4KI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpzaGFyZWRfcHRyOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCVRlc3RTZXQ6OlRlc3RTZXQoKSB7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3QyKSk7CgkJdGhpcy0+dGVzdHMucHVzaF9iYWNrKHNoYXJlZF9wdHI8VGVzdD4obmV3IFRlc3Q1KSk7Cgl9CgoJdm9pZCBUZXN0U2V0OjpydW4oKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsgZm9yIChhdXRvIGl0ZXIgOiB0aGlzLT50ZXN0cykgaXRlci0+cnVuKCk7IH0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KI2luY2x1ZGUgPHR1cGxlPgoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmNvczsKCXVzaW5nIHN0ZDo6Z2V0OwoJdXNpbmcgc3RkOjptYWtlX3R1cGxlOwoJdXNpbmcgc3RkOjpzaW47Cgl1c2luZyBzdGQ6OnNxcnQ7Cgl1c2luZyBzdGQ6OnR1cGxlOwoJCgl2b2lkIFRlc3QyOjpydW4oKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwyPjo6SURFTlRJVFkoKS5jWzBdWzBdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDI+OjpJREVOVElUWSgpLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mj46OklERU5USVRZKCkuY1sxXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwyPjo6SURFTlRJVFkoKS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMF1bMF0sIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1swXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1sxXVsxXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzFdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDM+OjpJREVOVElUWSgpLmNbMl1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8Mz46OklERU5USVRZKCkuY1syXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDwzPjo6SURFTlRJVFkoKS5jWzJdWzJdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMF1bMF0sIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1swXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzBdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMF1bM10sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1sxXVswXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzFdWzFdLCAxLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMV1bMl0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1sxXVszXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzJdWzBdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbMl1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1syXVsyXSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzJdWzNdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbM11bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChNYXRyaXg8ND46OklERU5USVRZKCkuY1szXVsxXSwgMC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKE1hdHJpeDw0Pjo6SURFTlRJVFkoKS5jWzNdWzJdLCAwLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoTWF0cml4PDQ+OjpJREVOVElUWSgpLmNbM11bM10sIDEuMCkpCgkJCgkJTWF0cml4PDI+IGZ7CgkJCTEuMCwgIC0yLjAsIAoJCQktMy4wLCA0LjAsIAoJCX0sIGd7CgkJCS05LjAsIDguMCwgCgkJCTcuMCwgIC02LjAsIAoJCX07CgkJCgkJQVNTRVJUKG5lYXJFcXVhbChmLmNbMF1bMF0sIDEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChmLmNbMF1bMV0sIC0yLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZi5jWzFdWzBdLCAtMy4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGYuY1sxXVsxXSwgNC4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGcuY1swXVswXSwgLTkuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnLmNbMF1bMV0sIDguMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnLmNbMV1bMF0sIDcuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnLmNbMV1bMV0sIC02LjApKQoJCQoJCUFTU0VSVChmLmV4dCgpID09IChNYXRyaXg8Mz57CgkJCTEuMCwgIC0yLjAsIDAuMCwgCgkJCS0zLjAsIDQuMCwgIDAuMCwgCgkJCTAuMCwgIDAuMCwgIDEuMCwgCgkJfSkpCgkJQVNTRVJUKGYuZXh0KChNYXRyaXg8Mz57CgkJCTMuMCwgIC04LjAsIDUuMCwgCgkJCS0yLjAsIDcuMCwgIDQuMCwgCgkJCTEuMCwgIC01LjAsIDAuMCwgCgkJfSkpID09IChNYXRyaXg8Mz57CgkJCTEuMCwgIC0yLjAsIDUuMCwgCgkJCS0zLjAsIDQuMCwgIDQuMCwgCgkJCTEuMCwgIC01LjAsIDAuMCwgCgkJfSkpCgkJQVNTRVJUKGYudHJ1bmMoKSA9PSAoTWF0cml4PDE+ewoJCQkxLjAsIAoJCX0pKQoJCQoJCUFTU0VSVChmID09IChNYXRyaXg8Mj57CgkJCTEuMCwgIC0yLjAsIAoJCQktMy4wLCA0LjAsIAoJCX0pKQoJCUFTU0VSVChnID09IChNYXRyaXg8Mj57CgkJCS05LjAsIDguMCwgCgkJCTcuMCwgIC02LjAsIAoJCX0pKQoJCUFTU0VSVChmICE9IChNYXRyaXg8Mj57CgkJCS05LjAsIDguMCwgCgkJCTcuMCwgIC02LjAsIAoJCX0pKQoJCUFTU0VSVChnICE9IChNYXRyaXg8Mj57CgkJCTEuMCwgIC0yLjAsIAoJCQktMy4wLCA0LjAsIAoJCX0pKQoJCQoJCUFTU0VSVCgtZiA9PSAoTWF0cml4PDI+ewoJCQktMS4wLCAyLjAsIAoJCQkzLjAsICAtNC4wLCAKCQl9KSkKCQlBU1NFUlQoLWcgPT0gKE1hdHJpeDwyPnsKCQkJOS4wLCAgLTguMCwgCgkJCS03LjAsIDYuMCwgCgkJfSkpCgkJQVNTRVJUKGYgKyBnID09IChNYXRyaXg8Mj57CgkJCS04LjAsIDYuMCwgCgkJCTQuMCwgIC0yLjAsIAoJCX0pKQoJCUFTU0VSVChmIC0gZyA9PSAoTWF0cml4PDI+ewoJCQkxMC4wLCAgLTEwLjAsIAoJCQktMTAuMCwgMTAuMCwgCgkJfSkpCgkJQVNTRVJUKGYgKiBnID09IChNYXRyaXg8Mj57CgkJCS0yMy4wLCAyMC4wLCAKCQkJNTUuMCwgIC00OC4wLCAKCQl9KSkKCQlBU1NFUlQoZyAqIGYgPT0gKE1hdHJpeDwyPnsKCQkJLTMzLjAsIDUwLjAsIAoJCQkyNS4wLCAgLTM4LjAsIAoJCX0pKQoJCUFTU0VSVChmICogTWF0cml4PDI+OjpJREVOVElUWSgpID09IChNYXRyaXg8Mj57CgkJCTEuMCwgIC0yLjAsIAoJCQktMy4wLCA0LjAsIAoJCX0pKQoJCUFTU0VSVChNYXRyaXg8Mj46OklERU5USVRZKCkgKiBnID09IChNYXRyaXg8Mj57CgkJCS05LjAsIDguMCwgCgkJCTcuMCwgIC02LjAsIAoJCX0pKQoJCUFTU0VSVChmICogMy4wID09IChNYXRyaXg8Mj57CgkJCTMuMCwgIC02LjAsIAoJCQktOS4wLCAxMi4wLCAKCQl9KSkKCQlBU1NFUlQoZyAvIC0yLjAgPT0gKE1hdHJpeDwyPnsKCQkJNC41LCAgLTQuMCwgCgkJCS0zLjUsIDMuMCwgCgkJfSkpCgkJCgkJTWF0cml4PDI+IGg7CgkJaCA9IGY7CgkJQVNTRVJUKGggPT0gKE1hdHJpeDwyPnsKCQkJMS4wLCAgLTIuMCwgCgkJCS0zLjAsIDQuMCwgCgkJfSkpCgkJQVNTRVJUKChoICs9IChNYXRyaXg8Mj57CgkJCTAuMSwgIC0wLjIsIAoJCQktMC4zLCAwLjQsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQkxLjEsICAtMi4yLCAKCQkJLTMuMywgNC40LCAKCQl9KSkKCQlBU1NFUlQoKGggLT0gKE1hdHJpeDwyPnsKCQkJLTAuOSwgMC44LCAKCQkJMC43LCAgLTAuNiwgCgkJfSkpID09IChNYXRyaXg8Mj57CgkJCTIuMCwgIC0zLjAsIAoJCQktNC4wLCA1LjAsIAoJCX0pKQoJCUFTU0VSVCgoaCAqPSAoTWF0cml4PDI+ewoJCQktNC4wLCAzLjAsIAoJCQktMi4wLCAxLjAsIAoJCX0pKSA9PSAoTWF0cml4PDI+ewoJCQktMi4wLCAzLjAsIAoJCQk2LjAsICAtNy4wLCAKCQl9KSkKCQlBU1NFUlQoKGggKj0gMy4wKSA9PSAoTWF0cml4PDI+ewoJCQktNi4wLCA5LjAsIAoJCQkxOC4wLCAtMjEuMCwgCgkJfSkpCgkJQVNTRVJUKChoIC89IC0yLjApID09IChNYXRyaXg8Mj57CgkJCTMuMCwgIC00LjUsIAoJCQktOS4wLCAxMC41LCAKCQl9KSkKCQloLmNsZWFyKCk7CgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMF1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMF1bMV0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMV1bMF0sIDAuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChoLmNbMV1bMV0sIDAuMCkpCgkJCgkJQVNTRVJUKG5lYXJFcXVhbCgoTWF0cml4PDI+ewoJCQkyLjAsICA3LjAsIAoJCQktMy4wLCAwLjUsIAoJCX0pLmRldCgpLCAyMi4wKSkKCQlBU1NFUlQobmVhckVxdWFsKChNYXRyaXg8Mz57CgkJCTAuMCwgMC4wLCAxLjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAKCQkJMS4wLCAwLjAsIDAuMCwgCgkJfSkuZGV0KCksIC0xLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoKE1hdHJpeDwzPnsKCQkJMC41LCAgICAgICAgICAgICAgc3FydCgzLjApIC8gMi4wLCAwLjAsIAoJCQktc3FydCgzLjApIC8gMi4wLCAwLjUsICAgICAgICAgICAgIDAuMCwgCgkJCTAuMCwgICAgICAgICAgICAgIDAuMCwgICAgICAgICAgICAgMS4wLCAKCQl9KS5kZXQoKSwgMS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKChNYXRyaXg8Mz57CgkJCTUsICA3LCAgMSwgCgkJCTE3LCAyLCAgNjQsIAoJCQkxMCwgMTQsIDIsIAoJCX0pLmRldCgpLCAwLjApKQoJCQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkyLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAzLjAsIDAuMCwgCgkJCTAuMCwgMC4wLCA0LjAsIAoJCX0pLmludigpID09IChNYXRyaXg8Mz57CgkJCTEyLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAgOC4wLCAwLjAsIAoJCQkwLjAsICAwLjAsIDYuMCwgCgkJfSkgLyAyNC4wKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkyLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAzLjAsIDAuMCwgCgkJCTAuMCwgMC4wLCA0LjAsIAoJCX0pICogKChNYXRyaXg8Mz57CgkJCTEyLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAgOC4wLCAwLjAsIAoJCQkwLjAsICAwLjAsIDYuMCwgCgkJfSkgLyAyNC4wKSA9PSBNYXRyaXg8Mz46OklERU5USVRZKCkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTEuMCwgMC4wLCAwLjAsIAoJCQkwLjAsIDIuMCwgMi4wLCAKCQkJMy4wLCAwLjAsIDguMCwgCgkJfSkuaW52KCkgPT0gKE1hdHJpeDwzPnsKCQkJMTYuMCwgMC4wLCAwLjAsIAoJCQk2LjAsICA4LjAsIC0yLjAsIAoJCQktNi4wLCAwLjAsIDIuMCwgCgkJfSkgLyAxNi4wKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkxLjAsIDAuMCwgMC4wLCAKCQkJMC4wLCAyLjAsIDIuMCwgCgkJCTMuMCwgMC4wLCA4LjAsIAoJCX0pICogKChNYXRyaXg8Mz57CgkJCTE2LjAsIDAuMCwgMC4wLCAKCQkJNi4wLCAgOC4wLCAtMi4wLCAKCQkJLTYuMCwgMC4wLCAyLjAsIAoJCX0pIC8gMTYuMCkgPT0gTWF0cml4PDM+OjpJREVOVElUWSgpKQoJCUZMT0FUIHRoZXRhX3JhZCA9IGFuZ2xlVG9SYWRpYW4oNjAuMCk7CgkJRkxPQVQgY29zX3JlcyA9IGNvcyh0aGV0YV9yYWQpOwoJCUZMT0FUIHNpbl9yZXMgPSBzaW4odGhldGFfcmFkKTsKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJY29zX3JlcywgMC4wLCAtc2luX3JlcywgCgkJCTAuMCwgICAgIDEuMCwgMC4wLCAKCQkJc2luX3JlcywgMC4wLCBjb3NfcmVzLCAKCQl9KS5pbnYoKSA9PSAoTWF0cml4PDM+ewoJCQljb3NfcmVzLCAgMC4wLCBzaW5fcmVzLCAKCQkJMC4wLCAgICAgIDEuMCwgMC4wLCAKCQkJLXNpbl9yZXMsIDAuMCwgY29zX3JlcywgCgkJfSkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCWNvc19yZXMsIDAuMCwgLXNpbl9yZXMsIAoJCQkwLjAsICAgICAxLjAsIDAuMCwgCgkJCXNpbl9yZXMsIDAuMCwgY29zX3JlcywgCgkJfSkgKiAoTWF0cml4PDM+ewoJCQljb3NfcmVzLCAgMC4wLCBzaW5fcmVzLCAKCQkJMC4wLCAgICAgIDEuMCwgMC4wLCAKCQkJLXNpbl9yZXMsIDAuMCwgY29zX3JlcywgCgkJfSkgPT0gTWF0cml4PDM+OjpJREVOVElUWSgpKQoJCUFTU0VSVCgoTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCA0LjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAzLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCA3LjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX0pLmludigpID09IChNYXRyaXg8ND57CgkJCTEuMCwgMC4wLCAwLjAsIC00LjAsIAoJCQkwLjAsIDEuMCwgMC4wLCAtMy4wLCAKCQkJMC4wLCAwLjAsIDEuMCwgLTcuMCwgCgkJCTAuMCwgMC4wLCAwLjAsIDEuMCwgCgkJfSkpCgkJQVNTRVJUKChNYXRyaXg8ND57CgkJCTEuMCwgMC4wLCAwLjAsIDQuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIDMuMCwgCgkJCTAuMCwgMC4wLCAxLjAsIDcuMCwgCgkJCTAuMCwgMC4wLCAwLjAsIDEuMCwgCgkJfSkgKiAoTWF0cml4PDQ+ewoJCQkxLjAsIDAuMCwgMC4wLCAtNC4wLCAKCQkJMC4wLCAxLjAsIDAuMCwgLTMuMCwgCgkJCTAuMCwgMC4wLCAxLjAsIC03LjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAxLjAsIAoJCX0pID09IE1hdHJpeDw0Pjo6SURFTlRJVFkoKSkKCQkKCQlBU1NFUlQoKE1hdHJpeDwyPnsKCQkJMS4wLCAgLTMuMCwgCgkJCS00LjAsIDYuMCwgCgkJfSkgKiAoVmVjdG9yPDI+ezIuMCwgNS4wfSkgPT0gKFZlY3RvcjwyPnstMTMuMCwgMjIuMH0pKQoJCUFTU0VSVCgoTWF0cml4PDI+ewoJCQkwLjAsICA4LjAsIAoJCQktOS4wLCAzLjAsIAoJCX0pICogKFZlY3RvcjwyPnsxLjAsIC00LjB9KSA9PSAoVmVjdG9yPDI+ey0zMi4wLCAtMjEuMH0pKQoJCQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkzLjAsIDIuMCwgIC0zLjAsIAoJCQk0LjAsIC0zLjAsIDYuMCwgCgkJCTEuMCwgMC4wLCAgLTEuMCwgCgkJfSkucmVkdWNlKChWZWN0b3I8Mz57CgkJCTUuMCwgCgkJCTEuMCwgCgkJCTMuMCwgCgkJfSkpID09IG1ha2VfdHVwbGUoKE1hdHJpeDwzPnsKCQkJMS4wLCAwLjAsIDAuMCwgCgkJCTAuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgMS4wLCAKCQl9KSwgKFZlY3RvcjwzPnsKCQkJMTMuMCAvIDEwLjAsIAoJCQktMi4wLCAKCQkJLTE3LjAgLyAxMC4wLCAKCQl9KSkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTIuMCwgMS4wLCAzLjAsIAoJCQkwLjAsIDEuMCwgLTEuMCwgCgkJCTEuMCwgMy4wLCAtMS4wLCAKCQl9KS5yZWR1Y2UoKSA9PSBtYWtlX3R1cGxlKChNYXRyaXg8Mz57CgkJCTEuMCwgMC4wLCAyLjAsIAoJCQkwLjAsIDEuMCwgLTEuMCwgCgkJCTAuMCwgMC4wLCAwLjAsIAoJCX0pLCAoVmVjdG9yPDM+ewoJCQkwLjAsIAoJCQkwLjAsIAoJCQkwLjAsIAoJCX0pKSkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJNC4wLCAzLjAsICAyLjAsIAoJCQkxLjAsIC0xLjAsIC0zLjAsIAoJCQkyLjAsIDMuMCwgIDQuMCwgCgkJfSkucmVkdWNlKCkgPT0gbWFrZV90dXBsZSgoTWF0cml4PDM+ewoJCQkxLjAsIDAuMCwgLTEuMCwgCgkJCTAuMCwgMS4wLCAyLjAsIAoJCQkwLjAsIDAuMCwgMC4wLCAKCQl9KSwgKFZlY3RvcjwzPnsKCQkJMC4wLCAKCQkJMC4wLCAKCQkJMC4wLCAKCQl9KSkpCgkJCgkJCgkJQVNTRVJUKChNYXRyaXg8Mj57CgkJCTEuMCwgMS4wLCAKCQkJMy4wLCAtMS4wLCAKCQl9KS5laWd2YWxzKCkgPT0gKFZlY3RvcjwyPnstMi4wLCAyLjB9KSkKCQlBU1NFUlQoKE1hdHJpeDwzPnsKCQkJMi4wLCAxLjAsIDAuMCwgCgkJCTEuMCwgMS4wLCAwLjAsIAoJCQkwLjAsIDAuMCwgLTEuMCwgCgkJfSkuZWlndmFscygpID09IChWZWN0b3I8Mz57LTEuMCwgKDMuMCAtIHNxcnQoNS4wKSkgLyAyLjAsICgzLjAgKyBzcXJ0KDUuMCkpIC8gMi4wfSkpCgkJQVNTRVJUKChNYXRyaXg8Mz57CgkJCTIuMCwgIDAuMCwgMC4wLCAKCQkJNS4wLCAgMi4wLCAzLjAsIAoJCQktNC4wLCAzLjAsIDIuMCwgCgkJfSkuZWlndmFscygpID09IChWZWN0b3I8Mz57LTEuMCwgMi4wLCA1LjB9KSkKCQkKCQlBU1NFUlQoKE1hdHJpeDwyPnsKCQkJMS4wLCAxLjAsIAoJCQkzLjAsIC0xLjAsIAoJCX0pLmVpZ3ZlY3MoKSA9PSAoVmVjdG9yPDIsIFZlY3RvcjwyPj57CgkJCShWZWN0b3I8Mj57LTEuMCAvIDMuMCAvIChzcXJ0KDEwLjApIC8gMy4wKSwgMS4wIC8gKHNxcnQoMTAuMCkgLyAzLjApfSksIAoJCQkoVmVjdG9yPDI+ezEuMCAvIHNxcnQoMi4wKSwgMS4wIC8gc3FydCgyLjApfSksIAoJCX0pKQoJCUFTU0VSVCgoTWF0cml4PDM+ewoJCQkyLjAsICAwLjAsIDAuMCwgCgkJCTUuMCwgIDIuMCwgMy4wLCAKCQkJLTQuMCwgMy4wLCAyLjAsIAoJCX0pLmVpZ3ZlY3MoKSA9PSAoVmVjdG9yPDMsIFZlY3RvcjwzPj57CgkJCShWZWN0b3I8Mz57MC4wIC8gc3FydCgyLjApLCAtMS4wIC8gc3FydCgyLjApLCAxLjAgLyBzcXJ0KDIuMCl9KSwgCgkJCShWZWN0b3I8Mz57LTMuMCAvIDUuMCAvIHNxcnQoMi4wKSwgLTQuMCAvIDUuMCAvIHNxcnQoMi4wKSwgMS4wIC8gc3FydCgyLjApfSksIAoJCQkoVmVjdG9yPDM+ezAuMCAvIHNxcnQoMi4wKSwgMS4wIC8gc3FydCgyLjApLCAxLjAgLyBzcXJ0KDIuMCl9KSwgCgkJfSkpCgl9Cn07CgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDx0dXBsZT4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpnZXQ7Cgl1c2luZyBzdGQ6OnR1cGxlOwoJCgl2b2lkIFRlc3Q1OjpydW4oKSBjb25zdCB0aHJvdyhzdHJpbmcpIHsKCQlQb2x5RXhwPDI+IGF7VmVjdG9yPDM+ezEuMCwgLTIuMCwgMy4wfX07CgkJQVNTRVJUKChQb2x5RXhwPDI+e1ZlY3RvcjwzPnszLjAsIC0yLjAsIDEuMH19KS5jID09IAoJCQkoVmVjdG9yPDM+ezMuMCwgLTIuMCwgMS4wfSkpCgkJQVNTRVJUKC0oUG9seUV4cDwyPntWZWN0b3I8Mz57My4wLCAtMi4wLCAxLjB9fSkgPT0gCgkJCShQb2x5RXhwPDI+e1ZlY3RvcjwzPnstMy4wLCAyLjAsIC0xLjB9fSkpCgkJQVNTRVJUKChQb2x5RXhwPDI+e1ZlY3RvcjwzPnszLjAsIC0yLjAsIDEuMH19KSAqIDIuMCA9PSAKCQkJKFBvbHlFeHA8Mj57VmVjdG9yPDM+ezYuMCwgLTQuMCwgMi4wfX0pKQoJCUFTU0VSVCgoUG9seUV4cDwyPntWZWN0b3I8Mz57My4wLCAtMi4wLCAxLjB9fSkgLyAyLjAgPT0gCgkJCShQb2x5RXhwPDI+e1ZlY3RvcjwzPnsxLjUsIC0xLjAsIDAuNX19KSkKCQlBU1NFUlQoKFBvbHlFeHA8Mj57VmVjdG9yPDM+ezMuMCwgLTIuMCwgMS4wfX0pICsgCgkJCShQb2x5RXhwPDI+e1ZlY3RvcjwzPnstNS4wLCAxLjAsIDAuMH19KSA9PSAKCQkJKFBvbHlFeHA8Mj57VmVjdG9yPDM+ey0yLjAsIC0xLjAsIDEuMH19KSkKCQlBU1NFUlQoKFBvbHlFeHA8Mj57VmVjdG9yPDM+ezMuMCwgLTIuMCwgMS4wfX0pIC0gCgkJCShQb2x5RXhwPDI+e1ZlY3RvcjwzPnstNS4wLCAxLjAsIDAuMH19KSA9PSAKCQkJKFBvbHlFeHA8Mj57VmVjdG9yPDM+ezguMCwgLTMuMCwgMS4wfX0pKQoJCUFTU0VSVCgoUG9seUV4cDwyPntWZWN0b3I8Mz57NC4wLCAyLjAsIDAuMH19KSAqIAoJCQkoUG9seUV4cDwyPntWZWN0b3I8Mz57LTMuMCwgMS4wLCAwLjB9fSkgPT0gCgkJCShQb2x5RXhwPDI+e1ZlY3RvcjwzPnstMTIuMCwgLTIuMCwgMi4wfX0pKQoJCQoJCXR1cGxlPHVuc2lnbmVkIGludCwgVmVjdG9yPDI+PiBydHNfcmVzMjsKCQlydHNfcmVzMiA9IChQb2x5RXhwPDI+e1ZlY3RvcjwzPnsyLjAsIC00LjAsIDIuMH19KS5ydHMoKTsKCQlBU1NFUlQoZ2V0PDA+KHJ0c19yZXMyKSA9PSAxKQoJCUFTU0VSVChuZWFyRXF1YWwoZ2V0PDE+KHJ0c19yZXMyKS5jWzBdLCAxLjApKQoJCXJ0c19yZXMyID0gKFBvbHlFeHA8Mj57VmVjdG9yPDM+ey0zLjAsIC02LjAsIC0zLjB9fSkucnRzKCk7CgkJQVNTRVJUKGdldDwwPihydHNfcmVzMikgPT0gMSkKCQlBU1NFUlQobmVhckVxdWFsKGdldDwxPihydHNfcmVzMikuY1swXSwgLTEuMCkpCgkJcnRzX3JlczIgPSAoUG9seUV4cDwyPntWZWN0b3I8Mz57LTIuMCwgMC4wLCAyLjB9fSkucnRzKCk7CgkJQVNTRVJUKGdldDwwPihydHNfcmVzMikgPT0gMikKCQlBU1NFUlQobmVhckVxdWFsKGdldDwxPihydHNfcmVzMikuY1swXSwgLTEuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnZXQ8MT4ocnRzX3JlczIpLmNbMV0sIDEuMCkpCgkJcnRzX3JlczIgPSAoUG9seUV4cDwyPntWZWN0b3I8Mz57MTguMCwgLTMuMCwgLTMuMH19KS5ydHMoKTsKCQlBU1NFUlQoZ2V0PDA+KHJ0c19yZXMyKSA9PSAyKQoJCUFTU0VSVChuZWFyRXF1YWwoZ2V0PDE+KHJ0c19yZXMyKS5jWzBdLCAtMy4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGdldDwxPihydHNfcmVzMikuY1sxXSwgMi4wKSkKCQkKCQl0dXBsZTx1bnNpZ25lZCBpbnQsIFZlY3RvcjwzPj4gcnRzX3JlczM7CgkJcnRzX3JlczMgPSAoUG9seUV4cDwzPntWZWN0b3I8ND57LTIuMCwgNi4wLCAtNi4wLCAyLjB9fSkucnRzKCk7CgkJQVNTRVJUKGdldDwwPihydHNfcmVzMykgPT0gMSkKCQlBU1NFUlQobmVhckVxdWFsKGdldDwxPihydHNfcmVzMykuY1swXSwgMS4wKSkKCQlydHNfcmVzMyA9IChQb2x5RXhwPDM+e1ZlY3Rvcjw0PnsyLjAsIDMuMCwgMC4wLCAtMS4wfX0pLnJ0cygpOwoJCUFTU0VSVChnZXQ8MD4ocnRzX3JlczMpID09IDIpCgkJQVNTRVJUKG5lYXJFcXVhbChnZXQ8MT4ocnRzX3JlczMpLmNbMF0sIC0xLjApKQoJCUFTU0VSVChuZWFyRXF1YWwoZ2V0PDE+KHJ0c19yZXMzKS5jWzFdLCAyLjApKQoJCXJ0c19yZXMzID0gKFBvbHlFeHA8Mz57VmVjdG9yPDQ+ezE4MC4wLCA1MS4wLCAtMTIuMCwgLTMuMH19KS5ydHMoKTsKCQlBU1NFUlQoZ2V0PDA+KHJ0c19yZXMzKSA9PSAzKQoJCUFTU0VSVChuZWFyRXF1YWwoZ2V0PDE+KHJ0c19yZXMzKS5jWzBdLCAtNS4wKSkKCQlBU1NFUlQobmVhckVxdWFsKGdldDwxPihydHNfcmVzMykuY1sxXSwgLTMuMCkpCgkJQVNTRVJUKG5lYXJFcXVhbChnZXQ8MT4ocnRzX3JlczMpLmNbMl0sIDQuMCkpCgl9Cn07CgojaW5jbHVkZSA8aW9zdHJlYW0+CiNpbmNsdWRlIDxzdHJpbmc+CgppbnQgbWFpbigpIHsKCXVzaW5nIHN0ZDo6Y2VycjsKCXVzaW5nIHN0ZDo6ZW5kbDsKCXVzaW5nIHN0ZDo6c3RyaW5nOwoJCgl0cnkgewoJCWgzZDo6VGVzdFNldCgpLnJ1bigpOwoJfQoJY2F0Y2ggKGNvbnN0IHN0cmluZyYgbXNnKSB7CgkJY2VyciA8PCBtc2cgPDwgZW5kbDsKCQlyZXR1cm4gMTsKCX0KCXJldHVybiAwOwp9Cg==