Ensembling iMet

From: https://www.kaggle.com/axel81/ensembling-imet

Author: Ram Ramrakhya

iMet Collection 2019

This kernel is an implementation for KFolding using the already trained models. Run time for the kernel is 1.5hrs which will work even on the huge test set.

In this Kernel I'll use 6 Folds

  • I have used on Konrads Kernel from here
  • Four SeResNext50 with LB score between 0.597-0.608
  • One Resnet50
In [1]:
import pandas as pd
import gzip
import base64
import os
from pathlib import Path
from typing import Dict


# this is base64 encoded source code
file_data: Dict = {'imet/transforms.py': 'aW1wb3J0IHJhbmRvbQppbXBvcnQgbWF0aAoKZnJvbSBQSUwgaW1wb3J0IEltYWdlCmZyb20gdG9yY2h2aXNpb24udHJhbnNmb3JtcyBpbXBvcnQgKAogICAgVG9UZW5zb3IsIE5vcm1hbGl6ZSwgQ29tcG9zZSwgUmVzaXplLCBDZW50ZXJDcm9wLCBSYW5kb21Dcm9wLAogICAgUmFuZG9tSG9yaXpvbnRhbEZsaXApCgoKY2xhc3MgUmFuZG9tU2l6ZWRDcm9wOgogICAgIiIiUmFuZG9tIGNyb3AgdGhlIGdpdmVuIFBJTC5JbWFnZSB0byBhIHJhbmRvbSBzaXplCiAgICBvZiB0aGUgb3JpZ2luYWwgc2l6ZSBhbmQgYW5kIGEgcmFuZG9tIGFzcGVjdCByYXRpbwogICAgb2YgdGhlIG9yaWdpbmFsIGFzcGVjdCByYXRpby4KICAgIHNpemU6IHNpemUgb2YgdGhlIHNtYWxsZXIgZWRnZQogICAgaW50ZXJwb2xhdGlvbjogRGVmYXVsdDogUElMLkltYWdlLkJJTElORUFSCiAgICAiIiIKCiAgICBkZWYgX19pbml0X18oc2VsZiwgc2l6ZSwgaW50ZXJwb2xhdGlvbj1JbWFnZS5CSUxJTkVBUiwKICAgICAgICAgICAgICAgICBtaW5fYXNwZWN0PTQvNSwgbWF4X2FzcGVjdD01LzQsCiAgICAgICAgICAgICAgICAgbWluX2FyZWE9MC4yNSwgbWF4X2FyZWE9MSk6CiAgICAgICAgc2VsZi5zaXplID0gc2l6ZQogICAgICAgIHNlbGYuaW50ZXJwb2xhdGlvbiA9IGludGVycG9sYXRpb24KICAgICAgICBzZWxmLm1pbl9hc3BlY3QgPSBtaW5fYXNwZWN0CiAgICAgICAgc2VsZi5tYXhfYXNwZWN0ID0gbWF4X2FzcGVjdAogICAgICAgIHNlbGYubWluX2FyZWEgPSBtaW5fYXJlYQogICAgICAgIHNlbGYubWF4X2FyZWEgPSBtYXhfYXJlYQoKICAgIGRlZiBfX2NhbGxfXyhzZWxmLCBpbWcpOgogICAgICAgIGZvciBhdHRlbXB0IGluIHJhbmdlKDEwKToKICAgICAgICAgICAgYXJlYSA9IGltZy5zaXplWzBdICogaW1nLnNpemVbMV0KICAgICAgICAgICAgdGFyZ2V0X2FyZWEgPSByYW5kb20udW5pZm9ybShzZWxmLm1pbl9hcmVhLCBzZWxmLm1heF9hcmVhKSAqIGFyZWEKICAgICAgICAgICAgYXNwZWN0X3JhdGlvID0gcmFuZG9tLnVuaWZvcm0oc2VsZi5taW5fYXNwZWN0LCBzZWxmLm1heF9hc3BlY3QpCgogICAgICAgICAgICB3ID0gaW50KHJvdW5kKG1hdGguc3FydCh0YXJnZXRfYXJlYSAqIGFzcGVjdF9yYXRpbykpKQogICAgICAgICAgICBoID0gaW50KHJvdW5kKG1hdGguc3FydCh0YXJnZXRfYXJlYSAvIGFzcGVjdF9yYXRpbykpKQoKICAgICAgICAgICAgaWYgcmFuZG9tLnJhbmRvbSgpIDwgMC41OgogICAgICAgICAgICAgICAgdywgaCA9IGgsIHcKCiAgICAgICAgICAgIGlmIHcgPD0gaW1nLnNpemVbMF0gYW5kIGggPD0gaW1nLnNpemVbMV06CiAgICAgICAgICAgICAgICB4MSA9IHJhbmRvbS5yYW5kaW50KDAsIGltZy5zaXplWzBdIC0gdykKICAgICAgICAgICAgICAgIHkxID0gcmFuZG9tLnJhbmRpbnQoMCwgaW1nLnNpemVbMV0gLSBoKQoKICAgICAgICAgICAgICAgIGltZyA9IGltZy5jcm9wKCh4MSwgeTEsIHgxICsgdywgeTEgKyBoKSkKICAgICAgICAgICAgICAgIGFzc2VydChpbWcuc2l6ZSA9PSAodywgaCkpCgogICAgICAgICAgICAgICAgcmV0dXJuIGltZy5yZXNpemUoKHNlbGYuc2l6ZSwgc2VsZi5zaXplKSwgc2VsZi5pbnRlcnBvbGF0aW9uKQoKICAgICAgICAjIEZhbGxiYWNrCiAgICAgICAgc2NhbGUgPSBSZXNpemUoc2VsZi5zaXplLCBpbnRlcnBvbGF0aW9uPXNlbGYuaW50ZXJwb2xhdGlvbikKICAgICAgICBjcm9wID0gQ2VudGVyQ3JvcChzZWxmLnNpemUpCiAgICAgICAgcmV0dXJuIGNyb3Aoc2NhbGUoaW1nKSkKCgp0cmFpbl90cmFuc2Zvcm0gPSBDb21wb3NlKFsKICAgIFJhbmRvbUNyb3AoMjg4KSwKICAgIFJhbmRvbUhvcml6b250YWxGbGlwKCksCl0pCgoKdGVzdF90cmFuc2Zvcm0gPSBDb21wb3NlKFsKICAgIFJhbmRvbUNyb3AoMjg4KSwKICAgIFJhbmRvbUhvcml6b250YWxGbGlwKCksCl0pCgoKdGVuc29yX3RyYW5zZm9ybSA9IENvbXBvc2UoWwogICAgVG9UZW5zb3IoKSwKICAgIE5vcm1hbGl6ZShtZWFuPVswLjQ4NSwgMC40NTYsIDAuNDA2XSwgc3RkPVswLjIyOSwgMC4yMjQsIDAuMjI1XSksCl0pCg==', 
                    'imet/make_submission.py': 'aW1wb3J0IGFyZ3BhcnNlCgppbXBvcnQgcGFuZGFzIGFzIHBkCgpmcm9tIC51dGlscyBpbXBvcnQgbWVhbl9kZgpmcm9tIC5kYXRhc2V0IGltcG9ydCBEQVRBX1JPT1QKZnJvbSAubWFpbiBpbXBvcnQgYmluYXJpemVfcHJlZGljdGlvbgoKCmRlZiBtYWluKCk6CiAgICBwYXJzZXIgPSBhcmdwYXJzZS5Bcmd1bWVudFBhcnNlcigpCiAgICBhcmcgPSBwYXJzZXIuYWRkX2FyZ3VtZW50CiAgICBhcmcoJ3ByZWRpY3Rpb25zJywgbmFyZ3M9JysnKQogICAgYXJnKCdvdXRwdXQnKQogICAgYXJnKCctLXRocmVzaG9sZCcsIHR5cGU9ZmxvYXQsIGRlZmF1bHQ9MC4yKQogICAgYXJncyA9IHBhcnNlci5wYXJzZV9hcmdzKCkKICAgIHNhbXBsZV9zdWJtaXNzaW9uID0gcGQucmVhZF9jc3YoCiAgICAgICAgREFUQV9ST09UIC8gJ3NhbXBsZV9zdWJtaXNzaW9uLmNzdicsIGluZGV4X2NvbD0naWQnKQogICAgZGZzID0gW10KICAgIGZvciBwcmVkaWN0aW9uIGluIGFyZ3MucHJlZGljdGlvbnM6CiAgICAgICAgZGYgPSBwZC5yZWFkX2hkZihwcmVkaWN0aW9uLCBpbmRleF9jb2w9J2lkJykKICAgICAgICBkZiA9IGRmLnJlaW5kZXgoc2FtcGxlX3N1Ym1pc3Npb24uaW5kZXgpCiAgICAgICAgZGZzLmFwcGVuZChkZikKICAgIGRmID0gcGQuY29uY2F0KGRmcykKICAgIGRmID0gbWVhbl9kZihkZikKICAgIGRmWzpdID0gYmluYXJpemVfcHJlZGljdGlvbihkZi52YWx1ZXMsIHRocmVzaG9sZD1hcmdzLnRocmVzaG9sZCkKICAgIGRmID0gZGYuYXBwbHkoZ2V0X2NsYXNzZXMsIGF4aXM9MSkKICAgIGRmLm5hbWUgPSAnYXR0cmlidXRlX2lkcycKICAgIGRmLnRvX2NzdihhcmdzLm91dHB1dCwgaGVhZGVyPVRydWUpCgoKZGVmIGdldF9jbGFzc2VzKGl0ZW0pOgogICAgcmV0dXJuICcgJy5qb2luKGNscyBmb3IgY2xzLCBpc19wcmVzZW50IGluIGl0ZW0uaXRlbXMoKSBpZiBpc19wcmVzZW50KQoKCmlmIF9fbmFtZV9fID09ICdfX21haW5fXyc6CiAgICBtYWluKCkK', 
                    'imet/models.py': 'ZnJvbSBmdW5jdG9vbHMgaW1wb3J0IHBhcnRpYWwKCmltcG9ydCB0b3JjaApmcm9tIHRvcmNoIGltcG9ydCBubgpmcm9tIHRvcmNoLm5uIGltcG9ydCBmdW5jdGlvbmFsIGFzIEYKaW1wb3J0IHRvcmNodmlzaW9uLm1vZGVscyBhcyBNCgpmcm9tIC51dGlscyBpbXBvcnQgT05fS0FHR0xFCgoKY2xhc3MgQXZnUG9vbChubi5Nb2R1bGUpOgogICAgZGVmIGZvcndhcmQoc2VsZiwgeCk6CiAgICAgICAgcmV0dXJuIEYuYXZnX3Bvb2wyZCh4LCB4LnNoYXBlWzI6XSkKCgpkZWYgY3JlYXRlX25ldChuZXRfY2xzLCBwcmV0cmFpbmVkOiBib29sKToKICAgIGlmIE9OX0tBR0dMRSBhbmQgcHJldHJhaW5lZDoKICAgICAgICBuZXQgPSBuZXRfY2xzKCkKICAgICAgICBtb2RlbF9uYW1lID0gbmV0X2Nscy5fX25hbWVfXwogICAgICAgIHdlaWdodHNfcGF0aCA9IGYnLi4vaW5wdXQve21vZGVsX25hbWV9L3ttb2RlbF9uYW1lfS5wdGgnCiAgICAgICAgbmV0LmxvYWRfc3RhdGVfZGljdCh0b3JjaC5sb2FkKHdlaWdodHNfcGF0aCkpCiAgICBlbHNlOgogICAgICAgIG5ldCA9IG5ldF9jbHMocHJldHJhaW5lZD1wcmV0cmFpbmVkKQogICAgcmV0dXJuIG5ldAoKCmNsYXNzIFJlc05ldChubi5Nb2R1bGUpOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIG51bV9jbGFzc2VzLAogICAgICAgICAgICAgICAgIHByZXRyYWluZWQ9RmFsc2UsIG5ldF9jbHM9TS5yZXNuZXQxMDEsIGRyb3BvdXQ9RmFsc2UpOgogICAgICAgIHN1cGVyKCkuX19pbml0X18oKQogICAgICAgIHNlbGYubmV0ID0gY3JlYXRlX25ldChuZXRfY2xzLCBwcmV0cmFpbmVkPXByZXRyYWluZWQpCiAgICAgICAgc2VsZi5uZXQuYXZncG9vbCA9IEF2Z1Bvb2woKQogICAgICAgIGlmIGRyb3BvdXQ6CiAgICAgICAgICAgIHNlbGYubmV0LmZjID0gbm4uU2VxdWVudGlhbCgKICAgICAgICAgICAgICAgIG5uLkRyb3BvdXQoKSwKICAgICAgICAgICAgICAgIG5uLkxpbmVhcihzZWxmLm5ldC5mYy5pbl9mZWF0dXJlcywgbnVtX2NsYXNzZXMpLAogICAgICAgICAgICApCiAgICAgICAgZWxzZToKICAgICAgICAgICAgc2VsZi5uZXQuZmMgPSBubi5MaW5lYXIoc2VsZi5uZXQuZmMuaW5fZmVhdHVyZXMsIG51bV9jbGFzc2VzKQoKICAgIGRlZiBmcmVzaF9wYXJhbXMoc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYubmV0LmZjLnBhcmFtZXRlcnMoKQoKICAgIGRlZiBmb3J3YXJkKHNlbGYsIHgpOgogICAgICAgIHJldHVybiBzZWxmLm5ldCh4KQoKCmNsYXNzIERlbnNlTmV0KG5uLk1vZHVsZSk6CiAgICBkZWYgX19pbml0X18oc2VsZiwgbnVtX2NsYXNzZXMsCiAgICAgICAgICAgICAgICAgcHJldHJhaW5lZD1GYWxzZSwgbmV0X2Nscz1NLmRlbnNlbmV0MTIxKToKICAgICAgICBzdXBlcigpLl9faW5pdF9fKCkKICAgICAgICBzZWxmLm5ldCA9IGNyZWF0ZV9uZXQobmV0X2NscywgcHJldHJhaW5lZD1wcmV0cmFpbmVkKQogICAgICAgIHNlbGYuYXZnX3Bvb2wgPSBBdmdQb29sKCkKICAgICAgICBzZWxmLm5ldC5jbGFzc2lmaWVyID0gbm4uTGluZWFyKAogICAgICAgICAgICBzZWxmLm5ldC5jbGFzc2lmaWVyLmluX2ZlYXR1cmVzLCBudW1fY2xhc3NlcykKCiAgICBkZWYgZnJlc2hfcGFyYW1zKHNlbGYpOgogICAgICAgIHJldHVybiBzZWxmLm5ldC5jbGFzc2lmaWVyLnBhcmFtZXRlcnMoKQoKICAgIGRlZiBmb3J3YXJkKHNlbGYsIHgpOgogICAgICAgIG91dCA9IHNlbGYubmV0LmZlYXR1cmVzKHgpCiAgICAgICAgb3V0ID0gRi5yZWx1KG91dCwgaW5wbGFjZT1UcnVlKQogICAgICAgIG91dCA9IHNlbGYuYXZnX3Bvb2wob3V0KS52aWV3KG91dC5zaXplKDApLCAtMSkKICAgICAgICBvdXQgPSBzZWxmLm5ldC5jbGFzc2lmaWVyKG91dCkKICAgICAgICByZXR1cm4gb3V0CgoKcmVzbmV0MTggPSBwYXJ0aWFsKFJlc05ldCwgbmV0X2Nscz1NLnJlc25ldDE4KQpyZXNuZXQzNCA9IHBhcnRpYWwoUmVzTmV0LCBuZXRfY2xzPU0ucmVzbmV0MzQpCnJlc25ldDUwID0gcGFydGlhbChSZXNOZXQsIG5ldF9jbHM9TS5yZXNuZXQ1MCkKcmVzbmV0MTAxID0gcGFydGlhbChSZXNOZXQsIG5ldF9jbHM9TS5yZXNuZXQxMDEpCnJlc25ldDE1MiA9IHBhcnRpYWwoUmVzTmV0LCBuZXRfY2xzPU0ucmVzbmV0MTUyKQoKZGVuc2VuZXQxMjEgPSBwYXJ0aWFsKERlbnNlTmV0LCBuZXRfY2xzPU0uZGVuc2VuZXQxMjEpCmRlbnNlbmV0MTY5ID0gcGFydGlhbChEZW5zZU5ldCwgbmV0X2Nscz1NLmRlbnNlbmV0MTY5KQpkZW5zZW5ldDIwMSA9IHBhcnRpYWwoRGVuc2VOZXQsIG5ldF9jbHM9TS5kZW5zZW5ldDIwMSkKZGVuc2VuZXQxNjEgPSBwYXJ0aWFsKERlbnNlTmV0LCBuZXRfY2xzPU0uZGVuc2VuZXQxNjEpCg==', 
                    'imet/__init__.py': 'aW1wb3J0IGN2MgoKCmN2Mi5zZXROdW1UaHJlYWRzKDApICAjIGZpeCBwb3RlbnRpYWwgcHl0b3JjaCB3b3JrZXIgaXNzdWVzCg==', 
                    'imet/make_folds.py': 'aW1wb3J0IGFyZ3BhcnNlCmZyb20gY29sbGVjdGlvbnMgaW1wb3J0IGRlZmF1bHRkaWN0LCBDb3VudGVyCmltcG9ydCByYW5kb20KCmltcG9ydCBwYW5kYXMgYXMgcGQKaW1wb3J0IHRxZG0KCmZyb20gLmRhdGFzZXQgaW1wb3J0IERBVEFfUk9PVAoKCmRlZiBtYWtlX2ZvbGRzKG5fZm9sZHM6IGludCkgLT4gcGQuRGF0YUZyYW1lOgogICAgZGYgPSBwZC5yZWFkX2NzdihEQVRBX1JPT1QgLyAndHJhaW4uY3N2JykKICAgIGNsc19jb3VudHMgPSBDb3VudGVyKGNscyBmb3IgY2xhc3NlcyBpbiBkZlsnYXR0cmlidXRlX2lkcyddLnN0ci5zcGxpdCgpCiAgICAgICAgICAgICAgICAgICAgICAgICBmb3IgY2xzIGluIGNsYXNzZXMpCiAgICBmb2xkX2Nsc19jb3VudHMgPSBkZWZhdWx0ZGljdChpbnQpCiAgICBmb2xkcyA9IFstMV0gKiBsZW4oZGYpCiAgICBmb3IgaXRlbSBpbiB0cWRtLnRxZG0oZGYuc2FtcGxlKGZyYWM9MSwgcmFuZG9tX3N0YXRlPTQyKS5pdGVydHVwbGVzKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgdG90YWw9bGVuKGRmKSk6CiAgICAgICAgY2xzID0gbWluKGl0ZW0uYXR0cmlidXRlX2lkcy5zcGxpdCgpLCBrZXk9bGFtYmRhIGNsczogY2xzX2NvdW50c1tjbHNdKQogICAgICAgIGZvbGRfY291bnRzID0gWyhmLCBmb2xkX2Nsc19jb3VudHNbZiwgY2xzXSkgZm9yIGYgaW4gcmFuZ2Uobl9mb2xkcyldCiAgICAgICAgbWluX2NvdW50ID0gbWluKFtjb3VudCBmb3IgXywgY291bnQgaW4gZm9sZF9jb3VudHNdKQogICAgICAgIHJhbmRvbS5zZWVkKGl0ZW0uSW5kZXgpCiAgICAgICAgZm9sZCA9IHJhbmRvbS5jaG9pY2UoW2YgZm9yIGYsIGNvdW50IGluIGZvbGRfY291bnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIGNvdW50ID09IG1pbl9jb3VudF0pCiAgICAgICAgZm9sZHNbaXRlbS5JbmRleF0gPSBmb2xkCiAgICAgICAgZm9yIGNscyBpbiBpdGVtLmF0dHJpYnV0ZV9pZHMuc3BsaXQoKToKICAgICAgICAgICAgZm9sZF9jbHNfY291bnRzW2ZvbGQsIGNsc10gKz0gMQogICAgZGZbJ2ZvbGQnXSA9IGZvbGRzCiAgICByZXR1cm4gZGYKCgpkZWYgbWFpbigpOgogICAgcGFyc2VyID0gYXJncGFyc2UuQXJndW1lbnRQYXJzZXIoKQogICAgcGFyc2VyLmFkZF9hcmd1bWVudCgnLS1uLWZvbGRzJywgdHlwZT1pbnQsIGRlZmF1bHQ9NSkKICAgIGFyZ3MgPSBwYXJzZXIucGFyc2VfYXJncygpCiAgICBkZiA9IG1ha2VfZm9sZHMobl9mb2xkcz1hcmdzLm5fZm9sZHMpCiAgICBkZi50b19jc3YoJ2ZvbGRzLmNzdicsIGluZGV4PU5vbmUpCgoKaWYgX19uYW1lX18gPT0gJ19fbWFpbl9fJzoKICAgIG1haW4oKQo=', 
                    'imet/dataset.py': 'ZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCmZyb20gdHlwaW5nIGltcG9ydCBDYWxsYWJsZSwgTGlzdAoKaW1wb3J0IGN2MgppbXBvcnQgcGFuZGFzIGFzIHBkCmZyb20gUElMIGltcG9ydCBJbWFnZQppbXBvcnQgdG9yY2gKZnJvbSB0b3JjaC51dGlscy5kYXRhIGltcG9ydCBEYXRhc2V0Cgpmcm9tIC50cmFuc2Zvcm1zIGltcG9ydCB0ZW5zb3JfdHJhbnNmb3JtCmZyb20gLnV0aWxzIGltcG9ydCBPTl9LQUdHTEUKCgpOX0NMQVNTRVMgPSAxMTAzCkRBVEFfUk9PVCA9IFBhdGgoJy4uL2lucHV0L2ltZXQtMjAxOS1mZ3ZjNicgaWYgT05fS0FHR0xFIGVsc2UgJy4vZGF0YScpCgoKY2xhc3MgVHJhaW5EYXRhc2V0KERhdGFzZXQpOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHJvb3Q6IFBhdGgsIGRmOiBwZC5EYXRhRnJhbWUsCiAgICAgICAgICAgICAgICAgaW1hZ2VfdHJhbnNmb3JtOiBDYWxsYWJsZSwgZGVidWc6IGJvb2wgPSBUcnVlKToKICAgICAgICBzdXBlcigpLl9faW5pdF9fKCkKICAgICAgICBzZWxmLl9yb290ID0gcm9vdAogICAgICAgIHNlbGYuX2RmID0gZGYKICAgICAgICBzZWxmLl9pbWFnZV90cmFuc2Zvcm0gPSBpbWFnZV90cmFuc2Zvcm0KICAgICAgICBzZWxmLl9kZWJ1ZyA9IGRlYnVnCgogICAgZGVmIF9fbGVuX18oc2VsZik6CiAgICAgICAgcmV0dXJuIGxlbihzZWxmLl9kZikKCiAgICBkZWYgX19nZXRpdGVtX18oc2VsZiwgaWR4OiBpbnQpOgogICAgICAgIGl0ZW0gPSBzZWxmLl9kZi5pbG9jW2lkeF0KICAgICAgICBpbWFnZSA9IGxvYWRfdHJhbnNmb3JtX2ltYWdlKAogICAgICAgICAgICBpdGVtLCBzZWxmLl9yb290LCBzZWxmLl9pbWFnZV90cmFuc2Zvcm0sIGRlYnVnPXNlbGYuX2RlYnVnKQogICAgICAgIHRhcmdldCA9IHRvcmNoLnplcm9zKE5fQ0xBU1NFUykKICAgICAgICBmb3IgY2xzIGluIGl0ZW0uYXR0cmlidXRlX2lkcy5zcGxpdCgpOgogICAgICAgICAgICB0YXJnZXRbaW50KGNscyldID0gMQogICAgICAgIHJldHVybiBpbWFnZSwgdGFyZ2V0CgoKY2xhc3MgVFRBRGF0YXNldDoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCByb290OiBQYXRoLCBkZjogcGQuRGF0YUZyYW1lLAogICAgICAgICAgICAgICAgIGltYWdlX3RyYW5zZm9ybTogQ2FsbGFibGUsIHR0YTogaW50KToKICAgICAgICBzZWxmLl9yb290ID0gcm9vdAogICAgICAgIHNlbGYuX2RmID0gZGYKICAgICAgICBzZWxmLl9pbWFnZV90cmFuc2Zvcm0gPSBpbWFnZV90cmFuc2Zvcm0KICAgICAgICBzZWxmLl90dGEgPSB0dGEKCiAgICBkZWYgX19sZW5fXyhzZWxmKToKICAgICAgICByZXR1cm4gbGVuKHNlbGYuX2RmKSAqIHNlbGYuX3R0YQoKICAgIGRlZiBfX2dldGl0ZW1fXyhzZWxmLCBpZHgpOgogICAgICAgIGl0ZW0gPSBzZWxmLl9kZi5pbG9jW2lkeCAlIGxlbihzZWxmLl9kZildCiAgICAgICAgaW1hZ2UgPSBsb2FkX3RyYW5zZm9ybV9pbWFnZShpdGVtLCBzZWxmLl9yb290LCBzZWxmLl9pbWFnZV90cmFuc2Zvcm0pCiAgICAgICAgcmV0dXJuIGltYWdlLCBpdGVtLmlkCgoKZGVmIGxvYWRfdHJhbnNmb3JtX2ltYWdlKAogICAgICAgIGl0ZW0sIHJvb3Q6IFBhdGgsIGltYWdlX3RyYW5zZm9ybTogQ2FsbGFibGUsIGRlYnVnOiBib29sID0gRmFsc2UpOgogICAgaW1hZ2UgPSBsb2FkX2ltYWdlKGl0ZW0sIHJvb3QpCiAgICBpbWFnZSA9IGltYWdlX3RyYW5zZm9ybShpbWFnZSkKICAgIGlmIGRlYnVnOgogICAgICAgIGltYWdlLnNhdmUoJ19kZWJ1Zy5wbmcnKQogICAgcmV0dXJuIHRlbnNvcl90cmFuc2Zvcm0oaW1hZ2UpCgoKZGVmIGxvYWRfaW1hZ2UoaXRlbSwgcm9vdDogUGF0aCkgLT4gSW1hZ2UuSW1hZ2U6CiAgICBpbWFnZSA9IGN2Mi5pbXJlYWQoc3RyKHJvb3QgLyBmJ3tpdGVtLmlkfS5wbmcnKSkKICAgIGltYWdlID0gY3YyLmN2dENvbG9yKGltYWdlLCBjdjIuQ09MT1JfQkdSMlJHQikKICAgIHJldHVybiBJbWFnZS5mcm9tYXJyYXkoaW1hZ2UpCgoKZGVmIGdldF9pZHMocm9vdDogUGF0aCkgLT4gTGlzdFtzdHJdOgogICAgcmV0dXJuIHNvcnRlZCh7cC5uYW1lLnNwbGl0KCdfJylbMF0gZm9yIHAgaW4gcm9vdC5nbG9iKCcqLnBuZycpfSkK', 
                    'imet/utils.py': 'ZnJvbSBkYXRldGltZSBpbXBvcnQgZGF0ZXRpbWUKaW1wb3J0IGpzb24KaW1wb3J0IGdsb2IKaW1wb3J0IG9zCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aApmcm9tIG11bHRpcHJvY2Vzc2luZy5wb29sIGltcG9ydCBUaHJlYWRQb29sCmZyb20gdHlwaW5nIGltcG9ydCBEaWN0CgppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHBhbmRhcyBhcyBwZApmcm9tIHNjaXB5LnN0YXRzLm1zdGF0cyBpbXBvcnQgZ21lYW4KaW1wb3J0IHRvcmNoCmZyb20gdG9yY2ggaW1wb3J0IG5uCmZyb20gdG9yY2gudXRpbHMuZGF0YSBpbXBvcnQgRGF0YUxvYWRlcgoKCk9OX0tBR0dMRTogYm9vbCA9ICdLQUdHTEVfV09SS0lOR19ESVInIGluIG9zLmVudmlyb24KCgpkZWYgZ21lYW5fZGYoZGY6IHBkLkRhdGFGcmFtZSkgLT4gcGQuRGF0YUZyYW1lOgogICAgcmV0dXJuIGRmLmdyb3VwYnkobGV2ZWw9MCkuYWdnKGxhbWJkYSB4OiBnbWVhbihsaXN0KHgpKSkKCgpkZWYgbWVhbl9kZihkZjogcGQuRGF0YUZyYW1lKSAtPiBwZC5EYXRhRnJhbWU6CiAgICByZXR1cm4gZGYuZ3JvdXBieShsZXZlbD0wKS5tZWFuKCkKCgpkZWYgbG9hZF9tb2RlbChtb2RlbDogbm4uTW9kdWxlLCBwYXRoOiBQYXRoKSAtPiBEaWN0OgogICAgc3RhdGUgPSB0b3JjaC5sb2FkKHN0cihwYXRoKSkKICAgIG1vZGVsLmxvYWRfc3RhdGVfZGljdChzdGF0ZVsnbW9kZWwnXSkKICAgIHByaW50KCdMb2FkZWQgbW9kZWwgZnJvbSBlcG9jaCB7ZXBvY2h9LCBzdGVwIHtzdGVwOix9Jy5mb3JtYXQoKipzdGF0ZSkpCiAgICByZXR1cm4gc3RhdGUKCgpjbGFzcyBUaHJlYWRpbmdEYXRhTG9hZGVyKERhdGFMb2FkZXIpOgogICAgZGVmIF9faXRlcl9fKHNlbGYpOgogICAgICAgIHNhbXBsZV9pdGVyID0gaXRlcihzZWxmLmJhdGNoX3NhbXBsZXIpCiAgICAgICAgaWYgc2VsZi5udW1fd29ya2VycyA9PSAwOgogICAgICAgICAgICBmb3IgaW5kaWNlcyBpbiBzYW1wbGVfaXRlcjoKICAgICAgICAgICAgICAgIHlpZWxkIHNlbGYuY29sbGF0ZV9mbihbc2VsZi5fZ2V0X2l0ZW0oaSkgZm9yIGkgaW4gaW5kaWNlc10pCiAgICAgICAgZWxzZToKICAgICAgICAgICAgcHJlZmV0Y2ggPSAxCiAgICAgICAgICAgIHdpdGggVGhyZWFkUG9vbChwcm9jZXNzZXM9c2VsZi5udW1fd29ya2VycykgYXMgcG9vbDoKICAgICAgICAgICAgICAgIGZ1dHVyZXMgPSBbXQogICAgICAgICAgICAgICAgZm9yIGluZGljZXMgaW4gc2FtcGxlX2l0ZXI6CiAgICAgICAgICAgICAgICAgICAgZnV0dXJlcy5hcHBlbmQoW3Bvb2wuYXBwbHlfYXN5bmMoc2VsZi5fZ2V0X2l0ZW0sIGFyZ3M9KGksKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yIGkgaW4gaW5kaWNlc10pCiAgICAgICAgICAgICAgICAgICAgaWYgbGVuKGZ1dHVyZXMpID4gcHJlZmV0Y2g6CiAgICAgICAgICAgICAgICAgICAgICAgIHlpZWxkIHNlbGYuY29sbGF0ZV9mbihbZi5nZXQoKSBmb3IgZiBpbiBmdXR1cmVzLnBvcCgwKV0pCiAgICAgICAgICAgICAgICAgICAgIyBpdGVtcyA9IHBvb2wubWFwKGxhbWJkYSBpOiBzZWxmLmRhdGFzZXRbaV0sIGluZGljZXMpCiAgICAgICAgICAgICAgICAgICAgIyB5aWVsZCBzZWxmLmNvbGxhdGVfZm4oaXRlbXMpCiAgICAgICAgICAgICAgICBmb3IgYmF0Y2hfZnV0dXJlcyBpbiBmdXR1cmVzOgogICAgICAgICAgICAgICAgICAgIHlpZWxkIHNlbGYuY29sbGF0ZV9mbihbZi5nZXQoKSBmb3IgZiBpbiBiYXRjaF9mdXR1cmVzXSkKCiAgICBkZWYgX2dldF9pdGVtKHNlbGYsIGkpOgogICAgICAgIHJldHVybiBzZWxmLmRhdGFzZXRbaV0KCgpkZWYgd3JpdGVfZXZlbnQobG9nLCBzdGVwOiBpbnQsICoqZGF0YSk6CiAgICBkYXRhWydzdGVwJ10gPSBzdGVwCiAgICBkYXRhWydkdCddID0gZGF0ZXRpbWUubm93KCkuaXNvZm9ybWF0KCkKICAgIGxvZy53cml0ZShqc29uLmR1bXBzKGRhdGEsIHNvcnRfa2V5cz1UcnVlKSkKICAgIGxvZy53cml0ZSgnXG4nKQogICAgbG9nLmZsdXNoKCkKCgpkZWYgcGxvdCgqYXJncywgeW1pbj1Ob25lLCB5bWF4PU5vbmUsIHhtaW49Tm9uZSwgeG1heD1Ob25lLCBwYXJhbXM9RmFsc2UsCiAgICAgICAgIG1heF9wb2ludHM9MjAwLCBsZWdlbmQ9VHJ1ZSwgdGl0bGU9Tm9uZSwKICAgICAgICAgcHJpbnRfa2V5cz1GYWxzZSwgcHJpbnRfcGF0aHM9RmFsc2UsIHBsdD1Ob25lLCBuZXdmaWd1cmU9VHJ1ZSwKICAgICAgICAgeF9zY2FsZT0xKToKICAgICIiIgogICAgVXNlIGluIHRoZSBub3RlYm9vayBsaWtlIHRoaXM6OgoKICAgICAgICAlbWF0cGxvdGxpYiBpbmxpbmUKICAgICAgICBmcm9tIGltZXQudXRpbHMgaW1wb3J0IHBsb3QKICAgICAgICBwbG90KCcuL3J1bnMvb2MyJywgJy4vcnVucy9vYzEnLCAnbG9zcycsICd2YWxpZF9sb3NzJykKCiAgICAiIiIKICAgIGltcG9ydCBqc29uX2xpbmVzICAjIG5vIGF2YWlsYWJsZSBvbiBLYWdnbGUKCiAgICBpZiBwbHQgaXMgTm9uZToKICAgICAgICBmcm9tIG1hdHBsb3RsaWIgaW1wb3J0IHB5cGxvdCBhcyBwbHQKICAgIHBhdGhzLCBrZXlzID0gW10sIFtdCiAgICBmb3IgeCBpbiBhcmdzOgogICAgICAgIGlmIHguc3RhcnRzd2l0aCgnLicpIG9yICcvJyBpbiB4OgogICAgICAgICAgICBpZiAnKicgaW4geDoKICAgICAgICAgICAgICAgIHBhdGhzLmV4dGVuZChnbG9iLmdsb2IoeCkpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBwYXRocy5hcHBlbmQoeCkKICAgICAgICBlbHNlOgogICAgICAgICAgICBrZXlzLmFwcGVuZCh4KQogICAgaWYgcHJpbnRfcGF0aHM6CiAgICAgICAgcHJpbnQoJ0ZvdW5kIHBhdGhzOiB7fScuZm9ybWF0KCcgJy5qb2luKHNvcnRlZChwYXRocykpKSkKICAgIGlmIG5ld2ZpZ3VyZToKICAgICAgICBwbHQuZmlndXJlKGZpZ3NpemU9KDEyLCA4KSkKICAgIGtleXMgPSBrZXlzIG9yIFsnbG9zcycsICd2YWxpZF9sb3NzJ10KCiAgICB5bGltX2t3ID0ge30KICAgIGlmIHltaW4gaXMgbm90IE5vbmU6CiAgICAgICAgeWxpbV9rd1snYm90dG9tJ10gPSB5bWluCiAgICBpZiB5bWF4IGlzIG5vdCBOb25lOgogICAgICAgIHlsaW1fa3dbJ3RvcCddID0geW1heAogICAgaWYgeWxpbV9rdzoKICAgICAgICBwbHQueWxpbSgqKnlsaW1fa3cpCgogICAgeGxpbV9rdyA9IHt9CiAgICBpZiB4bWluIGlzIG5vdCBOb25lOgogICAgICAgIHhsaW1fa3dbJ2xlZnQnXSA9IHhtaW4KICAgIGlmIHhtYXggaXMgbm90IE5vbmU6CiAgICAgICAgeGxpbV9rd1sncmlnaHQnXSA9IHhtYXgKICAgIGlmIHhsaW1fa3c6CiAgICAgICAgcGx0LnhsaW0oKip4bGltX2t3KQogICAgYWxsX2tleXMgPSBzZXQoKQogICAgZm9yIHBhdGggaW4gc29ydGVkKHBhdGhzKToKICAgICAgICBwYXRoID0gUGF0aChwYXRoKQogICAgICAgIHdpdGgganNvbl9saW5lcy5vcGVuKHBhdGggLyAndHJhaW4ubG9nJywgYnJva2VuPVRydWUpIGFzIGY6CiAgICAgICAgICAgIGV2ZW50cyA9IGxpc3QoZikKICAgICAgICBhbGxfa2V5cy51cGRhdGUoayBmb3IgZSBpbiBldmVudHMgZm9yIGsgaW4gZSkKICAgICAgICBmb3Iga2V5IGluIHNvcnRlZChrZXlzKToKICAgICAgICAgICAgeHMsIHlzLCB5c19lcnIgPSBbXSwgW10sIFtdCiAgICAgICAgICAgIGZvciBlIGluIGV2ZW50czoKICAgICAgICAgICAgICAgIGlmIGtleSBpbiBlOgogICAgICAgICAgICAgICAgICAgIHhzLmFwcGVuZChlWydzdGVwJ10gKiB4X3NjYWxlKQogICAgICAgICAgICAgICAgICAgIHlzLmFwcGVuZChlW2tleV0pCiAgICAgICAgICAgICAgICAgICAgc3RkX2tleSA9IGtleSArICdfc3RkJwogICAgICAgICAgICAgICAgICAgIGlmIHN0ZF9rZXkgaW4gZToKICAgICAgICAgICAgICAgICAgICAgICAgeXNfZXJyLmFwcGVuZChlW3N0ZF9rZXldKQogICAgICAgICAgICBpZiB4czoKICAgICAgICAgICAgICAgIGlmIG5wLmlzbmFuKHlzKS5hbnkoKToKICAgICAgICAgICAgICAgICAgICBwcmludCgnV2FybmluZzogTmFOIHt9IGZvciB7fScuZm9ybWF0KGtleSwgcGF0aCkpCiAgICAgICAgICAgICAgICBpZiBsZW4oeHMpID4gMiAqIG1heF9wb2ludHM6CiAgICAgICAgICAgICAgICAgICAgaW5kaWNlcyA9IChucC5hcmFuZ2UoMCwgbGVuKHhzKSAtIDEsIGxlbih4cykgLyBtYXhfcG9pbnRzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmFzdHlwZShucC5pbnQzMikpCiAgICAgICAgICAgICAgICAgICAgeHMgPSBucC5hcnJheSh4cylbaW5kaWNlc1sxOl1dCiAgICAgICAgICAgICAgICAgICAgeXMgPSBfc21vb3RoKHlzLCBpbmRpY2VzKQogICAgICAgICAgICAgICAgICAgIGlmIHlzX2VycjoKICAgICAgICAgICAgICAgICAgICAgICAgeXNfZXJyID0gX3Ntb290aCh5c19lcnIsIGluZGljZXMpCiAgICAgICAgICAgICAgICBsYWJlbCA9ICd7fToge30nLmZvcm1hdChwYXRoLCBrZXkpCiAgICAgICAgICAgICAgICBpZiBsYWJlbC5zdGFydHN3aXRoKCdfJyk6CiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAnICcgKyBsYWJlbAogICAgICAgICAgICAgICAgaWYgeXNfZXJyOgogICAgICAgICAgICAgICAgICAgIHlzX2VyciA9IDEuOTYgKiBucC5hcnJheSh5c19lcnIpCiAgICAgICAgICAgICAgICAgICAgcGx0LmVycm9yYmFyKHhzLCB5cywgeWVycj15c19lcnIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZtdD0nLW8nLCBjYXBzaXplPTUsIGNhcHRoaWNrPTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPWxhYmVsKQogICAgICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgICAgICBwbHQucGxvdCh4cywgeXMsIGxhYmVsPWxhYmVsKQogICAgICAgICAgICAgICAgcGx0LmxlZ2VuZCgpCiAgICBpZiBuZXdmaWd1cmU6CiAgICAgICAgcGx0LmdyaWQoKQogICAgaWYgbGVnZW5kOgogICAgICAgIHBsdC5sZWdlbmQoKQogICAgaWYgdGl0bGU6CiAgICAgICAgcGx0LnRpdGxlKHRpdGxlKQogICAgaWYgcHJpbnRfa2V5czoKICAgICAgICBwcmludCgnRm91bmQga2V5czoge30nCiAgICAgICAgICAgICAgLmZvcm1hdCgnLCAnLmpvaW4oc29ydGVkKGFsbF9rZXlzIC0geydzdGVwJywgJ2R0J30pKSkpCgoKZGVmIF9zbW9vdGgoeXMsIGluZGljZXMpOgogICAgcmV0dXJuIFtucC5tZWFuKHlzW2lkeDogaW5kaWNlc1tpICsgMV1dKQogICAgICAgICAgICBmb3IgaSwgaWR4IGluIGVudW1lcmF0ZShpbmRpY2VzWzotMV0pXQo=', 
                    'imet/main.py': 'aW1wb3J0IGFyZ3BhcnNlCmZyb20gaXRlcnRvb2xzIGltcG9ydCBpc2xpY2UKaW1wb3J0IGpzb24KZnJvbSBwYXRobGliIGltcG9ydCBQYXRoCmltcG9ydCBzaHV0aWwKaW1wb3J0IHdhcm5pbmdzCmZyb20gdHlwaW5nIGltcG9ydCBEaWN0CgppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHBhbmRhcyBhcyBwZApmcm9tIHNrbGVhcm4ubWV0cmljcyBpbXBvcnQgZmJldGFfc2NvcmUKZnJvbSBza2xlYXJuLmV4Y2VwdGlvbnMgaW1wb3J0IFVuZGVmaW5lZE1ldHJpY1dhcm5pbmcKaW1wb3J0IHRvcmNoCmZyb20gdG9yY2ggaW1wb3J0IG5uLCBjdWRhCmZyb20gdG9yY2gub3B0aW0gaW1wb3J0IEFkYW0KaW1wb3J0IHRxZG0KCmZyb20gLiBpbXBvcnQgbW9kZWxzCmZyb20gLmRhdGFzZXQgaW1wb3J0IFRyYWluRGF0YXNldCwgVFRBRGF0YXNldCwgZ2V0X2lkcywgTl9DTEFTU0VTLCBEQVRBX1JPT1QKZnJvbSAudHJhbnNmb3JtcyBpbXBvcnQgdHJhaW5fdHJhbnNmb3JtLCB0ZXN0X3RyYW5zZm9ybQpmcm9tIC51dGlscyBpbXBvcnQgKAogICAgd3JpdGVfZXZlbnQsIGxvYWRfbW9kZWwsIG1lYW5fZGYsIFRocmVhZGluZ0RhdGFMb2FkZXIgYXMgRGF0YUxvYWRlciwKICAgIE9OX0tBR0dMRSkKCgpkZWYgbWFpbigpOgogICAgcGFyc2VyID0gYXJncGFyc2UuQXJndW1lbnRQYXJzZXIoKQogICAgYXJnID0gcGFyc2VyLmFkZF9hcmd1bWVudAogICAgYXJnKCdtb2RlJywgY2hvaWNlcz1bJ3RyYWluJywgJ3ZhbGlkYXRlJywgJ3ByZWRpY3RfdmFsaWQnLCAncHJlZGljdF90ZXN0J10pCiAgICBhcmcoJ3J1bl9yb290JykKICAgIGFyZygnLS1tb2RlbCcsIGRlZmF1bHQ9J3Jlc25ldDEwMScpCiAgICBhcmcoJy0tcHJldHJhaW5lZCcsIHR5cGU9aW50LCBkZWZhdWx0PTEpCiAgICBhcmcoJy0tYmF0Y2gtc2l6ZScsIHR5cGU9aW50LCBkZWZhdWx0PTY0KQogICAgYXJnKCctLXN0ZXAnLCB0eXBlPWludCwgZGVmYXVsdD0xKQogICAgYXJnKCctLXdvcmtlcnMnLCB0eXBlPWludCwgZGVmYXVsdD0yIGlmIE9OX0tBR0dMRSBlbHNlIDQpCiAgICBhcmcoJy0tbHInLCB0eXBlPWZsb2F0LCBkZWZhdWx0PTFlLTQpCiAgICBhcmcoJy0tcGF0aWVuY2UnLCB0eXBlPWludCwgZGVmYXVsdD00KQogICAgYXJnKCctLWNsZWFuJywgYWN0aW9uPSdzdG9yZV90cnVlJykKICAgIGFyZygnLS1uLWVwb2NocycsIHR5cGU9aW50LCBkZWZhdWx0PTEwMCkKICAgIGFyZygnLS1lcG9jaC1zaXplJywgdHlwZT1pbnQpCiAgICBhcmcoJy0tdHRhJywgdHlwZT1pbnQsIGRlZmF1bHQ9NCkKICAgIGFyZygnLS11c2Utc2FtcGxlJywgYWN0aW9uPSdzdG9yZV90cnVlJywgaGVscD0ndXNlIGEgc2FtcGxlIG9mIHRoZSBkYXRhc2V0JykKICAgIGFyZygnLS1kZWJ1ZycsIGFjdGlvbj0nc3RvcmVfdHJ1ZScpCiAgICBhcmcoJy0tbGltaXQnLCB0eXBlPWludCkKICAgIGFyZygnLS1mb2xkJywgdHlwZT1pbnQsIGRlZmF1bHQ9MCkKICAgIGFyZ3MgPSBwYXJzZXIucGFyc2VfYXJncygpCgogICAgcnVuX3Jvb3QgPSBQYXRoKGFyZ3MucnVuX3Jvb3QpCiAgICBmb2xkcyA9IHBkLnJlYWRfY3N2KCdmb2xkcy5jc3YnKQogICAgdHJhaW5fcm9vdCA9IERBVEFfUk9PVCAvICgndHJhaW5fc2FtcGxlJyBpZiBhcmdzLnVzZV9zYW1wbGUgZWxzZSAndHJhaW4nKQogICAgaWYgYXJncy51c2Vfc2FtcGxlOgogICAgICAgIGZvbGRzID0gZm9sZHNbZm9sZHNbJ0lkJ10uaXNpbihzZXQoZ2V0X2lkcyh0cmFpbl9yb290KSkpXQogICAgdHJhaW5fZm9sZCA9IGZvbGRzW2ZvbGRzWydmb2xkJ10gIT0gYXJncy5mb2xkXQogICAgdmFsaWRfZm9sZCA9IGZvbGRzW2ZvbGRzWydmb2xkJ10gPT0gYXJncy5mb2xkXQogICAgaWYgYXJncy5saW1pdDoKICAgICAgICB0cmFpbl9mb2xkID0gdHJhaW5fZm9sZFs6YXJncy5saW1pdF0KICAgICAgICB2YWxpZF9mb2xkID0gdmFsaWRfZm9sZFs6YXJncy5saW1pdF0KCiAgICBkZWYgbWFrZV9sb2FkZXIoZGY6IHBkLkRhdGFGcmFtZSwgaW1hZ2VfdHJhbnNmb3JtKSAtPiBEYXRhTG9hZGVyOgogICAgICAgIHJldHVybiBEYXRhTG9hZGVyKAogICAgICAgICAgICBUcmFpbkRhdGFzZXQodHJhaW5fcm9vdCwgZGYsIGltYWdlX3RyYW5zZm9ybSwgZGVidWc9YXJncy5kZWJ1ZyksCiAgICAgICAgICAgIHNodWZmbGU9VHJ1ZSwKICAgICAgICAgICAgYmF0Y2hfc2l6ZT1hcmdzLmJhdGNoX3NpemUsCiAgICAgICAgICAgIG51bV93b3JrZXJzPWFyZ3Mud29ya2VycywKICAgICAgICApCiAgICBjcml0ZXJpb24gPSBubi5CQ0VXaXRoTG9naXRzTG9zcyhyZWR1Y3Rpb249J25vbmUnKQogICAgbW9kZWwgPSBnZXRhdHRyKG1vZGVscywgYXJncy5tb2RlbCkoCiAgICAgICAgbnVtX2NsYXNzZXM9Tl9DTEFTU0VTLCBwcmV0cmFpbmVkPWFyZ3MucHJldHJhaW5lZCkKICAgIHVzZV9jdWRhID0gY3VkYS5pc19hdmFpbGFibGUoKQogICAgZnJlc2hfcGFyYW1zID0gbGlzdChtb2RlbC5mcmVzaF9wYXJhbXMoKSkKICAgIGFsbF9wYXJhbXMgPSBsaXN0KG1vZGVsLnBhcmFtZXRlcnMoKSkKICAgIGlmIHVzZV9jdWRhOgogICAgICAgIG1vZGVsID0gbW9kZWwuY3VkYSgpCgogICAgaWYgYXJncy5tb2RlID09ICd0cmFpbic6CiAgICAgICAgaWYgcnVuX3Jvb3QuZXhpc3RzKCkgYW5kIGFyZ3MuY2xlYW46CiAgICAgICAgICAgIHNodXRpbC5ybXRyZWUocnVuX3Jvb3QpCiAgICAgICAgcnVuX3Jvb3QubWtkaXIoZXhpc3Rfb2s9VHJ1ZSwgcGFyZW50cz1UcnVlKQogICAgICAgIChydW5fcm9vdCAvICdwYXJhbXMuanNvbicpLndyaXRlX3RleHQoCiAgICAgICAgICAgIGpzb24uZHVtcHModmFycyhhcmdzKSwgaW5kZW50PTQsIHNvcnRfa2V5cz1UcnVlKSkKCiAgICAgICAgdHJhaW5fbG9hZGVyID0gbWFrZV9sb2FkZXIodHJhaW5fZm9sZCwgdHJhaW5fdHJhbnNmb3JtKQogICAgICAgIHZhbGlkX2xvYWRlciA9IG1ha2VfbG9hZGVyKHZhbGlkX2ZvbGQsIHRlc3RfdHJhbnNmb3JtKQogICAgICAgIHByaW50KGYne2xlbih0cmFpbl9sb2FkZXIuZGF0YXNldCk6LH0gaXRlbXMgaW4gdHJhaW4sICcKICAgICAgICAgICAgICBmJ3tsZW4odmFsaWRfbG9hZGVyLmRhdGFzZXQpOix9IGluIHZhbGlkJykKCiAgICAgICAgdHJhaW5fa3dhcmdzID0gZGljdCgKICAgICAgICAgICAgYXJncz1hcmdzLAogICAgICAgICAgICBtb2RlbD1tb2RlbCwKICAgICAgICAgICAgY3JpdGVyaW9uPWNyaXRlcmlvbiwKICAgICAgICAgICAgdHJhaW5fbG9hZGVyPXRyYWluX2xvYWRlciwKICAgICAgICAgICAgdmFsaWRfbG9hZGVyPXZhbGlkX2xvYWRlciwKICAgICAgICAgICAgcGF0aWVuY2U9YXJncy5wYXRpZW5jZSwKICAgICAgICAgICAgaW5pdF9vcHRpbWl6ZXI9bGFtYmRhIHBhcmFtcywgbHI6IEFkYW0ocGFyYW1zLCBsciksCiAgICAgICAgICAgIHVzZV9jdWRhPXVzZV9jdWRhLAogICAgICAgICkKCiAgICAgICAgaWYgYXJncy5wcmV0cmFpbmVkOgogICAgICAgICAgICBpZiB0cmFpbihwYXJhbXM9ZnJlc2hfcGFyYW1zLCBuX2Vwb2Nocz0xLCAqKnRyYWluX2t3YXJncyk6CiAgICAgICAgICAgICAgICB0cmFpbihwYXJhbXM9YWxsX3BhcmFtcywgKip0cmFpbl9rd2FyZ3MpCiAgICAgICAgZWxzZToKICAgICAgICAgICAgdHJhaW4ocGFyYW1zPWFsbF9wYXJhbXMsICoqdHJhaW5fa3dhcmdzKQoKICAgIGVsaWYgYXJncy5tb2RlID09ICd2YWxpZGF0ZSc6CiAgICAgICAgdmFsaWRfbG9hZGVyID0gbWFrZV9sb2FkZXIodmFsaWRfZm9sZCwgdGVzdF90cmFuc2Zvcm0pCiAgICAgICAgbG9hZF9tb2RlbChtb2RlbCwgcnVuX3Jvb3QgLyAnbW9kZWwucHQnKQogICAgICAgIHZhbGlkYXRpb24obW9kZWwsIGNyaXRlcmlvbiwgdHFkbS50cWRtKHZhbGlkX2xvYWRlciwgZGVzYz0nVmFsaWRhdGlvbicpLAogICAgICAgICAgICAgICAgICAgdXNlX2N1ZGE9dXNlX2N1ZGEpCgogICAgZWxpZiBhcmdzLm1vZGUuc3RhcnRzd2l0aCgncHJlZGljdCcpOgogICAgICAgIGxvYWRfbW9kZWwobW9kZWwsIHJ1bl9yb290IC8gJ2Jlc3QtbW9kZWwucHQnKQogICAgICAgIHByZWRpY3Rfa3dhcmdzID0gZGljdCgKICAgICAgICAgICAgYmF0Y2hfc2l6ZT1hcmdzLmJhdGNoX3NpemUsCiAgICAgICAgICAgIHR0YT1hcmdzLnR0YSwKICAgICAgICAgICAgdXNlX2N1ZGE9dXNlX2N1ZGEsCiAgICAgICAgICAgIHdvcmtlcnM9YXJncy53b3JrZXJzLAogICAgICAgICkKICAgICAgICBpZiBhcmdzLm1vZGUgPT0gJ3ByZWRpY3RfdmFsaWQnOgogICAgICAgICAgICBwcmVkaWN0KG1vZGVsLCBkZj12YWxpZF9mb2xkLCByb290PXRyYWluX3Jvb3QsIG91dF9wYXRoPXJ1bl9yb290IC8gJ3ZhbC5oNScsICoqcHJlZGljdF9rd2FyZ3MpCiAgICAgICAgZWxpZiBhcmdzLm1vZGUgPT0gJ3ByZWRpY3RfdGVzdCc6CiAgICAgICAgICAgIHRlc3Rfcm9vdCA9IERBVEFfUk9PVCAvICgKICAgICAgICAgICAgICAgICd0ZXN0X3NhbXBsZScgaWYgYXJncy51c2Vfc2FtcGxlIGVsc2UgJ3Rlc3QnKQogICAgICAgICAgICBzcyA9IHBkLnJlYWRfY3N2KERBVEFfUk9PVCAvICdzYW1wbGVfc3VibWlzc2lvbi5jc3YnKQogICAgICAgICAgICBpZiBhcmdzLnVzZV9zYW1wbGU6CiAgICAgICAgICAgICAgICBzcyA9IHNzW3NzWydpZCddLmlzaW4oc2V0KGdldF9pZHModGVzdF9yb290KSkpXQogICAgICAgICAgICBpZiBhcmdzLmxpbWl0OgogICAgICAgICAgICAgICAgc3MgPSBzc1s6YXJncy5saW1pdF0KICAgICAgICAgICAgcHJlZGljdChtb2RlbCwgZGY9c3MsIHJvb3Q9dGVzdF9yb290LAogICAgICAgICAgICAgICAgICAgIG91dF9wYXRoPXJ1bl9yb290IC8gJ3Rlc3QuaDUnLAogICAgICAgICAgICAgICAgICAgICoqcHJlZGljdF9rd2FyZ3MpCgoKZGVmIHByZWRpY3QobW9kZWwsIHJvb3Q6IFBhdGgsIGRmOiBwZC5EYXRhRnJhbWUsIG91dF9wYXRoOiBQYXRoLAogICAgICAgICAgICBiYXRjaF9zaXplOiBpbnQsIHR0YTogaW50LCB3b3JrZXJzOiBpbnQsIHVzZV9jdWRhOiBib29sKToKICAgIGxvYWRlciA9IERhdGFMb2FkZXIoCiAgICAgICAgZGF0YXNldD1UVEFEYXRhc2V0KHJvb3QsIGRmLCB0ZXN0X3RyYW5zZm9ybSwgdHRhPXR0YSksCiAgICAgICAgc2h1ZmZsZT1GYWxzZSwKICAgICAgICBiYXRjaF9zaXplPWJhdGNoX3NpemUsCiAgICAgICAgbnVtX3dvcmtlcnM9d29ya2VycywKICAgICkKICAgIG1vZGVsLmV2YWwoKQogICAgYWxsX291dHB1dHMsIGFsbF9pZHMgPSBbXSwgW10KICAgIHdpdGggdG9yY2gubm9fZ3JhZCgpOgogICAgICAgIGZvciBpbnB1dHMsIGlkcyBpbiB0cWRtLnRxZG0obG9hZGVyLCBkZXNjPSdQcmVkaWN0Jyk6CiAgICAgICAgICAgIGlmIHVzZV9jdWRhOgogICAgICAgICAgICAgICAgaW5wdXRzID0gaW5wdXRzLmN1ZGEoKQogICAgICAgICAgICBvdXRwdXRzID0gdG9yY2guc2lnbW9pZChtb2RlbChpbnB1dHMpKQogICAgICAgICAgICBhbGxfb3V0cHV0cy5hcHBlbmQob3V0cHV0cy5kYXRhLmNwdSgpLm51bXB5KCkpCiAgICAgICAgICAgIGFsbF9pZHMuZXh0ZW5kKGlkcykKICAgIGRmID0gcGQuRGF0YUZyYW1lKAogICAgICAgIGRhdGE9bnAuY29uY2F0ZW5hdGUoYWxsX291dHB1dHMpLAogICAgICAgIGluZGV4PWFsbF9pZHMsCiAgICAgICAgY29sdW1ucz1tYXAoc3RyLCByYW5nZShOX0NMQVNTRVMpKSkKICAgIGRmID0gbWVhbl9kZihkZikKICAgIGRmLnRvX2hkZihvdXRfcGF0aCwgJ3Byb2InLCBpbmRleF9sYWJlbD0naWQnKQogICAgcHJpbnQoZidTYXZlZCBwcmVkaWN0aW9ucyB0byB7b3V0X3BhdGh9JykKCgpkZWYgdHJhaW4oYXJncywgbW9kZWw6IG5uLk1vZHVsZSwgY3JpdGVyaW9uLCAqLCBwYXJhbXMsCiAgICAgICAgICB0cmFpbl9sb2FkZXIsIHZhbGlkX2xvYWRlciwgaW5pdF9vcHRpbWl6ZXIsIHVzZV9jdWRhLAogICAgICAgICAgbl9lcG9jaHM9Tm9uZSwgcGF0aWVuY2U9MiwgbWF4X2xyX2NoYW5nZXM9MikgLT4gYm9vbDoKICAgIGxyID0gYXJncy5scgogICAgbl9lcG9jaHMgPSBuX2Vwb2NocyBvciBhcmdzLm5fZXBvY2hzCiAgICBwYXJhbXMgPSBsaXN0KHBhcmFtcykKICAgIG9wdGltaXplciA9IGluaXRfb3B0aW1pemVyKHBhcmFtcywgbHIpCgogICAgcnVuX3Jvb3QgPSBQYXRoKGFyZ3MucnVuX3Jvb3QpCiAgICBtb2RlbF9wYXRoID0gcnVuX3Jvb3QgLyAnbW9kZWwucHQnCiAgICBiZXN0X21vZGVsX3BhdGggPSBydW5fcm9vdCAvICdiZXN0LW1vZGVsLnB0JwogICAgaWYgbW9kZWxfcGF0aC5leGlzdHMoKToKICAgICAgICBzdGF0ZSA9IGxvYWRfbW9kZWwobW9kZWwsIG1vZGVsX3BhdGgpCiAgICAgICAgZXBvY2ggPSBzdGF0ZVsnZXBvY2gnXQogICAgICAgIHN0ZXAgPSBzdGF0ZVsnc3RlcCddCiAgICAgICAgYmVzdF92YWxpZF9sb3NzID0gc3RhdGVbJ2Jlc3RfdmFsaWRfbG9zcyddCiAgICBlbHNlOgogICAgICAgIGVwb2NoID0gMQogICAgICAgIHN0ZXAgPSAwCiAgICAgICAgYmVzdF92YWxpZF9sb3NzID0gZmxvYXQoJ2luZicpCiAgICBscl9jaGFuZ2VzID0gMAoKICAgIHNhdmUgPSBsYW1iZGEgZXA6IHRvcmNoLnNhdmUoewogICAgICAgICdtb2RlbCc6IG1vZGVsLnN0YXRlX2RpY3QoKSwKICAgICAgICAnZXBvY2gnOiBlcCwKICAgICAgICAnc3RlcCc6IHN0ZXAsCiAgICAgICAgJ2Jlc3RfdmFsaWRfbG9zcyc6IGJlc3RfdmFsaWRfbG9zcwogICAgfSwgc3RyKG1vZGVsX3BhdGgpKQoKICAgIHJlcG9ydF9lYWNoID0gMTAwMDAKICAgIGxvZyA9IHJ1bl9yb290LmpvaW5wYXRoKCd0cmFpbi5sb2cnKS5vcGVuKCdhdCcsIGVuY29kaW5nPSd1dGY4JykKICAgIHZhbGlkX2xvc3NlcyA9IFtdCiAgICBscl9yZXNldF9lcG9jaCA9IGVwb2NoCiAgICBmb3IgZXBvY2ggaW4gcmFuZ2UoZXBvY2gsIG5fZXBvY2hzICsgMSk6CiAgICAgICAgbW9kZWwudHJhaW4oKQogICAgICAgIHRxID0gdHFkbS50cWRtKHRvdGFsPShhcmdzLmVwb2NoX3NpemUgb3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuKHRyYWluX2xvYWRlcikgKiBhcmdzLmJhdGNoX3NpemUpKQogICAgICAgIHRxLnNldF9kZXNjcmlwdGlvbihmJ0Vwb2NoIHtlcG9jaH0sIGxyIHtscn0nKQogICAgICAgIGxvc3NlcyA9IFtdCiAgICAgICAgdGwgPSB0cmFpbl9sb2FkZXIKICAgICAgICBpZiBhcmdzLmVwb2NoX3NpemU6CiAgICAgICAgICAgIHRsID0gaXNsaWNlKHRsLCBhcmdzLmVwb2NoX3NpemUgLy8gYXJncy5iYXRjaF9zaXplKQogICAgICAgIHRyeToKICAgICAgICAgICAgbWVhbl9sb3NzID0gMAogICAgICAgICAgICBmb3IgaSwgKGlucHV0cywgdGFyZ2V0cykgaW4gZW51bWVyYXRlKHRsKToKICAgICAgICAgICAgICAgIGlmIHVzZV9jdWRhOgogICAgICAgICAgICAgICAgICAgIGlucHV0cywgdGFyZ2V0cyA9IGlucHV0cy5jdWRhKCksIHRhcmdldHMuY3VkYSgpCiAgICAgICAgICAgICAgICBvdXRwdXRzID0gbW9kZWwoaW5wdXRzKQogICAgICAgICAgICAgICAgbG9zcyA9IF9yZWR1Y2VfbG9zcyhjcml0ZXJpb24ob3V0cHV0cywgdGFyZ2V0cykpCiAgICAgICAgICAgICAgICBiYXRjaF9zaXplID0gaW5wdXRzLnNpemUoMCkKICAgICAgICAgICAgICAgIChiYXRjaF9zaXplICogbG9zcykuYmFja3dhcmQoKQogICAgICAgICAgICAgICAgaWYgKGkgKyAxKSAlIGFyZ3Muc3RlcCA9PSAwOgogICAgICAgICAgICAgICAgICAgIG9wdGltaXplci5zdGVwKCkKICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZXIuemVyb19ncmFkKCkKICAgICAgICAgICAgICAgICAgICBzdGVwICs9IDEKICAgICAgICAgICAgICAgIHRxLnVwZGF0ZShiYXRjaF9zaXplKQogICAgICAgICAgICAgICAgbG9zc2VzLmFwcGVuZChsb3NzLml0ZW0oKSkKICAgICAgICAgICAgICAgIG1lYW5fbG9zcyA9IG5wLm1lYW4obG9zc2VzWy1yZXBvcnRfZWFjaDpdKQogICAgICAgICAgICAgICAgdHEuc2V0X3Bvc3RmaXgobG9zcz1mJ3ttZWFuX2xvc3M6LjNmfScpCiAgICAgICAgICAgICAgICBpZiBpIGFuZCBpICUgcmVwb3J0X2VhY2ggPT0gMDoKICAgICAgICAgICAgICAgICAgICB3cml0ZV9ldmVudChsb2csIHN0ZXAsIGxvc3M9bWVhbl9sb3NzKQogICAgICAgICAgICB3cml0ZV9ldmVudChsb2csIHN0ZXAsIGxvc3M9bWVhbl9sb3NzKQogICAgICAgICAgICB0cS5jbG9zZSgpCiAgICAgICAgICAgIHNhdmUoZXBvY2ggKyAxKQogICAgICAgICAgICB2YWxpZF9tZXRyaWNzID0gdmFsaWRhdGlvbihtb2RlbCwgY3JpdGVyaW9uLCB2YWxpZF9sb2FkZXIsIHVzZV9jdWRhKQogICAgICAgICAgICB3cml0ZV9ldmVudChsb2csIHN0ZXAsICoqdmFsaWRfbWV0cmljcykKICAgICAgICAgICAgdmFsaWRfbG9zcyA9IHZhbGlkX21ldHJpY3NbJ3ZhbGlkX2xvc3MnXQogICAgICAgICAgICB2YWxpZF9sb3NzZXMuYXBwZW5kKHZhbGlkX2xvc3MpCiAgICAgICAgICAgIGlmIHZhbGlkX2xvc3MgPCBiZXN0X3ZhbGlkX2xvc3M6CiAgICAgICAgICAgICAgICBiZXN0X3ZhbGlkX2xvc3MgPSB2YWxpZF9sb3NzCiAgICAgICAgICAgICAgICBzaHV0aWwuY29weShzdHIobW9kZWxfcGF0aCksIHN0cihiZXN0X21vZGVsX3BhdGgpKQogICAgICAgICAgICBlbGlmIChwYXRpZW5jZSBhbmQgZXBvY2ggLSBscl9yZXNldF9lcG9jaCA+IHBhdGllbmNlIGFuZAogICAgICAgICAgICAgICAgICBtaW4odmFsaWRfbG9zc2VzWy1wYXRpZW5jZTpdKSA+IGJlc3RfdmFsaWRfbG9zcyk6CiAgICAgICAgICAgICAgICAjICJwYXRpZW5jZSIgZXBvY2hzIHdpdGhvdXQgaW1wcm92ZW1lbnQKICAgICAgICAgICAgICAgIGxyX2NoYW5nZXMgKz0xCiAgICAgICAgICAgICAgICBpZiBscl9jaGFuZ2VzID4gbWF4X2xyX2NoYW5nZXM6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgICAgIGxyIC89IDUKICAgICAgICAgICAgICAgIHByaW50KGYnbHIgdXBkYXRlZCB0byB7bHJ9JykKICAgICAgICAgICAgICAgIGxyX3Jlc2V0X2Vwb2NoID0gZXBvY2gKICAgICAgICAgICAgICAgIG9wdGltaXplciA9IGluaXRfb3B0aW1pemVyKHBhcmFtcywgbHIpCiAgICAgICAgZXhjZXB0IEtleWJvYXJkSW50ZXJydXB0OgogICAgICAgICAgICB0cS5jbG9zZSgpCiAgICAgICAgICAgIHByaW50KCdDdHJsK0MsIHNhdmluZyBzbmFwc2hvdCcpCiAgICAgICAgICAgIHNhdmUoZXBvY2gpCiAgICAgICAgICAgIHByaW50KCdkb25lLicpCiAgICAgICAgICAgIHJldHVybiBGYWxzZQogICAgcmV0dXJuIFRydWUKCgpkZWYgdmFsaWRhdGlvbigKICAgICAgICBtb2RlbDogbm4uTW9kdWxlLCBjcml0ZXJpb24sIHZhbGlkX2xvYWRlciwgdXNlX2N1ZGEsCiAgICAgICAgKSAtPiBEaWN0W3N0ciwgZmxvYXRdOgogICAgbW9kZWwuZXZhbCgpCiAgICBhbGxfbG9zc2VzLCBhbGxfcHJlZGljdGlvbnMsIGFsbF90YXJnZXRzID0gW10sIFtdLCBbXQogICAgd2l0aCB0b3JjaC5ub19ncmFkKCk6CiAgICAgICAgZm9yIGlucHV0cywgdGFyZ2V0cyBpbiB2YWxpZF9sb2FkZXI6CiAgICAgICAgICAgIGFsbF90YXJnZXRzLmFwcGVuZCh0YXJnZXRzLm51bXB5KCkuY29weSgpKQogICAgICAgICAgICBpZiB1c2VfY3VkYToKICAgICAgICAgICAgICAgIGlucHV0cywgdGFyZ2V0cyA9IGlucHV0cy5jdWRhKCksIHRhcmdldHMuY3VkYSgpCiAgICAgICAgICAgIG91dHB1dHMgPSBtb2RlbChpbnB1dHMpCiAgICAgICAgICAgIGxvc3MgPSBjcml0ZXJpb24ob3V0cHV0cywgdGFyZ2V0cykKICAgICAgICAgICAgYWxsX2xvc3Nlcy5hcHBlbmQoX3JlZHVjZV9sb3NzKGxvc3MpLml0ZW0oKSkKICAgICAgICAgICAgcHJlZGljdGlvbnMgPSB0b3JjaC5zaWdtb2lkKG91dHB1dHMpCiAgICAgICAgICAgIGFsbF9wcmVkaWN0aW9ucy5hcHBlbmQocHJlZGljdGlvbnMuY3B1KCkubnVtcHkoKSkKICAgIGFsbF9wcmVkaWN0aW9ucyA9IG5wLmNvbmNhdGVuYXRlKGFsbF9wcmVkaWN0aW9ucykKICAgIGFsbF90YXJnZXRzID0gbnAuY29uY2F0ZW5hdGUoYWxsX3RhcmdldHMpCgogICAgZGVmIGdldF9zY29yZSh5X3ByZWQpOgogICAgICAgIHdpdGggd2FybmluZ3MuY2F0Y2hfd2FybmluZ3MoKToKICAgICAgICAgICAgd2FybmluZ3Muc2ltcGxlZmlsdGVyKCdpZ25vcmUnLCBjYXRlZ29yeT1VbmRlZmluZWRNZXRyaWNXYXJuaW5nKQogICAgICAgICAgICByZXR1cm4gZmJldGFfc2NvcmUoCiAgICAgICAgICAgICAgICBhbGxfdGFyZ2V0cywgeV9wcmVkLCBiZXRhPTIsIGF2ZXJhZ2U9J3NhbXBsZXMnKQoKICAgIG1ldHJpY3MgPSB7fQogICAgYXJnc29ydGVkID0gYWxsX3ByZWRpY3Rpb25zLmFyZ3NvcnQoYXhpcz0xKQogICAgZm9yIHRocmVzaG9sZCBpbiBbMC4wNSwgMC4wNiwgMC4wNywgMC4wOCwgMC4wOSwgMC4xMCwgMC4xMSwgMC4xMiwgMC4xMywgMC4xNCwgMC4xNSwgMC4yMF06CiAgICAgICAgbWV0cmljc1tmJ3ZhbGlkX2YyX3RoX3t0aHJlc2hvbGQ6LjJmfSddID0gZ2V0X3Njb3JlKAogICAgICAgICAgICBiaW5hcml6ZV9wcmVkaWN0aW9uKGFsbF9wcmVkaWN0aW9ucywgdGhyZXNob2xkLCBhcmdzb3J0ZWQpKQogICAgbWV0cmljc1sndmFsaWRfbG9zcyddID0gbnAubWVhbihhbGxfbG9zc2VzKQogICAgcHJpbnQoJyB8ICcuam9pbihmJ3trfSB7djouM2Z9JyBmb3IgaywgdiBpbiBzb3J0ZWQoCiAgICAgICAgbWV0cmljcy5pdGVtcygpLCBrZXk9bGFtYmRhIGt2OiAta3ZbMV0pKSkKCiAgICByZXR1cm4gbWV0cmljcwoKCmRlZiBiaW5hcml6ZV9wcmVkaWN0aW9uKHByb2JhYmlsaXRpZXMsIHRocmVzaG9sZDogZmxvYXQsIGFyZ3NvcnRlZD1Ob25lLAogICAgICAgICAgICAgICAgICAgICAgICBtaW5fbGFiZWxzPTEsIG1heF9sYWJlbHM9MTApOgogICAgIiIiIFJldHVybiBtYXRyaXggb2YgMC8xIHByZWRpY3Rpb25zLCBzYW1lIHNoYXBlIGFzIHByb2JhYmlsaXRpZXMuCiAgICAiIiIKICAgIGFzc2VydCBwcm9iYWJpbGl0aWVzLnNoYXBlWzFdID09IE5fQ0xBU1NFUwogICAgaWYgYXJnc29ydGVkIGlzIE5vbmU6CiAgICAgICAgYXJnc29ydGVkID0gcHJvYmFiaWxpdGllcy5hcmdzb3J0KGF4aXM9MSkKICAgIG1heF9tYXNrID0gX21ha2VfbWFzayhhcmdzb3J0ZWQsIG1heF9sYWJlbHMpCiAgICBtaW5fbWFzayA9IF9tYWtlX21hc2soYXJnc29ydGVkLCBtaW5fbGFiZWxzKQogICAgcHJvYl9tYXNrID0gcHJvYmFiaWxpdGllcyA+IHRocmVzaG9sZAogICAgcmV0dXJuIChtYXhfbWFzayAmIHByb2JfbWFzaykgfCBtaW5fbWFzawoKCmRlZiBfbWFrZV9tYXNrKGFyZ3NvcnRlZCwgdG9wX246IGludCk6CiAgICBtYXNrID0gbnAuemVyb3NfbGlrZShhcmdzb3J0ZWQsIGR0eXBlPW5wLnVpbnQ4KQogICAgY29sX2luZGljZXMgPSBhcmdzb3J0ZWRbOiwgLXRvcF9uOl0ucmVzaGFwZSgtMSkKICAgIHJvd19pbmRpY2VzID0gW2kgLy8gdG9wX24gZm9yIGkgaW4gcmFuZ2UobGVuKGNvbF9pbmRpY2VzKSldCiAgICBtYXNrW3Jvd19pbmRpY2VzLCBjb2xfaW5kaWNlc10gPSAxCiAgICByZXR1cm4gbWFzawoKCmRlZiBfcmVkdWNlX2xvc3MobG9zcyk6CiAgICByZXR1cm4gbG9zcy5zdW0oKSAvIGxvc3Muc2hhcGVbMF0KCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogICAgbWFpbigpCg==',
                    'setup.py': 'ZnJvbSBzZXR1cHRvb2xzIGltcG9ydCBzZXR1cAoKc2V0dXAoCiAgICBuYW1lPSdpbWV0JywKICAgIHBhY2thZ2VzPVsnaW1ldCddLAop'
                    }

