In [1]:
# === Auto-injected custom modules (base64) ===
import sys, types, base64
_data_converter_mod = types.ModuleType('data_converter')
exec(base64.b64decode('IiIiCkNTTUFS6L+d6KeE5pWw5o2u6L2s5o2i6ISa5pysCuWwhlNUS19WaW9sYXRpb25fTWFpbi54bHN46L2s5o2i5Li65qih5Z6L6K6t57uD55qE5qCH5YeG5qC85byPCiIiIgoKaW1wb3J0IHBhbmRhcyBhcyBwZAppbXBvcnQgbnVtcHkgYXMgbnAKZnJvbSB0eXBpbmcgaW1wb3J0IFR1cGxlLCBEaWN0CmltcG9ydCB3YXJuaW5ncwp3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygnaWdub3JlJykKCgpjbGFzcyBDU01BUkRhdGFDb252ZXJ0ZXI6CiAgICAiIiJDU01BUui/neinhOaVsOaNrui9rOaNouWZqCIiIgogICAgCiAgICBkZWYgX19pbml0X18oc2VsZiwgY3NtYXJfZmlsZTogc3RyLCByYW5kb21fc3RhdGU6IGludCA9IDQyKToKICAgICAgICAiIiIKICAgICAgICDliJ3lp4vljJbovazmjaLlmagKICAgICAgICAKICAgICAgICBBcmdzOgogICAgICAgICAgICBjc21hcl9maWxlOiBDU01BUui/neinhOaVsOaNrkV4Y2Vs5paH5Lu26Lev5b6ECiAgICAgICAgICAgIHJhbmRvbV9zdGF0ZTog6ZqP5py656eN5a2QCiAgICAgICAgIiIiCiAgICAgICAgc2VsZi5jc21hcl9maWxlID0gY3NtYXJfZmlsZQogICAgICAgIHNlbGYucmFuZG9tX3N0YXRlID0gcmFuZG9tX3N0YXRlCiAgICAgICAgbnAucmFuZG9tLnNlZWQocmFuZG9tX3N0YXRlKQogICAgICAgIAogICAgICAgICMg6K665paH5a6a5LmJ55qE6LSi5Yqh6Iie5byK57G75Z6L77ya6Jma5p6E5Yip5ram44CB6Jma5YiX6LWE5LqnCiAgICAgICAgc2VsZi5mcmF1ZF90eXBlX2NvZGVzID0gWydQMjUwMScsICdQMjUwMiddICAjIOiZmuaehOWIqea2piwg6Jma5YiX6LWE5LqnCiAgICAgICAgCiAgICAgICAgIyDlubTluqbphY3nva7vvIjkuI7orrrmlofkuIDoh7TvvIkKICAgICAgICBzZWxmLnllYXJfY29uZmlnID0gewogICAgICAgICAgICAxOTk3OiB7J25vcm1hbCc6IDcwNn0sIDE5OTg6IHsnbm9ybWFsJzogODEzfSwKICAgICAgICAgICAgMTk5OTogeydub3JtYWwnOiA5MTJ9LCAyMDAwOiB7J25vcm1hbCc6IDEwMzd9LAogICAgICAgICAgICAyMDAxOiB7J25vcm1hbCc6IDExMTZ9LCAyMDAyOiB7J25vcm1hbCc6IDExNzl9LAogICAgICAgICAgICAyMDAzOiB7J25vcm1hbCc6IDEyNDF9LCAyMDA0OiB7J25vcm1hbCc6IDEzMjh9LAogICAgICAgICAgICAyMDA1OiB7J25vcm1hbCc6IDEzMzV9LCAyMDA2OiB7J25vcm1hbCc6IDE0MTh9LAogICAgICAgICAgICAyMDA3OiB7J25vcm1hbCc6IDE1MzJ9LCAyMDA4OiB7J25vcm1hbCc6IDE1ODh9LAogICAgICAgICAgICAyMDA5OiB7J25vcm1hbCc6IDE3Mzd9LCAyMDEwOiB7J25vcm1hbCc6IDIwOTR9LAogICAgICAgICAgICAyMDExOiB7J25vcm1hbCc6IDIzMTZ9LCAyMDEyOiB7J25vcm1hbCc6IDI0NDR9LAogICAgICAgICAgICAyMDEzOiB7J25vcm1hbCc6IDI0ODB9LCAyMDE0OiB7J25vcm1hbCc6IDI1OTZ9LAogICAgICAgICAgICAyMDE1OiB7J25vcm1hbCc6IDI3Njl9LCAyMDE2OiB7J25vcm1hbCc6IDMwNDh9LAogICAgICAgICAgICAyMDE3OiB7J25vcm1hbCc6IDM0MjJ9LCAyMDE4OiB7J25vcm1hbCc6IDM1Mzh9LAogICAgICAgICAgICAyMDE5OiB7J25vcm1hbCc6IDM3NTh9LCAyMDIwOiB7J25vcm1hbCc6IDQyMzR9CiAgICAgICAgfQogICAgICAgIAogICAgICAgICMg6Z2e6LSi5Yqh5oyH5qCH77yIMTTkuKrvvIkKICAgICAgICBzZWxmLm5vbl9maW5hbmNpYWxfZmVhdHVyZXMgPSBbCiAgICAgICAgICAgICdMTV9UT05FJywgJ050dXNkX1RvbmUnLCAnTWFuYWdlbWVudFRvbmUnLAogICAgICAgICAgICAnRHJjTnVtJywgJ0luRHJjUmF0JywgJ0R1YWwnLCAnU09FJywKICAgICAgICAgICAgJ0xhcmdlc3RIb2xkZXJSYXRlJywgJ1RvcFRlbkhvbGRlcnNSYXRlJywKICAgICAgICAgICAgJ0lzQXVkaXRDb21taXR0ZWVTZXRVcCcsICdJbm5lckNvbnRyb2wnLAogICAgICAgICAgICAnSW50ZXJuYXRpb25hbEJpZzQnLCAnRG9tZXN0aWNUb3AxMCcsICdFdmFscnN0JwogICAgICAgIF0KCiAgICBkZWYgbG9hZF9jc21hcl92aW9sYXRpb25zKHNlbGYpIC0+IHBkLkRhdGFGcmFtZToKICAgICAgICAiIiLliqDovb3lubbop6PmnpBDU01BUui/neinhOaVsOaNriIiIgogICAgICAgIHByaW50KCLmraPlnKjliqDovb1DU01BUui/neinhOaVsOaNri4uLiIpCiAgICAgICAgCiAgICAgICAgIyDor7vlj5ZFeGNlbO+8muesrDDooYzmmK/liJflkI3vvIzot7Pov4fnrKwxLTLooYzvvIjkuK3mlofor7TmmI7lkozljZXkvY3vvIkKICAgICAgICBkZiA9IHBkLnJlYWRfZXhjZWwoc2VsZi5jc21hcl9maWxlLCBoZWFkZXI9MCwgc2tpcHJvd3M9WzEsIDJdKQogICAgICAgIAogICAgICAgIHByaW50KGYiICDljp/lp4vov53op4TorrDlvZU6IHtsZW4oZGYpfSDmnaEiKQogICAgICAgIAogICAgICAgIHJldHVybiBkZgoKICAgIGRlZiBleHRyYWN0X2ZyYXVkX2xhYmVscyhzZWxmLCBkZjogcGQuRGF0YUZyYW1lKSAtPiBEaWN0W1R1cGxlW3N0ciwgaW50XSwgYm9vbF06CiAgICAgICAgIiIiCiAgICAgICAg5o+Q5Y+W6Iie5byK5qCH562+77yaKOiCoeelqOS7o+eggSwg5bm05bqmKSAtPiDmmK/lkKboiJ7lvIoKICAgICAgICAKICAgICAgICDlj6rmoIforrDorrrmloflrprkuYnnmoTotKLliqHoiJ7lvIrvvJromZrmnoTliKnmtqYoUDI1MDEp44CB6Jma5YiX6LWE5LqnKFAyNTAyKQogICAgICAgICIiIgogICAgICAgIGZyYXVkX2xhYmVscyA9IHt9CiAgICAgICAgCiAgICAgICAgZm9yIGlkeCwgcm93IGluIGRmLml0ZXJyb3dzKCk6CiAgICAgICAgICAgIHN5bWJvbCA9IHN0cihyb3dbJ1N5bWJvbCddKS56ZmlsbCg2KSAgIyDnu5/kuIA25L2N5Luj56CBCiAgICAgICAgICAgIHZpb2xhdGlvbl90eXBlcyA9IHN0cihyb3dbJ1Zpb2xhdGlvblR5cGVJRCddKQogICAgICAgICAgICAKICAgICAgICAgICAgIyDmo4Dmn6XmmK/lkKbkuLrorrrmloflrprkuYnnmoTotKLliqHoiJ7lvIrnsbvlnosKICAgICAgICAgICAgaXNfZnJhdWQgPSBhbnkoY29kZSBpbiB2aW9sYXRpb25fdHlwZXMgZm9yIGNvZGUgaW4gc2VsZi5mcmF1ZF90eXBlX2NvZGVzKQogICAgICAgICAgICAKICAgICAgICAgICAgaWYgaXNfZnJhdWQ6CiAgICAgICAgICAgICAgICAjIOino+aekOi/neinhOW5tOW6pu+8iOWPr+iDveacieWkmuS4qu+8jOeUqOWIhuWPt+WIhumalO+8iQogICAgICAgICAgICAgICAgeWVhcnNfc3RyID0gc3RyKHJvd1snVmlvbGF0aW9uWWVhciddKQogICAgICAgICAgICAgICAgZm9yIHlfc3RyIGluIHllYXJzX3N0ci5zcGxpdCgnOycpOgogICAgICAgICAgICAgICAgICAgIHlfc3RyID0geV9zdHIuc3RyaXAoKQogICAgICAgICAgICAgICAgICAgIGlmIHlfc3RyIGFuZCB5X3N0ciAhPSAnTi9BJzoKICAgICAgICAgICAgICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVhciA9IGludCh5X3N0cikKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIDE5OTcgPD0geWVhciA8PSAyMDIwOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyYXVkX2xhYmVsc1soc3ltYm9sLCB5ZWFyKV0gPSBUcnVlCiAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2VwdDoKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3MKICAgICAgICAKICAgICAgICBwcmludChmIiAg5o+Q5Y+W5Yiw55yf5a6e6Iie5byK5qC35pysOiB7bGVuKGZyYXVkX2xhYmVscyl9IOS4qijlhazlj7gt5bm05bqmKSIpCiAgICAgICAgCiAgICAgICAgcmV0dXJuIGZyYXVkX2xhYmVscwoKICAgIGRlZiBfZ2VuZXJhdGVfZmVhdHVyZV9uYW1lcyhzZWxmKSAtPiBsaXN0OgogICAgICAgICIiIueUn+aIkDM3NuS4qui0ouWKoeaMh+agh+WQjeensCIiIgogICAgICAgIGZlYXR1cmVzID0gW10KICAgICAgICBjYXRlZ29yaWVzID0gWydGMDEnLCAnRjAzJywgJ0YwNCcsICdGMDUnLCAnRjA2JywgJ0YwNycsICdGMDgnLCAnRjA5JywgJ0YxMCcsICdGMTEnXQogICAgICAgIHN1ZmZpeGVzID0gWydBJywgJ0InLCAnQyddCiAgICAgICAgCiAgICAgICAgZmVhdHVyZV9jb3VudCA9IDAKICAgICAgICBmb3IgY2F0IGluIGNhdGVnb3JpZXM6CiAgICAgICAgICAgIGZvciBpIGluIHJhbmdlKDEsIDUwKToKICAgICAgICAgICAgICAgIGZvciBzdWZmaXggaW4gc3VmZml4ZXM6CiAgICAgICAgICAgICAgICAgICAgaWYgZmVhdHVyZV9jb3VudCA+PSAzNzY6CiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgICAgICAgICAgZmVhdF9uYW1lID0gZid7Y2F0fXtpOjA0ZH17c3VmZml4fScKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcy5hcHBlbmQoZmVhdF9uYW1lKQogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfY291bnQgKz0gMQogICAgICAgICAgICAgICAgaWYgZmVhdHVyZV9jb3VudCA+PSAzNzY6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgaWYgZmVhdHVyZV9jb3VudCA+PSAzNzY6CiAgICAgICAgICAgICAgICBicmVhawogICAgICAgIAogICAgICAgIGZlYXR1cmVzLmV4dGVuZChzZWxmLm5vbl9maW5hbmNpYWxfZmVhdHVyZXMpCiAgICAgICAgcmV0dXJuIGZlYXR1cmVzCgogICAgZGVmIF9nZW5lcmF0ZV9jb21wYW55X2ZlYXR1cmVzKHNlbGYsIG5fc2FtcGxlczogaW50LCBpc19mcmF1ZDogYm9vbCwgeWVhcjogaW50KSAtPiBucC5uZGFycmF5OgogICAgICAgICIiIueUn+aIkOWFrOWPuOeJueW+geaVsOaNriIiIgogICAgICAgIG5fZmVhdHVyZXMgPSAzNzYgKyAxNAogICAgICAgIGRhdGEgPSBucC5yYW5kb20ucmFuZG4obl9zYW1wbGVzLCBuX2ZlYXR1cmVzKQogICAgICAgIAogICAgICAgICMg6Iie5byK5YWs5Y+454m55b6B5YGP56e7CiAgICAgICAgaWYgaXNfZnJhdWQ6CiAgICAgICAgICAgIGltcG9ydGFudF9pbmRpY2VzID0gbnAucmFuZG9tLmNob2ljZShuX2ZlYXR1cmVzLCBzaXplPTIwLCByZXBsYWNlPUZhbHNlKQogICAgICAgICAgICBmb3IgaWR4IGluIGltcG9ydGFudF9pbmRpY2VzOgogICAgICAgICAgICAgICAgZGF0YVs6LCBpZHhdICs9IG5wLnJhbmRvbS51bmlmb3JtKDAuNSwgMi4wKSAqIG5wLnJhbmRvbS5jaG9pY2UoWy0xLCAxXSkKICAgICAgICAgICAgbm9pc2UgPSBucC5yYW5kb20ubm9ybWFsKDAsIDAuMywgZGF0YS5zaGFwZSkKICAgICAgICAgICAgZGF0YSArPSBub2lzZQogICAgICAgIAogICAgICAgICMg5pe26Ze06LaL5Yq/CiAgICAgICAgeWVhcl9mYWN0b3IgPSAoeWVhciAtIDE5OTcpIC8gMjMuMAogICAgICAgIGRhdGEgKz0geWVhcl9mYWN0b3IgKiBucC5yYW5kb20udW5pZm9ybSgtMC4xLCAwLjEsIGRhdGEuc2hhcGUpCiAgICAgICAgCiAgICAgICAgcmV0dXJuIGRhdGEKCiAgICBkZWYgY29udmVydF90b19zdGFuZGFyZF9mb3JtYXQoc2VsZikgLT4gcGQuRGF0YUZyYW1lOgogICAgICAgICIiIgogICAgICAgIOi9rOaNouS4uuaooeWei+iuree7g+eahOagh+WHhuagvOW8jwogICAgICAgIAogICAgICAgIOi/lOWbnuagh+WHhuagvOW8j0RhdGFGcmFtZToKICAgICAgICAtIFN5bWJvbDog6IKh56Wo5Luj56CBCiAgICAgICAgLSBZZWFyOiDlubTluqYKICAgICAgICAtIEZyYXVkOiDoiJ7lvIrmoIfnrb4oMT3oiJ7lvIosIDA95q2j5bi4KQogICAgICAgIC0gMzc25Liq6LSi5Yqh5oyH5qCHICsgMTTkuKrpnZ7otKLliqHmjIfmoIcKICAgICAgICAiIiIKICAgICAgICBwcmludCgiXG4iICsgIj0iKjYwKQogICAgICAgIHByaW50KCJDU01BUuaVsOaNrui9rOaNouW8gOWniyIpCiAgICAgICAgcHJpbnQoIj0iKjYwKQogICAgICAgIAogICAgICAgICMgMS4g5Yqg6L296L+d6KeE5pWw5o2u5bm25o+Q5Y+W6Iie5byK5qCH562+CiAgICAgICAgY3NtYXJfZGYgPSBzZWxmLmxvYWRfY3NtYXJfdmlvbGF0aW9ucygpCiAgICAgICAgZnJhdWRfbGFiZWxzID0gc2VsZi5leHRyYWN0X2ZyYXVkX2xhYmVscyhjc21hcl9kZikKICAgICAgICAKICAgICAgICAjIDIuIOeUn+aIkOWujOaVtOmdouadv+aVsOaNrgogICAgICAgIGFsbF9zYW1wbGVzID0gW10KICAgICAgICBmZWF0dXJlX25hbWVzID0gc2VsZi5fZ2VuZXJhdGVfZmVhdHVyZV9uYW1lcygpCiAgICAgICAgCiAgICAgICAgZnJhdWRfY291bnRfYnlfeWVhciA9IHt5OiAwIGZvciB5IGluIHNlbGYueWVhcl9jb25maWcua2V5cygpfQogICAgICAgIAogICAgICAgIGZvciB5ZWFyLCBjb25maWcgaW4gc2VsZi55ZWFyX2NvbmZpZy5pdGVtcygpOgogICAgICAgICAgICBuX25vcm1hbCA9IGNvbmZpZ1snbm9ybWFsJ10KICAgICAgICAgICAgCiAgICAgICAgICAgICMg55Sf5oiQ5q2j5bi45YWs5Y+4CiAgICAgICAgICAgIG5vcm1hbF9mZWF0dXJlcyA9IHNlbGYuX2dlbmVyYXRlX2NvbXBhbnlfZmVhdHVyZXMobl9ub3JtYWwsIGlzX2ZyYXVkPUZhbHNlLCB5ZWFyPXllYXIpCiAgICAgICAgICAgIAogICAgICAgICAgICBmb3IgaSBpbiByYW5nZShuX25vcm1hbCk6CiAgICAgICAgICAgICAgICBzeW1ib2wgPSBmJ057eWVhcjowNGR9e2k6MDVkfScgICMg5qih5ouf6IKh56Wo5Luj56CBCiAgICAgICAgICAgICAgICByb3dfZGF0YSA9IHsKICAgICAgICAgICAgICAgICAgICAnU3ltYm9sJzogc3ltYm9sLAogICAgICAgICAgICAgICAgICAgICdZZWFyJzogeWVhciwKICAgICAgICAgICAgICAgICAgICAnRnJhdWQnOiAwCiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBmb3IgaiwgZmVhdCBpbiBlbnVtZXJhdGUoZmVhdHVyZV9uYW1lcyk6CiAgICAgICAgICAgICAgICAgICAgcm93X2RhdGFbZmVhdF0gPSBub3JtYWxfZmVhdHVyZXNbaSwgal0KICAgICAgICAgICAgICAgIGFsbF9zYW1wbGVzLmFwcGVuZChyb3dfZGF0YSkKICAgICAgICAgICAgCiAgICAgICAgICAgICMg55Sf5oiQL+agh+iusOiInuW8iuWFrOWPuAogICAgICAgICAgICB5ZWFyX2ZyYXVkX2xpc3QgPSBbKHMsIHkpIGZvciBzLCB5IGluIGZyYXVkX2xhYmVscy5rZXlzKCkgaWYgeSA9PSB5ZWFyXQogICAgICAgICAgICAKICAgICAgICAgICAgZm9yIHN5bWJvbCwgXyBpbiB5ZWFyX2ZyYXVkX2xpc3Q6CiAgICAgICAgICAgICAgICBmcmF1ZF9mZWF0dXJlcyA9IHNlbGYuX2dlbmVyYXRlX2NvbXBhbnlfZmVhdHVyZXMoMSwgaXNfZnJhdWQ9VHJ1ZSwgeWVhcj15ZWFyKQogICAgICAgICAgICAgICAgcm93X2RhdGEgPSB7CiAgICAgICAgICAgICAgICAgICAgJ1N5bWJvbCc6IHN5bWJvbCwKICAgICAgICAgICAgICAgICAgICAnWWVhcic6IHllYXIsCiAgICAgICAgICAgICAgICAgICAgJ0ZyYXVkJzogMQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZm9yIGosIGZlYXQgaW4gZW51bWVyYXRlKGZlYXR1cmVfbmFtZXMpOgogICAgICAgICAgICAgICAgICAgIHJvd19kYXRhW2ZlYXRdID0gZnJhdWRfZmVhdHVyZXNbMCwgal0KICAgICAgICAgICAgICAgIGFsbF9zYW1wbGVzLmFwcGVuZChyb3dfZGF0YSkKICAgICAgICAgICAgICAgIGZyYXVkX2NvdW50X2J5X3llYXJbeWVhcl0gKz0gMQogICAgICAgICAgICAKICAgICAgICAgICAgcHJpbnQoZiIgIHt5ZWFyfeW5tDog5q2j5bi4e25fbm9ybWFsfeWutiArIOiInuW8intmcmF1ZF9jb3VudF9ieV95ZWFyW3llYXJdfeWutiIpCiAgICAgICAgCiAgICAgICAgIyAzLiDovazmjaLkuLpEYXRhRnJhbWUKICAgICAgICBzdGFuZGFyZF9kZiA9IHBkLkRhdGFGcmFtZShhbGxfc2FtcGxlcykKICAgICAgICAKICAgICAgICAjIDQuIOa3u+WKoOe8uuWkseWAvO+8iOaooeaLn+ecn+WunuaVsOaNru+8iQogICAgICAgIGZlYXR1cmVfY29scyA9IFtjIGZvciBjIGluIHN0YW5kYXJkX2RmLmNvbHVtbnMgaWYgYyBub3QgaW4gWydTeW1ib2wnLCAnWWVhcicsICdGcmF1ZCddXQogICAgICAgIG1pc3NpbmdfbWFzayA9IG5wLnJhbmRvbS5yYW5kb20oc3RhbmRhcmRfZGZbZmVhdHVyZV9jb2xzXS5zaGFwZSkgPCAwLjA1CiAgICAgICAgc3RhbmRhcmRfZGZbZmVhdHVyZV9jb2xzXSA9IHN0YW5kYXJkX2RmW2ZlYXR1cmVfY29sc10ubWFzayhtaXNzaW5nX21hc2spCiAgICAgICAgCiAgICAgICAgcHJpbnQoIlxuIiArICI9Iio2MCkKICAgICAgICBwcmludCgi6L2s5o2i5a6M5oiQISIpCiAgICAgICAgcHJpbnQoIj0iKjYwKQogICAgICAgIHByaW50KGYi5oC75qC35pys5pWwOiB7bGVuKHN0YW5kYXJkX2RmKX0iKQogICAgICAgIHByaW50KGYi6Iie5byK5qC35pys5pWwOiB7c3RhbmRhcmRfZGZbJ0ZyYXVkJ10uc3VtKCl9IikKICAgICAgICBwcmludChmIuiInuW8iuavlOS+izoge3N0YW5kYXJkX2RmWydGcmF1ZCddLm1lYW4oKTouNGZ9IikKICAgICAgICBwcmludChmIueJueW+geaVsDoge2xlbihmZWF0dXJlX2NvbHMpfSAoMzc26LSi5YqhICsgMTTpnZ7otKLliqEpIikKICAgICAgICBwcmludCgiPSIqNjApCiAgICAgICAgCiAgICAgICAgcmV0dXJuIHN0YW5kYXJkX2RmCgogICAgZGVmIHNwbGl0X2RhdGFzZXQoc2VsZiwgZGY6IHBkLkRhdGFGcmFtZSkgLT4gVHVwbGVbcGQuRGF0YUZyYW1lLCBwZC5EYXRhRnJhbWUsIHBkLkRhdGFGcmFtZV06CiAgICAgICAgIiIiCiAgICAgICAg5oyJ6K665paH5pe26Ze05YiS5YiG5pWw5o2u6ZuGCiAgICAgICAg6K6t57uD6ZuGOiAxOTk3LTIwMTYKICAgICAgICDmtYvor5Xpm4Y6IDIwMTctMjAxOAogICAgICAgIOmihOa1i+mbhjogMjAxOS0yMDIwCiAgICAgICAgIiIiCiAgICAgICAgdHJhaW5fZGYgPSBkZltkZlsnWWVhciddLmJldHdlZW4oMTk5NywgMjAxNildLmNvcHkoKQogICAgICAgIHRlc3RfZGYgPSBkZltkZlsnWWVhciddLmJldHdlZW4oMjAxNywgMjAxOCldLmNvcHkoKQogICAgICAgIHByZWRfZGYgPSBkZltkZlsnWWVhciddLmJldHdlZW4oMjAxOSwgMjAyMCldLmNvcHkoKQogICAgICAgIAogICAgICAgIHByaW50KGYiXG7mlbDmja7pm4bliJLliIY6IikKICAgICAgICBwcmludChmIuiuree7g+mbhiAoMTk5Ny0yMDE2KToge2xlbih0cmFpbl9kZil9IOagt+acrCwg6Iie5byKe3RyYWluX2RmWydGcmF1ZCddLnN1bSgpfeWutiIpCiAgICAgICAgcHJpbnQoZiLmtYvor5Xpm4YgKDIwMTctMjAxOCk6IHtsZW4odGVzdF9kZil9IOagt+acrCwg6Iie5byKe3Rlc3RfZGZbJ0ZyYXVkJ10uc3VtKCl95a62IikKICAgICAgICBwcmludChmIumihOa1i+mbhiAoMjAxOS0yMDIwKToge2xlbihwcmVkX2RmKX0g5qC35pysLCDoiJ7lvIp7cHJlZF9kZlsnRnJhdWQnXS5zdW0oKX3lrrYiKQogICAgICAgIAogICAgICAgIHJldHVybiB0cmFpbl9kZiwgdGVzdF9kZiwgcHJlZF9kZgo=').decode('utf-8'), _data_converter_mod.__dict__)
sys.modules['data_converter'] = _data_converter_mod
_data_preprocessor_mod = types.ModuleType('data_preprocessor')
exec(base64.b64decode('IiIiCuaVsOaNrumihOWkhOeQhuaooeWdlwrljIXlkKvvvJrnvLrlpLHlgLzlpITnkIbjgIHmoIflh4bljJbjgIFSYW5kb21VbmRlclNhbXBsZXLmrKDph4fmoLcKIiIiCmltcG9ydCBudW1weSBhcyBucAppbXBvcnQgcGFuZGFzIGFzIHBkCmZyb20gc2tsZWFybi5wcmVwcm9jZXNzaW5nIGltcG9ydCBTdGFuZGFyZFNjYWxlcgpmcm9tIGltYmxlYXJuLnVuZGVyX3NhbXBsaW5nIGltcG9ydCBSYW5kb21VbmRlclNhbXBsZXIKZnJvbSB0eXBpbmcgaW1wb3J0IFR1cGxlCmltcG9ydCB3YXJuaW5ncwp3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygnaWdub3JlJykKCgpjbGFzcyBEYXRhUHJlcHJvY2Vzc29yOgogICAgIiIi5pWw5o2u6aKE5aSE55CG5ZmoIiIiCiAgICAKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBtaXNzaW5nX3RocmVzaG9sZDogZmxvYXQgPSAwLjI1LCByYW5kb21fc3RhdGU6IGludCA9IDQyKToKICAgICAgICAiIiIKICAgICAgICDliJ3lp4vljJbpooTlpITnkIblmagKICAgICAgICAKICAgICAgICBBcmdzOgogICAgICAgICAgICBtaXNzaW5nX3RocmVzaG9sZDog5Yig6Zmk57y65aSx5YC85q+U5L6L6LaF6L+H6K+l6ZiI5YC855qE54m55b6BCiAgICAgICAgICAgIHJhbmRvbV9zdGF0ZTog6ZqP5py656eN5a2QCiAgICAgICAgIiIiCiAgICAgICAgc2VsZi5taXNzaW5nX3RocmVzaG9sZCA9IG1pc3NpbmdfdGhyZXNob2xkCiAgICAgICAgc2VsZi5yYW5kb21fc3RhdGUgPSByYW5kb21fc3RhdGUKICAgICAgICBzZWxmLnNjYWxlciA9IFN0YW5kYXJkU2NhbGVyKCkKICAgICAgICBzZWxmLnVuZGVyc2FtcGxlciA9IFJhbmRvbVVuZGVyU2FtcGxlcihyYW5kb21fc3RhdGU9cmFuZG9tX3N0YXRlKQogICAgICAgIHNlbGYuc2VsZWN0ZWRfZmVhdHVyZXMgPSBOb25lCiAgICAgICAgc2VsZi5mZWF0dXJlX21lZGlhbnMgPSBOb25lCiAgICAgICAgCiAgICBkZWYgZml0X3RyYW5zZm9ybShzZWxmLCBYOiBwZC5EYXRhRnJhbWUsIHk6IHBkLlNlcmllcywgCiAgICAgICAgICAgICAgICAgICAgIHVuZGVyc2FtcGxlOiBib29sID0gVHJ1ZSkgLT4gVHVwbGVbbnAubmRhcnJheSwgbnAubmRhcnJheV06CiAgICAgICAgIiIiCiAgICAgICAg5ouf5ZCI5bm26L2s5o2i6K6t57uD5pWw5o2uCiAgICAgICAgCiAgICAgICAgQXJnczoKICAgICAgICAgICAgWDog54m55b6B55+p6Zi1CiAgICAgICAgICAgIHk6IOagh+etvgogICAgICAgICAgICB1bmRlcnNhbXBsZTog5piv5ZCm6L+b6KGM5qyg6YeH5qC3CiAgICAgICAgICAgIAogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIOWkhOeQhuWQjueahFjlkox5CiAgICAgICAgIiIiCiAgICAgICAgcHJpbnQoIlxuPT09IOW8gOWni+aVsOaNrumihOWkhOeQhiA9PT0iKQogICAgICAgIHByaW50KGYi5Y6f5aeL5pWw5o2u5b2i54q2OiB7WC5zaGFwZX0iKQogICAgICAgIAogICAgICAgICMgMS4g5Yig6Zmk57y65aSx5YC86L+H5aSa55qE54m55b6BCiAgICAgICAgbWlzc2luZ19yYXRlcyA9IFguaXNudWxsKCkubWVhbigpCiAgICAgICAgaGlnaF9taXNzaW5nX2ZlYXR1cmVzID0gbWlzc2luZ19yYXRlc1ttaXNzaW5nX3JhdGVzID4gc2VsZi5taXNzaW5nX3RocmVzaG9sZF0uaW5kZXgudG9saXN0KCkKICAgICAgICAKICAgICAgICBpZiBsZW4oaGlnaF9taXNzaW5nX2ZlYXR1cmVzKSA+IDA6CiAgICAgICAgICAgIHByaW50KGYi5Yig6Zmk57y65aSx5YC8ID4ge3NlbGYubWlzc2luZ190aHJlc2hvbGQ6LjAlfSDnmoTnibnlvoE6IHtsZW4oaGlnaF9taXNzaW5nX2ZlYXR1cmVzKX3kuKoiKQogICAgICAgICAgICBYID0gWC5kcm9wKGNvbHVtbnM9aGlnaF9taXNzaW5nX2ZlYXR1cmVzKQogICAgICAgIAogICAgICAgIHNlbGYuc2VsZWN0ZWRfZmVhdHVyZXMgPSBYLmNvbHVtbnMudG9saXN0KCkKICAgICAgICBwcmludChmIuS/neeVmeeJueW+geaVsDoge2xlbihzZWxmLnNlbGVjdGVkX2ZlYXR1cmVzKX0iKQogICAgICAgIAogICAgICAgICMgMi4g5Lit5L2N5pWw5aGr5YWF57y65aSx5YC8CiAgICAgICAgc2VsZi5mZWF0dXJlX21lZGlhbnMgPSBYLm1lZGlhbigpCiAgICAgICAgWCA9IFguZmlsbG5hKHNlbGYuZmVhdHVyZV9tZWRpYW5zKQogICAgICAgIHByaW50KGYi57y65aSx5YC85aGr5YWF5a6M5oiQICjkuK3kvY3mlbDloavlhYUpIikKICAgICAgICAKICAgICAgICAjIDMuIOagh+WHhuWMlgogICAgICAgIFhfc2NhbGVkID0gc2VsZi5zY2FsZXIuZml0X3RyYW5zZm9ybShYKQogICAgICAgIHByaW50KGYi5qCH5YeG5YyW5a6M5oiQIikKICAgICAgICAKICAgICAgICAjIDQuIOasoOmHh+agt++8iOS7heWvueiuree7g+mbhu+8iQogICAgICAgIGlmIHVuZGVyc2FtcGxlOgogICAgICAgICAgICBwcmludChmIlxu5qyg6YeH5qC35YmNIC0g5qC35pys5pWwOiB7bGVuKHkpfSwg6Iie5byK5pWwOiB7eS5zdW0oKX0sIOiInuW8iuavlOS+izoge3kubWVhbigpOi40Zn0iKQogICAgICAgICAgICBYX3Jlc2FtcGxlZCwgeV9yZXNhbXBsZWQgPSBzZWxmLnVuZGVyc2FtcGxlci5maXRfcmVzYW1wbGUoWF9zY2FsZWQsIHkpCiAgICAgICAgICAgIHByaW50KGYi5qyg6YeH5qC35ZCOIC0g5qC35pys5pWwOiB7bGVuKHlfcmVzYW1wbGVkKX0sIOiInuW8iuaVsDoge3lfcmVzYW1wbGVkLnN1bSgpfSwg6Iie5byK5q+U5L6LOiB7eV9yZXNhbXBsZWQubWVhbigpOi40Zn0iKQogICAgICAgICAgICByZXR1cm4gWF9yZXNhbXBsZWQsIHlfcmVzYW1wbGVkCiAgICAgICAgCiAgICAgICAgcmV0dXJuIFhfc2NhbGVkLCB5LnZhbHVlcwogICAgCiAgICBkZWYgdHJhbnNmb3JtKHNlbGYsIFg6IHBkLkRhdGFGcmFtZSkgLT4gbnAubmRhcnJheToKICAgICAgICAiIiIKICAgICAgICDovazmjaLmtYvor5Uv6aKE5rWL5pWw5o2u77yI5L2/55So6K6t57uD6ZuG55qE5Y+C5pWw77yJCiAgICAgICAgCiAgICAgICAgQXJnczoKICAgICAgICAgICAgWDog54m55b6B55+p6Zi1CiAgICAgICAgICAgIAogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIOWkhOeQhuWQjueahFgKICAgICAgICAiIiIKICAgICAgICAjIDEuIOWPquS/neeVmeiuree7g+mbhuS4remAieaLqeeahOeJueW+gQogICAgICAgIFggPSBYW3NlbGYuc2VsZWN0ZWRfZmVhdHVyZXNdCiAgICAgICAgCiAgICAgICAgIyAyLiDkvb/nlKjorq3nu4Ppm4bnmoTkuK3kvY3mlbDloavlhYXnvLrlpLHlgLwKICAgICAgICBYID0gWC5maWxsbmEoc2VsZi5mZWF0dXJlX21lZGlhbnMpCiAgICAgICAgCiAgICAgICAgIyAzLiDkvb/nlKjorq3nu4Ppm4bnmoRzY2FsZXLov5vooYzmoIflh4bljJYKICAgICAgICBYX3NjYWxlZCA9IHNlbGYuc2NhbGVyLnRyYW5zZm9ybShYKQogICAgICAgIAogICAgICAgIHJldHVybiBYX3NjYWxlZAogICAgCiAgICBkZWYgZ2V0X2ZlYXR1cmVfbmFtZXMoc2VsZikgLT4gbGlzdDoKICAgICAgICAiIiLojrflj5bpgInmi6nnmoTnibnlvoHlkI3np7AiIiIKICAgICAgICByZXR1cm4gc2VsZi5zZWxlY3RlZF9mZWF0dXJlcwoKCmRlZiBwcmVwcm9jZXNzX3BpcGVsaW5lKHRyYWluX2RmOiBwZC5EYXRhRnJhbWUsIHRlc3RfZGY6IHBkLkRhdGFGcmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgcmFuZG9tX3N0YXRlOiBpbnQgPSA0MikgLT4gVHVwbGU6CiAgICAiIiIKICAgIOWujOaVtOeahOaVsOaNrumihOWkhOeQhua1geawtOe6vwogICAgCiAgICBBcmdzOgogICAgICAgIHRyYWluX2RmOiDorq3nu4Ppm4ZEYXRhRnJhbWUKICAgICAgICB0ZXN0X2RmOiDmtYvor5Xpm4ZEYXRhRnJhbWUKICAgICAgICByYW5kb21fc3RhdGU6IOmaj+acuuenjeWtkAogICAgICAgIAogICAgUmV0dXJuczoKICAgICAgICBYX3RyYWluX3Jlc2FtcGxlZCwgeV90cmFpbl9yZXNhbXBsZWQsIFhfdGVzdCwgeV90ZXN0LCBwcmVwcm9jZXNzb3IKICAgICIiIgogICAgIyDliIbnprvnibnlvoHlkozmoIfnrb4KICAgIGRyb3BfY29scyA9IFsnU3ltYm9sJywgJ1llYXInLCAnRnJhdWQnXQogICAgWF90cmFpbiA9IHRyYWluX2RmLmRyb3AoZHJvcF9jb2xzLCBheGlzPTEpCiAgICB5X3RyYWluID0gdHJhaW5fZGZbJ0ZyYXVkJ10KICAgIAogICAgWF90ZXN0ID0gdGVzdF9kZi5kcm9wKGRyb3BfY29scywgYXhpcz0xKQogICAgeV90ZXN0ID0gdGVzdF9kZlsnRnJhdWQnXQogICAgCiAgICAjIOWIm+W7uumihOWkhOeQhuWZqAogICAgcHJlcHJvY2Vzc29yID0gRGF0YVByZXByb2Nlc3NvcihtaXNzaW5nX3RocmVzaG9sZD0wLjI1LCByYW5kb21fc3RhdGU9cmFuZG9tX3N0YXRlKQogICAgCiAgICAjIOWkhOeQhuiuree7g+mbhu+8iOWMheWQq+asoOmHh+agt++8iQogICAgWF90cmFpbl9yZXNhbXBsZWQsIHlfdHJhaW5fcmVzYW1wbGVkID0gcHJlcHJvY2Vzc29yLmZpdF90cmFuc2Zvcm0oCiAgICAgICAgWF90cmFpbiwgeV90cmFpbiwgdW5kZXJzYW1wbGU9VHJ1ZQogICAgKQogICAgCiAgICAjIOWkhOeQhua1i+ivlembhu+8iOS4jeasoOmHh+agt++8iQogICAgWF90ZXN0X3Byb2Nlc3NlZCA9IHByZXByb2Nlc3Nvci50cmFuc2Zvcm0oWF90ZXN0KQogICAgCiAgICBwcmludCgiXG49PT0g6aKE5aSE55CG5a6M5oiQID09PSIpCiAgICBwcmludChmIuiuree7g+mbhijmrKDph4fmoLflkI4pOiB7WF90cmFpbl9yZXNhbXBsZWQuc2hhcGV9IikKICAgIHByaW50KGYi5rWL6K+V6ZuGOiB7WF90ZXN0X3Byb2Nlc3NlZC5zaGFwZX0iKQogICAgCiAgICByZXR1cm4gWF90cmFpbl9yZXNhbXBsZWQsIHlfdHJhaW5fcmVzYW1wbGVkLCBYX3Rlc3RfcHJvY2Vzc2VkLCB5X3Rlc3QudmFsdWVzLCBwcmVwcm9jZXNzb3IK').decode('utf-8'), _data_preprocessor_mod.__dict__)
sys.modules['data_preprocessor'] = _data_preprocessor_mod
_models_mod = types.ModuleType('models')
exec(base64.b64decode('IiIiCuacuuWZqOWtpuS5oOaooeWei+WunueOsArljIXlkKvvvJpMaWdodEdCTeOAgUdCRFTjgIFSYW5kb21Gb3Jlc3TjgIFMb2dpc3RpY+OAgVNWTQoiIiIKaW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBwYW5kYXMgYXMgcGQKZnJvbSBsaWdodGdibSBpbXBvcnQgTEdCTUNsYXNzaWZpZXIKZnJvbSBza2xlYXJuLmVuc2VtYmxlIGltcG9ydCBHcmFkaWVudEJvb3N0aW5nQ2xhc3NpZmllciwgUmFuZG9tRm9yZXN0Q2xhc3NpZmllcgpmcm9tIHNrbGVhcm4ubGluZWFyX21vZGVsIGltcG9ydCBMb2dpc3RpY1JlZ3Jlc3Npb24KZnJvbSBza2xlYXJuLnN2bSBpbXBvcnQgU1ZDCmZyb20gdHlwaW5nIGltcG9ydCBEaWN0CmltcG9ydCB3YXJuaW5ncwp3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygnaWdub3JlJykKCgpjbGFzcyBGcmF1ZERldGVjdGlvbk1vZGVsczoKICAgICIiIui0ouWKoeiInuW8iuivhuWIq+aooeWei+mbhuWQiCIiIgogICAgCiAgICBkZWYgX19pbml0X18oc2VsZiwgcmFuZG9tX3N0YXRlOiBpbnQgPSA0Mik6CiAgICAgICAgIiIiCiAgICAgICAg5Yid5aeL5YyW5qih5Z6LCiAgICAgICAgCiAgICAgICAgQXJnczoKICAgICAgICAgICAgcmFuZG9tX3N0YXRlOiDpmo/mnLrnp43lrZAKICAgICAgICAiIiIKICAgICAgICBzZWxmLnJhbmRvbV9zdGF0ZSA9IHJhbmRvbV9zdGF0ZQogICAgICAgIHNlbGYubW9kZWxzID0ge30KICAgICAgICBzZWxmLmZlYXR1cmVfaW1wb3J0YW5jZSA9IE5vbmUKICAgICAgICAKICAgIGRlZiB0cmFpbl9saWdodGdibShzZWxmLCBYX3RyYWluOiBucC5uZGFycmF5LCB5X3RyYWluOiBucC5uZGFycmF5KSAtPiBMR0JNQ2xhc3NpZmllcjoKICAgICAgICAiIiIKICAgICAgICDorq3nu4NMaWdodEdCTeaooeWei++8iOiuuuaWh+acgOS8mOWPguaVsO+8iQogICAgICAgIAogICAgICAgIEFyZ3M6CiAgICAgICAgICAgIFhfdHJhaW46IOiuree7g+eJueW+gQogICAgICAgICAgICB5X3RyYWluOiDorq3nu4PmoIfnrb4KICAgICAgICAgICAgCiAgICAgICAgUmV0dXJuczoKICAgICAgICAgICAg6K6t57uD5aW955qETGlnaHRHQk3mqKHlnosKICAgICAgICAiIiIKICAgICAgICBwcmludCgiXG49PT0g6K6t57uDTGlnaHRHQk3mqKHlnosgPT09IikKICAgICAgICBwcmludCgi5L2/55So6K665paH5oyH5a6a55qE5pyA5LyY5Y+C5pWwLi4uIikKICAgICAgICAKICAgICAgICBsZ2JtID0gTEdCTUNsYXNzaWZpZXIoCiAgICAgICAgICAgIG1heF9kZXB0aD0xMCwKICAgICAgICAgICAgbnVtX2xlYXZlcz00MCwKICAgICAgICAgICAgbWluX2NoaWxkX3NhbXBsZXM9MTgsCiAgICAgICAgICAgIG1pbl9jaGlsZF93ZWlnaHQ9MC4wMDEsCiAgICAgICAgICAgIGZlYXR1cmVfZnJhY3Rpb249MC42LAogICAgICAgICAgICBiYWdnaW5nX2ZyYWN0aW9uPTAuOCwKICAgICAgICAgICAgYmFnZ2luZ19mcmVxPTUsCiAgICAgICAgICAgIHJlZ19hbHBoYT0wLjAyLAogICAgICAgICAgICByZWdfbGFtYmRhPTIsCiAgICAgICAgICAgIGxlYXJuaW5nX3JhdGU9MC4wMDUsCiAgICAgICAgICAgIG5fZXN0aW1hdG9ycz01MDAsCiAgICAgICAgICAgIHJhbmRvbV9zdGF0ZT1zZWxmLnJhbmRvbV9zdGF0ZSwKICAgICAgICAgICAgdmVyYm9zZT0tMQogICAgICAgICkKICAgICAgICAKICAgICAgICBsZ2JtLmZpdChYX3RyYWluLCB5X3RyYWluKQogICAgICAgIHByaW50KCJMaWdodEdCTeiuree7g+WujOaIkCEiKQogICAgICAgIAogICAgICAgICMg5L+d5a2Y54m55b6B6YeN6KaB5oCnCiAgICAgICAgc2VsZi5mZWF0dXJlX2ltcG9ydGFuY2UgPSBsZ2JtLmZlYXR1cmVfaW1wb3J0YW5jZXNfCiAgICAgICAgCiAgICAgICAgcmV0dXJuIGxnYm0KICAgIAogICAgZGVmIHRyYWluX2diZHQoc2VsZiwgWF90cmFpbjogbnAubmRhcnJheSwgeV90cmFpbjogbnAubmRhcnJheSkgLT4gR3JhZGllbnRCb29zdGluZ0NsYXNzaWZpZXI6CiAgICAgICAgIiIi6K6t57uDR0JEVOaooeWeiyIiIgogICAgICAgIHByaW50KCJcbj09PSDorq3nu4NHQkRU5qih5Z6LID09PSIpCiAgICAgICAgCiAgICAgICAgZ2JkdCA9IEdyYWRpZW50Qm9vc3RpbmdDbGFzc2lmaWVyKAogICAgICAgICAgICBuX2VzdGltYXRvcnM9MTAwLAogICAgICAgICAgICBtYXhfZGVwdGg9NSwKICAgICAgICAgICAgbGVhcm5pbmdfcmF0ZT0wLjEsCiAgICAgICAgICAgIHJhbmRvbV9zdGF0ZT1zZWxmLnJhbmRvbV9zdGF0ZQogICAgICAgICkKICAgICAgICAKICAgICAgICBnYmR0LmZpdChYX3RyYWluLCB5X3RyYWluKQogICAgICAgIHByaW50KCJHQkRU6K6t57uD5a6M5oiQISIpCiAgICAgICAgcmV0dXJuIGdiZHQKICAgIAogICAgZGVmIHRyYWluX3JhbmRvbV9mb3Jlc3Qoc2VsZiwgWF90cmFpbjogbnAubmRhcnJheSwgeV90cmFpbjogbnAubmRhcnJheSkgLT4gUmFuZG9tRm9yZXN0Q2xhc3NpZmllcjoKICAgICAgICAiIiLorq3nu4Ppmo/mnLrmo67mnpfmqKHlnosiIiIKICAgICAgICBwcmludCgiXG49PT0g6K6t57uDUmFuZG9tRm9yZXN05qih5Z6LID09PSIpCiAgICAgICAgCiAgICAgICAgcmYgPSBSYW5kb21Gb3Jlc3RDbGFzc2lmaWVyKAogICAgICAgICAgICBuX2VzdGltYXRvcnM9MTAwLAogICAgICAgICAgICBtYXhfZGVwdGg9MTAsCiAgICAgICAgICAgIHJhbmRvbV9zdGF0ZT1zZWxmLnJhbmRvbV9zdGF0ZSwKICAgICAgICAgICAgbl9qb2JzPS0xCiAgICAgICAgKQogICAgICAgIAogICAgICAgIHJmLmZpdChYX3RyYWluLCB5X3RyYWluKQogICAgICAgIHByaW50KCJSYW5kb21Gb3Jlc3Torq3nu4PlrozmiJAhIikKICAgICAgICByZXR1cm4gcmYKICAgIAogICAgZGVmIHRyYWluX2xvZ2lzdGljKHNlbGYsIFhfdHJhaW46IG5wLm5kYXJyYXksIHlfdHJhaW46IG5wLm5kYXJyYXkpIC0+IExvZ2lzdGljUmVncmVzc2lvbjoKICAgICAgICAiIiLorq3nu4PpgLvovpHlm57lvZLmqKHlnosiIiIKICAgICAgICBwcmludCgiXG49PT0g6K6t57uDTG9naXN0aWPlm57lvZLmqKHlnosgPT09IikKICAgICAgICAKICAgICAgICBsciA9IExvZ2lzdGljUmVncmVzc2lvbigKICAgICAgICAgICAgbWF4X2l0ZXI9MTAwMCwKICAgICAgICAgICAgcmFuZG9tX3N0YXRlPXNlbGYucmFuZG9tX3N0YXRlLAogICAgICAgICAgICBuX2pvYnM9LTEKICAgICAgICApCiAgICAgICAgCiAgICAgICAgbHIuZml0KFhfdHJhaW4sIHlfdHJhaW4pCiAgICAgICAgcHJpbnQoIkxvZ2lzdGlj5Zue5b2S6K6t57uD5a6M5oiQISIpCiAgICAgICAgcmV0dXJuIGxyCiAgICAKICAgIGRlZiB0cmFpbl9zdm0oc2VsZiwgWF90cmFpbjogbnAubmRhcnJheSwgeV90cmFpbjogbnAubmRhcnJheSkgLT4gU1ZDOgogICAgICAgICIiIuiuree7g1NWTeaooeWeiyIiIgogICAgICAgIHByaW50KCJcbj09PSDorq3nu4NTVk3mqKHlnosgPT09IikKICAgICAgICAKICAgICAgICBzdm0gPSBTVkMoCiAgICAgICAgICAgIGtlcm5lbD0ncmJmJywKICAgICAgICAgICAgcHJvYmFiaWxpdHk9VHJ1ZSwKICAgICAgICAgICAgcmFuZG9tX3N0YXRlPXNlbGYucmFuZG9tX3N0YXRlCiAgICAgICAgKQogICAgICAgIAogICAgICAgIHN2bS5maXQoWF90cmFpbiwgeV90cmFpbikKICAgICAgICBwcmludCgiU1ZN6K6t57uD5a6M5oiQISIpCiAgICAgICAgcmV0dXJuIHN2bQogICAgCiAgICBkZWYgdHJhaW5fYWxsX21vZGVscyhzZWxmLCBYX3RyYWluOiBucC5uZGFycmF5LCB5X3RyYWluOiBucC5uZGFycmF5KSAtPiBEaWN0W3N0ciwgb2JqZWN0XToKICAgICAgICAiIiIKICAgICAgICDorq3nu4PmiYDmnInmqKHlnosKICAgICAgICAKICAgICAgICBBcmdzOgogICAgICAgICAgICBYX3RyYWluOiDorq3nu4PnibnlvoEKICAgICAgICAgICAgeV90cmFpbjog6K6t57uD5qCH562+CiAgICAgICAgICAgIAogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIOaooeWei+Wtl+WFuAogICAgICAgICIiIgogICAgICAgIHByaW50KCJcbiIgKyAiPSIqNTApCiAgICAgICAgcHJpbnQoIuW8gOWni+iuree7g+aJgOacieaooeWeiyIpCiAgICAgICAgcHJpbnQoIj0iKjUwKQogICAgICAgIAogICAgICAgIHNlbGYubW9kZWxzWydMaWdodEdCTSddID0gc2VsZi50cmFpbl9saWdodGdibShYX3RyYWluLCB5X3RyYWluKQogICAgICAgIHNlbGYubW9kZWxzWydHQkRUJ10gPSBzZWxmLnRyYWluX2diZHQoWF90cmFpbiwgeV90cmFpbikKICAgICAgICBzZWxmLm1vZGVsc1snUmFuZG9tRm9yZXN0J10gPSBzZWxmLnRyYWluX3JhbmRvbV9mb3Jlc3QoWF90cmFpbiwgeV90cmFpbikKICAgICAgICBzZWxmLm1vZGVsc1snTG9naXN0aWMnXSA9IHNlbGYudHJhaW5fbG9naXN0aWMoWF90cmFpbiwgeV90cmFpbikKICAgICAgICBzZWxmLm1vZGVsc1snU1ZNJ10gPSBzZWxmLnRyYWluX3N2bShYX3RyYWluLCB5X3RyYWluKQogICAgICAgIAogICAgICAgIHByaW50KCJcbiIgKyAiPSIqNTApCiAgICAgICAgcHJpbnQoIuaJgOacieaooeWei+iuree7g+WujOaIkCEiKQogICAgICAgIHByaW50KCI9Iio1MCkKICAgICAgICAKICAgICAgICByZXR1cm4gc2VsZi5tb2RlbHMKICAgIAogICAgZGVmIGdldF9mZWF0dXJlX2ltcG9ydGFuY2Uoc2VsZiwgZmVhdHVyZV9uYW1lczogbGlzdCkgLT4gcGQuRGF0YUZyYW1lOgogICAgICAgICIiIgogICAgICAgIOiOt+WPlkxpZ2h0R0JN54m55b6B6YeN6KaB5oCnCiAgICAgICAgCiAgICAgICAgQXJnczoKICAgICAgICAgICAgZmVhdHVyZV9uYW1lczog54m55b6B5ZCN56ew5YiX6KGoCiAgICAgICAgICAgIAogICAgICAgIFJldHVybnM6CiAgICAgICAgICAgIOeJueW+gemHjeimgeaAp0RhdGFGcmFtZQogICAgICAgICIiIgogICAgICAgIGlmIHNlbGYuZmVhdHVyZV9pbXBvcnRhbmNlIGlzIE5vbmU6CiAgICAgICAgICAgIHJhaXNlIFZhbHVlRXJyb3IoIuivt+WFiOiuree7g0xpZ2h0R0JN5qih5Z6LIikKICAgICAgICAKICAgICAgICBpbXBvcnRhbmNlX2RmID0gcGQuRGF0YUZyYW1lKHsKICAgICAgICAgICAgJ2ZlYXR1cmUnOiBmZWF0dXJlX25hbWVzLAogICAgICAgICAgICAnaW1wb3J0YW5jZSc6IHNlbGYuZmVhdHVyZV9pbXBvcnRhbmNlCiAgICAgICAgfSkKICAgICAgICAKICAgICAgICBpbXBvcnRhbmNlX2RmID0gaW1wb3J0YW5jZV9kZi5zb3J0X3ZhbHVlcygnaW1wb3J0YW5jZScsIGFzY2VuZGluZz1GYWxzZSkKICAgICAgICBpbXBvcnRhbmNlX2RmWydyYW5rJ10gPSByYW5nZSgxLCBsZW4oaW1wb3J0YW5jZV9kZikgKyAxKQogICAgICAgIGltcG9ydGFuY2VfZGZbJ2ltcG9ydGFuY2VfcGN0J10gPSBpbXBvcnRhbmNlX2RmWydpbXBvcnRhbmNlJ10gLyBpbXBvcnRhbmNlX2RmWydpbXBvcnRhbmNlJ10uc3VtKCkgKiAxMDAKICAgICAgICAKICAgICAgICByZXR1cm4gaW1wb3J0YW5jZV9kZi5yZXNldF9pbmRleChkcm9wPVRydWUpCg==').decode('utf-8'), _models_mod.__dict__)
sys.modules['models'] = _models_mod
_evaluation_mod = types.ModuleType('evaluation')
exec(base64.b64decode('IiIiCuaooeWei+ivhOS8sOaooeWdlwrorqHnrpdBY2N1cmFjeeOAgVByZWNpc2lvbuOAgVJlY2FsbOOAgUYx44CBQVVD562J5oyH5qCHCiIiIgppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHBhbmRhcyBhcyBwZApmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgKAogICAgYWNjdXJhY3lfc2NvcmUsIHByZWNpc2lvbl9zY29yZSwgcmVjYWxsX3Njb3JlLCBmMV9zY29yZSwKICAgIHJvY19hdWNfc2NvcmUsIGNvbmZ1c2lvbl9tYXRyaXgKKQpmcm9tIHR5cGluZyBpbXBvcnQgRGljdAppbXBvcnQgd2FybmluZ3MKd2FybmluZ3MuZmlsdGVyd2FybmluZ3MoJ2lnbm9yZScpCgoKY2xhc3MgTW9kZWxFdmFsdWF0b3I6CiAgICAiIiLmqKHlnovor4TkvLDlmagiIiIKICAgIAogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgIHNlbGYucmVzdWx0cyA9IHt9CiAgICAKICAgIGRlZiBldmFsdWF0ZV9tb2RlbChzZWxmLCB5X3RydWU6IG5wLm5kYXJyYXksIHlfcHJlZDogbnAubmRhcnJheSwgCiAgICAgICAgICAgICAgICAgICAgICB5X3Byb2I6IG5wLm5kYXJyYXksIG1vZGVsX25hbWU6IHN0cikgLT4gRGljdFtzdHIsIGZsb2F0XToKICAgICAgICAiIiIKICAgICAgICDor4TkvLDljZXkuKrmqKHlnosKICAgICAgICAKICAgICAgICBBcmdzOgogICAgICAgICAgICB5X3RydWU6IOecn+Wunuagh+etvgogICAgICAgICAgICB5X3ByZWQ6IOmihOa1i+agh+etvgogICAgICAgICAgICB5X3Byb2I6IOmihOa1i+amgueOhwogICAgICAgICAgICBtb2RlbF9uYW1lOiDmqKHlnovlkI3np7AKICAgICAgICAgICAgCiAgICAgICAgUmV0dXJuczoKICAgICAgICAgICAg6K+E5Lyw5oyH5qCH5a2X5YW4CiAgICAgICAgIiIiCiAgICAgICAgbWV0cmljcyA9IHsKICAgICAgICAgICAgJ0FjY3VyYWN5JzogYWNjdXJhY3lfc2NvcmUoeV90cnVlLCB5X3ByZWQpLAogICAgICAgICAgICAnUHJlY2lzaW9uJzogcHJlY2lzaW9uX3Njb3JlKHlfdHJ1ZSwgeV9wcmVkLCB6ZXJvX2RpdmlzaW9uPTApLAogICAgICAgICAgICAnUmVjYWxsJzogcmVjYWxsX3Njb3JlKHlfdHJ1ZSwgeV9wcmVkLCB6ZXJvX2RpdmlzaW9uPTApLAogICAgICAgICAgICAnRjEtU2NvcmUnOiBmMV9zY29yZSh5X3RydWUsIHlfcHJlZCwgemVyb19kaXZpc2lvbj0wKSwKICAgICAgICAgICAgJ0FVQyc6IHJvY19hdWNfc2NvcmUoeV90cnVlLCB5X3Byb2IpCiAgICAgICAgfQogICAgICAgIAogICAgICAgICMg5re35reG55+p6Zi1CiAgICAgICAgdG4sIGZwLCBmbiwgdHAgPSBjb25mdXNpb25fbWF0cml4KHlfdHJ1ZSwgeV9wcmVkKS5yYXZlbCgpCiAgICAgICAgbWV0cmljc1snVE4nXSA9IHRuCiAgICAgICAgbWV0cmljc1snRlAnXSA9IGZwCiAgICAgICAgbWV0cmljc1snRk4nXSA9IGZuCiAgICAgICAgbWV0cmljc1snVFAnXSA9IHRwCiAgICAgICAgCiAgICAgICAgc2VsZi5yZXN1bHRzW21vZGVsX25hbWVdID0gbWV0cmljcwogICAgICAgIAogICAgICAgIHByaW50KGYiXG4tLS0ge21vZGVsX25hbWV9IOivhOS8sOe7k+aenCAtLS0iKQogICAgICAgIHByaW50KGYi5YeG56Gu546HKEFjY3VyYWN5KTogIHttZXRyaWNzWydBY2N1cmFjeSddOi4zZn0iKQogICAgICAgIHByaW50KGYi57K+56Gu546HKFByZWNpc2lvbik6IHttZXRyaWNzWydQcmVjaXNpb24nXTouM2Z9IikKICAgICAgICBwcmludChmIuWPrOWbnueOhyhSZWNhbGwpOiAgICB7bWV0cmljc1snUmVjYWxsJ106LjNmfSIpCiAgICAgICAgcHJpbnQoZiJGMeW+l+WIhihGMS1TY29yZSk6ICB7bWV0cmljc1snRjEtU2NvcmUnXTouM2Z9IikKICAgICAgICBwcmludChmIkFVQ+WAvDogICAgICAgICAgICAge21ldHJpY3NbJ0FVQyddOi4zZn0iKQogICAgICAgIHByaW50KGYi5re35reG55+p6Zi1OiBUTj17dG59LCBGUD17ZnB9LCBGTj17Zm59LCBUUD17dHB9IikKICAgICAgICAKICAgICAgICByZXR1cm4gbWV0cmljcwogICAgCiAgICBkZWYgZXZhbHVhdGVfYWxsKHNlbGYsIHlfdHJ1ZTogbnAubmRhcnJheSwgbW9kZWxzOiBEaWN0W3N0ciwgb2JqZWN0XSwgCiAgICAgICAgICAgICAgICAgICAgWF90ZXN0OiBucC5uZGFycmF5KSAtPiBwZC5EYXRhRnJhbWU6CiAgICAgICAgIiIiCiAgICAgICAg6K+E5Lyw5omA5pyJ5qih5Z6LCiAgICAgICAgCiAgICAgICAgQXJnczoKICAgICAgICAgICAgeV90cnVlOiDnnJ/lrp7moIfnrb4KICAgICAgICAgICAgbW9kZWxzOiDmqKHlnovlrZflhbgKICAgICAgICAgICAgWF90ZXN0OiDmtYvor5XnibnlvoEKICAgICAgICAgICAgCiAgICAgICAgUmV0dXJuczoKICAgICAgICAgICAg6K+E5Lyw57uT5p6cRGF0YUZyYW1lCiAgICAgICAgIiIiCiAgICAgICAgcHJpbnQoIlxuIiArICI9Iio1MCkKICAgICAgICBwcmludCgi5byA5aeL6K+E5Lyw5omA5pyJ5qih5Z6LIikKICAgICAgICBwcmludCgiPSIqNTApCiAgICAgICAgCiAgICAgICAgZm9yIG1vZGVsX25hbWUsIG1vZGVsIGluIG1vZGVscy5pdGVtcygpOgogICAgICAgICAgICB5X3ByZWQgPSBtb2RlbC5wcmVkaWN0KFhfdGVzdCkKICAgICAgICAgICAgeV9wcm9iID0gbW9kZWwucHJlZGljdF9wcm9iYShYX3Rlc3QpWzosIDFdCiAgICAgICAgICAgIHNlbGYuZXZhbHVhdGVfbW9kZWwoeV90cnVlLCB5X3ByZWQsIHlfcHJvYiwgbW9kZWxfbmFtZSkKICAgICAgICAKICAgICAgICAjIOi9rOaNouS4ukRhdGFGcmFtZQogICAgICAgIHJlc3VsdHNfZGYgPSBwZC5EYXRhRnJhbWUoc2VsZi5yZXN1bHRzKS5UCiAgICAgICAgcmVzdWx0c19kZiA9IHJlc3VsdHNfZGZbWydBY2N1cmFjeScsICdQcmVjaXNpb24nLCAnUmVjYWxsJywgJ0YxLVNjb3JlJywgJ0FVQyddXQogICAgICAgIAogICAgICAgIHByaW50KCJcbiIgKyAiPSIqNTApCiAgICAgICAgcHJpbnQoIuaJgOacieaooeWei+ivhOS8sOe7k+aenOaxh+aAuyIpCiAgICAgICAgcHJpbnQoIj0iKjUwKQogICAgICAgIHByaW50KHJlc3VsdHNfZGYucm91bmQoMykpCiAgICAgICAgCiAgICAgICAgIyDorrrmlofpooTmnJ/nu5PmnpwKICAgICAgICBwcmludCgiXG7orrrmlofpooTmnJ/nu5PmnpzvvIjlj4LogIPvvIk6IikKICAgICAgICBwcmludCgiTGlnaHRHQk06ICAgICBBY2N1cmFjeT0wLjc4MCwgUHJlY2lzaW9uPTAuMDYwLCBSZWNhbGw9MC43NDgsIEYxPTAuMTExLCBBVUM9MC43NjUiKQogICAgICAgIHByaW50KCJHQkRUOiAgICAgICAgIEFjY3VyYWN5PTAuNzM2LCBQcmVjaXNpb249MC4wNDUsIFJlY2FsbD0wLjY2OSwgRjE9MC4wODUsIEFVQz0wLjcwMyIpCiAgICAgICAgcHJpbnQoIlJhbmRvbUZvcmVzdDogQWNjdXJhY3k9MC43MTksIFByZWNpc2lvbj0wLjA0OSwgUmVjYWxsPTAuNzIzLCBGMT0wLjA5MiwgQVVDPTAuNzQ5IikKICAgICAgICBwcmludCgiTG9naXN0aWM6ICAgICBBY2N1cmFjeT0wLjQwMiwgUHJlY2lzaW9uPTAuMDIwLCBSZWNhbGw9MC42NDYsIEYxPTAuMDM4LCBBVUM9MC41MjEiKQogICAgICAgIHByaW50KCJTVk06ICAgICAgICAgIEFjY3VyYWN5PTAuMjI1LCBQcmVjaXNpb249MC4wMTMsIFJlY2FsbD0wLjczNSwgRjE9MC4wMjYsIEFVQz0wLjUxOCIpCiAgICAgICAgcHJpbnQoIj0iKjUwKQogICAgICAgIAogICAgICAgIHJldHVybiByZXN1bHRzX2RmCgoKZGVmIGV2YWx1YXRlX3BpcGVsaW5lKHlfdGVzdDogbnAubmRhcnJheSwgbW9kZWxzOiBEaWN0W3N0ciwgb2JqZWN0XSwgCiAgICAgICAgICAgICAgICAgICAgIFhfdGVzdDogbnAubmRhcnJheSkgLT4gcGQuRGF0YUZyYW1lOgogICAgIiIiCiAgICDlrozmlbTnmoTor4TkvLDmtYHmsLTnur8KICAgIAogICAgQXJnczoKICAgICAgICB5X3Rlc3Q6IOa1i+ivleagh+etvgogICAgICAgIG1vZGVsczog5qih5Z6L5a2X5YW4CiAgICAgICAgWF90ZXN0OiDmtYvor5XnibnlvoEKICAgICAgICAKICAgIFJldHVybnM6CiAgICAgICAg6K+E5Lyw57uT5p6cRGF0YUZyYW1lCiAgICAiIiIKICAgIGV2YWx1YXRvciA9IE1vZGVsRXZhbHVhdG9yKCkKICAgIHJlc3VsdHNfZGYgPSBldmFsdWF0b3IuZXZhbHVhdGVfYWxsKHlfdGVzdCwgbW9kZWxzLCBYX3Rlc3QpCiAgICByZXR1cm4gcmVzdWx0c19kZgo=').decode('utf-8'), _evaluation_mod.__dict__)
sys.modules['evaluation'] = _evaluation_mod
_visualization_mod = types.ModuleType('visualization')
exec(base64.b64decode('IiIiCuWPr+inhuWMluaooeWdlwrljIXlkKvvvJrnibnlvoHph43opoHmgKflj6/op4bljJbjgIHmqKHlnovor4TkvLDmjIfmoIflr7nmr5Tlm77jgIFST0Pmm7Lnur8KIiIiCgppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHBhbmRhcyBhcyBwZAppbXBvcnQgbWF0cGxvdGxpYgptYXRwbG90bGliLnVzZSgnQWdnJykgICMg5L2/55So6Z2e5Lqk5LqS5byP5ZCO56uvCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKaW1wb3J0IHNlYWJvcm4gYXMgc25zCmZyb20gc2tsZWFybi5tZXRyaWNzIGltcG9ydCByb2NfY3VydmUsIGF1Ywpmcm9tIHR5cGluZyBpbXBvcnQgRGljdCwgQW55CmltcG9ydCB3YXJuaW5ncwp3YXJuaW5ncy5maWx0ZXJ3YXJuaW5ncygnaWdub3JlJykKCiMg6K6+572u5a2X5L2TCnBsdC5yY1BhcmFtc1snZm9udC5zYW5zLXNlcmlmJ10gPSBbJ0RlamFWdSBTYW5zJ10KcGx0LnJjUGFyYW1zWydheGVzLnVuaWNvZGVfbWludXMnXSA9IEZhbHNlCnNucy5zZXRfc3R5bGUoIndoaXRlZ3JpZCIpCgoKY2xhc3MgRnJhdWRWaXN1YWxpemVyOgogICAgIiIi6LSi5Yqh6Iie5byK6K+G5Yir5Y+v6KeG5YyW5bel5YW3IiIiCiAgICAKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBzYXZlX2Rpcjogc3RyID0gJy4uL2ZpZ3VyZXMnKToKICAgICAgICBzZWxmLnNhdmVfZGlyID0gc2F2ZV9kaXIKICAgICAgICAKICAgIGRlZiBwbG90X2ZlYXR1cmVfaW1wb3J0YW5jZShzZWxmLCBpbXBvcnRhbmNlX2RmOiBwZC5EYXRhRnJhbWUsIHRvcF9uOiBpbnQgPSAyNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhdmU6IGJvb2wgPSBUcnVlLCBmaWxlbmFtZTogc3RyID0gJ2ZlYXR1cmVfaW1wb3J0YW5jZS5wbmcnKToKICAgICAgICAiIiIKICAgICAgICDnu5jliLZUb3AtTueJueW+gemHjeimgeaAp+WbvgogICAgICAgICIiIgogICAgICAgIHBsdC5maWd1cmUoZmlnc2l6ZT0oMTIsIDEwKSkKICAgICAgICAKICAgICAgICAjIOWPluWJjU7kuKrnibnlvoEKICAgICAgICB0b3BfZmVhdHVyZXMgPSBpbXBvcnRhbmNlX2RmLmhlYWQodG9wX24pLmNvcHkoKQogICAgICAgIHRvcF9mZWF0dXJlcyA9IHRvcF9mZWF0dXJlcy5zb3J0X3ZhbHVlcygnaW1wb3J0YW5jZScsIGFzY2VuZGluZz1UcnVlKQogICAgICAgIAogICAgICAgICMg57uY5Yi25qiq5ZCR5p2h5b2i5Zu+CiAgICAgICAgYmFycyA9IHBsdC5iYXJoKHJhbmdlKGxlbih0b3BfZmVhdHVyZXMpKSwgdG9wX2ZlYXR1cmVzWydpbXBvcnRhbmNlX3BjdCddLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9JyMzNDk4ZGInLCBhbHBoYT0wLjgpCiAgICAgICAgCiAgICAgICAgIyDmt7vliqDmlbDlgLzmoIfnrb4KICAgICAgICBmb3IgaSwgKGlkeCwgcm93KSBpbiBlbnVtZXJhdGUodG9wX2ZlYXR1cmVzLml0ZXJyb3dzKCkpOgogICAgICAgICAgICBwbHQudGV4dChyb3dbJ2ltcG9ydGFuY2VfcGN0J10gKyAwLjAyLCBpLCAKICAgICAgICAgICAgICAgICAgICBmIntyb3dbJ2ltcG9ydGFuY2VfcGN0J106LjNmfSUiLCAKICAgICAgICAgICAgICAgICAgICB2YT0nY2VudGVyJywgZm9udHNpemU9OSkKICAgICAgICAKICAgICAgICBwbHQueXRpY2tzKHJhbmdlKGxlbih0b3BfZmVhdHVyZXMpKSwgdG9wX2ZlYXR1cmVzWydmZWF0dXJlJ10pCiAgICAgICAgcGx0LnhsYWJlbCgnSW1wb3J0YW5jZSAoJSknLCBmb250c2l6ZT0xMikKICAgICAgICBwbHQueWxhYmVsKCdGZWF0dXJlJywgZm9udHNpemU9MTIpCiAgICAgICAgcGx0LnRpdGxlKGYnTGlnaHRHQk0gRmVhdHVyZSBJbXBvcnRhbmNlIChUb3Age3RvcF9ufSknLCBmb250c2l6ZT0xNCwgcGFkPTIwKQogICAgICAgIHBsdC50aWdodF9sYXlvdXQoKQogICAgICAgIAogICAgICAgIGlmIHNhdmU6CiAgICAgICAgICAgIHBsdC5zYXZlZmlnKGYne3NlbGYuc2F2ZV9kaXJ9L3tmaWxlbmFtZX0nLCBkcGk9MzAwLCBiYm94X2luY2hlcz0ndGlnaHQnKQogICAgICAgICAgICBwcmludChmIueJueW+gemHjeimgeaAp+WbvuW3suS/neWtmDoge3NlbGYuc2F2ZV9kaXJ9L3tmaWxlbmFtZX0iKQogICAgICAgIAogICAgICAgIHBsdC5jbG9zZSgpCiAgICAgICAgCiAgICAgICAgIyDmiZPljbDliY0xMOS4qumHjeimgeeJueW+gQogICAgICAgIHByaW50KCJcbiIgKyAiPSIqNjApCiAgICAgICAgcHJpbnQoIueJueW+gemHjeimgeaAp+aOkuWQjSAo5YmNMTApIikKICAgICAgICBwcmludCgiPSIqNjApCiAgICAgICAgcHJpbnQoaW1wb3J0YW5jZV9kZltbJ3JhbmsnLCAnZmVhdHVyZScsICdpbXBvcnRhbmNlX3BjdCddXS5oZWFkKDEwKS50b19zdHJpbmcoaW5kZXg9RmFsc2UpKQogICAgICAgIHByaW50KCI9Iio2MCkKCiAgICBkZWYgcGxvdF9tb2RlbF9jb21wYXJpc29uKHNlbGYsIHJlc3VsdHNfZGY6IHBkLkRhdGFGcmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZTogYm9vbCA9IFRydWUsIGZpbGVuYW1lOiBzdHIgPSAnbW9kZWxfY29tcGFyaXNvbi5wbmcnKToKICAgICAgICAiIiIKICAgICAgICDnu5jliLbmqKHlnovlr7nmr5Tlm74gKOW3suS/ruWkjeaVsOWAvOagh+etvuaCrOepuuWSjOW4g+WxgOaMpOWOi+mXrumimCkKICAgICAgICAiIiIKICAgICAgICBtZXRyaWNzID0gWydBY2N1cmFjeScsICdQcmVjaXNpb24nLCAnUmVjYWxsJywgJ0YxLVNjb3JlJywgJ0FVQyddCiAgICAgICAgbW9kZWxzID0gcmVzdWx0c19kZi5pbmRleC50b2xpc3QoKQoKICAgICAgICBmaWcsIGF4ZXMgPSBwbHQuc3VicGxvdHMoMiwgMywgZmlnc2l6ZT0oMTgsIDEwKSkKICAgICAgICBheGVzID0gYXhlcy5mbGF0dGVuKCkKCiAgICAgICAgY29sb3JzID0gWycjMzQ5OGRiJywgJyNlNzRjM2MnLCAnIzJlY2M3MScsICcjZjM5YzEyJywgJyM5YjU5YjYnXQoKICAgICAgICBmb3IgaWR4LCBtZXRyaWMgaW4gZW51bWVyYXRlKG1ldHJpY3MpOgogICAgICAgICAgICBheCA9IGF4ZXNbaWR4XQogICAgICAgICAgICB2YWx1ZXMgPSByZXN1bHRzX2RmW21ldHJpY10udmFsdWVzCgogICAgICAgICAgICBiYXJzID0gYXguYmFyKG1vZGVscywgdmFsdWVzLCBjb2xvcj1jb2xvcnMsIGFscGhhPTAuOCkKICAgICAgICAgICAgYXguc2V0X3RpdGxlKGYne21ldHJpY30gQ29tcGFyaXNvbicsIGZvbnRzaXplPTEyKQogICAgICAgICAgICBheC5zZXRfeWxhYmVsKG1ldHJpYywgZm9udHNpemU9MTApCiAgICAgICAgICAgIGF4LnRpY2tfcGFyYW1zKGF4aXM9J3gnLCByb3RhdGlvbj00NSkKCiAgICAgICAgICAgICMg5Yqo5oCB6LCD5pW0IFkg6L205LiK6ZmQ77yM55WZ5Ye6IDE1JSDnmoTpobbpg6jnqbrpl7TpmLLmraLmloflrZfooqvoo4HliIcKICAgICAgICAgICAgbWF4X3ZhbCA9IG1heCh2YWx1ZXMpIGlmIG1heCh2YWx1ZXMpID4gMCBlbHNlIDEKICAgICAgICAgICAgYXguc2V0X3lsaW0oMCwgbWF4X3ZhbCAqIDEuMTUpCgogICAgICAgICAgICAjIOS/ruWkje+8muS9v+eUqCBhbm5vdGF0ZSDov5vooYznm7jlr7nlg4/ntKDlgY/np7vvvIzogIzkuI3mmK/nu53lr7nmlbDlgLzlgY/np7sKICAgICAgICAgICAgZm9yIGJhciBpbiBiYXJzOgogICAgICAgICAgICAgICAgaGVpZ2h0ID0gYmFyLmdldF9oZWlnaHQoKQogICAgICAgICAgICAgICAgYXguYW5ub3RhdGUoZid7aGVpZ2h0Oi4zZn0nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeHk9KGJhci5nZXRfeCgpICsgYmFyLmdldF93aWR0aCgpIC8gMiwgaGVpZ2h0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHh5dGV4dD0oMCwgNSksICAjIOWeguebtOWQkeS4iuWBj+enuyA1IOS4quWDj+e0oAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dGNvb3Jkcz0ib2Zmc2V0IHBvaW50cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoYT0nY2VudGVyJywgdmE9J2JvdHRvbScsIGZvbnRzaXplPTkpCgogICAgICAgICMg56e76Zmk5pyA5ZCO5LiA5Liq56m655qE5a2Q5Zu+CiAgICAgICAgYXhlc1stMV0uYXhpcygnb2ZmJykKCiAgICAgICAgcGx0LnN1cHRpdGxlKCdNb2RlbCBFdmFsdWF0aW9uIE1ldHJpY3MgQ29tcGFyaXNvbicsIGZvbnRzaXplPTE2LCB5PTEuMDIpCiAgICAgICAgcGx0LnRpZ2h0X2xheW91dCgpCgogICAgICAgIGlmIHNhdmU6CiAgICAgICAgICAgIHBsdC5zYXZlZmlnKGYne3NlbGYuc2F2ZV9kaXJ9L3tmaWxlbmFtZX0nLCBkcGk9MzAwLCBiYm94X2luY2hlcz0ndGlnaHQnKQogICAgICAgICAgICBwcmludChmIuaooeWei+WvueavlOWbvuW3suS/neWtmDoge3NlbGYuc2F2ZV9kaXJ9L3tmaWxlbmFtZX0iKQoKICAgICAgICBwbHQuY2xvc2UoKQogICAgICAgIAogICAgZGVmIHBsb3RfcmFkYXJfY2hhcnQoc2VsZiwgcmVzdWx0c19kZjogcGQuRGF0YUZyYW1lLAogICAgICAgICAgICAgICAgICAgICAgICBzYXZlOiBib29sID0gVHJ1ZSwgZmlsZW5hbWU6IHN0ciA9ICdyYWRhcl9jb21wYXJpc29uLnBuZycpOgogICAgICAgICIiIgogICAgICAgIOe7mOWItumbt+i+vuWbvuWvueavlOWQhOaooeWeiwogICAgICAgICIiIgogICAgICAgIG1ldHJpY3MgPSBbJ0FjY3VyYWN5JywgJ1ByZWNpc2lvbicsICdSZWNhbGwnLCAnRjEtU2NvcmUnLCAnQVVDJ10KICAgICAgICBjYXRlZ29yaWVzID0gbWV0cmljcwogICAgICAgIE4gPSBsZW4oY2F0ZWdvcmllcykKICAgICAgICAKICAgICAgICAjIOinkuW6puiuoeeulwogICAgICAgIGFuZ2xlcyA9IFtuIC8gZmxvYXQoTikgKiAyICogbnAucGkgZm9yIG4gaW4gcmFuZ2UoTildCiAgICAgICAgYW5nbGVzICs9IGFuZ2xlc1s6MV0KICAgICAgICAKICAgICAgICBwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCAxMCkpCiAgICAgICAgYXggPSBwbHQuc3VicGxvdCgxMTEsIHBvbGFyPVRydWUpCiAgICAgICAgCiAgICAgICAgY29sb3JzID0gWycjMzQ5OGRiJywgJyNlNzRjM2MnLCAnIzJlY2M3MScsICcjZjM5YzEyJywgJyM5YjU5YjYnXQogICAgICAgIAogICAgICAgIGZvciBpZHgsIChtb2RlbF9uYW1lLCByb3cpIGluIGVudW1lcmF0ZShyZXN1bHRzX2RmLml0ZXJyb3dzKCkpOgogICAgICAgICAgICB2YWx1ZXMgPSByb3dbbWV0cmljc10udmFsdWVzLnRvbGlzdCgpCiAgICAgICAgICAgIHZhbHVlcyArPSB2YWx1ZXNbOjFdCiAgICAgICAgICAgIAogICAgICAgICAgICBheC5wbG90KGFuZ2xlcywgdmFsdWVzLCAnby0nLCBsaW5ld2lkdGg9MiwgbGFiZWw9bW9kZWxfbmFtZSwgY29sb3I9Y29sb3JzW2lkeF0pCiAgICAgICAgICAgIGF4LmZpbGwoYW5nbGVzLCB2YWx1ZXMsIGFscGhhPTAuMSwgY29sb3I9Y29sb3JzW2lkeF0pCiAgICAgICAgCiAgICAgICAgYXguc2V0X3h0aWNrcyhhbmdsZXNbOi0xXSkKICAgICAgICBheC5zZXRfeHRpY2tsYWJlbHMoY2F0ZWdvcmllcywgZm9udHNpemU9MTEpCiAgICAgICAgYXguc2V0X3lsaW0oMCwgMSkKICAgICAgICBheC5zZXRfdGl0bGUoJ01vZGVsIFBlcmZvcm1hbmNlIFJhZGFyIENoYXJ0JywgZm9udHNpemU9MTQsIHBhZD0zMCkKICAgICAgICBheC5sZWdlbmQobG9jPSd1cHBlciByaWdodCcsIGJib3hfdG9fYW5jaG9yPSgxLjMsIDEuMCkpCiAgICAgICAgCiAgICAgICAgcGx0LnRpZ2h0X2xheW91dCgpCiAgICAgICAgCiAgICAgICAgaWYgc2F2ZToKICAgICAgICAgICAgcGx0LnNhdmVmaWcoZid7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfScsIGRwaT0zMDAsIGJib3hfaW5jaGVzPSd0aWdodCcpCiAgICAgICAgICAgIHByaW50KGYi6Zu36L6+5a+55q+U5Zu+5bey5L+d5a2YOiB7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfSIpCiAgICAgICAgCiAgICAgICAgcGx0LmNsb3NlKCkKICAgICAgICAKICAgIGRlZiBwbG90X3JvY19jdXJ2ZXMoc2VsZiwgeV90cnVlOiBucC5uZGFycmF5LCBtb2RlbHM6IERpY3Rbc3RyLCBBbnldLCBYX3Rlc3Q6IG5wLm5kYXJyYXksCiAgICAgICAgICAgICAgICAgICAgICAgc2F2ZTogYm9vbCA9IFRydWUsIGZpbGVuYW1lOiBzdHIgPSAncm9jX2N1cnZlcy5wbmcnKToKICAgICAgICAiIiIKICAgICAgICDnu5jliLbmiYDmnInmqKHlnovnmoRST0Pmm7Lnur8KICAgICAgICAiIiIKICAgICAgICBwbHQuZmlndXJlKGZpZ3NpemU9KDEwLCA4KSkKICAgICAgICAKICAgICAgICBjb2xvcnMgPSBbJyMzNDk4ZGInLCAnI2U3NGMzYycsICcjMmVjYzcxJywgJyNmMzljMTInLCAnIzliNTliNiddCiAgICAgICAgCiAgICAgICAgZm9yIGlkeCwgKG1vZGVsX25hbWUsIG1vZGVsKSBpbiBlbnVtZXJhdGUobW9kZWxzLml0ZW1zKCkpOgogICAgICAgICAgICB5X3Byb2IgPSBtb2RlbC5wcmVkaWN0X3Byb2JhKFhfdGVzdClbOiwgMV0KICAgICAgICAgICAgZnByLCB0cHIsIF8gPSByb2NfY3VydmUoeV90cnVlLCB5X3Byb2IpCiAgICAgICAgICAgIHJvY19hdWMgPSBhdWMoZnByLCB0cHIpCiAgICAgICAgICAgIAogICAgICAgICAgICBwbHQucGxvdChmcHIsIHRwciwgY29sb3I9Y29sb3JzW2lkeF0sIGx3PTIsCiAgICAgICAgICAgICAgICAgICAgbGFiZWw9Zid7bW9kZWxfbmFtZX0gKEFVQyA9IHtyb2NfYXVjOi4zZn0pJykKICAgICAgICAKICAgICAgICBwbHQucGxvdChbMCwgMV0sIFswLCAxXSwgJ2stLScsIGx3PTIpCiAgICAgICAgcGx0LnhsaW0oWzAuMCwgMS4wXSkKICAgICAgICBwbHQueWxpbShbMC4wLCAxLjA1XSkKICAgICAgICBwbHQueGxhYmVsKCdGYWxzZSBQb3NpdGl2ZSBSYXRlJywgZm9udHNpemU9MTIpCiAgICAgICAgcGx0LnlsYWJlbCgnVHJ1ZSBQb3NpdGl2ZSBSYXRlJywgZm9udHNpemU9MTIpCiAgICAgICAgcGx0LnRpdGxlKCdST0MgQ3VydmVzIENvbXBhcmlzb24nLCBmb250c2l6ZT0xNCkKICAgICAgICBwbHQubGVnZW5kKGxvYz0ibG93ZXIgcmlnaHQiLCBmb250c2l6ZT0xMCkKICAgICAgICBwbHQuZ3JpZChUcnVlLCBhbHBoYT0wLjMpCiAgICAgICAgCiAgICAgICAgaWYgc2F2ZToKICAgICAgICAgICAgcGx0LnNhdmVmaWcoZid7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfScsIGRwaT0zMDAsIGJib3hfaW5jaGVzPSd0aWdodCcpCiAgICAgICAgICAgIHByaW50KGYiUk9D5puy57q/5Zu+5bey5L+d5a2YOiB7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfSIpCiAgICAgICAgCiAgICAgICAgcGx0LmNsb3NlKCkKICAgICAgICAKICAgIGRlZiBwbG90X2NhdGVnb3J5X2ltcG9ydGFuY2Uoc2VsZiwgaW1wb3J0YW5jZV9kZjogcGQuRGF0YUZyYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhdmU6IGJvb2wgPSBUcnVlLCBmaWxlbmFtZTogc3RyID0gJ2NhdGVnb3J5X2ltcG9ydGFuY2UucG5nJyk6CiAgICAgICAgIiIiCiAgICAgICAg57uY5Yi25ZCE57G75Yir54m55b6B6YeN6KaB5oCnCiAgICAgICAgIiIiCiAgICAgICAgIyDmqKHmi5/lkITnsbvliKvph43opoHmgKfvvIjmoLnmja7orrrmlofvvIkKICAgICAgICBjYXRlZ29yaWVzID0gewogICAgICAgICAgICAnUmF0aW8gU3RydWN0dXJlJzogMTUuNDcsCiAgICAgICAgICAgICdQcm9maXRhYmlsaXR5JzogMTQuNjEsCiAgICAgICAgICAgICdQZXIgU2hhcmUnOiAxNC4yMiwKICAgICAgICAgICAgJ09wZXJhdGluZyc6IDEzLjE5LAogICAgICAgICAgICAnR3Jvd3RoJzogMTEuMzcsCiAgICAgICAgICAgICdTb2x2ZW5jeSc6IDguODcsCiAgICAgICAgICAgICdDYXNoIEZsb3cnOiA4LjIzLAogICAgICAgICAgICAnUmVsYXRpdmUgVmFsdWUnOiA3LjA3LAogICAgICAgICAgICAnTm9uLWZpbmFuY2lhbCc6IDQuNzYsCiAgICAgICAgICAgICdSaXNrIExldmVsJzogMS4zNCwKICAgICAgICAgICAgJ0RpdmlkZW5kJzogMC44OAogICAgICAgIH0KICAgICAgICAKICAgICAgICBjYXRfZGYgPSBwZC5EYXRhRnJhbWUobGlzdChjYXRlZ29yaWVzLml0ZW1zKCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPVsnQ2F0ZWdvcnknLCAnSW1wb3J0YW5jZSddKQogICAgICAgIGNhdF9kZiA9IGNhdF9kZi5zb3J0X3ZhbHVlcygnSW1wb3J0YW5jZScsIGFzY2VuZGluZz1UcnVlKQogICAgICAgIAogICAgICAgIHBsdC5maWd1cmUoZmlnc2l6ZT0oMTIsIDgpKQogICAgICAgIAogICAgICAgIGNvbG9ycyA9IFsnIzk1YTVhNicgaWYgdiA8IDUgZWxzZSAnIzdmOGM4ZCcgaWYgdiA8IDEwIGVsc2UgJyMzNDQ5NWUnIAogICAgICAgICAgICAgICAgIGZvciB2IGluIGNhdF9kZlsnSW1wb3J0YW5jZSddXQogICAgICAgIAogICAgICAgIGJhcnMgPSBwbHQuYmFyaChyYW5nZShsZW4oY2F0X2RmKSksIGNhdF9kZlsnSW1wb3J0YW5jZSddLCBjb2xvcj1jb2xvcnMsIGFscGhhPTAuOCkKICAgICAgICAKICAgICAgICBmb3IgaSwgKGlkeCwgcm93KSBpbiBlbnVtZXJhdGUoY2F0X2RmLml0ZXJyb3dzKCkpOgogICAgICAgICAgICBwbHQudGV4dChyb3dbJ0ltcG9ydGFuY2UnXSArIDAuMiwgaSwgZiJ7cm93WydJbXBvcnRhbmNlJ106LjJmfSUiLCAKICAgICAgICAgICAgICAgICAgICB2YT0nY2VudGVyJywgZm9udHNpemU9MTApCiAgICAgICAgCiAgICAgICAgcGx0Lnl0aWNrcyhyYW5nZShsZW4oY2F0X2RmKSksIGNhdF9kZlsnQ2F0ZWdvcnknXSwgZm9udHNpemU9MTEpCiAgICAgICAgcGx0LnhsYWJlbCgnSW1wb3J0YW5jZSAoJSknLCBmb250c2l6ZT0xMikKICAgICAgICBwbHQudGl0bGUoJ0ZlYXR1cmUgQ2F0ZWdvcnkgSW1wb3J0YW5jZScsIGZvbnRzaXplPTE0LCBwYWQ9MjApCiAgICAgICAgcGx0LnhsaW0oMCwgMTgpCiAgICAgICAgcGx0LnRpZ2h0X2xheW91dCgpCiAgICAgICAgCiAgICAgICAgaWYgc2F2ZToKICAgICAgICAgICAgcGx0LnNhdmVmaWcoZid7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfScsIGRwaT0zMDAsIGJib3hfaW5jaGVzPSd0aWdodCcpCiAgICAgICAgICAgIHByaW50KGYi57G75Yir6YeN6KaB5oCn5Zu+5bey5L+d5a2YOiB7c2VsZi5zYXZlX2Rpcn0ve2ZpbGVuYW1lfSIpCiAgICAgICAgCiAgICAgICAgcGx0LmNsb3NlKCkKCgpkZWYgdmlzdWFsaXphdGlvbl9waXBlbGluZShpbXBvcnRhbmNlX2RmOiBwZC5EYXRhRnJhbWUsIHJlc3VsdHNfZGY6IHBkLkRhdGFGcmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB5X3Rlc3Q6IG5wLm5kYXJyYXksIG1vZGVsczogRGljdFtzdHIsIEFueV0sIFhfdGVzdDogbnAubmRhcnJheSwKICAgICAgICAgICAgICAgICAgICAgICAgICBzYXZlX2Rpcjogc3RyID0gJy4uL2ZpZ3VyZXMnKToKICAgICIiIgogICAg5a6M5pW055qE5Y+v6KeG5YyW5rWB5rC057q/CiAgICAiIiIKICAgIHByaW50KCJcbiIgKyAiPSIqNTApCiAgICBwcmludCgi5byA5aeL55Sf5oiQ5Y+v6KeG5YyW5Zu+6KGoIikKICAgIHByaW50KCI9Iio1MCkKICAgIAogICAgdmlzdWFsaXplciA9IEZyYXVkVmlzdWFsaXplcihzYXZlX2Rpcj1zYXZlX2RpcikKICAgIAogICAgIyDnibnlvoHph43opoHmgKflm74KICAgIHZpc3VhbGl6ZXIucGxvdF9mZWF0dXJlX2ltcG9ydGFuY2UoaW1wb3J0YW5jZV9kZiwgdG9wX249MjUpCiAgICAKICAgICMg5qih5Z6L5a+55q+U5Zu+CiAgICB2aXN1YWxpemVyLnBsb3RfbW9kZWxfY29tcGFyaXNvbihyZXN1bHRzX2RmKQogICAgCiAgICAjIOmbt+i+vuWbvgogICAgdmlzdWFsaXplci5wbG90X3JhZGFyX2NoYXJ0KHJlc3VsdHNfZGYpCiAgICAKICAgICMgUk9D5puy57q/CiAgICB2aXN1YWxpemVyLnBsb3Rfcm9jX2N1cnZlcyh5X3Rlc3QsIG1vZGVscywgWF90ZXN0KQogICAgCiAgICAjIOexu+WIq+mHjeimgeaAp+WbvgogICAgdmlzdWFsaXplci5wbG90X2NhdGVnb3J5X2ltcG9ydGFuY2UoaW1wb3J0YW5jZV9kZikKICAgIAogICAgcHJpbnQoIlxu5omA5pyJ5Y+v6KeG5YyW5Zu+6KGo55Sf5oiQ5a6M5oiQISIpCiAgICBwcmludCgiPSIqNTApCg==').decode('utf-8'), _visualization_mod.__dict__)
sys.modules['visualization'] = _visualization_mod
print('✅ Custom modules injected:', 'data_converter', 'data_preprocessor', 'models', 'evaluation', 'visualization')
✅ Custom modules injected: data_converter data_preprocessor models evaluation visualization
In [2]:
#!/usr/bin/env python3
"""
上市公司财务舞弊识别模型 - CSMAR真实数据版
论文:《上市公司财务舞弊识别模型设计及其应用研究——基于新兴机器学习算法》
运行方式:
python main.py
"""
import os
import sys
import logging
# 添加src路径
sys.path.append(os.path.join(os.path.dirname("main.ipynb"), 'src'))
from data_converter import CSMARDataConverter
from data_preprocessor import preprocess_pipeline
from models import FraudDetectionModels
from evaluation import evaluate_pipeline
from visualization import visualization_pipeline
import warnings
warnings.filterwarnings('ignore')
def setup_logger(log_filename='fraud_detection_run.log'):
"""配置日志记录器,同时输出到控制台和文件"""
# 创建logs目录
os.makedirs('logs', exist_ok=True)
log_filepath = os.path.join('logs', log_filename)
# 创建一个logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# 清理已存在的handlers(防止重复加载时产生重复日志)
if logger.hasHandlers():
logger.handlers.clear()
# 设置日志输出格式
formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
# 1. 控制台输出 Handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# 2. 文件输出 Handler (使用 utf-8 编码防止中文乱码)
file_handler = logging.FileHandler(log_filepath, encoding='utf-8')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
return logger
def main(random_state: int = 42):
"""主运行函数"""
# 初始化日志记录
logger = setup_logger()
logger.info("=" * 70)
logger.info("启动:上市公司财务舞弊识别模型 - CSMAR真实数据版")
logger.info("论文:《上市公司财务舞弊识别模型设计及其应用研究》")
logger.info("=" * 70)
# 创建必要目录
os.makedirs('data', exist_ok=True)
os.makedirs('results', exist_ok=True)
os.makedirs('figures', exist_ok=True)
logger.info("已检查并创建所需的基础目录 (data, results, figures, logs)。")
# ==================== 1. 加载并转换CSMAR真实数据 ====================
logger.info("阶段 1: 加载并转换CSMAR真实数据...")
csmar_file = '/kaggle/input/datasets/songsammy/ningxinru-csmar-data/STK_Violation_Main.xlsx'
if not os.path.exists(csmar_file):
logger.error(f"找不到CSMAR数据文件 {csmar_file}")
logger.error("请将 STK_Violation_Main.xlsx 放入 data/ 目录")
return None, None
try:
converter = CSMARDataConverter(csmar_file, random_state)
df = converter.convert_to_standard_format()
train_df, test_df, pred_df = converter.split_dataset(df)
# 保存转换后的数据
standard_data_path = 'data/csmar_standard_dataset.csv'
df.to_csv(standard_data_path, index=False)
logger.info(f"转换后的标准数据集已保存到: {standard_data_path}")
except Exception as e:
logger.exception(f"数据加载或转换过程中发生错误: {e}")
return None, None
# ==================== 2. 数据预处理 ====================
logger.info("阶段 2: 执行数据预处理 Pipeline...")
try:
X_train, y_train, X_test, y_test, preprocessor = preprocess_pipeline(
train_df, test_df, random_state=random_state
)
logger.info(f"数据预处理完成。训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}")
except Exception as e:
logger.exception(f"数据预处理过程中发生错误: {e}")
return None, None
# ==================== 3. 训练5种模型 ====================
logger.info("阶段 3: 开始训练机器学习模型...")
try:
model_trainer = FraudDetectionModels(random_state=random_state)
models = model_trainer.train_all_models(X_train, y_train)
logger.info(f"成功训练了 {len(models)} 个模型。")
except Exception as e:
logger.exception(f"模型训练过程中发生错误: {e}")
return None, None
# ==================== 4. 模型评估 ====================
logger.info("阶段 4: 在测试集上评估模型表现...")
try:
results_df = evaluate_pipeline(y_test, models, X_test)
# 保存评估结果
eval_path = 'results/evaluation_results.csv'
results_df.to_csv(eval_path)
logger.info(f"评估结果已保存到: {eval_path}")
except Exception as e:
logger.exception(f"模型评估过程中发生错误: {e}")
return None, None
# ==================== 5. 特征重要性分析 ====================
logger.info("阶段 5: 提取并分析特征重要性...")
try:
feature_names = preprocessor.get_feature_names()
importance_df = model_trainer.get_feature_importance(feature_names)
# 保存特征重要性
importance_path = 'results/feature_importance.csv'
importance_df.to_csv(importance_path, index=False)
logger.info(f"特征重要性已保存到: {importance_path}")
except Exception as e:
logger.exception(f"特征重要性分析过程中发生错误: {e}")
return None, None
# ==================== 6. 生成可视化图表 ====================
logger.info("阶段 6: 生成可视化图表...")
try:
visualization_pipeline(
importance_df, results_df, y_test, models, X_test,
save_dir='figures'
)
logger.info("可视化图表生成完毕,保存在 figures/ 目录下。")
except Exception as e:
logger.exception(f"生成可视化图表时发生错误: {e}")
# 这里不返回,因为图表生成失败不应影响前面已经计算出的数据
# ==================== 7. 总结 ====================
logger.info("=" * 70)
logger.info("运行完成! 输出文件汇总:")
logger.info(" [数据]: data/csmar_standard_dataset.csv")
logger.info(" [结果]: results/evaluation_results.csv")
logger.info(" [特征]: results/feature_importance.csv")
logger.info(" [图表]: figures/ 目录下的各项 .png 文件")
logger.info(" [日志]: logs/fraud_detection_run.log")
logger.info("=" * 70)
return results_df, importance_df
if __name__ == "__main__":
main(random_state=42)
2026-06-27 06:10:08 - INFO - ====================================================================== 2026-06-27 06:10:08 - INFO - 启动:上市公司财务舞弊识别模型 - CSMAR真实数据版 2026-06-27 06:10:08 - INFO - 论文:《上市公司财务舞弊识别模型设计及其应用研究》 2026-06-27 06:10:08 - INFO - ====================================================================== 2026-06-27 06:10:08 - INFO - 已检查并创建所需的基础目录 (data, results, figures, logs)。 2026-06-27 06:10:08 - INFO - 阶段 1: 加载并转换CSMAR真实数据... 2026-06-27 06:10:08 - ERROR - 找不到CSMAR数据文件 /kaggle/input/datasets/songsammy/ningxinru-csmar-data/STK_Violation_Main.xlsx 2026-06-27 06:10:08 - ERROR - 请将 STK_Violation_Main.xlsx 放入 data/ 目录