// vim: set et fenc=utf-8 :
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define UNICODE 1
#define _UNICODE 1
#define OEMRESOURCE
#include <windows.h>
#include <intrin.h>
#define BOOST_PARAMETER_MAX_ARITY 15
#include <boost/parameter.hpp>
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <boost/exception/all.hpp>
#include <boost/utility.hpp>
#include <string>
#include <iostream>
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#define SBOK_ASSERT(x) \
do { if (!(x)) { std::cerr << "Assert failed: " << #x << " at " << __FILE__ << ":" << __LINE__ << std::endl; __debugbreak(); } } while (false)
#if !defined(UNICODE) || !defined(_UNICODE) || _UNICODE != 1 || UNICODE != 1
# error Springbok requires UNICODE to be set.
#endif
namespace detail {
typedef boost::error_info<struct tag_error_msg, std::string> error_msg;
typedef boost::error_info<struct tag_error_code, int> error_code;
template <typename Exception>
Exception make_error(boost::optional<DWORD> code = boost::none) {
DWORD real_code = (code ? *code : GetLastError());
std::string message;
LPSTR buffer;
DWORD result = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
nullptr, real_code, 0, reinterpret_cast<LPSTR>(&buffer), 0, nullptr
);
if (result) {
message = buffer;
LocalFree(buffer);
} else {
message = "<FormatMessage failed>";
}
return Exception() << error_msg(message) << error_code(real_code);
}
}
struct base_error : virtual boost::exception, virtual std::exception {};
struct windowing_error : virtual base_error {};
#define SBOK_THROW_WIN(exc) BOOST_THROW_EXCEPTION(detail::make_error<exc>())
namespace window_class {
enum styles {
enable_double_clicks = CS_DBLCLKS,
redraw_on_width_change = CS_HREDRAW,
redraw_on_height_change = CS_VREDRAW,
no_close_button = CS_NOCLOSE,
use_shared_context = CS_CLASSDC,
use_unique_context = CS_OWNDC,
use_parent_context = CS_PARENTDC,
no_style = 0
};
namespace keywords {
BOOST_PARAMETER_NAME(class_name)
BOOST_PARAMETER_NAME(class_style)
BOOST_PARAMETER_NAME(icon)
BOOST_PARAMETER_NAME(cursor)
BOOST_PARAMETER_NAME(background)
BOOST_PARAMETER_NAME(menu_resource)
BOOST_PARAMETER_NAME(small_icon)
}
};
namespace detail {
LRESULT CALLBACK dispatch_wndproc(HWND, UINT, WPARAM, LPARAM);
typedef boost::variant<WORD, std::wstring> resource_id;
struct resource_id_visitor : boost::static_visitor<>, private boost::noncopyable {
mutable LPCWSTR& resource_id;
resource_id_visitor(LPCWSTR& resource_id) : resource_id(resource_id) {
}
void operator()(const WORD& int_id) const {
resource_id = MAKEINTRESOURCE(int_id);
}
void operator()(const std::wstring& str_id) const {
resource_id = str_id.c_str();
}
};
}
namespace detail {
struct window_impl;
struct window_class_impl {
window_class_impl(
const std::wstring& class_name, window_class::styles class_style, HICON icon, boost::optional<HCURSOR> cursor,
boost::optional<HBRUSH> background, const detail::resource_id& menu_resource, HICON small_icon
) : name(class_name), atom(), class_info()
{
SecureZeroMemory(&class_info, sizeof class_info);
HCURSOR use_cursor;
HBRUSH use_background;
if (cursor) {
use_cursor = *cursor;
} else {
HANDLE arrow_cursor = LoadImage(
class_info.hInstance, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0,
LR_DEFAULTSIZE | LR_SHARED
);
if (!arrow_cursor) {
SBOK_THROW_WIN(windowing_error);
}
use_cursor = static_cast<HCURSOR>(arrow_cursor);
}
if (background) {
use_background = *background;
} else {
use_background = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
}
class_info.cbSize = sizeof class_info;
class_info.style = static_cast<UINT>(class_style);
class_info.lpfnWndProc = detail::dispatch_wndproc;
class_info.cbClsExtra = 0;
class_info.cbWndExtra = sizeof (window_impl*);
class_info.hInstance = GetModuleHandle(nullptr);
class_info.hIcon = icon;
class_info.hCursor = use_cursor;
class_info.hbrBackground = use_background;
class_info.lpszClassName = class_name.c_str();
class_info.hIconSm = small_icon;
boost::apply_visitor(detail::resource_id_visitor(class_info.lpszMenuName), menu_resource);
atom = RegisterClassEx(&class_info);
if (!atom) {
SBOK_THROW_WIN(windowing_error);
}
}
std::wstring get_name() const {
return name;
}
private:
std::wstring name;
ATOM atom;
WNDCLASSEX class_info;
};
}
namespace window_position {
struct specific {
explicit specific(int x = CW_USEDEFAULT, int y = 0) : x(x), y(y) {
}
int x, y;
};
struct centered {};
};
namespace window {
enum extended_styles {
no_extended_style = 0,
accept_files = WS_EX_ACCEPTFILES,
is_app_window = WS_EX_APPWINDOW,
has_client_edge = WS_EX_CLIENTEDGE,
is_composited = WS_EX_COMPOSITED,
has_context_help = WS_EX_CONTEXTHELP,
is_control_parent = WS_EX_CONTROLPARENT,
has_modal_frame = WS_EX_DLGMODALFRAME,
is_layered = WS_EX_LAYERED,
is_mdi_child = WS_EX_MDICHILD,
no_inherited_layout = WS_EX_NOINHERITLAYOUT,
no_parent_notify = WS_EX_NOPARENTNOTIFY,
is_overlapped_ex = WS_EX_OVERLAPPEDWINDOW,
has_static_edge = WS_EX_STATICEDGE,
is_topmost = WS_EX_TOPMOST,
has_window_edge = WS_EX_WINDOWEDGE
};
enum styles {
default_style = WS_OVERLAPPED,
has_border = WS_BORDER,
has_title_bar = WS_CAPTION,
is_child = WS_CHILD,
is_disabled = WS_DISABLED,
has_dialog_frame = WS_DLGFRAME,
has_horiz_scrollbar = WS_HSCROLL,
create_minimised = WS_MINIMIZE,
create_maximised = WS_MAXIMIZE,
has_maximise_button = WS_MAXIMIZEBOX,
has_minimise_button = WS_MINIMIZEBOX,
is_overlapped = WS_OVERLAPPEDWINDOW,
has_sizing_border = WS_SIZEBOX,
has_system_menu = WS_SYSMENU,
create_visible = WS_VISIBLE,
has_vert_scrollbar = WS_VSCROLL
};
namespace keywords {
BOOST_PARAMETER_NAME(window_class)
BOOST_PARAMETER_NAME(extended_style)
BOOST_PARAMETER_NAME(name)
BOOST_PARAMETER_NAME(style)
BOOST_PARAMETER_NAME(position)
BOOST_PARAMETER_NAME(width)
BOOST_PARAMETER_NAME(height)
BOOST_PARAMETER_NAME(parent)
BOOST_PARAMETER_NAME(menu)
}
}
namespace detail {
typedef boost::variant<window_position::specific, window_position::centered> window_position_variant;
// called to get x, y for CreateWindowEx
struct position_create_visitor : boost::static_visitor<>, private boost::noncopyable {
mutable int &x, &y;
int width, height;
position_create_visitor(int& x, int& y, int width, int height) : x(x), y(y), width(width), height(height) {}
void operator()(const window_position::specific& pos) const {
x = pos.x;
y = pos.y;
}
void operator()(const window_position::centered&) const {
RECT rect;
GetClientRect(GetDesktopWindow(), &rect);
x = (rect.right / 2) - (width / 2);
y = (rect.bottom / 2) - (height / 2);
}
};
struct unhandled {};
typedef boost::variant<unhandled, LRESULT> process_result;
// wraps a window
struct window_impl {
window_impl(
const window_class_impl& window_class, window::extended_styles extended_style, const std::wstring& name,
window::styles style, const window_position_variant& position, int width, int height, HWND parent, HMENU menu
) : handle()
{
int x, y;
boost::apply_visitor(position_create_visitor(x, y, width, height), position);
std::wstring class_name = window_class.get_name();
handle = CreateWindowEx(
extended_style, class_name.c_str(), name.c_str(), style,
x, y, width, height, parent, menu, GetModuleHandle(nullptr),
nullptr
);
if (handle == INVALID_HANDLE_VALUE) {
SBOK_THROW_WIN(windowing_error);
}
SetWindowLongPtr(handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
UpdateWindow(handle);
}
process_result process_message(UINT, WPARAM, LPARAM) {
return unhandled();
}
HWND handle;
};
}
// factory functions
BOOST_PARAMETER_FUNCTION(
(detail::window_class_impl), create_window_class, window_class::keywords::tag,
(required
(class_name, *)
)
(optional
(class_style, *, window_class::no_style)
(icon, *, nullptr )
(cursor, *, boost::none )
(background, *, boost::none )
(menu_resource, *, L"" )
(small_icon, *, nullptr )
)
) {
return detail::window_class_impl(class_name, class_style, icon, cursor, background, menu_resource, small_icon);
}
BOOST_PARAMETER_FUNCTION(
(detail::window_impl), create_window, window::keywords::tag,
(required
(window_class, *)
)
(optional
(extended_style, *, window::is_overlapped_ex)
(name, *, L"")
(style, *, static_cast<window::styles>(window::default_style | window::create_visible))
(position, *, window_position::centered())
(width, *, 800)
(height, *, 600)
(parent, *, nullptr)
(menu, *, nullptr)
)
) {
return detail::window_impl(window_class, extended_style, name, style, position, width, height, parent, menu);
}
namespace detail {
struct dispatch_visitor : boost::static_visitor<LRESULT>, private boost::noncopyable {
const HWND& handle;
const UINT& message;
const WPARAM& w;
const LPARAM& l;
dispatch_visitor(const HWND& handle, const UINT& message, const WPARAM& w, const LPARAM& l)
: handle(handle), message(message), w(w), l(l) {}
LRESULT operator()(const LRESULT& result) const {
return result;
}
LRESULT operator()(const unhandled&) const {
return DefWindowProc(handle, message, w, l);
}
};
// delegates the message to process_msg member function inside window instance
LRESULT CALLBACK dispatch_wndproc(HWND handle, UINT message, WPARAM w, LPARAM l) {
LONG_PTR self = GetWindowLongPtr(handle, GWLP_USERDATA);
//SBOK_ASSERT(self != 0);
auto real_wndproc = &window_impl::process_message;
auto real_handle = reinterpret_cast<window_impl*>(self);
detail::process_result result = (real_handle->*real_wndproc)(message, w, l);
return boost::apply_visitor(dispatch_visitor(handle, message, w, l), result);
}
}
int main() {
using namespace window_class::keywords;
using namespace window::keywords;
try {
auto cls = create_window_class(_class_name = L"hello world", _class_style = window_class::no_style);
auto wnd = create_window(_window_class = cls, _name = L"ohai");
int result = 0;
MSG msg;
while ((result = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (result == -1) {
SBOK_THROW_WIN(windowing_error);
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} catch (const std::exception& e) {
std::cerr << boost::diagnostic_information(e) << std::endl;
}
return 0;
}
Ly8gdmltOiBzZXQgZXQgZmVuYz11dGYtOCA6CiNkZWZpbmUgV0lOMzJfTEVBTl9BTkRfTUVBTgojZGVmaW5lIE5PTUlOTUFYCiNkZWZpbmUgVU5JQ09ERSAxCiNkZWZpbmUgX1VOSUNPREUgMQojZGVmaW5lIE9FTVJFU09VUkNFCiNpbmNsdWRlIDx3aW5kb3dzLmg+CiNpbmNsdWRlIDxpbnRyaW4uaD4KI2RlZmluZSBCT09TVF9QQVJBTUVURVJfTUFYX0FSSVRZIDE1CiNpbmNsdWRlIDxib29zdC9wYXJhbWV0ZXIuaHBwPgojaW5jbHVkZSA8Ym9vc3Qvb3B0aW9uYWwuaHBwPgojaW5jbHVkZSA8Ym9vc3QvdmFyaWFudC5ocHA+CiNpbmNsdWRlIDxib29zdC9leGNlcHRpb24vYWxsLmhwcD4KI2luY2x1ZGUgPGJvb3N0L3V0aWxpdHkuaHBwPgojaW5jbHVkZSA8c3RyaW5nPgojaW5jbHVkZSA8aW9zdHJlYW0+CgojcHJhZ21hIGNvbW1lbnQobGliLCAidXNlcjMyLmxpYiIpCiNwcmFnbWEgY29tbWVudChsaWIsICJnZGkzMi5saWIiKQoKI2RlZmluZSBTQk9LX0FTU0VSVCh4KSBcCiAgICBkbyB7IGlmICghKHgpKSB7IHN0ZDo6Y2VyciA8PCAiQXNzZXJ0IGZhaWxlZDogIiA8PCAjeCA8PCAiIGF0ICIgPDwgX19GSUxFX18gPDwgIjoiIDw8IF9fTElORV9fIDw8IHN0ZDo6ZW5kbDsgX19kZWJ1Z2JyZWFrKCk7IH0gfSB3aGlsZSAoZmFsc2UpCgojaWYgIWRlZmluZWQoVU5JQ09ERSkgfHwgIWRlZmluZWQoX1VOSUNPREUpIHx8IF9VTklDT0RFICE9IDEgfHwgVU5JQ09ERSAhPSAxCiMgICBlcnJvciBTcHJpbmdib2sgcmVxdWlyZXMgVU5JQ09ERSB0byBiZSBzZXQuCiNlbmRpZgoKbmFtZXNwYWNlIGRldGFpbCB7CiAgICB0eXBlZGVmIGJvb3N0OjplcnJvcl9pbmZvPHN0cnVjdCB0YWdfZXJyb3JfbXNnLCAgc3RkOjpzdHJpbmc+IGVycm9yX21zZzsKICAgIHR5cGVkZWYgYm9vc3Q6OmVycm9yX2luZm88c3RydWN0IHRhZ19lcnJvcl9jb2RlLCBpbnQ+ICAgICAgICAgZXJyb3JfY29kZTsKICAgIAogICAgdGVtcGxhdGUgPHR5cGVuYW1lIEV4Y2VwdGlvbj4KICAgIEV4Y2VwdGlvbiBtYWtlX2Vycm9yKGJvb3N0OjpvcHRpb25hbDxEV09SRD4gY29kZSA9IGJvb3N0Ojpub25lKSB7CiAgICAgICAgRFdPUkQgcmVhbF9jb2RlID0gKGNvZGUgPyAqY29kZSA6IEdldExhc3RFcnJvcigpKTsKICAgICAgICBzdGQ6OnN0cmluZyBtZXNzYWdlOwogICAgICAgIAogICAgICAgIExQU1RSIGJ1ZmZlcjsKICAgICAgICBEV09SRCByZXN1bHQgPSBGb3JtYXRNZXNzYWdlQSgKICAgICAgICAgICAgRk9STUFUX01FU1NBR0VfQUxMT0NBVEVfQlVGRkVSIHwgRk9STUFUX01FU1NBR0VfRlJPTV9TWVNURU0sCiAgICAgICAgICAgIG51bGxwdHIsIHJlYWxfY29kZSwgMCwgcmVpbnRlcnByZXRfY2FzdDxMUFNUUj4oJmJ1ZmZlciksIDAsIG51bGxwdHIKICAgICAgICApOwogICAgICAgIAogICAgICAgIGlmIChyZXN1bHQpIHsKICAgICAgICAgICAgbWVzc2FnZSA9IGJ1ZmZlcjsKICAgICAgICAgICAgTG9jYWxGcmVlKGJ1ZmZlcik7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgbWVzc2FnZSA9ICI8Rm9ybWF0TWVzc2FnZSBmYWlsZWQ+IjsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgcmV0dXJuIEV4Y2VwdGlvbigpIDw8IGVycm9yX21zZyhtZXNzYWdlKSA8PCBlcnJvcl9jb2RlKHJlYWxfY29kZSk7CiAgICB9Cn0KCnN0cnVjdCBiYXNlX2Vycm9yIDogdmlydHVhbCBib29zdDo6ZXhjZXB0aW9uLCB2aXJ0dWFsIHN0ZDo6ZXhjZXB0aW9uIHt9OwpzdHJ1Y3Qgd2luZG93aW5nX2Vycm9yIDogdmlydHVhbCBiYXNlX2Vycm9yIHt9OwoKI2RlZmluZSBTQk9LX1RIUk9XX1dJTihleGMpIEJPT1NUX1RIUk9XX0VYQ0VQVElPTihkZXRhaWw6Om1ha2VfZXJyb3I8ZXhjPigpKQoKbmFtZXNwYWNlIHdpbmRvd19jbGFzcyB7CiAgICBlbnVtIHN0eWxlcyB7CiAgICAgICAgZW5hYmxlX2RvdWJsZV9jbGlja3MgICAgPSBDU19EQkxDTEtTLAogICAgICAgIHJlZHJhd19vbl93aWR0aF9jaGFuZ2UgID0gQ1NfSFJFRFJBVywKICAgICAgICByZWRyYXdfb25faGVpZ2h0X2NoYW5nZSA9IENTX1ZSRURSQVcsCiAgICAgICAgbm9fY2xvc2VfYnV0dG9uICAgICAgICAgPSBDU19OT0NMT1NFLAogICAgICAgIHVzZV9zaGFyZWRfY29udGV4dCAgICAgID0gQ1NfQ0xBU1NEQywKICAgICAgICB1c2VfdW5pcXVlX2NvbnRleHQgICAgICA9IENTX09XTkRDLAogICAgICAgIHVzZV9wYXJlbnRfY29udGV4dCAgICAgID0gQ1NfUEFSRU5UREMsCiAgICAgICAgbm9fc3R5bGUgICAgICAgICAgICAgICAgPSAwCiAgICB9OwogICAgCiAgICBuYW1lc3BhY2Uga2V5d29yZHMgewogICAgICAgIEJPT1NUX1BBUkFNRVRFUl9OQU1FKGNsYXNzX25hbWUpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUoY2xhc3Nfc3R5bGUpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUoaWNvbikKICAgICAgICBCT09TVF9QQVJBTUVURVJfTkFNRShjdXJzb3IpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUoYmFja2dyb3VuZCkKICAgICAgICBCT09TVF9QQVJBTUVURVJfTkFNRShtZW51X3Jlc291cmNlKQogICAgICAgIEJPT1NUX1BBUkFNRVRFUl9OQU1FKHNtYWxsX2ljb24pCiAgICB9Cn07CgpuYW1lc3BhY2UgZGV0YWlsIHsKICAgIExSRVNVTFQgQ0FMTEJBQ0sgZGlzcGF0Y2hfd25kcHJvYyhIV05ELCBVSU5ULCBXUEFSQU0sIExQQVJBTSk7CiAgICB0eXBlZGVmIGJvb3N0Ojp2YXJpYW50PFdPUkQsIHN0ZDo6d3N0cmluZz4gcmVzb3VyY2VfaWQ7CiAgICAKICAgIHN0cnVjdCByZXNvdXJjZV9pZF92aXNpdG9yIDogYm9vc3Q6OnN0YXRpY192aXNpdG9yPD4sIHByaXZhdGUgYm9vc3Q6Om5vbmNvcHlhYmxlIHsKICAgICAgICBtdXRhYmxlIExQQ1dTVFImIHJlc291cmNlX2lkOwogICAgICAgIAogICAgICAgIHJlc291cmNlX2lkX3Zpc2l0b3IoTFBDV1NUUiYgcmVzb3VyY2VfaWQpIDogcmVzb3VyY2VfaWQocmVzb3VyY2VfaWQpIHsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgdm9pZCBvcGVyYXRvcigpKGNvbnN0IFdPUkQmIGludF9pZCkgY29uc3QgewogICAgICAgICAgICByZXNvdXJjZV9pZCA9IE1BS0VJTlRSRVNPVVJDRShpbnRfaWQpOwogICAgICAgIH0KICAgICAgICAKICAgICAgICB2b2lkIG9wZXJhdG9yKCkoY29uc3Qgc3RkOjp3c3RyaW5nJiBzdHJfaWQpIGNvbnN0IHsKICAgICAgICAgICAgcmVzb3VyY2VfaWQgPSBzdHJfaWQuY19zdHIoKTsKICAgICAgICB9CiAgICB9Owp9CgpuYW1lc3BhY2UgZGV0YWlsIHsKICAgIHN0cnVjdCB3aW5kb3dfaW1wbDsKICAgIAogICAgc3RydWN0IHdpbmRvd19jbGFzc19pbXBsIHsKICAgICAgICB3aW5kb3dfY2xhc3NfaW1wbCgKICAgICAgICAgICAgY29uc3Qgc3RkOjp3c3RyaW5nJiBjbGFzc19uYW1lLCB3aW5kb3dfY2xhc3M6OnN0eWxlcyBjbGFzc19zdHlsZSwgSElDT04gaWNvbiwgYm9vc3Q6Om9wdGlvbmFsPEhDVVJTT1I+IGN1cnNvciwKICAgICAgICAgICAgYm9vc3Q6Om9wdGlvbmFsPEhCUlVTSD4gYmFja2dyb3VuZCwgY29uc3QgZGV0YWlsOjpyZXNvdXJjZV9pZCYgbWVudV9yZXNvdXJjZSwgSElDT04gc21hbGxfaWNvbgogICAgICAgICkgOiBuYW1lKGNsYXNzX25hbWUpLCBhdG9tKCksIGNsYXNzX2luZm8oKQogICAgICAgIHsKICAgICAgICAgICAgU2VjdXJlWmVyb01lbW9yeSgmY2xhc3NfaW5mbywgc2l6ZW9mIGNsYXNzX2luZm8pOwogICAgICAgICAgICAKICAgICAgICAgICAgSENVUlNPUiB1c2VfY3Vyc29yOwogICAgICAgICAgICBIQlJVU0ggIHVzZV9iYWNrZ3JvdW5kOwogICAgICAgICAgICAKICAgICAgICAgICAgaWYgKGN1cnNvcikgewogICAgICAgICAgICAgICAgdXNlX2N1cnNvciA9ICpjdXJzb3I7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICBIQU5ETEUgYXJyb3dfY3Vyc29yID0gTG9hZEltYWdlKAogICAgICAgICAgICAgICAgICAgIGNsYXNzX2luZm8uaEluc3RhbmNlLCBNQUtFSU5UUkVTT1VSQ0UoT0NSX05PUk1BTCksIElNQUdFX0NVUlNPUiwgMCwgMCwKICAgICAgICAgICAgICAgICAgICBMUl9ERUZBVUxUU0laRSB8IExSX1NIQVJFRAogICAgICAgICAgICAgICAgKTsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgaWYgKCFhcnJvd19jdXJzb3IpIHsKICAgICAgICAgICAgICAgICAgICBTQk9LX1RIUk9XX1dJTih3aW5kb3dpbmdfZXJyb3IpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICB1c2VfY3Vyc29yID0gc3RhdGljX2Nhc3Q8SENVUlNPUj4oYXJyb3dfY3Vyc29yKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgaWYgKGJhY2tncm91bmQpIHsKICAgICAgICAgICAgICAgIHVzZV9iYWNrZ3JvdW5kID0gKmJhY2tncm91bmQ7CiAgICAgICAgICAgIH0gZWxzZSB7CiAgICAgICAgICAgICAgICB1c2VfYmFja2dyb3VuZCA9IHN0YXRpY19jYXN0PEhCUlVTSD4oR2V0U3RvY2tPYmplY3QoQkxBQ0tfQlJVU0gpKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgY2xhc3NfaW5mby5jYlNpemUgICAgICAgID0gc2l6ZW9mIGNsYXNzX2luZm87CiAgICAgICAgICAgIGNsYXNzX2luZm8uc3R5bGUgICAgICAgICA9IHN0YXRpY19jYXN0PFVJTlQ+KGNsYXNzX3N0eWxlKTsKICAgICAgICAgICAgY2xhc3NfaW5mby5scGZuV25kUHJvYyAgID0gZGV0YWlsOjpkaXNwYXRjaF93bmRwcm9jOwogICAgICAgICAgICBjbGFzc19pbmZvLmNiQ2xzRXh0cmEgICAgPSAwOwogICAgICAgICAgICBjbGFzc19pbmZvLmNiV25kRXh0cmEgICAgPSBzaXplb2YgKHdpbmRvd19pbXBsKik7CiAgICAgICAgICAgIGNsYXNzX2luZm8uaEluc3RhbmNlICAgICA9IEdldE1vZHVsZUhhbmRsZShudWxscHRyKTsKICAgICAgICAgICAgY2xhc3NfaW5mby5oSWNvbiAgICAgICAgID0gaWNvbjsKICAgICAgICAgICAgY2xhc3NfaW5mby5oQ3Vyc29yICAgICAgID0gdXNlX2N1cnNvcjsKICAgICAgICAgICAgY2xhc3NfaW5mby5oYnJCYWNrZ3JvdW5kID0gdXNlX2JhY2tncm91bmQ7CiAgICAgICAgICAgIGNsYXNzX2luZm8ubHBzekNsYXNzTmFtZSA9IGNsYXNzX25hbWUuY19zdHIoKTsKICAgICAgICAgICAgY2xhc3NfaW5mby5oSWNvblNtICAgICAgID0gc21hbGxfaWNvbjsKICAgICAgICAgICAgCiAgICAgICAgICAgIGJvb3N0OjphcHBseV92aXNpdG9yKGRldGFpbDo6cmVzb3VyY2VfaWRfdmlzaXRvcihjbGFzc19pbmZvLmxwc3pNZW51TmFtZSksIG1lbnVfcmVzb3VyY2UpOwogICAgICAgICAgICAKICAgICAgICAgICAgYXRvbSA9IFJlZ2lzdGVyQ2xhc3NFeCgmY2xhc3NfaW5mbyk7CiAgICAgICAgICAgIGlmICghYXRvbSkgewogICAgICAgICAgICAgICAgU0JPS19USFJPV19XSU4od2luZG93aW5nX2Vycm9yKTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICAKICAgICAgICBzdGQ6OndzdHJpbmcgZ2V0X25hbWUoKSBjb25zdCB7CiAgICAgICAgICAgIHJldHVybiBuYW1lOwogICAgICAgIH0KICAgIHByaXZhdGU6CiAgICAgICAgc3RkOjp3c3RyaW5nIG5hbWU7CiAgICAgICAgQVRPTSBhdG9tOwogICAgICAgIFdORENMQVNTRVggY2xhc3NfaW5mbzsKICAgIH07Cn0KCm5hbWVzcGFjZSB3aW5kb3dfcG9zaXRpb24gewogICAgc3RydWN0IHNwZWNpZmljIHsKICAgICAgICBleHBsaWNpdCBzcGVjaWZpYyhpbnQgeCA9IENXX1VTRURFRkFVTFQsIGludCB5ID0gMCkgOiB4KHgpLCB5KHkpIHsKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgaW50IHgsIHk7CiAgICB9OwogICAgCiAgICBzdHJ1Y3QgY2VudGVyZWQge307Cn07CgpuYW1lc3BhY2Ugd2luZG93IHsKICAgIGVudW0gZXh0ZW5kZWRfc3R5bGVzIHsKICAgICAgICBub19leHRlbmRlZF9zdHlsZSAgID0gMCwKICAgICAgICBhY2NlcHRfZmlsZXMgICAgICAgID0gV1NfRVhfQUNDRVBURklMRVMsCiAgICAgICAgaXNfYXBwX3dpbmRvdyAgICAgICA9IFdTX0VYX0FQUFdJTkRPVywKICAgICAgICBoYXNfY2xpZW50X2VkZ2UgICAgID0gV1NfRVhfQ0xJRU5URURHRSwKICAgICAgICBpc19jb21wb3NpdGVkICAgICAgID0gV1NfRVhfQ09NUE9TSVRFRCwKICAgICAgICBoYXNfY29udGV4dF9oZWxwICAgID0gV1NfRVhfQ09OVEVYVEhFTFAsCiAgICAgICAgaXNfY29udHJvbF9wYXJlbnQgICA9IFdTX0VYX0NPTlRST0xQQVJFTlQsCiAgICAgICAgaGFzX21vZGFsX2ZyYW1lICAgICA9IFdTX0VYX0RMR01PREFMRlJBTUUsCiAgICAgICAgaXNfbGF5ZXJlZCAgICAgICAgICA9IFdTX0VYX0xBWUVSRUQsCiAgICAgICAgaXNfbWRpX2NoaWxkICAgICAgICA9IFdTX0VYX01ESUNISUxELAogICAgICAgIG5vX2luaGVyaXRlZF9sYXlvdXQgPSBXU19FWF9OT0lOSEVSSVRMQVlPVVQsCiAgICAgICAgbm9fcGFyZW50X25vdGlmeSAgICA9IFdTX0VYX05PUEFSRU5UTk9USUZZLAogICAgICAgIGlzX292ZXJsYXBwZWRfZXggICAgPSBXU19FWF9PVkVSTEFQUEVEV0lORE9XLAogICAgICAgIGhhc19zdGF0aWNfZWRnZSAgICAgPSBXU19FWF9TVEFUSUNFREdFLAogICAgICAgIGlzX3RvcG1vc3QgICAgICAgICAgPSBXU19FWF9UT1BNT1NULAogICAgICAgIGhhc193aW5kb3dfZWRnZSAgICAgPSBXU19FWF9XSU5ET1dFREdFCiAgICB9OwogICAgCiAgICBlbnVtIHN0eWxlcyB7CiAgICAgICAgZGVmYXVsdF9zdHlsZSAgICAgICA9IFdTX09WRVJMQVBQRUQsCiAgICAgICAgaGFzX2JvcmRlciAgICAgICAgICA9IFdTX0JPUkRFUiwKICAgICAgICBoYXNfdGl0bGVfYmFyICAgICAgID0gV1NfQ0FQVElPTiwKICAgICAgICBpc19jaGlsZCAgICAgICAgICAgID0gV1NfQ0hJTEQsCiAgICAgICAgaXNfZGlzYWJsZWQgICAgICAgICA9IFdTX0RJU0FCTEVELAogICAgICAgIGhhc19kaWFsb2dfZnJhbWUgICAgPSBXU19ETEdGUkFNRSwKICAgICAgICBoYXNfaG9yaXpfc2Nyb2xsYmFyID0gV1NfSFNDUk9MTCwKICAgICAgICBjcmVhdGVfbWluaW1pc2VkICAgID0gV1NfTUlOSU1JWkUsCiAgICAgICAgY3JlYXRlX21heGltaXNlZCAgICA9IFdTX01BWElNSVpFLAogICAgICAgIGhhc19tYXhpbWlzZV9idXR0b24gPSBXU19NQVhJTUlaRUJPWCwKICAgICAgICBoYXNfbWluaW1pc2VfYnV0dG9uID0gV1NfTUlOSU1JWkVCT1gsCiAgICAgICAgaXNfb3ZlcmxhcHBlZCAgICAgICA9IFdTX09WRVJMQVBQRURXSU5ET1csCiAgICAgICAgaGFzX3NpemluZ19ib3JkZXIgICA9IFdTX1NJWkVCT1gsCiAgICAgICAgaGFzX3N5c3RlbV9tZW51ICAgICA9IFdTX1NZU01FTlUsCiAgICAgICAgY3JlYXRlX3Zpc2libGUgICAgICA9IFdTX1ZJU0lCTEUsCiAgICAgICAgaGFzX3ZlcnRfc2Nyb2xsYmFyICA9IFdTX1ZTQ1JPTEwKICAgIH07CiAgICAKICAgIG5hbWVzcGFjZSBrZXl3b3JkcyB7CiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUod2luZG93X2NsYXNzKQogICAgICAgIEJPT1NUX1BBUkFNRVRFUl9OQU1FKGV4dGVuZGVkX3N0eWxlKQogICAgICAgIEJPT1NUX1BBUkFNRVRFUl9OQU1FKG5hbWUpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUoc3R5bGUpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUocG9zaXRpb24pCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUod2lkdGgpCiAgICAgICAgQk9PU1RfUEFSQU1FVEVSX05BTUUoaGVpZ2h0KQogICAgICAgIEJPT1NUX1BBUkFNRVRFUl9OQU1FKHBhcmVudCkKICAgICAgICBCT09TVF9QQVJBTUVURVJfTkFNRShtZW51KQogICAgfQp9CgpuYW1lc3BhY2UgZGV0YWlsIHsKICAgIHR5cGVkZWYgYm9vc3Q6OnZhcmlhbnQ8d2luZG93X3Bvc2l0aW9uOjpzcGVjaWZpYywgd2luZG93X3Bvc2l0aW9uOjpjZW50ZXJlZD4gd2luZG93X3Bvc2l0aW9uX3ZhcmlhbnQ7CiAgICAKICAgIC8vIGNhbGxlZCB0byBnZXQgeCwgeSBmb3IgQ3JlYXRlV2luZG93RXgKICAgIHN0cnVjdCBwb3NpdGlvbl9jcmVhdGVfdmlzaXRvciA6IGJvb3N0OjpzdGF0aWNfdmlzaXRvcjw+LCBwcml2YXRlIGJvb3N0Ojpub25jb3B5YWJsZSB7CiAgICAgICAgbXV0YWJsZSBpbnQgJngsICZ5OwogICAgICAgIGludCB3aWR0aCwgaGVpZ2h0OwogICAgICAgIHBvc2l0aW9uX2NyZWF0ZV92aXNpdG9yKGludCYgeCwgaW50JiB5LCBpbnQgd2lkdGgsIGludCBoZWlnaHQpIDogeCh4KSwgeSh5KSwgd2lkdGgod2lkdGgpLCBoZWlnaHQoaGVpZ2h0KSB7fQogICAgICAgIAogICAgICAgIHZvaWQgb3BlcmF0b3IoKShjb25zdCB3aW5kb3dfcG9zaXRpb246OnNwZWNpZmljJiBwb3MpIGNvbnN0IHsKICAgICAgICAgICAgeCA9IHBvcy54OwogICAgICAgICAgICB5ID0gcG9zLnk7CiAgICAgICAgfQogICAgICAgIAogICAgICAgIHZvaWQgb3BlcmF0b3IoKShjb25zdCB3aW5kb3dfcG9zaXRpb246OmNlbnRlcmVkJikgY29uc3QgewogICAgICAgICAgICBSRUNUIHJlY3Q7CiAgICAgICAgICAgIEdldENsaWVudFJlY3QoR2V0RGVza3RvcFdpbmRvdygpLCAmcmVjdCk7CiAgICAgICAgICAgIAogICAgICAgICAgICB4ID0gKHJlY3QucmlnaHQgIC8gMikgLSAod2lkdGggIC8gMik7CiAgICAgICAgICAgIHkgPSAocmVjdC5ib3R0b20gLyAyKSAtIChoZWlnaHQgLyAyKTsKICAgICAgICB9CiAgICB9OwogICAgCiAgICBzdHJ1Y3QgdW5oYW5kbGVkIHt9OwogICAgCiAgICB0eXBlZGVmIGJvb3N0Ojp2YXJpYW50PHVuaGFuZGxlZCwgTFJFU1VMVD4gcHJvY2Vzc19yZXN1bHQ7CiAgICAKICAgIC8vIHdyYXBzIGEgd2luZG93CiAgICBzdHJ1Y3Qgd2luZG93X2ltcGwgewogICAgICAgIHdpbmRvd19pbXBsKAogICAgICAgICAgICBjb25zdCB3aW5kb3dfY2xhc3NfaW1wbCYgd2luZG93X2NsYXNzLCB3aW5kb3c6OmV4dGVuZGVkX3N0eWxlcyBleHRlbmRlZF9zdHlsZSwgY29uc3Qgc3RkOjp3c3RyaW5nJiBuYW1lLAogICAgICAgICAgICB3aW5kb3c6OnN0eWxlcyBzdHlsZSwgY29uc3Qgd2luZG93X3Bvc2l0aW9uX3ZhcmlhbnQmIHBvc2l0aW9uLCBpbnQgd2lkdGgsIGludCBoZWlnaHQsIEhXTkQgcGFyZW50LCBITUVOVSBtZW51CiAgICAgICAgKSA6IGhhbmRsZSgpCiAgICAgICAgewogICAgICAgICAgICBpbnQgeCwgeTsKICAgICAgICAgICAgYm9vc3Q6OmFwcGx5X3Zpc2l0b3IocG9zaXRpb25fY3JlYXRlX3Zpc2l0b3IoeCwgeSwgd2lkdGgsIGhlaWdodCksIHBvc2l0aW9uKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHN0ZDo6d3N0cmluZyBjbGFzc19uYW1lID0gd2luZG93X2NsYXNzLmdldF9uYW1lKCk7CiAgICAgICAgICAgIGhhbmRsZSA9IENyZWF0ZVdpbmRvd0V4KAogICAgICAgICAgICAgICAgZXh0ZW5kZWRfc3R5bGUsIGNsYXNzX25hbWUuY19zdHIoKSwgbmFtZS5jX3N0cigpLCBzdHlsZSwKICAgICAgICAgICAgICAgIHgsIHksIHdpZHRoLCBoZWlnaHQsIHBhcmVudCwgbWVudSwgR2V0TW9kdWxlSGFuZGxlKG51bGxwdHIpLAogICAgICAgICAgICAgICAgbnVsbHB0cgogICAgICAgICAgICApOwogICAgICAgICAgICAKICAgICAgICAgICAgaWYgKGhhbmRsZSA9PSBJTlZBTElEX0hBTkRMRV9WQUxVRSkgewogICAgICAgICAgICAgICAgU0JPS19USFJPV19XSU4od2luZG93aW5nX2Vycm9yKTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgU2V0V2luZG93TG9uZ1B0cihoYW5kbGUsIEdXTFBfVVNFUkRBVEEsIHJlaW50ZXJwcmV0X2Nhc3Q8TE9OR19QVFI+KHRoaXMpKTsKICAgICAgICAgICAgVXBkYXRlV2luZG93KGhhbmRsZSk7CiAgICAgICAgfQogICAgICAgIAogICAgICAgIHByb2Nlc3NfcmVzdWx0IHByb2Nlc3NfbWVzc2FnZShVSU5ULCBXUEFSQU0sIExQQVJBTSkgewogICAgICAgICAgICByZXR1cm4gdW5oYW5kbGVkKCk7CiAgICAgICAgfQogICAgICAgIAogICAgICAgIEhXTkQgaGFuZGxlOwogICAgfTsKfQoKLy8gZmFjdG9yeSBmdW5jdGlvbnMKCkJPT1NUX1BBUkFNRVRFUl9GVU5DVElPTigKICAgIChkZXRhaWw6OndpbmRvd19jbGFzc19pbXBsKSwgY3JlYXRlX3dpbmRvd19jbGFzcywgd2luZG93X2NsYXNzOjprZXl3b3Jkczo6dGFnLAogICAgKHJlcXVpcmVkCiAgICAgICAgKGNsYXNzX25hbWUsICopCiAgICApCiAgICAob3B0aW9uYWwKICAgICAgICAoY2xhc3Nfc3R5bGUsICAgICAqLCB3aW5kb3dfY2xhc3M6Om5vX3N0eWxlKQogICAgICAgIChpY29uLCAgICAgICAgICAgICosIG51bGxwdHIgICAgICAgICAgICAgICApCiAgICAgICAgKGN1cnNvciwgICAgICAgICAgKiwgYm9vc3Q6Om5vbmUgICAgICAgICAgICkKICAgICAgICAoYmFja2dyb3VuZCwgICAgICAqLCBib29zdDo6bm9uZSAgICAgICAgICAgKQogICAgICAgIChtZW51X3Jlc291cmNlLCAgICosIEwiIiAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKHNtYWxsX2ljb24sICAgICAgKiwgbnVsbHB0ciAgICAgICAgICAgICAgICkKICAgICkKKSB7CiAgICByZXR1cm4gZGV0YWlsOjp3aW5kb3dfY2xhc3NfaW1wbChjbGFzc19uYW1lLCBjbGFzc19zdHlsZSwgaWNvbiwgY3Vyc29yLCBiYWNrZ3JvdW5kLCBtZW51X3Jlc291cmNlLCBzbWFsbF9pY29uKTsKfQoKQk9PU1RfUEFSQU1FVEVSX0ZVTkNUSU9OKAogICAgKGRldGFpbDo6d2luZG93X2ltcGwpLCBjcmVhdGVfd2luZG93LCB3aW5kb3c6OmtleXdvcmRzOjp0YWcsCiAgICAocmVxdWlyZWQKICAgICAgICAod2luZG93X2NsYXNzLCAqKQogICAgKQogICAgKG9wdGlvbmFsCiAgICAgICAgKGV4dGVuZGVkX3N0eWxlLCAqLCB3aW5kb3c6OmlzX292ZXJsYXBwZWRfZXgpCiAgICAgICAgKG5hbWUsICAgICAgICAgICAqLCBMIiIpCiAgICAgICAgKHN0eWxlLCAgICAgICAgICAqLCBzdGF0aWNfY2FzdDx3aW5kb3c6OnN0eWxlcz4od2luZG93OjpkZWZhdWx0X3N0eWxlIHwgd2luZG93OjpjcmVhdGVfdmlzaWJsZSkpCiAgICAgICAgKHBvc2l0aW9uLCAgICAgICAqLCB3aW5kb3dfcG9zaXRpb246OmNlbnRlcmVkKCkpCiAgICAgICAgKHdpZHRoLCAgICAgICAgICAqLCA4MDApCiAgICAgICAgKGhlaWdodCwgICAgICAgICAqLCA2MDApCiAgICAgICAgKHBhcmVudCwgICAgICAgICAqLCBudWxscHRyKQogICAgICAgIChtZW51LCAgICAgICAgICAgKiwgbnVsbHB0cikKICAgICkKKSB7CiAgICByZXR1cm4gZGV0YWlsOjp3aW5kb3dfaW1wbCh3aW5kb3dfY2xhc3MsIGV4dGVuZGVkX3N0eWxlLCBuYW1lLCBzdHlsZSwgcG9zaXRpb24sIHdpZHRoLCBoZWlnaHQsIHBhcmVudCwgbWVudSk7Cn0KCm5hbWVzcGFjZSBkZXRhaWwgewogICAgc3RydWN0IGRpc3BhdGNoX3Zpc2l0b3IgOiBib29zdDo6c3RhdGljX3Zpc2l0b3I8TFJFU1VMVD4sIHByaXZhdGUgYm9vc3Q6Om5vbmNvcHlhYmxlIHsKICAgICAgICBjb25zdCBIV05EJiBoYW5kbGU7CiAgICAgICAgY29uc3QgVUlOVCYgbWVzc2FnZTsKICAgICAgICBjb25zdCBXUEFSQU0mIHc7CiAgICAgICAgY29uc3QgTFBBUkFNJiBsOwogICAgICAgIAogICAgICAgIGRpc3BhdGNoX3Zpc2l0b3IoY29uc3QgSFdORCYgaGFuZGxlLCBjb25zdCBVSU5UJiBtZXNzYWdlLCBjb25zdCBXUEFSQU0mIHcsIGNvbnN0IExQQVJBTSYgbCkKICAgICAgICA6IGhhbmRsZShoYW5kbGUpLCBtZXNzYWdlKG1lc3NhZ2UpLCB3KHcpLCBsKGwpIHt9CiAgICAgICAgCiAgICAgICAgTFJFU1VMVCBvcGVyYXRvcigpKGNvbnN0IExSRVNVTFQmIHJlc3VsdCkgY29uc3QgewogICAgICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgICAgIH0KICAgICAgICAKICAgICAgICBMUkVTVUxUIG9wZXJhdG9yKCkoY29uc3QgdW5oYW5kbGVkJikgY29uc3QgewogICAgICAgICAgICByZXR1cm4gRGVmV2luZG93UHJvYyhoYW5kbGUsIG1lc3NhZ2UsIHcsIGwpOwogICAgICAgIH0KICAgIH07CiAgICAKICAgIC8vIGRlbGVnYXRlcyB0aGUgbWVzc2FnZSB0byBwcm9jZXNzX21zZyBtZW1iZXIgZnVuY3Rpb24gaW5zaWRlIHdpbmRvdyBpbnN0YW5jZQogICAgTFJFU1VMVCBDQUxMQkFDSyBkaXNwYXRjaF93bmRwcm9jKEhXTkQgaGFuZGxlLCBVSU5UIG1lc3NhZ2UsIFdQQVJBTSB3LCBMUEFSQU0gbCkgewogICAgICAgIExPTkdfUFRSIHNlbGYgPSBHZXRXaW5kb3dMb25nUHRyKGhhbmRsZSwgR1dMUF9VU0VSREFUQSk7CiAgICAgICAgLy9TQk9LX0FTU0VSVChzZWxmICE9IDApOwogICAgICAgIAogICAgICAgIGF1dG8gcmVhbF93bmRwcm9jID0gJndpbmRvd19pbXBsOjpwcm9jZXNzX21lc3NhZ2U7CiAgICAgICAgYXV0byByZWFsX2hhbmRsZSAgPSByZWludGVycHJldF9jYXN0PHdpbmRvd19pbXBsKj4oc2VsZik7CiAgICAgICAgCiAgICAgICAgZGV0YWlsOjpwcm9jZXNzX3Jlc3VsdCByZXN1bHQgPSAocmVhbF9oYW5kbGUtPipyZWFsX3duZHByb2MpKG1lc3NhZ2UsIHcsIGwpOwogICAgICAgIHJldHVybiBib29zdDo6YXBwbHlfdmlzaXRvcihkaXNwYXRjaF92aXNpdG9yKGhhbmRsZSwgbWVzc2FnZSwgdywgbCksIHJlc3VsdCk7CiAgICB9Cn0KCmludCBtYWluKCkgewogICAgdXNpbmcgbmFtZXNwYWNlIHdpbmRvd19jbGFzczo6a2V5d29yZHM7CiAgICB1c2luZyBuYW1lc3BhY2Ugd2luZG93OjprZXl3b3JkczsKICAgIAogICAgdHJ5IHsKICAgICAgICBhdXRvIGNscyA9IGNyZWF0ZV93aW5kb3dfY2xhc3MoX2NsYXNzX25hbWUgPSBMImhlbGxvIHdvcmxkIiwgX2NsYXNzX3N0eWxlID0gd2luZG93X2NsYXNzOjpub19zdHlsZSk7CiAgICAgICAgYXV0byB3bmQgPSBjcmVhdGVfd2luZG93KF93aW5kb3dfY2xhc3MgPSBjbHMsIF9uYW1lID0gTCJvaGFpIik7CiAgICAgICAgCiAgICAgICAgaW50IHJlc3VsdCA9IDA7CiAgICAgICAgTVNHIG1zZzsKICAgICAgICB3aGlsZSAoKHJlc3VsdCA9IEdldE1lc3NhZ2UoJm1zZywgTlVMTCwgMCwgMCkpICE9IDApIHsKICAgICAgICAgICAgaWYgKHJlc3VsdCA9PSAtMSkgewogICAgICAgICAgICAgICAgU0JPS19USFJPV19XSU4od2luZG93aW5nX2Vycm9yKTsKICAgICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgICAgIFRyYW5zbGF0ZU1lc3NhZ2UoJm1zZyk7CiAgICAgICAgICAgICAgICBEaXNwYXRjaE1lc3NhZ2UoJm1zZyk7CiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9IGNhdGNoIChjb25zdCBzdGQ6OmV4Y2VwdGlvbiYgZSkgewogICAgICAgIHN0ZDo6Y2VyciA8PCBib29zdDo6ZGlhZ25vc3RpY19pbmZvcm1hdGlvbihlKSA8PCBzdGQ6OmVuZGw7CiAgICB9CiAgICAKICAgIHJldHVybiAwOwp9Cg==