for path, encoded in file_data.items():
    print(path)
    path = Path(path)
    path.parent.mkdir(exist_ok=True)
    path.write_bytes(base64.b64decode(encoded))


def run(command):
    os.system('export PYTHONPATH=${PYTHONPATH}:/kaggle/working && ' + command)


print(os.listdir('/kaggle/working/'))
run('python setup.py develop --install-dir /kaggle/working')

!cp '../input/public-0615/best-model.pt' '/kaggle/working/'

run('python setup.py develop --install-dir /kaggle/working')

run('python -m imet.make_folds --n-folds 40')
# run('python -m imet.main train model_1 --n-epochs 16')

run('python -m imet.main predict_test .')
run('python -m imet.make_submission ./test.h5 /kaggle/working/sub.csv --threshold 0.10')

print(os.listdir('.'))
imet/transforms.py
imet/make_submission.py
imet/models.py
imet/__init__.py
imet/make_folds.py
imet/dataset.py
imet/utils.py
imet/main.py
setup.py
['imet', '__notebook_source__.ipynb', '.ipynb_checkpoints', 'setup.py']
['imet.egg-link', 'imet', '__notebook_source__.ipynb', 'best-model.pt', '.ipynb_checkpoints', '__pycache__', 'setup.py', 'folds.csv', 'test.h5', 'site.py', 'sub.csv', 'easy-install.pth', 'imet.egg-info']
In [2]:
try:
    test_preds_kon = pd.read_csv('sub.csv')
    attr_ids_kon = test_preds_kon['attribute_ids'].apply(lambda x: x.split()).values

    attr_id_thr = 0.10
