namespace h3d {
/**
* 許容誤差クラス。
* double型では、比較する二つの数の指数部と1023のうち最大のものをxとすると、
* 許容誤差は 2^(x - 精度 - 1023) となる。
* @param F 浮動小数点数(Floating-point number)の型。
*/
template<typename F>
class AllowableLimit {
public:
/** 精度(PreCiSion)。 */
static int pcs;
/**
* 浮動小数点数の指数部を取得する。
* @param num 数(NUMber)。
* @return 取得した指数部。
*/
inline static int exponent(const F& num);
/**
* 二つの浮動小数点数の許容誤差を作る。
* @param lhs 左側(Left Hand Side)の数。
* @param rhs 右側(Right Hand Side)の数。
* @return 作った許容誤差。
*/
inline static F make(const F& lhs, const F& rhs);
};
/**
* 二つの数が近いかどうかを判定する。
* @param X 数の型。
* @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 <iomanip>
#include <iostream>
#include <string>
#define ASSERT(pred) h3d::assert(#pred, (pred));
#define PRINT(val) cout << #val << "=" << (val) << endl;
namespace h3d {
using std::cout;
using std::dec;
using std::endl;
using std::hex;
using std::setfill;
using std::setw;
using std::setprecision;
using std::string;
/**
* アサーションを実行する。
* 成功なら標準出力に結果を出力する。
* @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 <algorithm>
#include <cmath>
namespace h3d {
using std::fabs;
using std::max;
template<>
int AllowableLimit<double>::pcs = 48;
template<>
inline int AllowableLimit<double>::exponent(const double& num) {
return (int)(*(unsigned long long*)&num >> 52) & ((1 << 11) - 1);
}
template<>
inline double AllowableLimit<double>::make(const double& lhs, const double& rhs) {
unsigned long long alw_lmt = max(max(exponent(lhs), exponent(rhs)), 1023) - pcs;
return *(double*)&(alw_lmt <<= 52);
}
template<typename X>
inline bool nearEqual(const X& lhs, const X& rhs) { return lhs == rhs; }
template<>
inline bool nearEqual<double>(const double& lhs, const double& rhs) {
return fabs(lhs - rhs) <= AllowableLimit<double>::make(lhs, rhs);
}
};
#include <iostream>
#include <string>
namespace h3d {
using std::cout;
using std::endl;
using std::string;
inline void assert(const string& pred_str, const bool& pred_res) throw(string) {
if (pred_res) cout << "アサート成功: " << pred_str << endl;
else throw "アサート失敗: " + pred_str;
}
};
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
int main() {
try {
double x, y, a;
std::cout << "xとy: 比較する二つの数, d: xとyの差の絶対値, a: 許容誤差" << std::endl;
h3d::AllowableLimit<double>::pcs = 48;
std::cout << "許容誤差の精度: " << h3d::AllowableLimit<double>::pcs << std::endl;
std::cout << std::endl;
x = 1.000000000000005;
y = 1.000000000000004;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 1.000000000000050;
y = 1.000000000000040;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
x = 100000000000000.5;
y = 100000000000000.4;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 100000000000005.0;
y = 100000000000004.0;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
x = 0.000000000000005;
y = 0.000000000000004;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 0.000000000000050;
y = 0.000000000000040;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
h3d::AllowableLimit<double>::pcs = 46;
std::cout << "許容誤差の精度: " << h3d::AllowableLimit<double>::pcs << std::endl;
std::cout << std::endl;
x = 1.000000000000050;
y = 1.000000000000040;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 1.000000000000500;
y = 1.000000000000400;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
x = 100000000000005.0;
y = 100000000000004.0;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 100000000000050.0;
y = 100000000000040.0;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
x = 0.000000000000050;
y = 0.000000000000040;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == true)
std::cout << std::endl;
x = 0.000000000000500;
y = 0.000000000000400;
a = h3d::AllowableLimit<double>::make(x, y);
std::printf("x=%31.15f\ny=%31.15f\nd=%31.15f\na=%31.15f\n", x, y, std::fabs(x - y), a);
ASSERT(h3d::nearEqual<double>(x, y) == false)
std::cout << std::endl;
}
catch (const std::string& msg) {
std::cerr << msg << std::endl;
return 1;
}
return 0;
}
bmFtZXNwYWNlIGgzZCB7CiAgICAvKioKCSAqIOioseWuueiqpOW3ruOCr+ODqeOCueOAggoJICogZG91Ymxl5Z6L44Gn44Gv44CB5q+U6LyD44GZ44KL5LqM44Gk44Gu5pWw44Gu5oyH5pWw6YOo44GoMTAyM+OBruOBhuOBoeacgOWkp+OBruOCguOBruOCknjjgajjgZnjgovjgajjgIEKCSAqIOioseWuueiqpOW3ruOBryAyXih4IC0g57K+5bqmIC0gMTAyMykg44Go44Gq44KL44CCCgkgKiBAcGFyYW0gRiDmta7li5XlsI/mlbDngrnmlbAoRmxvYXRpbmctcG9pbnQgbnVtYmVyKeOBruWei+OAggoJICovCgl0ZW1wbGF0ZTx0eXBlbmFtZSBGPiAKCWNsYXNzIEFsbG93YWJsZUxpbWl0IHsKCXB1YmxpYzoKCQkvKiog57K+5bqmKFByZUNpU2lvbinjgIIgKi8KCQlzdGF0aWMgaW50IHBjczsKCQkKCQkvKioKCQkgKiDmta7li5XlsI/mlbDngrnmlbDjga7mjIfmlbDpg6jjgpLlj5blvpfjgZnjgovjgIIKCQkgKiBAcGFyYW0gbnVtIOaVsChOVU1iZXIp44CCCgkJICogQHJldHVybiDlj5blvpfjgZfjgZ/mjIfmlbDpg6jjgIIKCQkgKi8KCQlpbmxpbmUgc3RhdGljIGludCBleHBvbmVudChjb25zdCBGJiBudW0pOwoJCQoJCS8qKgoJCSAqIOS6jOOBpOOBrua1ruWLleWwj+aVsOeCueaVsOOBruioseWuueiqpOW3ruOCkuS9nOOCi+OAggoJCSAqIEBwYXJhbSBsaHMg5bem5YG0KExlZnQgSGFuZCBTaWRlKeOBruaVsOOAggoJCSAqIEBwYXJhbSByaHMg5Y+z5YG0KFJpZ2h0IEhhbmQgU2lkZSnjga7mlbDjgIIKCQkgKiBAcmV0dXJuIOS9nOOBo+OBn+ioseWuueiqpOW3ruOAggoJCSAqLwoJCWlubGluZSBzdGF0aWMgRiBtYWtlKGNvbnN0IEYmIGxocywgY29uc3QgRiYgcmhzKTsKCX07CgkKCS8qKgoJICog5LqM44Gk44Gu5pWw44GM6L+R44GE44GL44Gp44GG44GL44KS5Yik5a6a44GZ44KL44CCCgkgKiBAcGFyYW0gWCDmlbDjga7lnovjgIIKCSAqIEBwYXJhbSBsaHMg5bem5YG0KExlZnQgSGFuZCBTaWRlKeOBruaVsOOAggoJICogQHBhcmFtIHJocyDlj7PlgbQoUmlnaHQgSGFuZCBTaWRlKeOBruaVsOOAggoJICogQHJldHVybiDov5HjgYTjgarjgol0cnVlLCDpgaDjgYTjgarjgolmYWxzZeOAggoJICovCgl0ZW1wbGF0ZTx0eXBlbmFtZSBYPiAKCWlubGluZSBib29sIG5lYXJFcXVhbChjb25zdCBYJiBsaHMsIGNvbnN0IFgmIHJocyk7Cn07CgojaW5jbHVkZSA8aW9tYW5pcD4KI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RyaW5nPgoKI2RlZmluZSBBU1NFUlQocHJlZCkgaDNkOjphc3NlcnQoI3ByZWQsIChwcmVkKSk7CgojZGVmaW5lIFBSSU5UKHZhbCkgY291dCA8PCAjdmFsIDw8ICI9IiA8PCAodmFsKSA8PCBlbmRsOwoKbmFtZXNwYWNlIGgzZCB7Cgl1c2luZyBzdGQ6OmNvdXQ7Cgl1c2luZyBzdGQ6OmRlYzsKCXVzaW5nIHN0ZDo6ZW5kbDsKCXVzaW5nIHN0ZDo6aGV4OwoJdXNpbmcgc3RkOjpzZXRmaWxsOwoJdXNpbmcgc3RkOjpzZXR3OwoJdXNpbmcgc3RkOjpzZXRwcmVjaXNpb247Cgl1c2luZyBzdGQ6OnN0cmluZzsKCQoJLyoqCgkgKiDjgqLjgrXjg7zjgrfjg6fjg7PjgpLlrp/ooYzjgZnjgovjgIIKCSAqIOaIkOWKn+OBquOCieaomea6luWHuuWKm+OBq+e1kOaenOOCkuWHuuWKm+OBmeOCi+OAggoJICogQHBhcmFtIHByZWRfc3RyIOWIpOWumuOBmeOCi+i/sOiqnihQUkVEaWNhdGUp44KS6KiY6L+w44GX44Gf5paH5a2X5YiXKFNUUmluZynjgIIKCSAqIEBwYXJhbSBwcmVkX3JlcyDliKTlrprjgZnjgovov7Doqp4oUFJFRGljYXRlKeOBrue1kOaenChSRVN1bHQp44CCCgkgKiB0cnVl44Gq44KJ5oiQ5Yqf77yMZmFsc2XjgarjgonlpLHmlZfjgajliKTlrprjgZnjgovjgIIKCSAqIEB0aHJvdyBzdHJpbmcg5aSx5pWX44Gq44KJ44Oh44OD44K744O844K444KS44K544Ot44O844GZ44KL44CCCgkgKi8KCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKTsKfTsKCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vCgojaW5jbHVkZSA8YWxnb3JpdGhtPgojaW5jbHVkZSA8Y21hdGg+CgpuYW1lc3BhY2UgaDNkIHsKCXVzaW5nIHN0ZDo6ZmFiczsKCXVzaW5nIHN0ZDo6bWF4OwoJCgl0ZW1wbGF0ZTw+IAoJaW50IEFsbG93YWJsZUxpbWl0PGRvdWJsZT46OnBjcyA9IDQ4OwoJCgl0ZW1wbGF0ZTw+IAoJaW5saW5lIGludCBBbGxvd2FibGVMaW1pdDxkb3VibGU+OjpleHBvbmVudChjb25zdCBkb3VibGUmIG51bSkgewoJCXJldHVybiAoaW50KSgqKHVuc2lnbmVkIGxvbmcgbG9uZyopJm51bSA+PiA1MikgJiAoKDEgPDwgMTEpIC0gMSk7Cgl9CgkKCXRlbXBsYXRlPD4gCglpbmxpbmUgZG91YmxlIEFsbG93YWJsZUxpbWl0PGRvdWJsZT46Om1ha2UoY29uc3QgZG91YmxlJiBsaHMsIGNvbnN0IGRvdWJsZSYgcmhzKSB7CgkJdW5zaWduZWQgbG9uZyBsb25nIGFsd19sbXQgPSBtYXgobWF4KGV4cG9uZW50KGxocyksIGV4cG9uZW50KHJocykpLCAxMDIzKSAtIHBjczsKCQlyZXR1cm4gKihkb3VibGUqKSYoYWx3X2xtdCA8PD0gNTIpOwoJfQoJCgl0ZW1wbGF0ZTx0eXBlbmFtZSBYPiAKCWlubGluZSBib29sIG5lYXJFcXVhbChjb25zdCBYJiBsaHMsIGNvbnN0IFgmIHJocykgeyByZXR1cm4gbGhzID09IHJoczsgfQoJCgl0ZW1wbGF0ZTw+IAoJaW5saW5lIGJvb2wgbmVhckVxdWFsPGRvdWJsZT4oY29uc3QgZG91YmxlJiBsaHMsIGNvbnN0IGRvdWJsZSYgcmhzKSB7CgkJcmV0dXJuIGZhYnMobGhzIC0gcmhzKSA8PSBBbGxvd2FibGVMaW1pdDxkb3VibGU+OjptYWtlKGxocywgcmhzKTsKCX0KfTsKCiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPHN0cmluZz4KCm5hbWVzcGFjZSBoM2QgewoJdXNpbmcgc3RkOjpjb3V0OwoJdXNpbmcgc3RkOjplbmRsOwoJdXNpbmcgc3RkOjpzdHJpbmc7CgkKCWlubGluZSB2b2lkIGFzc2VydChjb25zdCBzdHJpbmcmIHByZWRfc3RyLCBjb25zdCBib29sJiBwcmVkX3JlcykgdGhyb3coc3RyaW5nKSB7CgkJaWYgKHByZWRfcmVzKSBjb3V0IDw8ICLjgqLjgrXjg7zjg4jmiJDlip86ICIgPDwgcHJlZF9zdHIgPDwgZW5kbDsKCQllbHNlIHRocm93ICLjgqLjgrXjg7zjg4jlpLHmlZc6ICIgKyBwcmVkX3N0cjsKCX0KfTsKCiNpbmNsdWRlIDxjbWF0aD4KI2luY2x1ZGUgPGNzdGRpbz4KI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RyaW5nPgoKaW50IG1haW4oKSB7Cgl0cnkgewoJCWRvdWJsZSB4LCB5LCBhOwoJCXN0ZDo6Y291dCA8PCAieOOBqHk6IOavlOi8g+OBmeOCi+S6jOOBpOOBruaVsCwgZDogeOOBqHnjga7lt67jga7ntbblr77lgKQsIGE6IOioseWuueiqpOW3riIgPDwgc3RkOjplbmRsOwoJCQoJCWgzZDo6QWxsb3dhYmxlTGltaXQ8ZG91YmxlPjo6cGNzID0gNDg7CgkJc3RkOjpjb3V0IDw8ICLoqLHlrrnoqqTlt67jga7nsr7luqY6ICIgPDwgaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjpwY3MgPDwgc3RkOjplbmRsOwoJCXN0ZDo6Y291dCA8PCBzdGQ6OmVuZGw7CgkJCgkJeCA9IDEuMDAwMDAwMDAwMDAwMDA1OwoJCXkgPSAxLjAwMDAwMDAwMDAwMDAwNDsKCQlhID0gaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjptYWtlKHgsIHkpOwoJCXN0ZDo6cHJpbnRmKCJ4PSUzMS4xNWZcbnk9JTMxLjE1ZlxuZD0lMzEuMTVmXG5hPSUzMS4xNWZcbiIsIHgsIHksIHN0ZDo6ZmFicyh4IC0geSksIGEpOwoJCUFTU0VSVChoM2Q6Om5lYXJFcXVhbDxkb3VibGU+KHgsIHkpID09IHRydWUpCgkJc3RkOjpjb3V0IDw8IHN0ZDo6ZW5kbDsKCQkKCQl4ID0gMS4wMDAwMDAwMDAwMDAwNTA7CgkJeSA9IDEuMDAwMDAwMDAwMDAwMDQwOwoJCWEgPSBoM2Q6OkFsbG93YWJsZUxpbWl0PGRvdWJsZT46Om1ha2UoeCwgeSk7CgkJc3RkOjpwcmludGYoIng9JTMxLjE1ZlxueT0lMzEuMTVmXG5kPSUzMS4xNWZcbmE9JTMxLjE1ZlxuIiwgeCwgeSwgc3RkOjpmYWJzKHggLSB5KSwgYSk7CgkJQVNTRVJUKGgzZDo6bmVhckVxdWFsPGRvdWJsZT4oeCwgeSkgPT0gZmFsc2UpCgkJc3RkOjpjb3V0IDw8IHN0ZDo6ZW5kbDsKCQkKCQl4ID0gMTAwMDAwMDAwMDAwMDAwLjU7CgkJeSA9IDEwMDAwMDAwMDAwMDAwMC40OwoJCWEgPSBoM2Q6OkFsbG93YWJsZUxpbWl0PGRvdWJsZT46Om1ha2UoeCwgeSk7CgkJc3RkOjpwcmludGYoIng9JTMxLjE1ZlxueT0lMzEuMTVmXG5kPSUzMS4xNWZcbmE9JTMxLjE1ZlxuIiwgeCwgeSwgc3RkOjpmYWJzKHggLSB5KSwgYSk7CgkJQVNTRVJUKGgzZDo6bmVhckVxdWFsPGRvdWJsZT4oeCwgeSkgPT0gdHJ1ZSkKCQlzdGQ6OmNvdXQgPDwgc3RkOjplbmRsOwoJCQoJCXggPSAxMDAwMDAwMDAwMDAwMDUuMDsKCQl5ID0gMTAwMDAwMDAwMDAwMDA0LjA7CgkJYSA9IGgzZDo6QWxsb3dhYmxlTGltaXQ8ZG91YmxlPjo6bWFrZSh4LCB5KTsKCQlzdGQ6OnByaW50ZigieD0lMzEuMTVmXG55PSUzMS4xNWZcbmQ9JTMxLjE1ZlxuYT0lMzEuMTVmXG4iLCB4LCB5LCBzdGQ6OmZhYnMoeCAtIHkpLCBhKTsKCQlBU1NFUlQoaDNkOjpuZWFyRXF1YWw8ZG91YmxlPih4LCB5KSA9PSBmYWxzZSkKCQlzdGQ6OmNvdXQgPDwgc3RkOjplbmRsOwoJCQoJCXggPSAwLjAwMDAwMDAwMDAwMDAwNTsKCQl5ID0gMC4wMDAwMDAwMDAwMDAwMDQ7CgkJYSA9IGgzZDo6QWxsb3dhYmxlTGltaXQ8ZG91YmxlPjo6bWFrZSh4LCB5KTsKCQlzdGQ6OnByaW50ZigieD0lMzEuMTVmXG55PSUzMS4xNWZcbmQ9JTMxLjE1ZlxuYT0lMzEuMTVmXG4iLCB4LCB5LCBzdGQ6OmZhYnMoeCAtIHkpLCBhKTsKCQlBU1NFUlQoaDNkOjpuZWFyRXF1YWw8ZG91YmxlPih4LCB5KSA9PSB0cnVlKQoJCXN0ZDo6Y291dCA8PCBzdGQ6OmVuZGw7CgkJCgkJeCA9IDAuMDAwMDAwMDAwMDAwMDUwOwoJCXkgPSAwLjAwMDAwMDAwMDAwMDA0MDsKCQlhID0gaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjptYWtlKHgsIHkpOwoJCXN0ZDo6cHJpbnRmKCJ4PSUzMS4xNWZcbnk9JTMxLjE1ZlxuZD0lMzEuMTVmXG5hPSUzMS4xNWZcbiIsIHgsIHksIHN0ZDo6ZmFicyh4IC0geSksIGEpOwoJCUFTU0VSVChoM2Q6Om5lYXJFcXVhbDxkb3VibGU+KHgsIHkpID09IGZhbHNlKQoJCXN0ZDo6Y291dCA8PCBzdGQ6OmVuZGw7CgkJCgkJaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjpwY3MgPSA0NjsKCQlzdGQ6OmNvdXQgPDwgIuioseWuueiqpOW3ruOBrueyvuW6pjogIiA8PCBoM2Q6OkFsbG93YWJsZUxpbWl0PGRvdWJsZT46OnBjcyA8PCBzdGQ6OmVuZGw7CgkJc3RkOjpjb3V0IDw8IHN0ZDo6ZW5kbDsKCQkKCQl4ID0gMS4wMDAwMDAwMDAwMDAwNTA7CgkJeSA9IDEuMDAwMDAwMDAwMDAwMDQwOwoJCWEgPSBoM2Q6OkFsbG93YWJsZUxpbWl0PGRvdWJsZT46Om1ha2UoeCwgeSk7CgkJc3RkOjpwcmludGYoIng9JTMxLjE1ZlxueT0lMzEuMTVmXG5kPSUzMS4xNWZcbmE9JTMxLjE1ZlxuIiwgeCwgeSwgc3RkOjpmYWJzKHggLSB5KSwgYSk7CgkJQVNTRVJUKGgzZDo6bmVhckVxdWFsPGRvdWJsZT4oeCwgeSkgPT0gdHJ1ZSkKCQlzdGQ6OmNvdXQgPDwgc3RkOjplbmRsOwoJCQoJCXggPSAxLjAwMDAwMDAwMDAwMDUwMDsKCQl5ID0gMS4wMDAwMDAwMDAwMDA0MDA7CgkJYSA9IGgzZDo6QWxsb3dhYmxlTGltaXQ8ZG91YmxlPjo6bWFrZSh4LCB5KTsKCQlzdGQ6OnByaW50ZigieD0lMzEuMTVmXG55PSUzMS4xNWZcbmQ9JTMxLjE1ZlxuYT0lMzEuMTVmXG4iLCB4LCB5LCBzdGQ6OmZhYnMoeCAtIHkpLCBhKTsKCQlBU1NFUlQoaDNkOjpuZWFyRXF1YWw8ZG91YmxlPih4LCB5KSA9PSBmYWxzZSkKCQlzdGQ6OmNvdXQgPDwgc3RkOjplbmRsOwoJCQoJCXggPSAxMDAwMDAwMDAwMDAwMDUuMDsKCQl5ID0gMTAwMDAwMDAwMDAwMDA0LjA7CgkJYSA9IGgzZDo6QWxsb3dhYmxlTGltaXQ8ZG91YmxlPjo6bWFrZSh4LCB5KTsKCQlzdGQ6OnByaW50ZigieD0lMzEuMTVmXG55PSUzMS4xNWZcbmQ9JTMxLjE1ZlxuYT0lMzEuMTVmXG4iLCB4LCB5LCBzdGQ6OmZhYnMoeCAtIHkpLCBhKTsKCQlBU1NFUlQoaDNkOjpuZWFyRXF1YWw8ZG91YmxlPih4LCB5KSA9PSB0cnVlKQoJCXN0ZDo6Y291dCA8PCBzdGQ6OmVuZGw7CgkJCgkJeCA9IDEwMDAwMDAwMDAwMDA1MC4wOwoJCXkgPSAxMDAwMDAwMDAwMDAwNDAuMDsKCQlhID0gaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjptYWtlKHgsIHkpOwoJCXN0ZDo6cHJpbnRmKCJ4PSUzMS4xNWZcbnk9JTMxLjE1ZlxuZD0lMzEuMTVmXG5hPSUzMS4xNWZcbiIsIHgsIHksIHN0ZDo6ZmFicyh4IC0geSksIGEpOwoJCUFTU0VSVChoM2Q6Om5lYXJFcXVhbDxkb3VibGU+KHgsIHkpID09IGZhbHNlKQoJCXN0ZDo6Y291dCA8PCBzdGQ6OmVuZGw7CgkJCgkJeCA9IDAuMDAwMDAwMDAwMDAwMDUwOwoJCXkgPSAwLjAwMDAwMDAwMDAwMDA0MDsKCQlhID0gaDNkOjpBbGxvd2FibGVMaW1pdDxkb3VibGU+OjptYWtlKHgsIHkpOwoJCXN0ZDo6cHJpbnRmKCJ4PSUzMS4xNWZcbnk9JTMxLjE1ZlxuZD0lMzEuMTVmXG5hPSUzMS4xNWZcbiIsIHgsIHksIHN0ZDo6ZmFicyh4IC0geSksIGEpOwoJCUFTU0VSVChoM2Q6Om5lYXJFcXVhbDxkb3VibGU+KHgsIHkpID09IHRydWUpCgkJc3RkOjpjb3V0IDw8IHN0ZDo6ZW5kbDsKCQkKCQl4ID0gMC4wMDAwMDAwMDAwMDA1MDA7CgkJeSA9IDAuMDAwMDAwMDAwMDAwNDAwOwoJCWEgPSBoM2Q6OkFsbG93YWJsZUxpbWl0PGRvdWJsZT46Om1ha2UoeCwgeSk7CgkJc3RkOjpwcmludGYoIng9JTMxLjE1ZlxueT0lMzEuMTVmXG5kPSUzMS4xNWZcbmE9JTMxLjE1ZlxuIiwgeCwgeSwgc3RkOjpmYWJzKHggLSB5KSwgYSk7CgkJQVNTRVJUKGgzZDo6bmVhckVxdWFsPGRvdWJsZT4oeCwgeSkgPT0gZmFsc2UpCgkJc3RkOjpjb3V0IDw8IHN0ZDo6ZW5kbDsKCX0KCWNhdGNoIChjb25zdCBzdGQ6OnN0cmluZyYgbXNnKSB7CgkJc3RkOjpjZXJyIDw8IG1zZyA8PCBzdGQ6OmVuZGw7CgkJcmV0dXJuIDE7Cgl9CglyZXR1cm4gMDsKfQo=