diff --git a/main-packr.go b/main-packr.go index 771f349..549aab9 100644 --- a/main-packr.go +++ b/main-packr.go @@ -9,9 +9,9 @@ import "github.com/gobuffalo/packr" // Go binary. You can use the "packr clean" command to clean up this, // and any other packr generated files. func init() { - packr.PackJSONBytes("./tmpl", "model.html", "\"e3tkZWZpbmUgIm1vZGVsIn19CnBhY2thZ2Uge3sgLlBhY2thZ2VOYW1lIH19CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBDb2RlIGdlbmVyYXRlZCBieSBtb2RlbGdlbiB8CnwgICAgICAgIERPIE5PVCBFRElULiAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIElmIHlvdSB3YW50IHRvIGV4dGVuZCBiZWhhdmlvdXI6Ci8vIFBsZWFzZSBjcmVhdGUgYSBjdXN0b20gZmlsZSBpbiB0aGlzIGRpcmVjdG9yeQovLyBhbmQgY3JlYXRlIHRoZSBtZXRob2RzIHRoZXJlIHRvIGF2b2lkIG92ZXJ3cml0aW5nIHlvdXIgY29kZSB3aXRoIHRoZSBnZW5lcmF0ZWQgb25lLgoKaW1wb3J0ICgKImZtdCIKImRhdGFiYXNlL3NxbCIKICAgIHt7IHJhbmdlICRrLCAkdjo9IC5Nb2RlbC5JbXBvcnRzIH19CiAgICAie3ska319IgogICAge3stIGVuZCB9fQopCgovLyB7ey5Nb2RlbC5OYW1lfX0gcmVwcmVzZW50cyBhIHJvdyBpbiB0aGUge3suTW9kZWwuVGFibGVOYW1lfX0gdGFibGUKdHlwZSB7ey5Nb2RlbC5OYW1lfX0gc3RydWN0IHsKICAgIHt7IHJhbmdlICRrLCAkdjo9IC5Nb2RlbC5GaWVsZHMgfX0KICAgICAgICB7eyAkdi5OYW1lIH19IHt7ICR2LlR5cGUgfX0gYGpzb246Int7JHYuQ29sdW1uTmFtZX19ImAKICAgIHt7LSBlbmQgfX0Kb2Zmc2V0IGludApsaW1pdCBpbnQKfQoKLy8gSW5zZXJ0IGEgbmV3IHt7Lk1vZGVsLk5hbWV9fSByb3cgaW4gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgSW5zZXJ0KGRiICpzcWwuREIpIChsYXN0SW5zZXJ0SUQgaW50NjQsIGVyciBlcnJvcikgewogICAgY29uc3Qgc3RtdCA9ICJJTlNFUlQgSU5UTyB7ey5Nb2RlbC5UYWJsZU5hbWV9fSAoe3suTW9kZWwuRmllbGRzIHwgaW5zZXJ0X2ZpZWxkc319KSBWQUxVRVMgKHt7Lk1vZGVsLkZpZWxkcyB8IGluc2VydF92YWx1ZXN9fSkiCiAgICByZXMsIGVyciA6PSBkYi5FeGVjKHN0bXQsIHt7IC4gfCBpbnNlcnRfYXJncyB9fSkKICAgIGlmIGVyciAhPSBuaWwgewogICAgICAgIHJldHVybiAwLCBlcnIKICAgIH0KICAgIHJldHVybiByZXMuTGFzdEluc2VydElkKCkKfQoKLy8gVXBkYXRlIGFuIGV4aXN0aW5nIHt7Lk1vZGVsLk5hbWV9fSByb3cgaW4gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlLgpmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIFVwZGF0ZShkYiAqc3FsLkRCLCBpZCBpbnQ2NCkgKGludDY0LCBlcnJvcikgewogICAgY29uc3Qgc3RtdCA9ICJVUERBVEUge3suTW9kZWwuVGFibGVOYW1lfX0gU0VUIHt7IC4gfCB1cGRhdGVfdmFsdWVzIH19IFdIRVJFIGlkID0gPyIKICAgIHJlc3VsdCwgZXJyIDo9IGRiLkV4ZWMoc3RtdCwge3sgLiB8IHVwZGF0ZV9hcmdzIH19ICwgaWQpCiAgICBpZiBlcnIgIT0gbmlsIHsKICAgICAgICByZXR1cm4gMCwgZXJyCiAgICB9CiAgICByZXR1cm4gcmVzdWx0LlJvd3NBZmZlY3RlZCgpCn0KCi8vIFVwc2VydCBpbnNlcnRzIGEgbmV3IHt7Lk1vZGVsLk5hbWV9fSByb3cgaW4gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCi8vIGlmIHRoZSB1bmlxdWUgY29uc3RyYWludHMgYXJlIG5vdCBmb3VuZCwgb3RoZXJ3aXNlIGl0IHVwZGF0ZXMgaXQuCmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgVXBzZXJ0KGRiICpzcWwuREIpIChsYXN0SW5zZXJ0SUQgaW50NjQsIGVyciBlcnJvcikgewogICAgY29uc3Qgc3RtdCA9ICJJTlNFUlQgSU5UTyB7ey5Nb2RlbC5UYWJsZU5hbWV9fSAoe3suTW9kZWwuRmllbGRzIHwgdXBzZXJ0X2ZpZWxkc319KSBWQUxVRVMgKHt7Lk1vZGVsLkZpZWxkcyB8IHVwc2VydF92YWx1ZXN9fSkgT04gRFVQTElDQVRFIEtFWSBVUERBVEUge3sgLiB8IHVwc2VydF9vbl9kdXBsaWNhdGUgfX0iCiAgICByZXMsIGVyciA6PSBkYi5FeGVjKHN0bXQsIHt7IC4gfCB1cHNlcnRfYXJncyB9fSkKICAgIGlmIGVyciAhPSBuaWwgewogICAgICAgIHJldHVybiAwLCBlcnIKICAgIH0KICAgIHJldHVybiByZXMuTGFzdEluc2VydElkKCkKfQoKLy8gRmluZCBhbiBleGlzdGluZyB7ey5Nb2RlbC5OYW1lfX0gcm93IGluIHRoZSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSB0YWJsZQpmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIEZpbmQoZGIgKnNxbC5EQiwgaWQgaW50NjQpIGVycm9yIHsKICAgIGNvbnN0IHN0bXQgPSAiU0VMRUNUICogRlJPTSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSBXSEVSRSBpZCA9ID8iCiAgICByb3cgOj0gZGIuUXVlcnlSb3coc3RtdCwgaWQpCiAgICByZXR1cm4gcm93LlNjYW4oe3sgLiB8IHNjYW5fZmllbGRzfX0pCn0KCi8vIExvYWQgYWxsLCBvciBhIHN1YnNldCBvZiB7ey5Nb2RlbC5OYW1lfX0gcm93cyBmcm9tIHRoZSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSB0YWJsZQpmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIExvYWQoZGIgKnNxbC5EQikgKHNldCBbXXt7Lk1vZGVsLk5hbWV9fSwgZXJyIGVycm9yKSB7CiAgICBzdG10IDo9ICJTRUxFQ1QgKiBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IgoKICAgIGlmIHt7LlJlY2VpdmVyfX0ubGltaXQgPT0gMCAmJiB7ey5SZWNlaXZlcn19Lm9mZnNldCA+IDAgewogICAgICAgIHJldHVybiBzZXQsIGZtdC5FcnJvcmYoImNhbm5vdCBxdWVyeSB3aXRoIG9mZnNldCBidXQgbm8gbGltaXQiKQogICAgfQoKICAgIGlmIHt7LlJlY2VpdmVyfX0ubGltaXQgPiAwIHsKICAgICAgICBzdG10ICs9IGZtdC5TcHJpbnRmKCIgTElNSVQgJWQiLCB7ey5SZWNlaXZlcn19LmxpbWl0KQogICAgfQogICAgaWYge3suUmVjZWl2ZXJ9fS5vZmZzZXQgPiAwIHsKICAgICAgICBzdG10ICs9IGZtdC5TcHJpbnRmKCIgT0ZGU0VUICVkIiwge3suUmVjZWl2ZXJ9fS5vZmZzZXQpCiAgICB9CiAgICBkZWZlciBmdW5jKCkgewogICAgICAgIHt7LlJlY2VpdmVyfX0ubGltaXQgPSAwCiAgICAgICAge3suUmVjZWl2ZXJ9fS5vZmZzZXQgPSAwCiAgICB9KCkKICAgIHJvd3MsIGVyciA6PSBkYi5RdWVyeShzdG10KQogICAgaWYgZXJyICE9IG5pbCB7CiAgICAgICAgcmV0dXJuCiAgICB9CiAgICBkZWZlciByb3dzLkNsb3NlKCkKICAgICAgICBmb3Igcm93cy5OZXh0KCkgewogICAgICAgICAgICBpZiBlcnIgPSByb3dzLlNjYW4oe3suIHwgc2Nhbl9maWVsZHN9fSk7IGVyciAhPSBuaWwgewogICAgICAgICAgICByZXR1cm4KICAgICAgICB9CiAgICAgICAgc2V0ID0gYXBwZW5kKHNldCwgKnt7LlJlY2VpdmVyfX0pCiAgICB9CgogICAgcmV0dXJuCn0KCi8vIERlbGV0ZSBhbiBleGlzdGluZyB7ey5Nb2RlbC5OYW1lfX0gcm93IGZyb20gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgRGVsZXRlKGRiICpzcWwuREIsIGlkIGludDY0KSAocm93c0FmZmVjdGVkIGludDY0LCBlcnIgZXJyb3IpIHsKICAgIGNvbnN0IHN0bXQgPSAiREVMRVRFIEZST00ge3suTW9kZWwuVGFibGVOYW1lfX0gV0hFUkUgaWQgPSA/IgogICAgcmVzdWx0LCBlcnIgOj0gZGIuRXhlYyhzdG10LCBpZCkKCWlmIGVyciAhPSBuaWwgewoJCXJldHVybgoJfQoKCXJldHVybiByZXN1bHQuUm93c0FmZmVjdGVkKCkKfQoKLy8gQ291bnQgdGhlIG51bWJlciBvZiByb3dzIGZyb20gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCmZ1bmMoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBDb3VudChkYiAqc3FsLkRCKSAoY291bnQgaW50NjQsIGVyciBlcnJvcikgewogICAgY29uc3Qgc3RtdCA9ICJTRUxFQ1QgQ09VTlQoKikgRlJPTSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSIKICAgIHJvdyA6PSBkYi5RdWVyeVJvdyhzdG10KQogICAgaWYgZXJyID0gcm93LlNjYW4oJmNvdW50KTsgZXJyICE9IG5pbCB7CiAgICAgICAgcmV0dXJuCiAgICB9CiAgICByZXR1cm4KfQoKLy8gRXhpc3RzIGNoZWNrcyBmb3IgdGhlIGl0ZW1zIGV4aXN0ZW5jZSBpbiB0aGUgZGF0YWJhc2UsIGJhc2VkIG9uIGl0J3MgaWQuCi8vIEFuIGVycm9yIHdpbGwgb25seSBiZSByZXR1cm5lZCBpZiBhIFNRTCByZWxhdGVkIGZhaWx1cmUgaGFwcGVucy4KLy8gSW4gYWxsIG90aGVyIGNhc2VzLCBhIGJvb2wgYW5kIG5pbCB3aWxsIHJldHVybi4KZnVuYyh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIEV4aXN0cyhkYiAqc3FsLkRCLCBpZCBpbnQ2NCkgKGV4aXN0cyBib29sLCBlcnIgZXJyb3IpIHsKICAgIGNvbnN0IHN0bXQgPSAiU0VMRUNUIEVYSVNUUyhTRUxFQ1QgMSBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IFdIRVJFIGlkID0gPyBMSU1JVCAxKSBBUyBgZXhpc3RzYCIKICAgIHZhciBjb3VudCBpbnQKICAgIHJvdyA6PSBkYi5RdWVyeVJvdyhzdG10LCBpZCkKICAgIGlmIGVyciA9IHJvdy5TY2FuKCZjb3VudCk7IGVyciAhPSBuaWwgewogICAgICAgIHJldHVybgogICAgfQogICAgcmV0dXJuIGNvdW50ID4gMCwgbmlsCn0KCi8vIFRhYmxlTmFtZSByZXR1cm5zIHRoZSB0YWJsZSBuYW1lCmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgVGFibGVOYW1lKCkgc3RyaW5nIHsKcmV0dXJuICJ7ey5Nb2RlbC5UYWJsZU5hbWV9fSIKfQoKLy8gU2V0TGltaXQgc2V0cyB0aGUgcXVlcnkgbGltaXQKZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBTZXRMaW1pdChsaW1pdCBpbnQpICp7ey5Nb2RlbC5OYW1lfX0gewp7ey5SZWNlaXZlcn19LmxpbWl0ID0gbGltaXQKcmV0dXJuIHt7LlJlY2VpdmVyfX0KfQoKLy8gU2V0T2Zmc2V0IHNldHMgdGhlIHF1ZXJ5IG9mZnNldApmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIFNldE9mZnNldChvZmZzZXQgaW50KSAqe3suTW9kZWwuTmFtZX19IHsKe3suUmVjZWl2ZXJ9fS5vZmZzZXQgPSBvZmZzZXQKcmV0dXJuIHt7LlJlY2VpdmVyfX0KfQp7e2VuZH19Cgo=\"") + packr.PackJSONBytes("./tmpl", "model.html", "\"e3tkZWZpbmUgIm1vZGVsIn19CnBhY2thZ2Uge3sgLlBhY2thZ2VOYW1lIH19CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBDb2RlIGdlbmVyYXRlZCBieSBtb2RlbGdlbiB8CnwgICAgICAgIERPIE5PVCBFRElULiAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIElmIHlvdSB3YW50IHRvIGV4dGVuZCBiZWhhdmlvdXI6Ci8vIFBsZWFzZSBjcmVhdGUgYSBjdXN0b20gZmlsZSBpbiB0aGlzIGRpcmVjdG9yeQovLyBhbmQgY3JlYXRlIHRoZSBtZXRob2RzIHRoZXJlIHRvIGF2b2lkIG92ZXJ3cml0aW5nIHlvdXIgY29kZSB3aXRoIHRoZSBnZW5lcmF0ZWQgb25lLgoKaW1wb3J0ICgKImZtdCIKICAgIHt7IHJhbmdlICRrLCAkdjo9IC5Nb2RlbC5JbXBvcnRzIH19CiAgICAie3ska319IgogICAge3stIGVuZCB9fQopCgovLyB7ey5Nb2RlbC5OYW1lfX0gcmVwcmVzZW50cyBhIHJvdyBpbiB0aGUge3suTW9kZWwuVGFibGVOYW1lfX0gdGFibGUKdHlwZSB7ey5Nb2RlbC5OYW1lfX0gc3RydWN0IHsKICAgIHt7IHJhbmdlICRrLCAkdjo9IC5Nb2RlbC5GaWVsZHMgfX0KICAgICAgICB7eyAkdi5OYW1lIH19IHt7ICR2LlR5cGUgfX0gYGpzb246Int7JHYuQ29sdW1uTmFtZX19ImAKICAgIHt7LSBlbmQgfX0Kb2Zmc2V0IGludApsaW1pdCBpbnQKfQoKLy8gSW5zZXJ0IGEgbmV3IHt7Lk1vZGVsLk5hbWV9fSByb3cgaW4gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgSW5zZXJ0KHEgUXVlcnllcikgKGxhc3RJbnNlcnRJRCBpbnQ2NCwgZXJyIGVycm9yKSB7CiAgICBjb25zdCBzdG10ID0gIklOU0VSVCBJTlRPIHt7Lk1vZGVsLlRhYmxlTmFtZX19ICh7ey5Nb2RlbC5GaWVsZHMgfCBpbnNlcnRfZmllbGRzfX0pIFZBTFVFUyAoe3suTW9kZWwuRmllbGRzIHwgaW5zZXJ0X3ZhbHVlc319KSIKICAgIHJlcywgZXJyIDo9IHEuRXhlYyhzdG10LCB7eyAuIHwgaW5zZXJ0X2FyZ3MgfX0pCiAgICBpZiBlcnIgIT0gbmlsIHsKICAgICAgICByZXR1cm4gMCwgZXJyCiAgICB9CiAgICByZXR1cm4gcmVzLkxhc3RJbnNlcnRJZCgpCn0KCi8vIFVwZGF0ZSBhbiBleGlzdGluZyB7ey5Nb2RlbC5OYW1lfX0gcm93IGluIHRoZSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSB0YWJsZS4KZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBVcGRhdGUocSBRdWVyeWVyLCBpZCBpbnQ2NCkgKGludDY0LCBlcnJvcikgewogICAgY29uc3Qgc3RtdCA9ICJVUERBVEUge3suTW9kZWwuVGFibGVOYW1lfX0gU0VUIHt7IC4gfCB1cGRhdGVfdmFsdWVzIH19IFdIRVJFIGlkID0gPyIKICAgIHJlc3VsdCwgZXJyIDo9IHEuRXhlYyhzdG10LCB7eyAuIHwgdXBkYXRlX2FyZ3MgfX0gLCBpZCkKICAgIGlmIGVyciAhPSBuaWwgewogICAgICAgIHJldHVybiAwLCBlcnIKICAgIH0KICAgIHJldHVybiByZXN1bHQuUm93c0FmZmVjdGVkKCkKfQoKLy8gVXBzZXJ0IGluc2VydHMgYSBuZXcge3suTW9kZWwuTmFtZX19IHJvdyBpbiB0aGUge3suTW9kZWwuVGFibGVOYW1lfX0gdGFibGUKLy8gaWYgdGhlIHVuaXF1ZSBjb25zdHJhaW50cyBhcmUgbm90IGZvdW5kLCBvdGhlcndpc2UgaXQgdXBkYXRlcyBpdC4KZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBVcHNlcnQocSBRdWVyeWVyKSAobGFzdEluc2VydElEIGludDY0LCBlcnIgZXJyb3IpIHsKICAgIGNvbnN0IHN0bXQgPSAiSU5TRVJUIElOVE8ge3suTW9kZWwuVGFibGVOYW1lfX0gKHt7Lk1vZGVsLkZpZWxkcyB8IHVwc2VydF9maWVsZHN9fSkgVkFMVUVTICh7ey5Nb2RlbC5GaWVsZHMgfCB1cHNlcnRfdmFsdWVzfX0pIE9OIERVUExJQ0FURSBLRVkgVVBEQVRFIHt7IC4gfCB1cHNlcnRfb25fZHVwbGljYXRlIH19IgogICAgcmVzLCBlcnIgOj0gcS5FeGVjKHN0bXQsIHt7IC4gfCB1cHNlcnRfYXJncyB9fSkKICAgIGlmIGVyciAhPSBuaWwgewogICAgICAgIHJldHVybiAwLCBlcnIKICAgIH0KICAgIHJldHVybiByZXMuTGFzdEluc2VydElkKCkKfQoKLy8gRmluZCBhbiBleGlzdGluZyB7ey5Nb2RlbC5OYW1lfX0gcm93IGluIHRoZSB7ey5Nb2RlbC5UYWJsZU5hbWV9fSB0YWJsZQpmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIEZpbmQocSBRdWVyeWVyLCBpZCBpbnQ2NCkgZXJyb3IgewogICAgY29uc3Qgc3RtdCA9ICJTRUxFQ1QgKiBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IFdIRVJFIGlkID0gPyIKICAgIHJvdyA6PSBxLlF1ZXJ5Um93KHN0bXQsIGlkKQogICAgcmV0dXJuIHJvdy5TY2FuKHt7IC4gfCBzY2FuX2ZpZWxkc319KQp9CgovLyBMb2FkIGFsbCwgb3IgYSBzdWJzZXQgb2Yge3suTW9kZWwuTmFtZX19IHJvd3MgZnJvbSB0aGUge3suTW9kZWwuVGFibGVOYW1lfX0gdGFibGUKZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBMb2FkKHEgUXVlcnllcikgKHNldCBbXXt7Lk1vZGVsLk5hbWV9fSwgZXJyIGVycm9yKSB7CiAgICBzdG10IDo9ICJTRUxFQ1QgKiBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IgoKICAgIGlmIHt7LlJlY2VpdmVyfX0ubGltaXQgPT0gMCAmJiB7ey5SZWNlaXZlcn19Lm9mZnNldCA+IDAgewogICAgICAgIHJldHVybiBzZXQsIGZtdC5FcnJvcmYoImNhbm5vdCBxdWVyeSB3aXRoIG9mZnNldCBidXQgbm8gbGltaXQiKQogICAgfQoKICAgIGlmIHt7LlJlY2VpdmVyfX0ubGltaXQgPiAwIHsKICAgICAgICBzdG10ICs9IGZtdC5TcHJpbnRmKCIgTElNSVQgJWQiLCB7ey5SZWNlaXZlcn19LmxpbWl0KQogICAgfQogICAgaWYge3suUmVjZWl2ZXJ9fS5vZmZzZXQgPiAwIHsKICAgICAgICBzdG10ICs9IGZtdC5TcHJpbnRmKCIgT0ZGU0VUICVkIiwge3suUmVjZWl2ZXJ9fS5vZmZzZXQpCiAgICB9CiAgICBkZWZlciBmdW5jKCkgewogICAgICAgIHt7LlJlY2VpdmVyfX0ubGltaXQgPSAwCiAgICAgICAge3suUmVjZWl2ZXJ9fS5vZmZzZXQgPSAwCiAgICB9KCkKICAgIHJvd3MsIGVyciA6PSBxLlF1ZXJ5KHN0bXQpCiAgICBpZiBlcnIgIT0gbmlsIHsKICAgICAgICByZXR1cm4KICAgIH0KICAgIGRlZmVyIHJvd3MuQ2xvc2UoKQogICAgICAgIGZvciByb3dzLk5leHQoKSB7CiAgICAgICAgICAgIGlmIGVyciA9IHJvd3MuU2Nhbih7ey4gfCBzY2FuX2ZpZWxkc319KTsgZXJyICE9IG5pbCB7CiAgICAgICAgICAgIHJldHVybgogICAgICAgIH0KICAgICAgICBzZXQgPSBhcHBlbmQoc2V0LCAqe3suUmVjZWl2ZXJ9fSkKICAgIH0KCiAgICByZXR1cm4KfQoKLy8gRGVsZXRlIGFuIGV4aXN0aW5nIHt7Lk1vZGVsLk5hbWV9fSByb3cgZnJvbSB0aGUge3suTW9kZWwuVGFibGVOYW1lfX0gdGFibGUKZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBEZWxldGUocSBRdWVyeWVyLCBpZCBpbnQ2NCkgKHJvd3NBZmZlY3RlZCBpbnQ2NCwgZXJyIGVycm9yKSB7CiAgICBjb25zdCBzdG10ID0gIkRFTEVURSBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IFdIRVJFIGlkID0gPyIKICAgIHJlc3VsdCwgZXJyIDo9IHEuRXhlYyhzdG10LCBpZCkKCWlmIGVyciAhPSBuaWwgewoJCXJldHVybgoJfQoKCXJldHVybiByZXN1bHQuUm93c0FmZmVjdGVkKCkKfQoKLy8gQ291bnQgdGhlIG51bWJlciBvZiByb3dzIGZyb20gdGhlIHt7Lk1vZGVsLlRhYmxlTmFtZX19IHRhYmxlCmZ1bmMoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBDb3VudChxIFF1ZXJ5ZXIpIChjb3VudCBpbnQ2NCwgZXJyIGVycm9yKSB7CiAgICBjb25zdCBzdG10ID0gIlNFTEVDVCBDT1VOVCgqKSBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IgogICAgcm93IDo9IHEuUXVlcnlSb3coc3RtdCkKICAgIGlmIGVyciA9IHJvdy5TY2FuKCZjb3VudCk7IGVyciAhPSBuaWwgewogICAgICAgIHJldHVybgogICAgfQogICAgcmV0dXJuCn0KCi8vIEV4aXN0cyBjaGVja3MgZm9yIHRoZSBpdGVtcyBleGlzdGVuY2UgaW4gdGhlIGRhdGFiYXNlLCBiYXNlZCBvbiBpdCdzIGlkLgovLyBBbiBlcnJvciB3aWxsIG9ubHkgYmUgcmV0dXJuZWQgaWYgYSBTUUwgcmVsYXRlZCBmYWlsdXJlIGhhcHBlbnMuCi8vIEluIGFsbCBvdGhlciBjYXNlcywgYSBib29sIGFuZCBuaWwgd2lsbCByZXR1cm4uCmZ1bmMoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBFeGlzdHMocSBRdWVyeWVyLCBpZCBpbnQ2NCkgKGV4aXN0cyBib29sLCBlcnIgZXJyb3IpIHsKICAgIGNvbnN0IHN0bXQgPSAiU0VMRUNUIEVYSVNUUyhTRUxFQ1QgMSBGUk9NIHt7Lk1vZGVsLlRhYmxlTmFtZX19IFdIRVJFIGlkID0gPyBMSU1JVCAxKSBBUyBgZXhpc3RzYCIKICAgIHZhciBjb3VudCBpbnQKICAgIHJvdyA6PSBxLlF1ZXJ5Um93KHN0bXQsIGlkKQogICAgaWYgZXJyID0gcm93LlNjYW4oJmNvdW50KTsgZXJyICE9IG5pbCB7CiAgICAgICAgcmV0dXJuCiAgICB9CiAgICByZXR1cm4gY291bnQgPiAwLCBuaWwKfQoKLy8gVGFibGVOYW1lIHJldHVybnMgdGhlIHRhYmxlIG5hbWUKZnVuYyAoe3suUmVjZWl2ZXJ9fSAqe3suTW9kZWwuTmFtZX19KSBUYWJsZU5hbWUoKSBzdHJpbmcgewpyZXR1cm4gInt7Lk1vZGVsLlRhYmxlTmFtZX19Igp9CgovLyBTZXRMaW1pdCBzZXRzIHRoZSBxdWVyeSBsaW1pdApmdW5jICh7ey5SZWNlaXZlcn19ICp7ey5Nb2RlbC5OYW1lfX0pIFNldExpbWl0KGxpbWl0IGludCkgKnt7Lk1vZGVsLk5hbWV9fSB7Cnt7LlJlY2VpdmVyfX0ubGltaXQgPSBsaW1pdApyZXR1cm4ge3suUmVjZWl2ZXJ9fQp9CgovLyBTZXRPZmZzZXQgc2V0cyB0aGUgcXVlcnkgb2Zmc2V0CmZ1bmMgKHt7LlJlY2VpdmVyfX0gKnt7Lk1vZGVsLk5hbWV9fSkgU2V0T2Zmc2V0KG9mZnNldCBpbnQpICp7ey5Nb2RlbC5OYW1lfX0gewp7ey5SZWNlaXZlcn19Lm9mZnNldCA9IG9mZnNldApyZXR1cm4ge3suUmVjZWl2ZXJ9fQp9Cnt7ZW5kfX0KCg==\"") packr.PackJSONBytes("./tmpl", "tmpl.go", "\"cGFja2FnZSB0bXBsCgppbXBvcnQgKAoJImZtdCIKCSJodG1sL3RlbXBsYXRlIgoJInN0cmluZ3MiCikKCnZhciBGdW5jTWFwID0gdGVtcGxhdGUuRnVuY01hcHsKCSJpbnNlcnRfZmllbGRzIjogR2V0SW5zZXJ0RmllbGRzLAoJImluc2VydF92YWx1ZXMiOiBHZXRJbnNlcnRWYWx1ZXMsCgkiaW5zZXJ0X2FyZ3MiOiAgIEdldEluc2VydEFyZ3MsCgkic2Nhbl9maWVsZHMiOiAgIEdldFNjYW5GaWVsZHMsCgkidXBkYXRlX2FyZ3MiOiAgIEdldFVwZGF0ZUFyZ3MsCgkidXBkYXRlX3ZhbHVlcyI6IEdldFVwZGF0ZVZhbHVlcywKCSJ1cHNlcnRfZmllbGRzIjogR2V0VXBzZXJ0RmllbGRzLAoJInVwc2VydF92YWx1ZXMiOiBHZXRVcHNlcnRWYWx1ZXMsCgkidXBzZXJ0X29uX2R1cGxpY2F0ZSI6IEdldFVwc2VydE9uRHVwbGljYXRlLAoJInVwc2VydF9hcmdzIjogR2V0VXBzZXJ0QXJncywKfQoKZnVuYyBHZXRJbnNlcnRGaWVsZHMoZmllbGRzIFtdVG1wbEZpZWxkKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgZmllbGRzIHsKCQlpZiBmbC5Db2x1bW5OYW1lID09ICJpZCIgewoJCQljb250aW51ZQoJCX0KCQlwYXJ0cyA9IGFwcGVuZChwYXJ0cywgImAiK2ZsLkNvbHVtbk5hbWUrImAiKQoJfQoJcmV0dXJuIHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikKfQoKZnVuYyBHZXRJbnNlcnRWYWx1ZXMoZmllbGRzIFtdVG1wbEZpZWxkKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgZmllbGRzIHsKCQlzd2l0Y2ggZmwuQ29sdW1uTmFtZSB7CgkJY2FzZSAiaWQiOgoJCQljb250aW51ZQoJCWNhc2UgImNyZWF0ZWRfYXQiOgoJCQlwYXJ0cyA9IGFwcGVuZChwYXJ0cywgIk5PVygpIikKCQkJY29udGludWUKCQlkZWZhdWx0OgoJCQlwYXJ0cyA9IGFwcGVuZChwYXJ0cywgIj8iKQoJCX0KCX0KCXJldHVybiBzdHJpbmdzLkpvaW4ocGFydHMsICIsICIpCn0KCmZ1bmMgR2V0SW5zZXJ0QXJncyhtIFN0cnVjdFRtcGxEYXRhKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgbS5Nb2RlbC5GaWVsZHMgewoJCXN3aXRjaCBmbC5OYW1lIHsKCQljYXNlICJJRCIsICJDcmVhdGVkQXQiOgoJCQljb250aW51ZQoJCX0KCQlwYXJ0cyA9IGFwcGVuZChwYXJ0cywgZm10LlNwcmludGYoIiVzLiVzIiwgbS5SZWNlaXZlciwgZmwuTmFtZSkpCgl9CglyZXR1cm4gc3RyaW5ncy5Kb2luKHBhcnRzLCAiLCAiKQp9CgpmdW5jIEdldFNjYW5GaWVsZHMobSBTdHJ1Y3RUbXBsRGF0YSkgdGVtcGxhdGUuSFRNTCB7Cgl2YXIgcGFydHMgW11zdHJpbmcKCWZvciBfLCBmbCA6PSByYW5nZSBtLk1vZGVsLkZpZWxkcyB7CgkJcGFydHMgPSBhcHBlbmQocGFydHMsIGZtdC5TcHJpbnRmKCImJXMuJXMiLCBtLlJlY2VpdmVyLCBmbC5OYW1lKSkKCX0KCXJldHVybiB0ZW1wbGF0ZS5IVE1MKHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikpCn0KCmZ1bmMgR2V0VXBkYXRlQXJncyhtIFN0cnVjdFRtcGxEYXRhKSB0ZW1wbGF0ZS5IVE1MIHsKCXZhciBwYXJ0cyBbXXN0cmluZwoJZm9yIF8sIGZsIDo9IHJhbmdlIG0uTW9kZWwuRmllbGRzIHsKCQlzd2l0Y2ggZmwuTmFtZSB7CgkJY2FzZSAiSUQiLCAiQ3JlYXRlZEF0IiwgIlVwZGF0ZWRBdCI6CgkJCWNvbnRpbnVlCgkJfQoJCXBhcnRzID0gYXBwZW5kKHBhcnRzLCBmbXQuU3ByaW50ZigiJXMuJXMiLCBtLlJlY2VpdmVyLCBmbC5OYW1lKSkKCX0KCXJldHVybiB0ZW1wbGF0ZS5IVE1MKHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikpCn0KCmZ1bmMgR2V0VXBkYXRlVmFsdWVzKG0gU3RydWN0VG1wbERhdGEpIHN0cmluZyB7Cgl2YXIgcGFydHMgW11zdHJpbmcKCWZvciBfLCBmbCA6PSByYW5nZSBtLk1vZGVsLkZpZWxkcyB7CgkJc3dpdGNoIGZsLk5hbWUgewoJCWNhc2UgIklEIiwgIkNyZWF0ZWRBdCI6CgkJCWNvbnRpbnVlCgkJY2FzZSAiVXBkYXRlZEF0IjoKCQkJcGFydHMgPSBhcHBlbmQocGFydHMsIGZtdC5TcHJpbnRmKCJgJXNgPVVUQ19USU1FU1RBTVAoKSIsIGZsLkNvbHVtbk5hbWUpKQoJCWRlZmF1bHQ6CgkJCXBhcnRzID0gYXBwZW5kKHBhcnRzLCBmbXQuU3ByaW50ZigiYCVzYD0/IiwgZmwuQ29sdW1uTmFtZSkpCgkJfQoJfQoJcmV0dXJuIHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikKfQoKZnVuYyBHZXRVcHNlcnRGaWVsZHMoZmllbGRzIFtdVG1wbEZpZWxkKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgZmllbGRzIHsKCQlwYXJ0cyA9IGFwcGVuZChwYXJ0cywgImAiK2ZsLkNvbHVtbk5hbWUrImAiKQoJfQoJcmV0dXJuIHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikKfQoKZnVuYyBHZXRVcHNlcnRWYWx1ZXMoZmllbGRzIFtdVG1wbEZpZWxkKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgZmllbGRzIHsKCQlzd2l0Y2ggZmwuQ29sdW1uTmFtZSB7CgkJY2FzZSAiY3JlYXRlZF9hdCI6CgkJCXBhcnRzID0gYXBwZW5kKHBhcnRzLCAiTk9XKCkiKQoJCQljb250aW51ZQoJCWRlZmF1bHQ6CgkJCXBhcnRzID0gYXBwZW5kKHBhcnRzLCAiPyIpCgkJfQoJfQoJcmV0dXJuIHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikKfQoKZnVuYyBHZXRVcHNlcnRPbkR1cGxpY2F0ZShtIFN0cnVjdFRtcGxEYXRhKSBzdHJpbmcgewoJdmFyIHBhcnRzIFtdc3RyaW5nCglmb3IgXywgZmwgOj0gcmFuZ2UgbS5Nb2RlbC5GaWVsZHMgewoJCXN3aXRjaCBmbC5OYW1lIHsKCQljYXNlICJDcmVhdGVkQXQiOgoJCQljb250aW51ZQoJCWNhc2UgIklEIjoKCQkJcGFydHMgPSBhcHBlbmQocGFydHMsIGZtdC5TcHJpbnRmKCJgJXNgPUxBU1RfSU5TRVJUX0lEKGAlc2ApIiwgZmwuQ29sdW1uTmFtZSwgZmwuQ29sdW1uTmFtZSkpCgkJY2FzZSAiVXBkYXRlZEF0IjoKCQkJcGFydHMgPSBhcHBlbmQocGFydHMsIGZtdC5TcHJpbnRmKCJgJXNgPVVUQ19USU1FU1RBTVAoKSIsIGZsLkNvbHVtbk5hbWUpKQoJCWRlZmF1bHQ6CgkJCXBhcnRzID0gYXBwZW5kKHBhcnRzLCBmbXQuU3ByaW50ZigiYCVzYD1WQUxVRVMoYCVzYCkiLCBmbC5Db2x1bW5OYW1lLCBmbC5Db2x1bW5OYW1lKSkKCQl9Cgl9CglyZXR1cm4gc3RyaW5ncy5Kb2luKHBhcnRzLCAiLCAiKQp9CgpmdW5jIEdldFVwc2VydEFyZ3MobSBTdHJ1Y3RUbXBsRGF0YSkgc3RyaW5nIHsKCXZhciBwYXJ0cyBbXXN0cmluZwoJZm9yIF8sIGZsIDo9IHJhbmdlIG0uTW9kZWwuRmllbGRzIHsKCQlzd2l0Y2ggZmwuTmFtZSB7CgkJY2FzZSAiQ3JlYXRlZEF0IjoKCQkJY29udGludWUKCQl9CgkJcGFydHMgPSBhcHBlbmQocGFydHMsIGZtdC5TcHJpbnRmKCIlcy4lcyIsIG0uUmVjZWl2ZXIsIGZsLk5hbWUpKQoJfQoJcmV0dXJuIHN0cmluZ3MuSm9pbihwYXJ0cywgIiwgIikKfQ==\"") packr.PackJSONBytes("./tmpl", "types.go", "\"cGFja2FnZSB0bXBsCgovLyBUbXBsU3RydWN0cyBpcyBhIGNvbGxlY3Rpb24gb24gVG1wbFN0cnVjdAp0eXBlIFRtcGxTdHJ1Y3RzIFtdVG1wbFN0cnVjdAoKLy8gVG1wbFN0cnVjdCBkZWZpbmVzIHRoZSB0YWJsZSBkYXRhIHRvIHBhc3MgdG8gdGhlIG1vZGVscwp0eXBlIFRtcGxTdHJ1Y3Qgc3RydWN0IHsKCU5hbWUgICAgICBzdHJpbmcKCVRhYmxlTmFtZSBzdHJpbmcKCUZpZWxkcyAgICBbXVRtcGxGaWVsZAoJSW1wb3J0cyAgIG1hcFtzdHJpbmddc3RydWN0e30KfQoKLy8gVG1wbEZpZWxkIGRlZmluZXMgYSB0YWJsZSBmaWVsZCB0ZW1wbGF0ZQp0eXBlIFRtcGxGaWVsZCBzdHJ1Y3QgewoJTmFtZSAgICAgICBzdHJpbmcKCVR5cGUgICAgICAgc3RyaW5nCglDb2x1bW5OYW1lIHN0cmluZwoJTnVsbGFibGUgICBib29sCn0KCi8vIFN0cnVjdFRtcGxEYXRhIGRlZmluZXMgdGhlIHRvcCBsZXZlbCBzdHJ1Y3QgZGF0YSB0byBwYXNzIHRvIHRoZSBtb2RlbHMKdHlwZSBTdHJ1Y3RUbXBsRGF0YSBzdHJ1Y3QgewoJTW9kZWwgICAgICAgVG1wbFN0cnVjdAoJUmVjZWl2ZXIgICAgc3RyaW5nCglQYWNrYWdlTmFtZSBzdHJpbmcKfQo=\"") - packr.PackJSONBytes("./tmpl", "x_helpers.html", "\"e3tkZWZpbmUgImhlbHBlcnMifX0KCnBhY2thZ2Uge3sgLlBhY2thZ2VOYW1lIH19CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBDb2RlIGdlbmVyYXRlZCBieSBtb2RlbGdlbiB8CnwgICAgICAgIERPIE5PVCBFRElULiAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCmltcG9ydCAoCiJkYXRhYmFzZS9zcWwiCiJkYXRhYmFzZS9zcWwvZHJpdmVyIgoiZW5jb2RpbmcvanNvbiIKInJlZmxlY3QiCiJzdHJpbmdzIgoidGltZSIKCiJnaXRodWIuY29tL2dvLXNxbC1kcml2ZXIvbXlzcWwiCikKCi8vIFN0ZFRpbWUgcHJvdmlkZXMgZGVmYXVsdCBTUUwgVElNRSBmb3JtYXQKY29uc3QgU3RkVGltZSA9ICIxNTowNDowNSIKCi8vIGVtcHR5VGltZSBhbGxvd3MgZGVmYXVsdCB0aW1lcyB0byBiZSBjb25zaWRlcmVkCi8vIG51bGwgZm9yIGluc2VydGlvbiBpbnRvIHRoZSBkYXRhYmFzZS4KdmFyIGVtcHR5VGltZSA9IHRpbWUuVGltZXt9CgovKi0tLS0tLS0tLS0tLS0rCnwgVHlwZSBhbGlhc2VzIHwKKy0tLS0tLS0tLS0tLS0qLwoKLy8gTnVsbEZsb2F0NjQgYWxpYXNlcyBzcWwuTnVsbEZsb2F0NjQKdHlwZSBOdWxsRmxvYXQ2NCBzcWwuTnVsbEZsb2F0NjQKCi8vIE51bGxTdHJpbmcgYWxpYXNlcyBzcWwuTnVsbFN0cmluZwp0eXBlIE51bGxTdHJpbmcgc3FsLk51bGxTdHJpbmcKCi8vIE51bGxCb29sIGFsaWFzZXMgc3FsLk51bGxCb29sCnR5cGUgTnVsbEJvb2wgc3FsLk51bGxCb29sCgovLyBOdWxsSW50NjQgYWxpYXNlcyBzcWwuTnVsbEludDY0CnR5cGUgTnVsbEludDY0IHNxbC5OdWxsSW50NjQKCi8vIE51bGxUaW1lIGFsaWFzZXMgc3FsLk51bGxUaW1lCnR5cGUgTnVsbFRpbWUgbXlzcWwuTnVsbFRpbWUKCi8vIFJhd0pTT04gYWxpYXNlcyBqc29uLlJhd01lc3NhZ2UKdHlwZSBSYXdKU09OIGpzb24uUmF3TWVzc2FnZQoKLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgTnVsbFN0cmluZyBpbXBsZW1lbnRhdGlvbnMgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIE1hcnNoYWxKU09OIGZvciBOdWxsU3RyaW5nCmZ1bmMgKG4gTnVsbFN0cmluZykgTWFyc2hhbEpTT04oKSAoW11ieXRlLCBlcnJvcikgewp2YXIgYSAqc3RyaW5nCmlmIG4uVmFsaWQgewphID0gJm4uU3RyaW5nCn0KcmV0dXJuIGpzb24uTWFyc2hhbChhKQp9CgovLyBVbm1hcnNoYWxKU09OIGZvciBOdWxsU3RyaW5nCmZ1bmMgKG4gKk51bGxTdHJpbmcpIFVubWFyc2hhbEpTT04oYiBbXWJ5dGUpIGVycm9yIHsKZXJyIDo9IGpzb24uVW5tYXJzaGFsKGIsICZuLlN0cmluZykKbi5WYWxpZCA9IGVyciA9PSBuaWwKcmV0dXJuIGVycgp9CgovLyBWYWx1ZSBmb3IgTnVsbFN0cmluZwpmdW5jIChuIE51bGxTdHJpbmcpIFZhbHVlKCkgKGRyaXZlci5WYWx1ZSwgZXJyb3IpIHsKaWYgIW4uVmFsaWQgewpyZXR1cm4gbmlsLCBuaWwKfQpyZXR1cm4gbi5TdHJpbmcsIG5pbAp9CgovLyBTY2FuIGZvciBOdWxsU3RyaW5nCmZ1bmMgKG4gKk51bGxTdHJpbmcpIFNjYW4oc3JjIGludGVyZmFjZXt9KSBlcnJvciB7CnZhciBhIHNxbC5OdWxsU3RyaW5nCmlmIGVyciA6PSBhLlNjYW4oc3JjKTsgZXJyICE9IG5pbCB7CnJldHVybiBlcnIKfQpuLlN0cmluZyA9IGEuU3RyaW5nCmlmIHJlZmxlY3QuVHlwZU9mKHNyYykgIT0gbmlsIHsKbi5WYWxpZCA9IHRydWUKfQpyZXR1cm4gbmlsCn0KCi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBOdWxsRmxvYXQ2NCBpbXBsZW1lbnRhdGlvbnMgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovCgovLyBNYXJzaGFsSlNPTiBmb3IgTnVsbEZsb2F0NjQKZnVuYyAobiBOdWxsRmxvYXQ2NCkgTWFyc2hhbEpTT04oKSAoW11ieXRlLCBlcnJvcikgewp2YXIgYSAqZmxvYXQ2NAppZiBuLlZhbGlkIHsKYSA9ICZuLkZsb2F0NjQKfQpyZXR1cm4ganNvbi5NYXJzaGFsKGEpCn0KCi8vIFZhbHVlIGZvciBOdWxsRmxvYXQ2NApmdW5jIChuIE51bGxGbG9hdDY0KSBWYWx1ZSgpIChkcml2ZXIuVmFsdWUsIGVycm9yKSB7CmlmICFuLlZhbGlkIHsKcmV0dXJuIG5pbCwgbmlsCn0KcmV0dXJuIG4uRmxvYXQ2NCwgbmlsCn0KCi8vIFVubWFyc2hhbEpTT04gZm9yIE51bGxGbG9hdDY0CmZ1bmMgKG4gKk51bGxGbG9hdDY0KSBVbm1hcnNoYWxKU09OKGIgW11ieXRlKSBlcnJvciB7CmVyciA6PSBqc29uLlVubWFyc2hhbChiLCAmbi5GbG9hdDY0KQpuLlZhbGlkID0gZXJyID09IG5pbApyZXR1cm4gZXJyCn0KCi8vIFNjYW4gZm9yIE51bGxGbG9hdDY0CmZ1bmMgKG4gKk51bGxGbG9hdDY0KSBTY2FuKHNyYyBpbnRlcmZhY2V7fSkgZXJyb3Igewp2YXIgYSBzcWwuTnVsbEZsb2F0NjQKaWYgZXJyIDo9IGEuU2NhbihzcmMpOyBlcnIgIT0gbmlsIHsKcmV0dXJuIGVycgp9Cm4uRmxvYXQ2NCA9IGEuRmxvYXQ2NAppZiByZWZsZWN0LlR5cGVPZihzcmMpICE9IG5pbCB7Cm4uVmFsaWQgPSB0cnVlCn0KcmV0dXJuIG5pbAp9CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IE51bGxJbnQ2NCBpbXBsZW1lbnRhdGlvbnMgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qLwoKLy8gTWFyc2hhbEpTT04gZm9yIE51bGxJbnQ2NApmdW5jIChuIE51bGxJbnQ2NCkgTWFyc2hhbEpTT04oKSAoW11ieXRlLCBlcnJvcikgewp2YXIgYSAqaW50NjQKaWYgbi5WYWxpZCB7CmEgPSAmbi5JbnQ2NAp9CnJldHVybiBqc29uLk1hcnNoYWwoYSkKfQoKLy8gVmFsdWUgZm9yIE51bGxJbnQ2NApmdW5jIChuIE51bGxJbnQ2NCkgVmFsdWUoKSAoZHJpdmVyLlZhbHVlLCBlcnJvcikgewppZiAhbi5WYWxpZCB7CnJldHVybiBuaWwsIG5pbAp9CnJldHVybiBuLkludDY0LCBuaWwKfQoKLy8gVW5tYXJzaGFsSlNPTiBmb3IgTnVsbEludDY0CmZ1bmMgKG4gKk51bGxJbnQ2NCkgVW5tYXJzaGFsSlNPTihiIFtdYnl0ZSkgZXJyb3IgewplcnIgOj0ganNvbi5Vbm1hcnNoYWwoYiwgJm4uSW50NjQpCm4uVmFsaWQgPSBlcnIgPT0gbmlsCnJldHVybiBlcnIKfQoKLy8gU2NhbiBmb3IgTnVsbEludDY0CmZ1bmMgKG4gKk51bGxJbnQ2NCkgU2NhbihzcmMgaW50ZXJmYWNle30pIGVycm9yIHsKdmFyIGEgc3FsLk51bGxJbnQ2NAppZiBlcnIgOj0gYS5TY2FuKHNyYyk7IGVyciAhPSBuaWwgewpyZXR1cm4gZXJyCn0Kbi5JbnQ2NCA9IGEuSW50NjQKaWYgcmVmbGVjdC5UeXBlT2Yoc3JjKSAhPSBuaWwgewpuLlZhbGlkID0gdHJ1ZQp9CnJldHVybiBuaWwKfQoKLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IE51bGxCb29sIGltcGxlbWVudGF0aW9ucyB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIE1hcnNoYWxKU09OIGZvciBOdWxsQm9vbApmdW5jIChuIE51bGxCb29sKSBNYXJzaGFsSlNPTigpIChbXWJ5dGUsIGVycm9yKSB7CnZhciBhICpib29sCmlmIG4uVmFsaWQgewphID0gJm4uQm9vbAp9CnJldHVybiBqc29uLk1hcnNoYWwoYSkKfQoKLy8gVmFsdWUgZm9yIE51bGxCb29sCmZ1bmMgKG4gTnVsbEJvb2wpIFZhbHVlKCkgKGRyaXZlci5WYWx1ZSwgZXJyb3IpIHsKaWYgIW4uVmFsaWQgewpyZXR1cm4gbmlsLCBuaWwKfQpyZXR1cm4gbi5Cb29sLCBuaWwKfQoKLy8gVW5tYXJzaGFsSlNPTiBmb3IgTnVsbEJvb2wKZnVuYyAobiAqTnVsbEJvb2wpIFVubWFyc2hhbEpTT04oYiBbXWJ5dGUpIGVycm9yIHsKdmFyIGZpZWxkICpib29sCmVyciA6PSBqc29uLlVubWFyc2hhbChiLCAmZmllbGQpCmlmIGZpZWxkICE9IG5pbCB7Cm4uVmFsaWQgPSB0cnVlCm4uQm9vbCA9ICpmaWVsZAp9CnJldHVybiBlcnIKfQoKCi8vIFNjYW4gZm9yIE51bGxCb29sCmZ1bmMgKG4gKk51bGxCb29sKSBTY2FuKHNyYyBpbnRlcmZhY2V7fSkgZXJyb3Igewp2YXIgYSBzcWwuTnVsbEJvb2wKaWYgZXJyIDo9IGEuU2NhbihzcmMpOyBlcnIgIT0gbmlsIHsKcmV0dXJuIGVycgp9Cm4uQm9vbCA9IGEuQm9vbAppZiByZWZsZWN0LlR5cGVPZihzcmMpICE9IG5pbCB7Cm4uVmFsaWQgPSB0cnVlCn0KcmV0dXJuIG5pbAp9CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgTnVsbFRpbWUgaW1wbGVtZW50YXRpb25zIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qLwoKLy8gTWFyc2hhbEpTT04gZm9yIE51bGxUaW1lCmZ1bmMgKG4gTnVsbFRpbWUpIE1hcnNoYWxKU09OKCkgKFtdYnl0ZSwgZXJyb3IpIHsKdmFyIGEgKnRpbWUuVGltZQppZiBuLlZhbGlkIHsKYSA9ICZuLlRpbWUKfQpyZXR1cm4ganNvbi5NYXJzaGFsKGEpCn0KCi8vIFZhbHVlIGZvciBOdWxsVGltZQpmdW5jIChuIE51bGxUaW1lKSBWYWx1ZSgpIChkcml2ZXIuVmFsdWUsIGVycm9yKSB7CmlmICFuLlZhbGlkIHsKcmV0dXJuIG5pbCwgbmlsCn0KcmV0dXJuIG4uVGltZSwgbmlsCn0KCi8vIFVubWFyc2hhbEpTT04gZm9yIE51bGxUaW1lCmZ1bmMgKG4gKk51bGxUaW1lKSBVbm1hcnNoYWxKU09OKGIgW11ieXRlKSBlcnJvciB7CnMgOj0gc3RyaW5nKGIpCnMgPSBzdHJpbmdzLlRyaW0ocywgYCJgKQp0aW0sIGVyciA6PSB0aW1lLlBhcnNlKHRpbWUuUkZDMzMzOSwgcykKaWYgZXJyICE9IG5pbCB7Cm4uVmFsaWQgPSBmYWxzZQpyZXR1cm4gZXJyCn0KCm4uVGltZSA9IHRpbQpuLlZhbGlkID0gdHJ1ZQpyZXR1cm4gbmlsCn0KCi8vIFNjYW4gZm9yIE51bGxUaW1lCmZ1bmMgKG4gKk51bGxUaW1lKSBTY2FuKHNyYyBpbnRlcmZhY2V7fSkgZXJyb3IgewovLyBTZXQgaW5pdGlhbCBzdGF0ZSBmb3Igc3Vic2VxdWVudCBzY2Fucy4Kbi5WYWxpZCA9IGZhbHNlCgp2YXIgYSBteXNxbC5OdWxsVGltZQppZiBlcnIgOj0gYS5TY2FuKHNyYyk7IGVyciAhPSBuaWwgewpyZXR1cm4gZXJyCn0Kbi5UaW1lID0gYS5UaW1lCmlmIHJlZmxlY3QuVHlwZU9mKHNyYykgIT0gbmlsIHsKbi5WYWxpZCA9IHRydWUKfQpyZXR1cm4gbmlsCn0KCi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IFJhd0pTT04gaW1wbGVtZW50YXRpb25zIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovCgovLyBNYXJzaGFsSlNPTiBmb3IgTnVsbFN0cmluZwpmdW5jIChuIFJhd0pTT04pIE1hcnNoYWxKU09OKCkgKFtdYnl0ZSwgZXJyb3IpIHsKYSA6PSBqc29uLlJhd01lc3NhZ2UobikKcmV0dXJuIGEuTWFyc2hhbEpTT04oKQp9CgovLyBWYWx1ZSBmb3IgTnVsbFN0cmluZwpmdW5jIChuIFJhd0pTT04pIFZhbHVlKCkgKGRyaXZlci5WYWx1ZSwgZXJyb3IpIHsKcmV0dXJuIHN0cmluZyhuKSwgbmlsCn0KCi8vIFVubWFyc2hhbEpTT04gZm9yIE51bGxTdHJpbmcKZnVuYyAobiAqUmF3SlNPTikgVW5tYXJzaGFsSlNPTihiIFtdYnl0ZSkgZXJyb3Igewp2YXIgYSBqc29uLlJhd01lc3NhZ2UKanNvbi5Vbm1hcnNoYWwoYiwgJmEpCmMgOj0gUmF3SlNPTihhKQoqbiA9IGMKcmV0dXJuIG5pbAp9CgovLyBTY2FuIGZvciBOdWxsU3RyaW5nCmZ1bmMgKG4gKlJhd0pTT04pIFNjYW4oc3JjIGludGVyZmFjZXt9KSBlcnJvciB7CnZhciBhIHNxbC5OdWxsU3RyaW5nCmlmIGVyciA6PSBhLlNjYW4oc3JjKTsgZXJyICE9IG5pbCB7CnJldHVybiBlcnIKfQpqc24gOj0gUmF3SlNPTihbXWJ5dGUoYS5TdHJpbmcpKQoqbiA9IGpzbgpyZXR1cm4gbmlsCn0KCi8qLS0tLS0tLS0tLS0tLS0tLS0rCnwgSGVscGVyIGZ1bmN0aW9ucyB8CistLS0tLS0tLS0tLS0tLS0tLSovCgovLyBUb051bGxTdHJpbmcgcmV0dXJucyBhIG5ldyBOdWxsU3RyaW5nCmZ1bmMgVG9OdWxsU3RyaW5nKHMgKnN0cmluZykgTnVsbFN0cmluZyB7CmlmIHMgPT0gbmlsIHsKcmV0dXJuIE51bGxTdHJpbmcoc3FsLk51bGxTdHJpbmd7VmFsaWQ6IGZhbHNlfSkKfQpyZXR1cm4gTnVsbFN0cmluZyhzcWwuTnVsbFN0cmluZ3tTdHJpbmc6ICpzLCBWYWxpZDogdHJ1ZX0pCn0KCi8vIFRvTnVsbEludDY0IHJldHVybnMgYSBuZXcgTnVsbEludDY0CmZ1bmMgVG9OdWxsSW50NjQoaSAqaW50NjQpIE51bGxJbnQ2NCB7CmlmIGkgPT0gbmlsIHsKcmV0dXJuIE51bGxJbnQ2NChzcWwuTnVsbEludDY0e1ZhbGlkOiBmYWxzZX0pCn0KcmV0dXJuIE51bGxJbnQ2NChzcWwuTnVsbEludDY0e0ludDY0OiAqaSwgVmFsaWQ6IHRydWV9KQp9CgovLyBUb051bGxGbG9hdDY0IHJldHVybnMgYSBuZXcgTnVsbEZsb2F0NjQKZnVuYyBUb051bGxGbG9hdDY0KGkgKmZsb2F0NjQpIE51bGxGbG9hdDY0IHsKaWYgaSA9PSBuaWwgewpyZXR1cm4gTnVsbEZsb2F0NjQoc3FsLk51bGxGbG9hdDY0e1ZhbGlkOiBmYWxzZX0pCn0KcmV0dXJuIE51bGxGbG9hdDY0KHNxbC5OdWxsRmxvYXQ2NHtGbG9hdDY0OiAqaSwgVmFsaWQ6IHRydWV9KQp9CgovLyBUb051bGxCb29sIGNyZWF0ZXMgYSBuZXcgTnVsbEJvb2wKZnVuYyBUb051bGxCb29sKGIgKmJvb2wpIE51bGxCb29sIHsKaWYgYiA9PSBuaWwgewpyZXR1cm4gTnVsbEJvb2woc3FsLk51bGxCb29se1ZhbGlkOiBmYWxzZX0pCn0KcmV0dXJuIE51bGxCb29sKHNxbC5OdWxsQm9vbHtCb29sOiAqYiwgVmFsaWQ6IHRydWV9KQp9CgovLyBUb051bGxUaW1lIGNyZWF0ZXMgYSBuZXcgTnVsbFRpbWUKZnVuYyBUb051bGxUaW1lKHQgdGltZS5UaW1lKSBOdWxsVGltZSB7CmlmIHQgPT0gZW1wdHlUaW1lIHsKcmV0dXJuIE51bGxUaW1lKG15c3FsLk51bGxUaW1le1ZhbGlkOiBmYWxzZX0pCn0KcmV0dXJuIE51bGxUaW1lKG15c3FsLk51bGxUaW1le1RpbWU6IHQsIFZhbGlkOiB0cnVlfSkKfQp7e2VuZH19Cg==\"") + packr.PackJSONBytes("./tmpl", "x_helpers.html", "\"e3tkZWZpbmUgImhlbHBlcnMifX0KCnBhY2thZ2Uge3sgLlBhY2thZ2VOYW1lIH19CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBDb2RlIGdlbmVyYXRlZCBieSBtb2RlbGdlbiB8CnwgICAgICAgIERPIE5PVCBFRElULiAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCmltcG9ydCAoCiJkYXRhYmFzZS9zcWwiCiJkYXRhYmFzZS9zcWwvZHJpdmVyIgoiZW5jb2RpbmcvanNvbiIKImxvZyIKInJlZmxlY3QiCiJzdHJpbmdzIgoidGltZSIKCiJnaXRodWIuY29tL2dvLXNxbC1kcml2ZXIvbXlzcWwiCikKCi8vIFN0ZFRpbWUgcHJvdmlkZXMgZGVmYXVsdCBTUUwgVElNRSBmb3JtYXQKY29uc3QgU3RkVGltZSA9ICIxNTowNDowNSIKCi8vIGVtcHR5VGltZSBhbGxvd3MgZGVmYXVsdCB0aW1lcyB0byBiZSBjb25zaWRlcmVkCi8vIG51bGwgZm9yIGluc2VydGlvbiBpbnRvIHRoZSBkYXRhYmFzZS4KdmFyIGVtcHR5VGltZSA9IHRpbWUuVGltZXt9CgovKioqKioqKioKKiBUeXBlcyAqCioqKioqKioqLwoKLy8gUXVlcnllciBhbGxvd3Mgc3FsLkRCIGFuZCBzcWwuVHggdG8gYmUgdXNlZCBpbnRlcmNoYW5nZWFibHksIGFsbG93aW5nIHlvdQovLyB0byB1c2UgYW55IG9mIHRoZSBtb2RlbCBtZXRob2RzIGluc2lkZSB0cmFuc2FjdGlvbnMgb3Igc3RhbmRhbG9uZSBjYWxscy4KdHlwZSBRdWVyeWVyIGludGVyZmFjZSB7CglRdWVyeShxdWVyeSBzdHJpbmcsIGFyZ3MgLi4uaW50ZXJmYWNle30pICgqc3FsLlJvd3MsIGVycm9yKQoJUXVlcnlSb3cocXVlcnkgc3RyaW5nLCBhcmdzIC4uLmludGVyZmFjZXt9KSAqc3FsLlJvdwoJRXhlYyhxdWVyeSBzdHJpbmcsIGFyZ3MgLi4uaW50ZXJmYWNle30pIChzcWwuUmVzdWx0LCBlcnJvcikKfQoKLyotLS0tLS0tLS0tLS0tKwp8IFR5cGUgYWxpYXNlcyB8CistLS0tLS0tLS0tLS0tKi8KCi8vIE51bGxGbG9hdDY0IGFsaWFzZXMgc3FsLk51bGxGbG9hdDY0CnR5cGUgTnVsbEZsb2F0NjQgc3FsLk51bGxGbG9hdDY0CgovLyBOdWxsU3RyaW5nIGFsaWFzZXMgc3FsLk51bGxTdHJpbmcKdHlwZSBOdWxsU3RyaW5nIHNxbC5OdWxsU3RyaW5nCgovLyBOdWxsQm9vbCBhbGlhc2VzIHNxbC5OdWxsQm9vbAp0eXBlIE51bGxCb29sIHNxbC5OdWxsQm9vbAoKLy8gTnVsbEludDY0IGFsaWFzZXMgc3FsLk51bGxJbnQ2NAp0eXBlIE51bGxJbnQ2NCBzcWwuTnVsbEludDY0CgovLyBOdWxsVGltZSBhbGlhc2VzIHNxbC5OdWxsVGltZQp0eXBlIE51bGxUaW1lIG15c3FsLk51bGxUaW1lCgovLyBSYXdKU09OIGFsaWFzZXMganNvbi5SYXdNZXNzYWdlCnR5cGUgUmF3SlNPTiBqc29uLlJhd01lc3NhZ2UKCi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IE51bGxTdHJpbmcgaW1wbGVtZW50YXRpb25zIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovCgovLyBNYXJzaGFsSlNPTiBmb3IgTnVsbFN0cmluZwpmdW5jIChuIE51bGxTdHJpbmcpIE1hcnNoYWxKU09OKCkgKFtdYnl0ZSwgZXJyb3IpIHsKdmFyIGEgKnN0cmluZwppZiBuLlZhbGlkIHsKYSA9ICZuLlN0cmluZwp9CnJldHVybiBqc29uLk1hcnNoYWwoYSkKfQoKLy8gVW5tYXJzaGFsSlNPTiBmb3IgTnVsbFN0cmluZwpmdW5jIChuICpOdWxsU3RyaW5nKSBVbm1hcnNoYWxKU09OKGIgW11ieXRlKSBlcnJvciB7CmVyciA6PSBqc29uLlVubWFyc2hhbChiLCAmbi5TdHJpbmcpCm4uVmFsaWQgPSBlcnIgPT0gbmlsCnJldHVybiBlcnIKfQoKLy8gVmFsdWUgZm9yIE51bGxTdHJpbmcKZnVuYyAobiBOdWxsU3RyaW5nKSBWYWx1ZSgpIChkcml2ZXIuVmFsdWUsIGVycm9yKSB7CmlmICFuLlZhbGlkIHsKcmV0dXJuIG5pbCwgbmlsCn0KcmV0dXJuIG4uU3RyaW5nLCBuaWwKfQoKLy8gU2NhbiBmb3IgTnVsbFN0cmluZwpmdW5jIChuICpOdWxsU3RyaW5nKSBTY2FuKHNyYyBpbnRlcmZhY2V7fSkgZXJyb3Igewp2YXIgYSBzcWwuTnVsbFN0cmluZwppZiBlcnIgOj0gYS5TY2FuKHNyYyk7IGVyciAhPSBuaWwgewpyZXR1cm4gZXJyCn0Kbi5TdHJpbmcgPSBhLlN0cmluZwppZiByZWZsZWN0LlR5cGVPZihzcmMpICE9IG5pbCB7Cm4uVmFsaWQgPSB0cnVlCn0KcmV0dXJuIG5pbAp9CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgTnVsbEZsb2F0NjQgaW1wbGVtZW50YXRpb25zIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qLwoKLy8gTWFyc2hhbEpTT04gZm9yIE51bGxGbG9hdDY0CmZ1bmMgKG4gTnVsbEZsb2F0NjQpIE1hcnNoYWxKU09OKCkgKFtdYnl0ZSwgZXJyb3IpIHsKdmFyIGEgKmZsb2F0NjQKaWYgbi5WYWxpZCB7CmEgPSAmbi5GbG9hdDY0Cn0KcmV0dXJuIGpzb24uTWFyc2hhbChhKQp9CgovLyBWYWx1ZSBmb3IgTnVsbEZsb2F0NjQKZnVuYyAobiBOdWxsRmxvYXQ2NCkgVmFsdWUoKSAoZHJpdmVyLlZhbHVlLCBlcnJvcikgewppZiAhbi5WYWxpZCB7CnJldHVybiBuaWwsIG5pbAp9CnJldHVybiBuLkZsb2F0NjQsIG5pbAp9CgovLyBVbm1hcnNoYWxKU09OIGZvciBOdWxsRmxvYXQ2NApmdW5jIChuICpOdWxsRmxvYXQ2NCkgVW5tYXJzaGFsSlNPTihiIFtdYnl0ZSkgZXJyb3IgewplcnIgOj0ganNvbi5Vbm1hcnNoYWwoYiwgJm4uRmxvYXQ2NCkKbi5WYWxpZCA9IGVyciA9PSBuaWwKcmV0dXJuIGVycgp9CgovLyBTY2FuIGZvciBOdWxsRmxvYXQ2NApmdW5jIChuICpOdWxsRmxvYXQ2NCkgU2NhbihzcmMgaW50ZXJmYWNle30pIGVycm9yIHsKdmFyIGEgc3FsLk51bGxGbG9hdDY0CmlmIGVyciA6PSBhLlNjYW4oc3JjKTsgZXJyICE9IG5pbCB7CnJldHVybiBlcnIKfQpuLkZsb2F0NjQgPSBhLkZsb2F0NjQKaWYgcmVmbGVjdC5UeXBlT2Yoc3JjKSAhPSBuaWwgewpuLlZhbGlkID0gdHJ1ZQp9CnJldHVybiBuaWwKfQoKLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBOdWxsSW50NjQgaW1wbGVtZW50YXRpb25zIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIE1hcnNoYWxKU09OIGZvciBOdWxsSW50NjQKZnVuYyAobiBOdWxsSW50NjQpIE1hcnNoYWxKU09OKCkgKFtdYnl0ZSwgZXJyb3IpIHsKdmFyIGEgKmludDY0CmlmIG4uVmFsaWQgewphID0gJm4uSW50NjQKfQpyZXR1cm4ganNvbi5NYXJzaGFsKGEpCn0KCi8vIFZhbHVlIGZvciBOdWxsSW50NjQKZnVuYyAobiBOdWxsSW50NjQpIFZhbHVlKCkgKGRyaXZlci5WYWx1ZSwgZXJyb3IpIHsKaWYgIW4uVmFsaWQgewpyZXR1cm4gbmlsLCBuaWwKfQpyZXR1cm4gbi5JbnQ2NCwgbmlsCn0KCi8vIFVubWFyc2hhbEpTT04gZm9yIE51bGxJbnQ2NApmdW5jIChuICpOdWxsSW50NjQpIFVubWFyc2hhbEpTT04oYiBbXWJ5dGUpIGVycm9yIHsKZXJyIDo9IGpzb24uVW5tYXJzaGFsKGIsICZuLkludDY0KQpuLlZhbGlkID0gZXJyID09IG5pbApyZXR1cm4gZXJyCn0KCi8vIFNjYW4gZm9yIE51bGxJbnQ2NApmdW5jIChuICpOdWxsSW50NjQpIFNjYW4oc3JjIGludGVyZmFjZXt9KSBlcnJvciB7CnZhciBhIHNxbC5OdWxsSW50NjQKaWYgZXJyIDo9IGEuU2NhbihzcmMpOyBlcnIgIT0gbmlsIHsKcmV0dXJuIGVycgp9Cm4uSW50NjQgPSBhLkludDY0CmlmIHJlZmxlY3QuVHlwZU9mKHNyYykgIT0gbmlsIHsKbi5WYWxpZCA9IHRydWUKfQpyZXR1cm4gbmlsCn0KCi8qLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBOdWxsQm9vbCBpbXBsZW1lbnRhdGlvbnMgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSovCgovLyBNYXJzaGFsSlNPTiBmb3IgTnVsbEJvb2wKZnVuYyAobiBOdWxsQm9vbCkgTWFyc2hhbEpTT04oKSAoW11ieXRlLCBlcnJvcikgewp2YXIgYSAqYm9vbAppZiBuLlZhbGlkIHsKYSA9ICZuLkJvb2wKfQpyZXR1cm4ganNvbi5NYXJzaGFsKGEpCn0KCi8vIFZhbHVlIGZvciBOdWxsQm9vbApmdW5jIChuIE51bGxCb29sKSBWYWx1ZSgpIChkcml2ZXIuVmFsdWUsIGVycm9yKSB7CmlmICFuLlZhbGlkIHsKcmV0dXJuIG5pbCwgbmlsCn0KcmV0dXJuIG4uQm9vbCwgbmlsCn0KCi8vIFVubWFyc2hhbEpTT04gZm9yIE51bGxCb29sCmZ1bmMgKG4gKk51bGxCb29sKSBVbm1hcnNoYWxKU09OKGIgW11ieXRlKSBlcnJvciB7CnZhciBmaWVsZCAqYm9vbAplcnIgOj0ganNvbi5Vbm1hcnNoYWwoYiwgJmZpZWxkKQppZiBmaWVsZCAhPSBuaWwgewpuLlZhbGlkID0gdHJ1ZQpuLkJvb2wgPSAqZmllbGQKfQpyZXR1cm4gZXJyCn0KCgovLyBTY2FuIGZvciBOdWxsQm9vbApmdW5jIChuICpOdWxsQm9vbCkgU2NhbihzcmMgaW50ZXJmYWNle30pIGVycm9yIHsKdmFyIGEgc3FsLk51bGxCb29sCmlmIGVyciA6PSBhLlNjYW4oc3JjKTsgZXJyICE9IG5pbCB7CnJldHVybiBlcnIKfQpuLkJvb2wgPSBhLkJvb2wKaWYgcmVmbGVjdC5UeXBlT2Yoc3JjKSAhPSBuaWwgewpuLlZhbGlkID0gdHJ1ZQp9CnJldHVybiBuaWwKfQoKLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IE51bGxUaW1lIGltcGxlbWVudGF0aW9ucyB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCi8vIE1hcnNoYWxKU09OIGZvciBOdWxsVGltZQpmdW5jIChuIE51bGxUaW1lKSBNYXJzaGFsSlNPTigpIChbXWJ5dGUsIGVycm9yKSB7CnZhciBhICp0aW1lLlRpbWUKaWYgbi5WYWxpZCB7CmEgPSAmbi5UaW1lCn0KcmV0dXJuIGpzb24uTWFyc2hhbChhKQp9CgovLyBWYWx1ZSBmb3IgTnVsbFRpbWUKZnVuYyAobiBOdWxsVGltZSkgVmFsdWUoKSAoZHJpdmVyLlZhbHVlLCBlcnJvcikgewppZiAhbi5WYWxpZCB7CnJldHVybiBuaWwsIG5pbAp9CnJldHVybiBuLlRpbWUsIG5pbAp9CgovLyBVbm1hcnNoYWxKU09OIGZvciBOdWxsVGltZQpmdW5jIChuICpOdWxsVGltZSkgVW5tYXJzaGFsSlNPTihiIFtdYnl0ZSkgZXJyb3IgewpzIDo9IHN0cmluZyhiKQpzID0gc3RyaW5ncy5UcmltKHMsIGAiYCkKdGltLCBlcnIgOj0gdGltZS5QYXJzZSh0aW1lLlJGQzMzMzksIHMpCmlmIGVyciAhPSBuaWwgewpuLlZhbGlkID0gZmFsc2UKcmV0dXJuIGVycgp9CgpuLlRpbWUgPSB0aW0Kbi5WYWxpZCA9IHRydWUKcmV0dXJuIG5pbAp9CgovLyBTY2FuIGZvciBOdWxsVGltZQpmdW5jIChuICpOdWxsVGltZSkgU2NhbihzcmMgaW50ZXJmYWNle30pIGVycm9yIHsKLy8gU2V0IGluaXRpYWwgc3RhdGUgZm9yIHN1YnNlcXVlbnQgc2NhbnMuCm4uVmFsaWQgPSBmYWxzZQoKdmFyIGEgbXlzcWwuTnVsbFRpbWUKaWYgZXJyIDo9IGEuU2NhbihzcmMpOyBlcnIgIT0gbmlsIHsKcmV0dXJuIGVycgp9Cm4uVGltZSA9IGEuVGltZQppZiByZWZsZWN0LlR5cGVPZihzcmMpICE9IG5pbCB7Cm4uVmFsaWQgPSB0cnVlCn0KcmV0dXJuIG5pbAp9CgovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBSYXdKU09OIGltcGxlbWVudGF0aW9ucyB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0qLwoKLy8gTWFyc2hhbEpTT04gZm9yIE51bGxTdHJpbmcKZnVuYyAobiBSYXdKU09OKSBNYXJzaGFsSlNPTigpIChbXWJ5dGUsIGVycm9yKSB7CmEgOj0ganNvbi5SYXdNZXNzYWdlKG4pCnJldHVybiBhLk1hcnNoYWxKU09OKCkKfQoKLy8gVmFsdWUgZm9yIE51bGxTdHJpbmcKZnVuYyAobiBSYXdKU09OKSBWYWx1ZSgpIChkcml2ZXIuVmFsdWUsIGVycm9yKSB7CnJldHVybiBzdHJpbmcobiksIG5pbAp9CgovLyBVbm1hcnNoYWxKU09OIGZvciBOdWxsU3RyaW5nCmZ1bmMgKG4gKlJhd0pTT04pIFVubWFyc2hhbEpTT04oYiBbXWJ5dGUpIGVycm9yIHsKdmFyIGEganNvbi5SYXdNZXNzYWdlCmpzb24uVW5tYXJzaGFsKGIsICZhKQpjIDo9IFJhd0pTT04oYSkKKm4gPSBjCnJldHVybiBuaWwKfQoKLy8gU2NhbiBmb3IgTnVsbFN0cmluZwpmdW5jIChuICpSYXdKU09OKSBTY2FuKHNyYyBpbnRlcmZhY2V7fSkgZXJyb3Igewp2YXIgYSBzcWwuTnVsbFN0cmluZwppZiBlcnIgOj0gYS5TY2FuKHNyYyk7IGVyciAhPSBuaWwgewpyZXR1cm4gZXJyCn0KanNuIDo9IFJhd0pTT04oW11ieXRlKGEuU3RyaW5nKSkKKm4gPSBqc24KcmV0dXJuIG5pbAp9CgovKi0tLS0tLS0tLS0tLS0tLS0tKwp8IEhlbHBlciBmdW5jdGlvbnMgfAorLS0tLS0tLS0tLS0tLS0tLS0qLwoKLy8gVG9OdWxsU3RyaW5nIHJldHVybnMgYSBuZXcgTnVsbFN0cmluZwpmdW5jIFRvTnVsbFN0cmluZyhzICpzdHJpbmcpIE51bGxTdHJpbmcgewppZiBzID09IG5pbCB7CnJldHVybiBOdWxsU3RyaW5nKHNxbC5OdWxsU3RyaW5ne1ZhbGlkOiBmYWxzZX0pCn0KcmV0dXJuIE51bGxTdHJpbmcoc3FsLk51bGxTdHJpbmd7U3RyaW5nOiAqcywgVmFsaWQ6IHRydWV9KQp9CgovLyBUb051bGxJbnQ2NCByZXR1cm5zIGEgbmV3IE51bGxJbnQ2NApmdW5jIFRvTnVsbEludDY0KGkgKmludDY0KSBOdWxsSW50NjQgewppZiBpID09IG5pbCB7CnJldHVybiBOdWxsSW50NjQoc3FsLk51bGxJbnQ2NHtWYWxpZDogZmFsc2V9KQp9CnJldHVybiBOdWxsSW50NjQoc3FsLk51bGxJbnQ2NHtJbnQ2NDogKmksIFZhbGlkOiB0cnVlfSkKfQoKLy8gVG9OdWxsRmxvYXQ2NCByZXR1cm5zIGEgbmV3IE51bGxGbG9hdDY0CmZ1bmMgVG9OdWxsRmxvYXQ2NChpICpmbG9hdDY0KSBOdWxsRmxvYXQ2NCB7CmlmIGkgPT0gbmlsIHsKcmV0dXJuIE51bGxGbG9hdDY0KHNxbC5OdWxsRmxvYXQ2NHtWYWxpZDogZmFsc2V9KQp9CnJldHVybiBOdWxsRmxvYXQ2NChzcWwuTnVsbEZsb2F0NjR7RmxvYXQ2NDogKmksIFZhbGlkOiB0cnVlfSkKfQoKLy8gVG9OdWxsQm9vbCBjcmVhdGVzIGEgbmV3IE51bGxCb29sCmZ1bmMgVG9OdWxsQm9vbChiICpib29sKSBOdWxsQm9vbCB7CmlmIGIgPT0gbmlsIHsKcmV0dXJuIE51bGxCb29sKHNxbC5OdWxsQm9vbHtWYWxpZDogZmFsc2V9KQp9CnJldHVybiBOdWxsQm9vbChzcWwuTnVsbEJvb2x7Qm9vbDogKmIsIFZhbGlkOiB0cnVlfSkKfQoKLy8gVG9OdWxsVGltZSBjcmVhdGVzIGEgbmV3IE51bGxUaW1lCmZ1bmMgVG9OdWxsVGltZSh0IHRpbWUuVGltZSkgTnVsbFRpbWUgewppZiB0ID09IGVtcHR5VGltZSB7CnJldHVybiBOdWxsVGltZShteXNxbC5OdWxsVGltZXtWYWxpZDogZmFsc2V9KQp9CnJldHVybiBOdWxsVGltZShteXNxbC5OdWxsVGltZXtUaW1lOiB0LCBWYWxpZDogdHJ1ZX0pCn0KCi8vIEV4ZWN1dGVUcmFuc2FjdGlvbiBjbG9zZXMgb3ZlciBhIHRyYW5zYWN0aW9uIGFuZCBhdXRvbWF0aWNhbGx5IGNvbW1pdHMKLy8gb3Igcm9sbGJhY2tzIGRlcGVuZGluZyBvbiB3aGV0aGVyIGVycm9ycyB3ZXJlIGVuY291bnRlcmVkLgpmdW5jIEV4ZWN1dGVUcmFuc2FjdGlvbihkYiAqc3FsLkRCLCBmIGZ1bmMoKnNxbC5UeCkgZXJyb3IpIGVycm9yIHsKCXR4LCBlcnIgOj0gZGIuQmVnaW4oKQoJaWYgZXJyICE9IG5pbCB7CgkJcmV0dXJuIGVycgoJfQoKCWRlZmVyIGZ1bmMoKSBlcnJvciB7CgkJaWYgciA6PSByZWNvdmVyKCk7IHIgIT0gbmlsIHsKCQkJLy8gT25seSBuZWVkIHRvIGxvZyBoZXJlIGJlY2F1c2UgcGFuaWMgd29uJ3QgcmVwb3J0IHdoZXRoZXIKCQkJLy8gdGhlIHJvbGxiYWNrIHdhcyBzdWNjZXNzZnVsIG9yIG5vdC4KCQkJaWYgZXJyID0gdHguUm9sbGJhY2soKTsgZXJyICE9IG5pbCB7CgkJCQlsb2cuUHJpbnRsbigiZGIgcm9sbGJhY2sgZXJyb3I6IiwgZXJyKQoJCQl9CgkJCXBhbmljKHIpCgkJfSBlbHNlIGlmIGVyciAhPSBuaWwgewoJCQlyZXR1cm4gZm10LkVycm9yZigiZGIgZXJyb3I6ICV2IHJvbGxiYWNrIGVycm9yOiAldiIsIGVyciwgdHguUm9sbGJhY2soKSkKCQl9IGVsc2UgewoJCQlpZiBlcnIgPSB0eC5Db21taXQoKTsgZXJyICE9IG5pbCB7CgkJCQlyZXR1cm4gZXJyCgkJCX0KCQl9CgkJcmV0dXJuIG5pbAoJfSgpCgoJcmV0dXJuIGYodHgpCn0Ke3tlbmR9fQo=\"") packr.PackJSONBytes("./tmpl", "x_helpers_test.html", "\"e3tkZWZpbmUgImhlbHBlcnN0ZXN0In19Ci8vK2J1aWxkICFoZWxwZXJzCgpwYWNrYWdlIHt7IC5QYWNrYWdlTmFtZSB9fQovKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBDb2RlIGdlbmVyYXRlZCBieSBtb2RlbGdlbiB8CnwgICAgICAgIERPIE5PVCBFRElULiAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi8KCmltcG9ydCAoCiJkYXRhYmFzZS9zcWwvZHJpdmVyIgoiZW5jb2RpbmcvanNvbiIKInJlZmxlY3QiCiJ0ZXN0aW5nIgoidGltZSIKKQoKZnVuYyBUZXN0U3RydWN0RW1iZWRkaW5nKHQgKnRlc3RpbmcuVCkgewp0aW0gOj0gdGltZS5EYXRlKDIwMTcsIDEsIDEsIDAsIDAsIDAsIDAsIHRpbWUuVVRDKQpleHBlY3RlZCA6PSBbXWJ5dGUoYHsiYSI6MTIzLCJiIjp0cnVlLCJjIjoxMjMuMTIzLCJkIjoic3RyaW5nIiwiZSI6IjIwMTctMDEtMDFUMDA6MDA6MDBaIiwiZiI6WzEsMiwzXX1gKQp0eXBlIGVtYmVkIHN0cnVjdCB7CkEgTnVsbEludDY0ICAgYGpzb246ImEsb21pdGVtcHR5ImAKQiBOdWxsQm9vbCAgICBganNvbjoiYixvbWl0ZW1wdHkiYApDIE51bGxGbG9hdDY0IGBqc29uOiJjLG9taXRlbXB0eSJgCkQgTnVsbFN0cmluZyAgYGpzb246ImQsb21pdGVtcHR5ImAKRSBOdWxsVGltZSAgICBganNvbjoiZSxvbWl0ZW1wdHkiYApGIFJhd0pTT04gICAgIGBqc29uOiJmLG9taXRlbXB0eSJgCn0KZW0gOj0gZW1iZWR7CkE6IE51bGxJbnQ2NHtWYWxpZDogdHJ1ZSwgSW50NjQ6IDEyM30sCkI6IE51bGxCb29se1ZhbGlkOiB0cnVlLCBCb29sOiB0cnVlfSwKQzogTnVsbEZsb2F0NjR7VmFsaWQ6IHRydWUsIEZsb2F0NjQ6IDEyMy4xMjN9LApEOiBOdWxsU3RyaW5ne1ZhbGlkOiB0cnVlLCBTdHJpbmc6ICJzdHJpbmcifSwKRTogTnVsbFRpbWV7VmFsaWQ6IHRydWUsIFRpbWU6IHRpbX0sCkY6IFJhd0pTT04oYFsxLDIsM11gKSwKfQpiLCBlcnIgOj0ganNvbi5NYXJzaGFsKGVtKQppZiBlcnIgIT0gbmlsIHsKdC5GYXRhbChlcnIpCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGV4cGVjdGVkLCBiKSB7CnQuRmF0YWwoIm5vdCB0aGUgc2FtZSBKU09OISIpCn0KaWYgIShzdHJpbmcoYikgPT0gc3RyaW5nKGV4cGVjdGVkKSkgewp0LkZhdGFsKCJub3QgdGhlIHNhbWUhIikKfQoKdmFyIGVtMiBlbWJlZAppZiBlcnIgOj0ganNvbi5Vbm1hcnNoYWwoZXhwZWN0ZWQsICZlbTIpOyBlcnIgIT0gbmlsIHsKdC5GYXRhbChlcnIpCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGVtMiwgZW0pIHsKdC5GYXRhbCgibm90IGNvcnJlY3QiKQp9Cn0KCmZ1bmMgVGVzdE51bGxTdHJpbmdfVW5tYXJzaGFsSlNPTih0ICp0ZXN0aW5nLlQpIHsKdGVzdHMgOj0gW11zdHJ1Y3QgewpuYW1lICAgIHN0cmluZwpuICAgICAgIE51bGxTdHJpbmcKc291cmNlICBbXWJ5dGUKd2FudEVyciBib29sCn17CnsKbmFtZTogICAgInZhbGlkIiwKc291cmNlOiAgW11ieXRlKGAiaGVsbG8iYCksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICAgICJpbnZhbGlkIiwKc291cmNlOiAgW11ieXRlKGB7ImtleSI6InZhbHVlIn1gKSwKd2FudEVycjogdHJ1ZSwKfSwKewpuYW1lOiAgICAiZW1wdHkiLApzb3VyY2U6ICBbXWJ5dGV7fSwKd2FudEVycjogdHJ1ZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5Vbm1hcnNoYWxKU09OKHR0LnNvdXJjZSk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxTdHJpbmcuVW5tYXJzaGFsSlNPTigpIGVycm9yID0gJXYsIHdhbnRFcnIgJXYiLCBlcnIsIHR0LndhbnRFcnIpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsU3RyaW5nX1ZhbHVlKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgTnVsbFN0cmluZwp3YW50ICAgIGRyaXZlci5WYWx1ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAidmFsaWQiLApuOiBOdWxsU3RyaW5newpWYWxpZDogIHRydWUsClN0cmluZzogImhlbGxvIiwKfSwKd2FudDogICAgZHJpdmVyLlZhbHVlKCJoZWxsbyIpLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAiaW52YWxpZCIsCm46IE51bGxTdHJpbmd7ClZhbGlkOiBmYWxzZSwKfSwKd2FudDogICAgbmlsLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewpnb3QsIGVyciA6PSB0dC5uLlZhbHVlKCkKaWYgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbFN0cmluZy5WYWx1ZSgpIGVycm9yID0gJXYsIHdhbnRFcnIgJXYiLCBlcnIsIHR0LndhbnRFcnIpCnJldHVybgp9CmlmICFyZWZsZWN0LkRlZXBFcXVhbChnb3QsIHR0LndhbnQpIHsKdC5FcnJvcmYoIk51bGxTdHJpbmcuVmFsdWUoKSA9ICV2LCB3YW50ICV2IiwgZ290LCB0dC53YW50KQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbFN0cmluZ19TY2FuKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxTdHJpbmcKd2FudEVyciBib29sCnNyYyAgICAgaW50ZXJmYWNle30KfXsKewpuYW1lOiAidmFsaWQiLApuOiAmTnVsbFN0cmluZ3sKU3RyaW5nOiAiaGVsbG8iLApWYWxpZDogIHRydWUsCn0sCnNyYzogICAgICIiLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAibmlsIHZhbHVlIiwKbjogJk51bGxTdHJpbmd7ClN0cmluZzogImhlbGxvIiwKVmFsaWQ6ICBmYWxzZSwKfSwKc3JjOiAgICAgbmlsLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5TY2FuKHR0LnNyYyk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxTdHJpbmcuU2NhbigpIGVycm9yID0gJXYsIHdhbnRFcnIgJXYiLCBlcnIsIHR0LndhbnRFcnIpCn0KaWYgdHQubi5WYWxpZCA9PSBmYWxzZSAmJiB0dC5zcmMgIT0gbmlsIHsKdC5FcnJvcmYoInNob3VsZCByZXR1cm4gbnVsbCIpCn0KaWYgdHQubi5WYWxpZCAmJiB0dC5zcmMgIT0gdHQubi5TdHJpbmcgewp0LkVycm9yZigiaW52YWxpZCB2YWx1ZSIpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsU3RyaW5nX01hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxTdHJpbmcKd2FudCAgICBbXWJ5dGUKd2FudEVyciBib29sCn17CnsKbmFtZTogInZhbGlkIiwKbjogJk51bGxTdHJpbmd7ClN0cmluZzogImhlbGxvIiwKVmFsaWQ6ICB0cnVlLAp9LAp3YW50OiAgICBbXWJ5dGUoYCJoZWxsbyJgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogInZhbGlkIG51bGwiLApuOiAmTnVsbFN0cmluZ3sKU3RyaW5nOiAiIiwKVmFsaWQ6ICBmYWxzZSwKfSwKd2FudDogICAgW11ieXRlKGBudWxsYCksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJpbnZhbGlkIiwKbjogJk51bGxTdHJpbmd7ClZhbGlkOiB0cnVlLAp9LAp3YW50OiAgICBbXWJ5dGUoYCIiYCksCndhbnRFcnI6IGZhbHNlLAp9LAp9CmZvciBfLCB0dCA6PSByYW5nZSB0ZXN0cyB7CnQuUnVuKHR0Lm5hbWUsIGZ1bmModCAqdGVzdGluZy5UKSB7CmdvdCwgZXJyIDo9IHR0Lm4uTWFyc2hhbEpTT04oKQppZiAoZXJyICE9IG5pbCkgIT0gdHQud2FudEVyciB7CnQuRXJyb3JmKCJOdWxsU3RyaW5nLk1hcnNoYWxKU09OKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKcmV0dXJuCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGdvdCwgdHQud2FudCkgewp0LkVycm9yZigiTnVsbFN0cmluZy5NYXJzaGFsSlNPTigpID0gJXYsIHdhbnQgJXYiLCBnb3QsIHR0LndhbnQpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsQm9vbF9Vbm1hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgICAgICBzdHJpbmcKbiAgICAgICAgICAgIE51bGxCb29sCnNvdXJjZSAgICAgICBbXWJ5dGUKd2FudEVyciAgICAgIGJvb2wKd2FudFZhbGlkaXR5IGJvb2wKfXsKewpuYW1lOiAgICAgICAgICJ2YWxpZCIsCnNvdXJjZTogICAgICAgW11ieXRlKGBmYWxzZWApLAp3YW50RXJyOiAgICAgIGZhbHNlLAp3YW50VmFsaWRpdHk6IHRydWUsCn0sCnsKbmFtZTogICAgICAgICAiaW52YWxpZCIsCnNvdXJjZTogICAgICAgW11ieXRlKGB7ImtleSI6InZhbHVlIn1gKSwKd2FudEVycjogICAgICB0cnVlLAp3YW50VmFsaWRpdHk6IGZhbHNlLAp9LAp7Cm5hbWU6ICAgICAgICAgImVtcHR5IiwKc291cmNlOiAgICAgICBbXWJ5dGV7fSwKd2FudEVycjogICAgICB0cnVlLAp3YW50VmFsaWRpdHk6IGZhbHNlLAp9LAp7Cm5hbWU6ICAgICAgICAgImV4cGxpY2l0IG51bGwiLApzb3VyY2U6ICAgICAgIFtdYnl0ZSgibnVsbCIpLAp3YW50RXJyOiAgICAgIGZhbHNlLAp3YW50VmFsaWRpdHk6IGZhbHNlLAp9LAp9CmZvciBfLCB0dCA6PSByYW5nZSB0ZXN0cyB7CnQuUnVuKHR0Lm5hbWUsIGZ1bmModCAqdGVzdGluZy5UKSB7CmlmIGVyciA6PSB0dC5uLlVubWFyc2hhbEpTT04odHQuc291cmNlKTsgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgJiYgdHQubi5WYWxpZCA9PSB0dC53YW50VmFsaWRpdHkgewogICAgdC5FcnJvcmYoIk51bGxCb29sLlVubWFyc2hhbEpTT04oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbEJvb2xfVmFsdWUodCAqdGVzdGluZy5UKSB7CnRlc3RzIDo9IFtdc3RydWN0IHsKbmFtZSAgICBzdHJpbmcKbiAgICAgICBOdWxsQm9vbAp3YW50ICAgIGRyaXZlci5WYWx1ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAidmFsaWQiLApuOiBOdWxsQm9vbHsKVmFsaWQ6IHRydWUsCkJvb2w6ICB0cnVlLAp9LAp3YW50OiAgICBkcml2ZXIuVmFsdWUodHJ1ZSksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJpbnZhbGlkIiwKbjogTnVsbEJvb2x7ClZhbGlkOiBmYWxzZSwKfSwKd2FudDogICAgbmlsLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewpnb3QsIGVyciA6PSB0dC5uLlZhbHVlKCkKaWYgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEJvb2wuVmFsdWUoKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQpyZXR1cm4KfQppZiAhcmVmbGVjdC5EZWVwRXF1YWwoZ290LCB0dC53YW50KSB7CnQuRXJyb3JmKCJOdWxsQm9vbC5WYWx1ZSgpID0gJXYsIHdhbnQgJXYiLCBnb3QsIHR0LndhbnQpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsQm9vbF9TY2FuKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxCb29sCndhbnRFcnIgYm9vbApzcmMgICAgIGludGVyZmFjZXt9Cn17CnsKbmFtZTogInZhbGlkIiwKbjogJk51bGxCb29sewpCb29sOiAgdHJ1ZSwKVmFsaWQ6IHRydWUsCn0sCnNyYzogICAgIHRydWUsCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJuaWwgdmFsdWUiLApuOiAmTnVsbEJvb2x7CkJvb2w6ICB0cnVlLApWYWxpZDogZmFsc2UsCn0sCnNyYzogICAgIGZhbHNlLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5TY2FuKHR0LnNyYyk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxCb29sLlNjYW4oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQp9CmlmIHR0Lm4uVmFsaWQgPT0gZmFsc2UgJiYgdHQuc3JjICE9IG5pbCB7CnQuRXJyb3JmKCJzaG91bGQgcmV0dXJuIG51bGwiKQp9CmlmIHR0Lm4uVmFsaWQgJiYgdHQuc3JjICE9IHR0Lm4uQm9vbCB7CnQuRXJyb3JmKCJpbnZhbGlkIHZhbHVlIikKfQp9KQp9Cn0KCmZ1bmMgVGVzdE51bGxCb29sX01hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxCb29sCndhbnQgICAgW11ieXRlCndhbnRFcnIgYm9vbAp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46ICZOdWxsQm9vbHsKVmFsaWQ6IHRydWUsCn0sCndhbnQ6ICAgIFtdYnl0ZShgZmFsc2VgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogInZhbGlkIG51bGwiLApuOiAmTnVsbEJvb2x7ClZhbGlkOiBmYWxzZSwKfSwKd2FudDogICAgW11ieXRlKGBudWxsYCksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJpbnZhbGlkIiwKbjogJk51bGxCb29sewpWYWxpZDogdHJ1ZSwKfSwKd2FudDogICAgW11ieXRlKGBmYWxzZWApLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewpnb3QsIGVyciA6PSB0dC5uLk1hcnNoYWxKU09OKCkKaWYgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEJvb2wuTWFyc2hhbEpTT04oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQpyZXR1cm4KfQppZiAhcmVmbGVjdC5EZWVwRXF1YWwoZ290LCB0dC53YW50KSB7CnQuRXJyb3JmKCJOdWxsQm9vbC5NYXJzaGFsSlNPTigpID0gJXYsIHdhbnQgJXYiLCBnb3QsIHR0LndhbnQpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsVGltZV9Vbm1hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgTnVsbFRpbWUKc291cmNlICBbXWJ5dGUKd2FudEVyciBib29sCn17CnsKbmFtZTogICAgInZhbGlkIiwKc291cmNlOiAgW11ieXRlKGAiMjAxNy0xMS0yNFQwMDowMDowMFoiYCksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICAgICJpbnZhbGlkIiwKc291cmNlOiAgW11ieXRlKGB7ImtleSI6InZhbHVlIn1gKSwKd2FudEVycjogdHJ1ZSwKfSwKewpuYW1lOiAgICAiZW1wdHkiLApzb3VyY2U6ICBbXWJ5dGV7fSwKd2FudEVycjogdHJ1ZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5Vbm1hcnNoYWxKU09OKHR0LnNvdXJjZSk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxCb29sLlVubWFyc2hhbEpTT04oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbFRpbWVfVmFsdWUodCAqdGVzdGluZy5UKSB7CnRpbSA6PSB0aW1lLk5vdygpCnRlc3RzIDo9IFtdc3RydWN0IHsKbmFtZSAgICBzdHJpbmcKbiAgICAgICBOdWxsVGltZQp3YW50ICAgIGRyaXZlci5WYWx1ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAidmFsaWQiLApuOiBOdWxsVGltZXsKVmFsaWQ6IHRydWUsClRpbWU6ICB0aW0sCn0sCndhbnQ6ICAgIGRyaXZlci5WYWx1ZSh0aW0pLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAiaW52YWxpZCIsCm46IE51bGxUaW1lewpWYWxpZDogZmFsc2UsCn0sCndhbnQ6ICAgIG5pbCwKd2FudEVycjogZmFsc2UsCn0sCn0KZm9yIF8sIHR0IDo9IHJhbmdlIHRlc3RzIHsKdC5SdW4odHQubmFtZSwgZnVuYyh0ICp0ZXN0aW5nLlQpIHsKZ290LCBlcnIgOj0gdHQubi5WYWx1ZSgpCmlmIChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxUaW1lLlZhbHVlKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKcmV0dXJuCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGdvdCwgdHQud2FudCkgewp0LkVycm9yZigiTnVsbFRpbWUuVmFsdWUoKSA9ICV2LCB3YW50ICV2IiwgZ290LCB0dC53YW50KQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbFRpbWVfU2Nhbih0ICp0ZXN0aW5nLlQpIHsKdGltIDo9IHRpbWUuTm93KCkKdGVzdHMgOj0gW11zdHJ1Y3QgewpuYW1lICAgIHN0cmluZwpuICAgICAgICpOdWxsVGltZQp3YW50RXJyIGJvb2wKc3JjICAgICBpbnRlcmZhY2V7fQp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46ICZOdWxsVGltZXsKVGltZTogIHRpbSwKVmFsaWQ6IHRydWUsCn0sCnNyYzogICAgIHRpbSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogIm5pbCB2YWx1ZSIsCm46ICZOdWxsVGltZXsKVGltZTogIHRpbSwKVmFsaWQ6IGZhbHNlLAp9LApzcmM6ICAgICB0aW1lLk5vdygpLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5TY2FuKHR0LnNyYyk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxUaW1lLlNjYW4oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQp9CmlmIHR0Lm4uVmFsaWQgPT0gZmFsc2UgJiYgdHQuc3JjICE9IG5pbCB7CnQuRXJyb3JmKCJzaG91bGQgcmV0dXJuIG51bGwiKQp9CmlmIHR0Lm4uVmFsaWQgJiYgdHQuc3JjICE9IHR0Lm4uVGltZSB7CnQuRXJyb3JmKCJpbnZhbGlkIHZhbHVlIikKfQp9KQp9Cn0KCmZ1bmMgVGVzdE51bGxUaW1lX01hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxUaW1lCndhbnQgICAgW11ieXRlCndhbnRFcnIgYm9vbAp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46ICZOdWxsVGltZXsKVGltZTogIHRpbWUuRGF0ZSgyMDE3LCAxMSwgMjQsIDAsIDAsIDAsIDAsIHRpbWUuVVRDKSwKVmFsaWQ6IHRydWUsCn0sCndhbnQ6ICAgIFtdYnl0ZShgIjIwMTctMTEtMjRUMDA6MDA6MDBaImApLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAidmFsaWQgbnVsbCIsCm46ICZOdWxsVGltZXsKVmFsaWQ6IGZhbHNlLAp9LAp3YW50OiAgICBbXWJ5dGUoYG51bGxgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogImludmFsaWQiLApuOiAmTnVsbFRpbWV7ClZhbGlkOiB0cnVlLAp9LAp3YW50OiAgICBbXWJ5dGUoYCIwMDAxLTAxLTAxVDAwOjAwOjAwWiJgKSwKd2FudEVycjogZmFsc2UsCn0sCn0KZm9yIF8sIHR0IDo9IHJhbmdlIHRlc3RzIHsKdC5SdW4odHQubmFtZSwgZnVuYyh0ICp0ZXN0aW5nLlQpIHsKZ290LCBlcnIgOj0gdHQubi5NYXJzaGFsSlNPTigpCmlmIChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxUaW1lLk1hcnNoYWxKU09OKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKcmV0dXJuCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGdvdCwgdHQud2FudCkgewp0LkVycm9yZigiTnVsbFRpbWUuTWFyc2hhbEpTT04oKSA9ICV2LCB3YW50ICV2IiwgZ290LCB0dC53YW50KQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbEludDY0X1VubWFyc2hhbEpTT04odCAqdGVzdGluZy5UKSB7CnRlc3RzIDo9IFtdc3RydWN0IHsKbmFtZSAgICBzdHJpbmcKbiAgICAgICBOdWxsSW50NjQKc291cmNlICBbXWJ5dGUKd2FudEVyciBib29sCn17CnsKbmFtZTogICAgInZhbGlkIiwKc291cmNlOiAgW11ieXRlKGAxMjNgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogICAgImludmFsaWQiLApzb3VyY2U6ICBbXWJ5dGUoYHsia2V5IjoidmFsdWUifWApLAp3YW50RXJyOiB0cnVlLAp9LAp7Cm5hbWU6ICAgICJlbXB0eSIsCnNvdXJjZTogIFtdYnl0ZXt9LAp3YW50RXJyOiB0cnVlLAp9LAp9CmZvciBfLCB0dCA6PSByYW5nZSB0ZXN0cyB7CnQuUnVuKHR0Lm5hbWUsIGZ1bmModCAqdGVzdGluZy5UKSB7CmlmIGVyciA6PSB0dC5uLlVubWFyc2hhbEpTT04odHQuc291cmNlKTsgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEludDY0LlVubWFyc2hhbEpTT04oKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbEludDY0X1ZhbHVlKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgTnVsbEludDY0CndhbnQgICAgZHJpdmVyLlZhbHVlCndhbnRFcnIgYm9vbAp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46IE51bGxJbnQ2NHsKVmFsaWQ6IHRydWUsCkludDY0OiAxMjMsCn0sCndhbnQ6ICAgIGRyaXZlci5WYWx1ZShpbnQ2NCgxMjMpKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogImludmFsaWQiLApuOiBOdWxsSW50NjR7ClZhbGlkOiBmYWxzZSwKfSwKd2FudDogICAgbmlsLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewpnb3QsIGVyciA6PSB0dC5uLlZhbHVlKCkKaWYgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEludDY0LlZhbHVlKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKcmV0dXJuCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGdvdCwgdHQud2FudCkgewp0LkVycm9yZigiTnVsbEludDY0LlZhbHVlKCkgPSAldiwgd2FudCAldiIsIGdvdCwgdHQud2FudCkKfQp9KQp9Cn0KCmZ1bmMgVGVzdE51bGxJbnQ2NF9TY2FuKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxJbnQ2NAp3YW50RXJyIGJvb2wKc3JjICAgICBpbnRlcmZhY2V7fQp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46ICZOdWxsSW50NjR7CkludDY0OiAxMjMsClZhbGlkOiB0cnVlLAp9LApzcmM6ICAgICBpbnQ2NCgxMjMpLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAibmlsIHZhbHVlIiwKbjogJk51bGxJbnQ2NHsKVmFsaWQ6IGZhbHNlLAp9LApzcmM6ICAgICBpbnQ2NCgxMjMpLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewppZiBlcnIgOj0gdHQubi5TY2FuKHR0LnNyYyk7IChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxJbnQ2NC5TY2FuKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKfQppZiB0dC5uLlZhbGlkID09IGZhbHNlICYmIHR0LnNyYyAhPSBuaWwgewp0LkVycm9yZigic2hvdWxkIHJldHVybiBudWxsIikKfQppZiB0dC5uLlZhbGlkICYmIHR0LnNyYyAhPSB0dC5uLkludDY0IHsKdC5FcnJvcmYoImludmFsaWQgdmFsdWUiKQp9Cn0pCn0KfQoKZnVuYyBUZXN0TnVsbEludDY0X01hcnNoYWxKU09OKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxJbnQ2NAp3YW50ICAgIFtdYnl0ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAidmFsaWQiLApuOiAmTnVsbEludDY0ewpJbnQ2NDogMTIzLApWYWxpZDogdHJ1ZSwKfSwKd2FudDogICAgW11ieXRlKGAxMjNgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogInZhbGlkIG51bGwiLApuOiAmTnVsbEludDY0ewpWYWxpZDogZmFsc2UsCn0sCndhbnQ6ICAgIFtdYnl0ZShgbnVsbGApLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAiaW52YWxpZCIsCm46ICZOdWxsSW50NjR7ClZhbGlkOiB0cnVlLAp9LAp3YW50OiAgICBbXWJ5dGUoYDBgKSwKd2FudEVycjogZmFsc2UsCn0sCn0KZm9yIF8sIHR0IDo9IHJhbmdlIHRlc3RzIHsKdC5SdW4odHQubmFtZSwgZnVuYyh0ICp0ZXN0aW5nLlQpIHsKZ290LCBlcnIgOj0gdHQubi5NYXJzaGFsSlNPTigpCmlmIChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxJbnQ2NC5NYXJzaGFsSlNPTigpIGVycm9yID0gJXYsIHdhbnRFcnIgJXYiLCBlcnIsIHR0LndhbnRFcnIpCnJldHVybgp9CmlmICFyZWZsZWN0LkRlZXBFcXVhbChnb3QsIHR0LndhbnQpIHsKdC5FcnJvcmYoIk51bGxJbnQ2NC5NYXJzaGFsSlNPTigpID0gJXYsIHdhbnQgJXYiLCBnb3QsIHR0LndhbnQpCn0KfSkKfQp9CmZ1bmMgVGVzdE51bGxGbG9hdDY0X1VubWFyc2hhbEpTT04odCAqdGVzdGluZy5UKSB7CnRlc3RzIDo9IFtdc3RydWN0IHsKbmFtZSAgICBzdHJpbmcKbiAgICAgICBOdWxsRmxvYXQ2NApzb3VyY2UgIFtdYnl0ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAgICAidmFsaWQiLApzb3VyY2U6ICBbXWJ5dGUoYDEyMy4xMjNgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogICAgImludmFsaWQiLApzb3VyY2U6ICBbXWJ5dGUoYHsia2V5IjoidmFsdWUifWApLAp3YW50RXJyOiB0cnVlLAp9LAp7Cm5hbWU6ICAgICJlbXB0eSIsCnNvdXJjZTogIFtdYnl0ZXt9LAp3YW50RXJyOiB0cnVlLAp9LAp9CmZvciBfLCB0dCA6PSByYW5nZSB0ZXN0cyB7CnQuUnVuKHR0Lm5hbWUsIGZ1bmModCAqdGVzdGluZy5UKSB7CmlmIGVyciA6PSB0dC5uLlVubWFyc2hhbEpTT04odHQuc291cmNlKTsgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEZsb2F0NjQuVW5tYXJzaGFsSlNPTigpIGVycm9yID0gJXYsIHdhbnRFcnIgJXYiLCBlcnIsIHR0LndhbnRFcnIpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsRmxvYXQ2NF9WYWx1ZSh0ICp0ZXN0aW5nLlQpIHsKdGVzdHMgOj0gW11zdHJ1Y3QgewpuYW1lICAgIHN0cmluZwpuICAgICAgIE51bGxGbG9hdDY0CndhbnQgICAgZHJpdmVyLlZhbHVlCndhbnRFcnIgYm9vbAp9ewp7Cm5hbWU6ICJ2YWxpZCIsCm46IE51bGxGbG9hdDY0ewpWYWxpZDogICB0cnVlLApGbG9hdDY0OiAxMjMuMTIzLAp9LAp3YW50OiAgICBkcml2ZXIuVmFsdWUoZmxvYXQ2NCgxMjMuMTIzKSksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJpbnZhbGlkIiwKbjogTnVsbEZsb2F0NjR7ClZhbGlkOiBmYWxzZSwKfSwKd2FudDogICAgbmlsLAp3YW50RXJyOiBmYWxzZSwKfSwKfQpmb3IgXywgdHQgOj0gcmFuZ2UgdGVzdHMgewp0LlJ1bih0dC5uYW1lLCBmdW5jKHQgKnRlc3RpbmcuVCkgewpnb3QsIGVyciA6PSB0dC5uLlZhbHVlKCkKaWYgKGVyciAhPSBuaWwpICE9IHR0LndhbnRFcnIgewp0LkVycm9yZigiTnVsbEZsb2F0NjQuVmFsdWUoKSBlcnJvciA9ICV2LCB3YW50RXJyICV2IiwgZXJyLCB0dC53YW50RXJyKQpyZXR1cm4KfQppZiAhcmVmbGVjdC5EZWVwRXF1YWwoZ290LCB0dC53YW50KSB7CnQuRXJyb3JmKCJOdWxsRmxvYXQ2NC5WYWx1ZSgpID0gJXYsIHdhbnQgJXYiLCBnb3QsIHR0LndhbnQpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsRmxvYXQ2NF9TY2FuKHQgKnRlc3RpbmcuVCkgewp0ZXN0cyA6PSBbXXN0cnVjdCB7Cm5hbWUgICAgc3RyaW5nCm4gICAgICAgKk51bGxGbG9hdDY0CndhbnRFcnIgYm9vbApzcmMgICAgIGludGVyZmFjZXt9Cn17CnsKbmFtZTogInZhbGlkIiwKbjogJk51bGxGbG9hdDY0ewpGbG9hdDY0OiAxMjMuMTIzLApWYWxpZDogICB0cnVlLAp9LApzcmM6ICAgICBmbG9hdDY0KDEyMyksCndhbnRFcnI6IGZhbHNlLAp9LAp7Cm5hbWU6ICJuaWwgdmFsdWUiLApuOiAmTnVsbEZsb2F0NjR7ClZhbGlkOiBmYWxzZSwKfSwKc3JjOiAgICAgZmxvYXQ2NCgxMjMuMTIzKSwKd2FudEVycjogZmFsc2UsCn0sCn0KZm9yIF8sIHR0IDo9IHJhbmdlIHRlc3RzIHsKdC5SdW4odHQubmFtZSwgZnVuYyh0ICp0ZXN0aW5nLlQpIHsKaWYgZXJyIDo9IHR0Lm4uU2Nhbih0dC5zcmMpOyAoZXJyICE9IG5pbCkgIT0gdHQud2FudEVyciB7CnQuRXJyb3JmKCJOdWxsRmxvYXQ2NC5TY2FuKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKfQppZiB0dC5uLlZhbGlkID09IGZhbHNlICYmIHR0LnNyYyAhPSBuaWwgewp0LkVycm9yZigic2hvdWxkIHJldHVybiBudWxsIikKfQppZiB0dC5uLlZhbGlkICYmIHR0LnNyYyAhPSB0dC5uLkZsb2F0NjQgewp0LkVycm9yZigiaW52YWxpZCB2YWx1ZSIpCn0KfSkKfQp9CgpmdW5jIFRlc3ROdWxsRmxvYXQ2NF9NYXJzaGFsSlNPTih0ICp0ZXN0aW5nLlQpIHsKdGVzdHMgOj0gW11zdHJ1Y3QgewpuYW1lICAgIHN0cmluZwpuICAgICAgICpOdWxsRmxvYXQ2NAp3YW50ICAgIFtdYnl0ZQp3YW50RXJyIGJvb2wKfXsKewpuYW1lOiAidmFsaWQiLApuOiAmTnVsbEZsb2F0NjR7CkZsb2F0NjQ6IDEyMy4xMjMsClZhbGlkOiAgIHRydWUsCn0sCndhbnQ6ICAgIFtdYnl0ZShgMTIzLjEyM2ApLAp3YW50RXJyOiBmYWxzZSwKfSwKewpuYW1lOiAidmFsaWQgbnVsbCIsCm46ICZOdWxsRmxvYXQ2NHsKVmFsaWQ6IGZhbHNlLAp9LAp3YW50OiAgICBbXWJ5dGUoYG51bGxgKSwKd2FudEVycjogZmFsc2UsCn0sCnsKbmFtZTogImludmFsaWQiLApuOiAmTnVsbEZsb2F0NjR7ClZhbGlkOiB0cnVlLAp9LAp3YW50OiAgICBbXWJ5dGUoYDBgKSwKd2FudEVycjogZmFsc2UsCn0sCn0KZm9yIF8sIHR0IDo9IHJhbmdlIHRlc3RzIHsKdC5SdW4odHQubmFtZSwgZnVuYyh0ICp0ZXN0aW5nLlQpIHsKZ290LCBlcnIgOj0gdHQubi5NYXJzaGFsSlNPTigpCmlmIChlcnIgIT0gbmlsKSAhPSB0dC53YW50RXJyIHsKdC5FcnJvcmYoIk51bGxGbG9hdDY0Lk1hcnNoYWxKU09OKCkgZXJyb3IgPSAldiwgd2FudEVyciAldiIsIGVyciwgdHQud2FudEVycikKcmV0dXJuCn0KaWYgIXJlZmxlY3QuRGVlcEVxdWFsKGdvdCwgdHQud2FudCkgewp0LkVycm9yZigiTnVsbEZsb2F0NjQuTWFyc2hhbEpTT04oKSA9ICV2LCB3YW50ICV2IiwgZ290LCB0dC53YW50KQp9Cn0pCn0KfQoKZnVuYyBUZXN0VG9OdWxsQm9vbCh0ICp0ZXN0aW5nLlQpIHsKYiA6PSB0cnVlCmJiIDo9IFRvTnVsbEJvb2woJmIpCmlmICFiYi5WYWxpZCB7CnQuRXJyb3JmKCJleHBlY3RlZCB2YWxpZCwgZ290ICV2IiwgYmIuVmFsaWQpCn0KaWYgIWJiLkJvb2wgewp0LkVycm9yZigiZXhwZWN0ZWQgdHJ1ZSwgZ290ICV2IiwgYmIuQm9vbCkKfQoKdmFyIGIyICpib29sCmJiMiA6PSBUb051bGxCb29sKGIyKQppZiBiYjIuVmFsaWQgewp0LkVycm9yZigiZXhwZWN0ZWQgbm90IHZhbGlkLCBnb3QgJXYiLCBiYjIuVmFsaWQpCn0KaWYgYmIyLkJvb2wgewp0LkVycm9yZigiZXhwZWN0ZWQgZmFsc2UsIGdvdCAldiIsIGJiMi5Cb29sKQp9Cn0KZnVuYyBUZXN0VG9OdWxsSW50NjQodCAqdGVzdGluZy5UKSB7CmIgOj0gaW50NjQoMTIzKQpiYiA6PSBUb051bGxJbnQ2NCgmYikKaWYgIWJiLlZhbGlkIHsKdC5FcnJvcmYoImV4cGVjdGVkIHZhbGlkLCBnb3QgJXYiLCBiYi5WYWxpZCkKfQppZiBiYi5JbnQ2NCAhPSAxMjMgewp0LkVycm9yZigiZXhwZWN0ZWQgMTIzLCBnb3QgJXYiLCBiYi5JbnQ2NCkKfQoKdmFyIGIyICppbnQ2NApiYjIgOj0gVG9OdWxsSW50NjQoYjIpCmlmIGJiMi5WYWxpZCB7CnQuRXJyb3JmKCJleHBlY3RlZCBub3QgdmFsaWQsIGdvdCAldiIsIGJiMi5WYWxpZCkKfQppZiBiYjIuSW50NjQgIT0gMCB7CnQuRXJyb3JmKCJleHBlY3RlZCAwLCBnb3QgJXYiLCBiYjIuSW50NjQpCn0KfQoKZnVuYyBUZXN0VG9OdWxsRmxvYXQ2NCh0ICp0ZXN0aW5nLlQpIHsKYiA6PSBmbG9hdDY0KDEyMy4xMjMpCmJiIDo9IFRvTnVsbEZsb2F0NjQoJmIpCmlmICFiYi5WYWxpZCB7CnQuRXJyb3JmKCJleHBlY3RlZCB2YWxpZCwgZ290ICV2IiwgYmIuVmFsaWQpCn0KaWYgYmIuRmxvYXQ2NCAhPSAxMjMuMTIzIHsKdC5FcnJvcmYoImV4cGVjdGVkIDEyMy4xMjMsIGdvdCAldiIsIGJiLkZsb2F0NjQpCn0KCnZhciBiMiAqZmxvYXQ2NApiYjIgOj0gVG9OdWxsRmxvYXQ2NChiMikKaWYgYmIyLlZhbGlkIHsKdC5FcnJvcmYoImV4cGVjdGVkIG5vdCB2YWxpZCwgZ290ICV2IiwgYmIyLlZhbGlkKQp9CmlmIGJiMi5GbG9hdDY0ICE9IDAgewp0LkVycm9yZigiZXhwZWN0ZWQgMCwgZ290ICV2IiwgYmIyLkZsb2F0NjQpCn0KfQpmdW5jIFRlc3RUb051bGxTdHJpbmcodCAqdGVzdGluZy5UKSB7CmIgOj0gInF3ZSIKYmIgOj0gVG9OdWxsU3RyaW5nKCZiKQppZiAhYmIuVmFsaWQgewp0LkVycm9yZigiZXhwZWN0ZWQgdmFsaWQsIGdvdCAldiIsIGJiLlZhbGlkKQp9CmlmIGJiLlN0cmluZyAhPSAicXdlIiB7CnQuRXJyb3JmKCJleHBlY3RlZCBxd2UsIGdvdCAldiIsIGJiLlN0cmluZykKfQoKdmFyIGIyICpzdHJpbmcKYmIyIDo9IFRvTnVsbFN0cmluZyhiMikKaWYgYmIyLlZhbGlkIHsKdC5FcnJvcmYoImV4cGVjdGVkIG5vdCB2YWxpZCwgZ290ICV2IiwgYmIyLlZhbGlkKQp9CmlmIGJiMi5TdHJpbmcgIT0gIiIgewp0LkVycm9yZigiZXhwZWN0ZWQgPGVtcHR5IHN0cmluZz4sIGdvdCAldiIsIGJiMi5TdHJpbmcpCiAgICB9CiAgICB9CiAgICBmdW5jIFRlc3RUb051bGxUaW1lKHQgKnRlc3RpbmcuVCkgewogICAgdGltIDo9IHRpbWUuTm93KCkKICAgIGJiIDo9IFRvTnVsbFRpbWUodGltKQogICAgaWYgIWJiLlZhbGlkIHsKICAgIHQuRXJyb3JmKCJleHBlY3RlZCB2YWxpZCwgZ290ICV2IiwgYmIuVmFsaWQpCiAgICB9CiAgICBpZiBiYi5UaW1lICE9IHRpbSB7CiAgICB0LkVycm9yZigiZXhwZWN0ZWQgJXYsIGdvdCAldiIsIHRpbSwgYmIuVGltZSkKICAgIH0KCiAgICB0aW0gPSB0aW1lLlRpbWV7fQogICAgYmIgPSBUb051bGxUaW1lKHRpbSkKICAgIGlmIGJiLlZhbGlkIHsKICAgIHQuRXJyb3JmKCJleHBlY3RlZCBpbnZhbGlkLCBnb3QgJXYiLCBiYi5WYWxpZCkKICAgIH0KICAgIGlmIGJiLlRpbWUgIT0gdGltIHsKICAgIHQuRXJyb3JmKCJleHBlY3RlZCAldiwgZ290ICV2IiwgdGltLCBiYi5UaW1lKQogICAgfQogICAgfQp7e2VuZH19Cg==\"") } diff --git a/tmpl/model.html b/tmpl/model.html index 24c08d7..8684e9b 100644 --- a/tmpl/model.html +++ b/tmpl/model.html @@ -12,7 +12,6 @@ import ( "fmt" -"database/sql" {{ range $k, $v:= .Model.Imports }} "{{$k}}" {{- end }} @@ -28,9 +27,9 @@ } // Insert a new {{.Model.Name}} row in the {{.Model.TableName}} table -func ({{.Receiver}} *{{.Model.Name}}) Insert(db *sql.DB) (lastInsertID int64, err error) { +func ({{.Receiver}} *{{.Model.Name}}) Insert(q Queryer) (lastInsertID int64, err error) { const stmt = "INSERT INTO {{.Model.TableName}} ({{.Model.Fields | insert_fields}}) VALUES ({{.Model.Fields | insert_values}})" - res, err := db.Exec(stmt, {{ . | insert_args }}) + res, err := q.Exec(stmt, {{ . | insert_args }}) if err != nil { return 0, err } @@ -38,9 +37,9 @@ } // Update an existing {{.Model.Name}} row in the {{.Model.TableName}} table. -func ({{.Receiver}} *{{.Model.Name}}) Update(db *sql.DB, id int64) (int64, error) { +func ({{.Receiver}} *{{.Model.Name}}) Update(q Queryer, id int64) (int64, error) { const stmt = "UPDATE {{.Model.TableName}} SET {{ . | update_values }} WHERE id = ?" - result, err := db.Exec(stmt, {{ . | update_args }} , id) + result, err := q.Exec(stmt, {{ . | update_args }} , id) if err != nil { return 0, err } @@ -49,9 +48,9 @@ // Upsert inserts a new {{.Model.Name}} row in the {{.Model.TableName}} table // if the unique constraints are not found, otherwise it updates it. -func ({{.Receiver}} *{{.Model.Name}}) Upsert(db *sql.DB) (lastInsertID int64, err error) { +func ({{.Receiver}} *{{.Model.Name}}) Upsert(q Queryer) (lastInsertID int64, err error) { const stmt = "INSERT INTO {{.Model.TableName}} ({{.Model.Fields | upsert_fields}}) VALUES ({{.Model.Fields | upsert_values}}) ON DUPLICATE KEY UPDATE {{ . | upsert_on_duplicate }}" - res, err := db.Exec(stmt, {{ . | upsert_args }}) + res, err := q.Exec(stmt, {{ . | upsert_args }}) if err != nil { return 0, err } @@ -59,14 +58,14 @@ } // Find an existing {{.Model.Name}} row in the {{.Model.TableName}} table -func ({{.Receiver}} *{{.Model.Name}}) Find(db *sql.DB, id int64) error { +func ({{.Receiver}} *{{.Model.Name}}) Find(q Queryer, id int64) error { const stmt = "SELECT * FROM {{.Model.TableName}} WHERE id = ?" - row := db.QueryRow(stmt, id) + row := q.QueryRow(stmt, id) return row.Scan({{ . | scan_fields}}) } // Load all, or a subset of {{.Model.Name}} rows from the {{.Model.TableName}} table -func ({{.Receiver}} *{{.Model.Name}}) Load(db *sql.DB) (set []{{.Model.Name}}, err error) { +func ({{.Receiver}} *{{.Model.Name}}) Load(q Queryer) (set []{{.Model.Name}}, err error) { stmt := "SELECT * FROM {{.Model.TableName}}" if {{.Receiver}}.limit == 0 && {{.Receiver}}.offset > 0 { @@ -83,7 +82,7 @@ {{.Receiver}}.limit = 0 {{.Receiver}}.offset = 0 }() - rows, err := db.Query(stmt) + rows, err := q.Query(stmt) if err != nil { return } @@ -99,9 +98,9 @@ } // Delete an existing {{.Model.Name}} row from the {{.Model.TableName}} table -func ({{.Receiver}} *{{.Model.Name}}) Delete(db *sql.DB, id int64) (rowsAffected int64, err error) { +func ({{.Receiver}} *{{.Model.Name}}) Delete(q Queryer, id int64) (rowsAffected int64, err error) { const stmt = "DELETE FROM {{.Model.TableName}} WHERE id = ?" - result, err := db.Exec(stmt, id) + result, err := q.Exec(stmt, id) if err != nil { return } @@ -110,9 +109,9 @@ } // Count the number of rows from the {{.Model.TableName}} table -func({{.Receiver}} *{{.Model.Name}}) Count(db *sql.DB) (count int64, err error) { +func({{.Receiver}} *{{.Model.Name}}) Count(q Queryer) (count int64, err error) { const stmt = "SELECT COUNT(*) FROM {{.Model.TableName}}" - row := db.QueryRow(stmt) + row := q.QueryRow(stmt) if err = row.Scan(&count); err != nil { return } @@ -122,10 +121,10 @@ // Exists checks for the items existence in the database, based on it's id. // An error will only be returned if a SQL related failure happens. // In all other cases, a bool and nil will return. -func({{.Receiver}} *{{.Model.Name}}) Exists(db *sql.DB, id int64) (exists bool, err error) { +func({{.Receiver}} *{{.Model.Name}}) Exists(q Queryer, id int64) (exists bool, err error) { const stmt = "SELECT EXISTS(SELECT 1 FROM {{.Model.TableName}} WHERE id = ? LIMIT 1) AS `exists`" var count int - row := db.QueryRow(stmt, id) + row := q.QueryRow(stmt, id) if err = row.Scan(&count); err != nil { return } diff --git a/tmpl/x_helpers.html b/tmpl/x_helpers.html index 08fb670..d05bbf4 100644 --- a/tmpl/x_helpers.html +++ b/tmpl/x_helpers.html @@ -11,6 +11,7 @@ "database/sql" "database/sql/driver" "encoding/json" +"log" "reflect" "strings" "time" @@ -25,6 +26,18 @@ // null for insertion into the database. var emptyTime = time.Time{} +/******** +* Types * +********/ + +// Queryer allows sql.DB and sql.Tx to be used interchangeably, allowing you +// to use any of the model methods inside transactions or standalone calls. +type Queryer interface { + Query(query string, args ...interface{}) (*sql.Rows, error) + QueryRow(query string, args ...interface{}) *sql.Row + Exec(query string, args ...interface{}) (sql.Result, error) +} + /*-------------+ | Type aliases | +-------------*/ @@ -346,4 +359,33 @@ } return NullTime(mysql.NullTime{Time: t, Valid: true}) } + +// ExecuteTransaction closes over a transaction and automatically commits +// or rollbacks depending on whether errors were encountered. +func ExecuteTransaction(db *sql.DB, f func(*sql.Tx) error) error { + tx, err := db.Begin() + if err != nil { + return err + } + + defer func() error { + if r := recover(); r != nil { + // Only need to log here because panic won't report whether + // the rollback was successful or not. + if err = tx.Rollback(); err != nil { + log.Println("db rollback error:", err) + } + panic(r) + } else if err != nil { + return fmt.Errorf("db error: %v rollback error: %v", err, tx.Rollback()) + } else { + if err = tx.Commit(); err != nil { + return err + } + } + return nil + }() + + return f(tx) +} {{end}}