except Exception as e:
    pass
In [3]:
len(attr_ids_kon)
Out[3]:
7443
In [4]:
!rm -rf /kaggle/working/*

This step is to make SeResNext work in FastAI

In [5]:
import os
os.system('cp -r ../input/pretrained-models-cadene/pretrained_models_pytroch/ /kaggle/working/')
os.chdir('/kaggle/working/pretrained_models_pytroch/pretrained-models.pytorch-master')
!python setup.py install
running install
running bdist_egg
running egg_info
creating pretrainedmodels.egg-info
writing pretrainedmodels.egg-info/PKG-INFO
writing dependency_links to pretrainedmodels.egg-info/dependency_links.txt
writing requirements to pretrainedmodels.egg-info/requires.txt
writing top-level names to pretrainedmodels.egg-info/top_level.txt
writing manifest file 'pretrainedmodels.egg-info/SOURCES.txt'
reading manifest file 'pretrainedmodels.egg-info/SOURCES.txt'
writing manifest file 'pretrainedmodels.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-x86_64/egg
running install_lib
running build_py
creating build
creating build/lib
creating build/lib/pretrainedmodels
copying pretrainedmodels/version.py -> build/lib/pretrainedmodels
copying pretrainedmodels/utils.py -> build/lib/pretrainedmodels
copying pretrainedmodels/__init__.py -> build/lib/pretrainedmodels
creating build/lib/pretrainedmodels/datasets
copying pretrainedmodels/datasets/utils.py -> build/lib/pretrainedmodels/datasets
copying pretrainedmodels/datasets/__init__.py -> build/lib/pretrainedmodels/datasets
copying pretrainedmodels/datasets/voc.py -> build/lib/pretrainedmodels/datasets
creating build/lib/pretrainedmodels/models
copying pretrainedmodels/models/polynet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/nasnet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/bninception.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/wideresnet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/utils.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/__init__.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/torchvision_models.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/inceptionv4.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/vggm.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/xception.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/cafferesnet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/resnext.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/fbresnet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/inceptionresnetv2.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/dpn.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/senet.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/nasnet_mobile.py -> build/lib/pretrainedmodels/models
copying pretrainedmodels/models/pnasnet.py -> build/lib/pretrainedmodels/models
creating build/lib/pretrainedmodels/models/resnext_features
copying pretrainedmodels/models/resnext_features/__init__.py -> build/lib/pretrainedmodels/models/resnext_features
copying pretrainedmodels/models/resnext_features/resnext101_64x4d_features.py -> build/lib/pretrainedmodels/models/resnext_features
copying pretrainedmodels/models/resnext_features/resnext101_32x4d_features.py -> build/lib/pretrainedmodels/models/resnext_features
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/egg
creating build/bdist.linux-x86_64/egg/pretrainedmodels
creating build/bdist.linux-x86_64/egg/pretrainedmodels/datasets
copying build/lib/pretrainedmodels/datasets/utils.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/datasets
copying build/lib/pretrainedmodels/datasets/__init__.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/datasets
copying build/lib/pretrainedmodels/datasets/voc.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/datasets
copying build/lib/pretrainedmodels/version.py -> build/bdist.linux-x86_64/egg/pretrainedmodels
copying build/lib/pretrainedmodels/utils.py -> build/bdist.linux-x86_64/egg/pretrainedmodels
copying build/lib/pretrainedmodels/__init__.py -> build/bdist.linux-x86_64/egg/pretrainedmodels
creating build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/polynet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/nasnet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/bninception.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/wideresnet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/utils.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/__init__.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/torchvision_models.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/inceptionv4.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/vggm.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/xception.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/cafferesnet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
creating build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features
copying build/lib/pretrainedmodels/models/resnext_features/__init__.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features
copying build/lib/pretrainedmodels/models/resnext_features/resnext101_64x4d_features.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features
copying build/lib/pretrainedmodels/models/resnext_features/resnext101_32x4d_features.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features
copying build/lib/pretrainedmodels/models/resnext.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/fbresnet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/inceptionresnetv2.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/dpn.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/senet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/nasnet_mobile.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
copying build/lib/pretrainedmodels/models/pnasnet.py -> build/bdist.linux-x86_64/egg/pretrainedmodels/models
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/datasets/utils.py to utils.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/datasets/__init__.py to __init__.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/datasets/voc.py to voc.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/version.py to version.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/utils.py to utils.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/__init__.py to __init__.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/polynet.py to polynet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/nasnet.py to nasnet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/bninception.py to bninception.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/wideresnet.py to wideresnet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/utils.py to utils.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/__init__.py to __init__.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/torchvision_models.py to torchvision_models.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/inceptionv4.py to inceptionv4.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/vggm.py to vggm.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/xception.py to xception.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/cafferesnet.py to cafferesnet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features/__init__.py to __init__.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features/resnext101_64x4d_features.py to resnext101_64x4d_features.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext_features/resnext101_32x4d_features.py to resnext101_32x4d_features.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/resnext.py to resnext.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/fbresnet.py to fbresnet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/inceptionresnetv2.py to inceptionresnetv2.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/dpn.py to dpn.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/senet.py to senet.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/nasnet_mobile.py to nasnet_mobile.cpython-36.pyc
byte-compiling build/bdist.linux-x86_64/egg/pretrainedmodels/models/pnasnet.py to pnasnet.cpython-36.pyc
creating build/bdist.linux-x86_64/egg/EGG-INFO
copying pretrainedmodels.egg-info/PKG-INFO -> build/bdist.linux-x86_64/egg/EGG-INFO
copying pretrainedmodels.egg-info/SOURCES.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying pretrainedmodels.egg-info/dependency_links.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying pretrainedmodels.egg-info/requires.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
copying pretrainedmodels.egg-info/top_level.txt -> build/bdist.linux-x86_64/egg/EGG-INFO
zip_safe flag not set; analyzing archive contents...
creating dist
creating 'dist/pretrainedmodels-0.7.4-py3.6.egg' and adding 'build/bdist.linux-x86_64/egg' to it
removing 'build/bdist.linux-x86_64/egg' (and everything under it)
Processing pretrainedmodels-0.7.4-py3.6.egg
Copying pretrainedmodels-0.7.4-py3.6.egg to /opt/conda/lib/python3.6/site-packages
Adding pretrainedmodels 0.7.4 to easy-install.pth file

Installed /opt/conda/lib/python3.6/site-packages/pretrainedmodels-0.7.4-py3.6.egg
Processing dependencies for pretrainedmodels==0.7.4
Searching for torchvision
Reading https://pypi.org/simple/torchvision/
Download error on https://pypi.org/simple/torchvision/: [Errno -3] Temporary failure in name resolution -- Some packages may not be found!
Couldn't find index page for 'torchvision' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.org/simple/
Download error on https://pypi.org/simple/: [Errno -3] Temporary failure in name resolution -- Some packages may not be found!
No local packages or working download links found for torchvision
error: Could not find suitable distribution for Requirement.parse('torchvision')
In [6]:
import os
import pickle
import fastai

from fastai.vision import *
from fastai.vision.models.cadene_models import *

from shutil import copyfile
fastai.__version__
Out[6]:
'1.0.51'
In [7]:
os.chdir('/kaggle/working')
os.listdir('.')
Out[7]:
['.ipynb_checkpoints', 'pretrained_models_pytroch']
In [8]:
# Source: https://www.kaggle.com/c/human-protein-atlas-image-classification/discussion/78109
class FocalLoss(nn.Module):
    def __init__(self, gamma=2):
        super().__init__()
        self.gamma = gamma

    def forward(self, logit, target):
        target = target.float()
        max_val = (-logit).clamp(min=0)
        loss = logit - logit * target + max_val + \
               ((-max_val).exp() + (-logit - max_val).exp()).log()

        invprobs = F.logsigmoid(-logit * (target * 2.0 - 1.0))
        loss = (invprobs * self.gamma).exp() * loss
        if len(loss.size())==2:
            loss = loss.sum(dim=1)
        return loss.mean()
In [9]:
path = Path('../input/imet-2019-fgvc6/') # iMet data path
In [10]:
BATCH  = 32
SIZE   = 256
In [11]:
from torch.utils import model_zoo
Path('models').mkdir(exist_ok=True)
In [12]:
test_df = pd.read_csv(path/'sample_submission.csv')
test_df.head()
Out[12]:
id attribute_ids
0 10023b2cc4ed5f68 0 1 2
1 100fbe75ed8fd887 0 1 2
2 101b627524a04f19 0 1 2
3 10234480c41284c6 0 1 2
4 1023b0e2636dcea8 0 1 2
In [13]:
labels_df = pd.read_csv(path/'labels.csv')
labels_df.head()
Out[13]:
attribute_id attribute_name
0 0 culture::abruzzi
1 1 culture::achaemenid
2 2 culture::aegean
3 3 culture::afghan
4 4 culture::after british
In [14]:
train_df = pd.read_csv(path/'train.csv')
train_df.head()
Out[14]:
id attribute_ids
0 1000483014d91860 147 616 813
1 1000fe2e667721fe 51 616 734 813
2 1001614cb89646ee 776
3 10041eb49b297c08 51 671 698 813 1092
4 100501c227f8beea 13 404 492 903 1093
In [15]:
t_preds = []

n_fold = 5

val_preds = np.zeros((2, 10923, 1103))
In [16]:
tfms = get_transforms(do_flip=True, flip_vert=False, max_rotate=0.10, max_zoom=1.5, max_warp=0.2, max_lighting=0.2,
                     xtra_tfms=[(symmetric_warp(magnitude=(-0,0), p=0)), rand_crop(p=0.75),])

train, test = [ImageList.from_df(df, path=path, cols='id', folder=folder, suffix='.png') 
               for df, folder in zip([train_df, test_df], ['train', 'test'])]


data = (train.split_by_rand_pct(0.1, seed=42)
        .label_from_df(cols='attribute_ids', label_delim=' ')
        .add_test(test)
        .transform(tfms, size=SIZE, resize_method=ResizeMethod.PAD, padding_mode='border',)
        .databunch(path=Path('.'), bs=BATCH).normalize(imagenet_stats))

Here starts the Ensemble magic

In [17]:
# model dictionary
model = {
    'seresnextkfolds_15': 'se_resnext50_32x4d-a260b3a4',
    'seresnextkfolds_11': 'se_resnext50_32x4d-a260b3a4',
    'resnet5022epochs': 'resnet50',
    'seresnextkfolds_16': 'se_resnext50_32x4d-a260b3a4',
    'seresnextkfolds_49': 'se_resnext50_32x4d-a260b3a4',
}
In [ ]:
for model_path in model.keys():
    m_path = 'stage-1.pth'
    
    if len(model_path.split('_')) > 1:
        m_path = 'seresnext-' + model_path.split('_')[1] + '.pth'
    print(model_path, m_path)

    copyfile('../input/' + model_path.split('_')[0] + '/' + m_path, 'models/'+ model[model_path] +'.pth')

    def load_url(*args, **kwargs):
        model_dir = Path('models')
        filename  = model[model_path] + '.pth'
        if not (model_dir/filename).is_file(): raise FileNotFoundError
        return torch.load(model_dir/filename)
    model_zoo.load_url = load_url
    
    arch = se_resnext50_32x4d
    if model[model_path] == 'resnet50':
        arch = models.resnet50

    learn = cnn_learner(data, base_arch=arch, loss_func=FocalLoss(), metrics=fbeta, pretrained=False)
    learn.load(model[model_path])
    
    preds = learn.TTA(ds_type=DatasetType.Test)
    np.save(model_path + '.npy', preds[0].numpy())
    t_preds.append(preds[0].numpy())
87.50% [7/8 10:55<01:33]
43.35% [101/233 00:42<00:54]
In [ ]:
attr_ids = []
thrs = [0.27, 0.27, 0.28, 0.27, 0.29]

def preds_ids(preds, thr):
    preds = [torch.tensor(preds)]
    return [i2c[np.where(t==1)[0],1].astype(str) for t in (preds[0].sigmoid()>thr).long()]
In [ ]:
attr_ids = [preds_ids(t_preds[i], thrs[i]) for i in range(len(t_preds))]

Ensembling approach

I take all the preds generated by all models and for every sample I count the frequency of attribute ids.

So for example,

For sample 1 let's say 3 model predicted attr_id_1, and 1 model predicted attr_id_2. Then there is a high chance attr_id_1 must be right one.

In such a way I calculate frequency of all attr ids and then all the attr_id frequency which are greater than equal to a threshold thr I choose those for final preds

In [ ]:
attr_ids.append(attr_ids_kon)
thrs.append(attr_id_thr)

res_attr_ids = []
thr = 4/6
for i in range(len(attr_ids[0])):
    id_s = np.concatenate([p_attr_id[i] for p_attr_id in attr_ids],
                         axis=None)
    id_s, counts = np.unique(list(id_s),
                             return_counts=True)
    counts = counts/6
    pids = ' '.join(id_s[np.where(counts >= thr)])
    res_attr_ids.append(pids)

print(len(res_attr_ids), len(attr_ids[0]))
In [ ]:
test_df.attribute_ids = res_attr_ids
test_df.to_csv('submission.csv', index=False)
In [ ]:
test_df.head()

Please upvote if you find the kernel interesting.

Note: I haven't revealed my private datasets so I dont think this kernel should affect the standings. I have just revealed the approach of my solution.

In [ ]:
!rm -rf /kaggle/working/pretrained_models_pytroch

If this notebook is not running after forking just copy the code and datasets in the new kernel. Some Kaggle side issue.