Skip to content
KernelIndex
Search⌘K

submission 411292

oofbaroomf · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

No package. Vendor the mirrored source: 945 lines, June 9 Researcher Reciprocity License v1.0.

submission_tile128_k256.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-nvfp4-group-gemm-411292?include=source"
interfacepython
Compatibility
measured onNVIDIA B200
declared hardwareNVIDIA B200
architecturessm_100
dtypesfp8_e4m3, nvfp4

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
NVFP4 group GEMMsuite of 4 cases
NVIDIA B200
57.2µs
#67 of 145
2026-01-30

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:6a19812352586db297fe24393ba543304ef90a50656e1814eb5693f29ead24f1
license declaredunknown
license concludedunknown
authorsoofbaroomf
imported2026-08-15

Kernel source

submission_tile128_k256.py945 lines
import base64
import os
import torch
from task import input_t, output_t

try:
    torch.backends.cuda.matmul.allow_fp16_reduced_precision_reduction = True
    torch.backends.cuda.matmul.allow_bf16_reduced_precision_reduction = True
except Exception:
    pass

_CPP_B64 = "CiAgICAjaW5jbHVkZSA8dG9yY2gvZXh0ZW5zaW9uLmg+CiAgICAjaW5jbHVkZSA8dmVjdG9yPgoKICAgIHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+IGdyb3VwZWRfZ2VtbV8xc20oCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEIsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEMsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZCKTsKCiAgICBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiBncm91cGVkX2dlbW1fMnNtKAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBCLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBDLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQik7CiAgICA="
_CU_B64 = "CiAgICAjaW5jbHVkZSA8dG9yY2gvZXh0ZW5zaW9uLmg+CiAgICAjaW5jbHVkZSA8QVRlbi9jdWRhL0NVREFDb250ZXh0Lmg+CiAgICAjaW5jbHVkZSA8YzEwL2N1ZGEvQ1VEQVN0cmVhbS5oPgogICAgI2luY2x1ZGUgPGMxMC9jdWRhL0NVREFHdWFyZC5oPgogICAgI2luY2x1ZGUgPHZlY3Rvcj4KICAgICNpbmNsdWRlIDxzdGRleGNlcHQ+CiAgICAjaW5jbHVkZSA8dHlwZV90cmFpdHM+CgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX09QRVJBVE9SU19fCiAgICAjdW5kZWYgX19DVURBX05PX0hBTEZfT1BFUkFUT1JTX18KICAgICNlbmRpZgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX0NPTlZFUlNJT05TX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRl9DT05WRVJTSU9OU19fCiAgICAjZW5kaWYKICAgICNpZmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICNlbmRpZgoKICAgICNpbmNsdWRlIDxjdWRhX3J1bnRpbWUuaD4KICAgICNpZiBkZWZpbmVkKF9fQ1VEQV9BUkNIX18pICYmICFkZWZpbmVkKENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQpCiAgICAjZGVmaW5lIENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQgMQogICAgI2VuZGlmCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9jdXRsYXNzLmgiCiAgICAjaW5jbHVkZSAiY3V0ZS90ZW5zb3IuaHBwIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdGVuc29yX3JlZi5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvZXBpbG9ndWUvY29sbGVjdGl2ZS9kZWZhdWx0X2VwaWxvZ3VlLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL3RocmVhZC9saW5lYXJfY29tYmluYXRpb24uaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGlzcGF0Y2hfcG9saWN5LmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZ3JvdXBfYXJyYXlfcHJvYmxlbV9zaGFwZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGV2aWNlL2dlbW1fdW5pdmVyc2FsX2FkYXB0ZXIuaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0va2VybmVsL2dlbW1fdW5pdmVyc2FsLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL3V0aWwvcGFja2VkX3N0cmlkZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9rZXJuZWxfaGFyZHdhcmVfaW5mby5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdXRpbC9kZXZpY2VfbWVtb3J5LmgiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2tlcm5lbC90aWxlX3NjaGVkdWxlcl9wYXJhbXMuaCIKCiAgICAjZGVmaW5lIENVVExBU1NfQ0hFQ0soc3RhdHVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdXRsYXNzOjpTdGF0dXMgX3N0YXR1cyA9IChzdGF0dXMpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9zdGF0dXMgIT0gY3V0bGFzczo6U3RhdHVzOjprU3VjY2VzcykgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKCJDVVRMQVNTIGVycm9yIik7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgI2RlZmluZSBDVURBX0NIRUNLKGV4cHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdWRhRXJyb3JfdCBfZXJyID0gKGV4cHIpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9lcnIgIT0gY3VkYVN1Y2Nlc3MpIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKGN1ZGFHZXRFcnJvclN0cmluZyhfZXJyKSk7ICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgdXNpbmcgUHJvYmxlbVNoYXBlID0gY3V0bGFzczo6Z2VtbTo6R3JvdXBQcm9ibGVtU2hhcGU8Y3V0ZTo6U2hhcGU8aW50LGludCxpbnQ+PjsKICAgIHVzaW5nIEVsZW1lbnRJbnB1dCA9IGN1dGxhc3M6OmZsb2F0X2UybTFfdDsKICAgIHVzaW5nIEVsZW1lbnRTRiAgICA9IGN1dGxhc3M6OmZsb2F0X3VlNG0zX3Q7CiAgICB1c2luZyBFbGVtZW50QyAgICAgPSBjdXRsYXNzOjpoYWxmX3Q7CgogICAgdXNpbmcgRWxlbWVudEEgPSBjdXRsYXNzOjpudl9mbG9hdDRfdDxFbGVtZW50SW5wdXQ+OwogICAgdXNpbmcgTGF5b3V0QSAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRBICA9IDMyOwoKICAgIHVzaW5nIEVsZW1lbnRCID0gY3V0bGFzczo6bnZfZmxvYXQ0X3Q8RWxlbWVudElucHV0PjsKICAgIHVzaW5nIExheW91dEIgID0gY3V0bGFzczo6bGF5b3V0OjpDb2x1bW5NYWpvcjsKICAgIGNvbnN0ZXhwciBpbnQgQWxpZ25tZW50QiAgPSAzMjsKCiAgICB1c2luZyBFbGVtZW50RCA9IEVsZW1lbnRDOwogICAgdXNpbmcgTGF5b3V0QyAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRDICA9IDEyOCAvIGN1dGxhc3M6OnNpemVvZl9iaXRzPEVsZW1lbnRDPjo6dmFsdWU7CiAgICBjb25zdGV4cHIgaW50IEFsaWdubWVudEQgID0gMTI4IC8gY3V0bGFzczo6c2l6ZW9mX2JpdHM8RWxlbWVudEQ+Ojp2YWx1ZTsKICAgIHVzaW5nIEVsZW1lbnRBY2N1bXVsYXRvciAgPSBmbG9hdDsKCiAgICB1c2luZyBBcmNoVGFnID0gY3V0bGFzczo6YXJjaDo6U20xMDA7CiAgICB1c2luZyBPcGVyYXRvckNsYXNzID0gY3V0bGFzczo6YXJjaDo6T3BDbGFzc0Jsb2NrU2NhbGVkVGVuc29yT3A7CiAgICB1c2luZyBTdGFnZUNvdW50VHlwZSA9IGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvOwogICAgdXNpbmcgQ2x1c3RlclNoYXBlID0gY3V0ZTo6U2hhcGU8aW50MzJfdCxpbnQzMl90LGN1dGU6Ol8xPjsKCiAgICBzdHJ1Y3QgTU1BMVNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtOwogICAgfTsKCiAgICBzdHJ1Y3QgTU1BMlNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8yNTYsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtOwogICAgfTsKCiAgICB1c2luZyBDb2xsZWN0aXZlRXBpbG9ndWUxU00gPSB0eXBlbmFtZSBjdXRsYXNzOjplcGlsb2d1ZTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgICAgQXJjaFRhZywgT3BlcmF0b3JDbGFzcywKICAgICAgICB0eXBlbmFtZSBNTUExU01Db25maWc6Ok1tYVRpbGVTaGFwZSwgQ2x1c3RlclNoYXBlLAogICAgICAgIGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzY0PiwKICAgICAgICBFbGVtZW50QWNjdW11bGF0b3IsIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgICBFbGVtZW50QywgTGF5b3V0QyAqLCBBbGlnbm1lbnRDLAogICAgICAgIEVsZW1lbnRELCBMYXlvdXRDICosIEFsaWdubWVudEQsCiAgICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpFcGlsb2d1ZVNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgQ29sbGVjdGl2ZU1haW5sb29wMVNNID0gdHlwZW5hbWUgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgIEVsZW1lbnRBLCBMYXlvdXRBICosIEFsaWdubWVudEEsCiAgICAgIEVsZW1lbnRCLCBMYXlvdXRCICosIEFsaWdubWVudEIsCiAgICAgIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6U3RhZ2VDb3VudEF1dG9DYXJ2ZW91dDwKICAgICAgICBzdGF0aWNfY2FzdDxpbnQ+KHNpemVvZih0eXBlbmFtZSBDb2xsZWN0aXZlRXBpbG9ndWUxU006OlNoYXJlZFN0b3JhZ2UpKT4sCiAgICAgIHR5cGVuYW1lIE1NQTFTTUNvbmZpZzo6S2VybmVsU2NoZWR1bGUKICAgID46OkNvbGxlY3RpdmVPcDsKCiAgICB1c2luZyBHZW1tS2VybmVsMVNNID0gY3V0bGFzczo6Z2VtbTo6a2VybmVsOjpHZW1tVW5pdmVyc2FsPAogICAgICAgIFByb2JsZW1TaGFwZSwKICAgICAgICBDb2xsZWN0aXZlTWFpbmxvb3AxU00sCiAgICAgICAgQ29sbGVjdGl2ZUVwaWxvZ3VlMVNNCiAgICA+OwogICAgdXNpbmcgR2VtbTFTTSA9IGN1dGxhc3M6OmdlbW06OmRldmljZTo6R2VtbVVuaXZlcnNhbEFkYXB0ZXI8R2VtbUtlcm5lbDFTTT47CgogICAgdXNpbmcgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNID0gdHlwZW5hbWUgY3V0bGFzczo6ZXBpbG9ndWU6OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgICAgdHlwZW5hbWUgTU1BMlNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgICBjdXRlOjpTaGFwZTxjdXRlOjpfMTI4LGN1dGU6Ol82ND4sCiAgICAgICAgRWxlbWVudEFjY3VtdWxhdG9yLCBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgICAgRWxlbWVudEMsIExheW91dEMgKiwgQWxpZ25tZW50QywKICAgICAgICBFbGVtZW50RCwgTGF5b3V0QyAqLCBBbGlnbm1lbnRELAogICAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6RXBpbG9ndWVTY2hlZHVsZQogICAgPjo6Q29sbGVjdGl2ZU9wOwoKICAgIHVzaW5nIENvbGxlY3RpdmVNYWlubG9vcDJTTSA9IHR5cGVuYW1lIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICBBcmNoVGFnLCBPcGVyYXRvckNsYXNzLAogICAgICBFbGVtZW50QSwgTGF5b3V0QSAqLCBBbGlnbm1lbnRBLAogICAgICBFbGVtZW50QiwgTGF5b3V0QiAqLCBBbGlnbm1lbnRCLAogICAgICBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6TW1hVGlsZVNoYXBlLCBDbHVzdGVyU2hhcGUsCiAgICAgIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvQ2FydmVvdXQ8CiAgICAgICAgc3RhdGljX2Nhc3Q8aW50PihzaXplb2YodHlwZW5hbWUgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNOjpTaGFyZWRTdG9yYWdlKSk+LAogICAgICB0eXBlbmFtZSBNTUEyU01Db25maWc6Oktlcm5lbFNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgR2VtbUtlcm5lbDJTTSA9IGN1dGxhc3M6OmdlbW06Omtlcm5lbDo6R2VtbVVuaXZlcnNhbDwKICAgICAgICBQcm9ibGVtU2hhcGUsCiAgICAgICAgQ29sbGVjdGl2ZU1haW5sb29wMlNNLAogICAgICAgIENvbGxlY3RpdmVFcGlsb2d1ZTJTTQogICAgPjsKICAgIHVzaW5nIEdlbW0yU00gPSBjdXRsYXNzOjpnZW1tOjpkZXZpY2U6OkdlbW1Vbml2ZXJzYWxBZGFwdGVyPEdlbW1LZXJuZWwyU00+OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IFBpbm5lZEhvc3RCdWZmZXIgewogICAgICBUKiBwdHIgPSBudWxscHRyOwogICAgICBzaXplX3QgY291bnQgPSAwOwoKICAgICAgUGlubmVkSG9zdEJ1ZmZlcigpID0gZGVmYXVsdDsKICAgICAgZXhwbGljaXQgUGlubmVkSG9zdEJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYUhvc3RBbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCksIGN1ZGFIb3N0QWxsb2NQb3J0YWJsZSkpOwogICAgICB9CgogICAgICB2b2lkIHJlbGVhc2UoKSB7CiAgICAgICAgaWYgKHB0cikgewogICAgICAgICAgY3VkYUZyZWVIb3N0KHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgflBpbm5lZEhvc3RCdWZmZXIoKSB7IHJlbGVhc2UoKTsgfQoKICAgICAgVCogZGF0YSgpIGNvbnN0IHsgcmV0dXJuIHB0cjsgfQogICAgICBUJiBvcGVyYXRvcltdKHNpemVfdCBpZHgpIHsgcmV0dXJuIHB0cltpZHhdOyB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IERldmljZUJ1ZmZlciB7CiAgICAgIFQqIHB0ciA9IG51bGxwdHI7CiAgICAgIHNpemVfdCBjb3VudCA9IDA7CgogICAgICBEZXZpY2VCdWZmZXIoKSA9IGRlZmF1bHQ7CiAgICAgIGV4cGxpY2l0IERldmljZUJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYU1hbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCkpKTsKICAgICAgfQoKICAgICAgdm9pZCByZWxlYXNlKCkgewogICAgICAgIGlmIChwdHIpIHsKICAgICAgICAgIGN1ZGFGcmVlKHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgfkRldmljZUJ1ZmZlcigpIHsgcmVsZWFzZSgpOyB9CgogICAgICBUKiBnZXQoKSBjb25zdCB7IHJldHVybiBwdHI7IH0KCiAgICAgIHZvaWQgY29weV9mcm9tX2hvc3QoY29uc3QgVCogaG9zdCwgc2l6ZV90IG4sIGN1ZGFTdHJlYW1fdCBzdHJlYW0pIHsKICAgICAgICBDVURBX0NIRUNLKGN1ZGFNZW1jcHlBc3luYyhwdHIsIGhvc3QsIG4gKiBzaXplb2YoVCksIGN1ZGFNZW1jcHlIb3N0VG9EZXZpY2UsIHN0cmVhbSkpOwogICAgICB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBHZW1tPgogICAgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4gcnVuX2dyb3VwZWRfaW1wbCgKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQiwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQywKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkIpIHsKCiAgICAgIFRPUkNIX0NIRUNLKEEuc2l6ZSgpID09IEIuc2l6ZSgpLCAiQS9CIHNpemUgbWlzbWF0Y2giKTsKICAgICAgVE9SQ0hfQ0hFQ0soQS5zaXplKCkgPT0gQy5zaXplKCksICJBL0Mgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkEuc2l6ZSgpLCAiQS9TRkEgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkIuc2l6ZSgpLCAiQS9TRkIgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBjb25zdCBpbnQzMl90IGdyb3VwcyA9IHN0YXRpY19jYXN0PGludDMyX3Q+KEEuc2l6ZSgpKTsKICAgICAgVE9SQ0hfQ0hFQ0soZ3JvdXBzID4gMCwgIk5vIGdyb3VwcyIpOwoKICAgICAgaW50IGRldmljZV9pZCA9IEFbMF0uZ2V0X2RldmljZSgpOwogICAgICBjMTA6OmN1ZGE6OkNVREFHdWFyZCBkZXZpY2VfZ3VhcmQoZGV2aWNlX2lkKTsKCiAgICAgIHVzaW5nIFN0cmlkZUEgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUE7CiAgICAgIHVzaW5nIFN0cmlkZUIgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUI7CiAgICAgIHVzaW5nIFN0cmlkZUMgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUM7CiAgICAgIHVzaW5nIFN0cmlkZUQgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUQ7CiAgICAgIHVzaW5nIExheW91dFNGQSA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkE7CiAgICAgIHVzaW5nIExheW91dFNGQiA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkI7CiAgICAgIHVzaW5nIFNtMXh4QmxrU2NhbGVkQ29uZmlnID0gdHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6Q29sbGVjdGl2ZU1haW5sb29wOjpTbTF4eEJsa1NjYWxlZENvbmZpZzsKCiAgICAgIHN0YXRpYyBzaXplX3QgY2FwYWNpdHkgPSAwOwogICAgICBzdGF0aWMgaW50IGNhY2hlZF9kZXZpY2UgPSAtMTsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEEgY29uc3QqPiBwdHJfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QiBjb25zdCo+IHB0cl9CX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4gcHRyX0NfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RXBpbG9ndWVPdXRwdXRPcDo6RWxlbWVudE91dHB1dCo+IHB0cl9EX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6RWxlbWVudFNGIGNvbnN0Kj4gcHRyX1NGQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVBPiBzdHJpZGVfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVCPiBzdHJpZGVfQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVDPiBzdHJpZGVfQ19ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVEPiBzdHJpZGVfRF9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxMYXlvdXRTRkE+IGxheW91dF9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8TGF5b3V0U0ZCPiBsYXlvdXRfU0ZCX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIFByb2JsZW1TaGFwZTo6VW5kZXJseWluZ1Byb2JsZW1TaGFwZT4gcHJvYmxlbV9zaXplc19ob3N0OwoKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBQcm9ibGVtU2hhcGU6OlVuZGVybHlpbmdQcm9ibGVtU2hhcGU+IHByb2JsZW1fc2l6ZXNfZGV2aWNlOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRBIGNvbnN0Kj4gcHRyX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPiBwdHJfQjsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpFbGVtZW50U0YgY29uc3QqPiBwdHJfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkI7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEMgY29uc3QqPiBwdHJfQzsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4gcHRyX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQT4gc3RyaWRlX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQj4gc3RyaWRlX0I7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQz4gc3RyaWRlX0M7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlRD4gc3RyaWRlX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8TGF5b3V0U0ZBPiBsYXlvdXRfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPExheW91dFNGQj4gbGF5b3V0X1NGQjsKICAgICAgc3RhdGljIHNpemVfdCB3b3Jrc3BhY2Vfc2l6ZV9jYWNoZWQgPSAwOwogICAgICBzdGF0aWMgdG9yY2g6OlRlbnNvciB3b3Jrc3BhY2VfdGVuc29yOwoKICAgICAgaWYgKGNhY2hlZF9kZXZpY2UgIT0gZGV2aWNlX2lkIHx8IGNhcGFjaXR5IDwgc3RhdGljX2Nhc3Q8c2l6ZV90Pihncm91cHMpKSB7CiAgICAgICAgY2FjaGVkX2RldmljZSA9IGRldmljZV9pZDsKICAgICAgICBjYXBhY2l0eSA9IHN0YXRpY19jYXN0PHNpemVfdD4oZ3JvdXBzKTsKICAgICAgICBwdHJfQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0JfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfRF9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQl9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0FfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQ19ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0RfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkFfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkJfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHByb2JsZW1fc2l6ZXNfaG9zdC5hbGxvY2F0ZShncm91cHMpOwoKICAgICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9BLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0IuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfU0ZBLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQi5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0MuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfRC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkEuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBsYXlvdXRfU0ZCLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgIH0KCiAgICAgIGZvciAoaW50MzJfdCBpID0gMDsgaSA8IGdyb3VwczsgKytpKSB7CiAgICAgICAgVE9SQ0hfQ0hFQ0soQVtpXS5pc19jdWRhKCksICJBIG11c3QgYmUgQ1VEQSIpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uaXNfY3VkYSgpLCAiQiBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLmlzX2N1ZGEoKSwgIkMgbXVzdCBiZSBDVURBIik7CiAgICAgICAgVE9SQ0hfQ0hFQ0soU0ZBW2ldLmlzX2N1ZGEoKSwgIlNGQSBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhTRkJbaV0uaXNfY3VkYSgpLCAiU0ZCIG11c3QgYmUgQ1VEQSIpOwoKICAgICAgICBpbnQzMl90IG0gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMCkpOwogICAgICAgIGludDMyX3Qga19wYWNrZWQgPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMSkpOwogICAgICAgIGludDMyX3QgayA9IGtfcGFja2VkICogMjsKICAgICAgICBpbnQzMl90IG4gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihCW2ldLnNpemUoMCkpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uc2l6ZSgxKSAqIDIgPT0gaywgIksgbWlzbWF0Y2giKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLnNpemUoMCkgPT0gbSAmJiBDW2ldLnNpemUoMSkgPT0gbiwgIkMgc2hhcGUgbWlzbWF0Y2giKTsKCiAgICAgICAgcHJvYmxlbV9zaXplc19ob3N0W2ldID0gY3V0ZTo6bWFrZV9zaGFwZShtLCBuLCBrKTsKICAgICAgICBzdHJpZGVfQV9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlQXt9LCB7bSwgaywgMX0pOwogICAgICAgIHN0cmlkZV9CX2hvc3RbaV0gPSBjdXRsYXNzOjptYWtlX2N1dGVfcGFja2VkX3N0cmlkZShTdHJpZGVCe30sIHtuLCBrLCAxfSk7CiAgICAgICAgc3RyaWRlX0NfaG9zdFtpXSA9IGN1dGxhc3M6Om1ha2VfY3V0ZV9wYWNrZWRfc3RyaWRlKFN0cmlkZUN7fSwge20sIG4sIDF9KTsKICAgICAgICBzdHJpZGVfRF9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlRHt9LCB7bSwgbiwgMX0pOwogICAgICAgIGxheW91dF9TRkFfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZBKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwogICAgICAgIGxheW91dF9TRkJfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZCKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwoKICAgICAgICBwdHJfQV9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QSBjb25zdCo+KEFbaV0uZGF0YV9wdHIoKSk7CiAgICAgICAgcHRyX0JfaG9zdFtpXSA9IHJlaW50ZXJwcmV0X2Nhc3Q8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPihCW2ldLmRhdGFfcHRyKCkpOwogICAgICAgIHB0cl9DX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfRF9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZBX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQVtpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZCX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQltpXS5kYXRhX3B0cigpKTsKICAgICAgfQoKICAgICAgYXV0byBzdHJlYW1fb2JqID0gYzEwOjpjdWRhOjpnZXRDdXJyZW50Q1VEQVN0cmVhbShkZXZpY2VfaWQpOwogICAgICBjMTA6OmN1ZGE6OkNVREFTdHJlYW1HdWFyZCBzdHJlYW1fZ3VhcmQoc3RyZWFtX29iaik7CiAgICAgIGN1ZGFTdHJlYW1fdCBzdHJlYW0gPSBzdHJlYW1fb2JqLnN0cmVhbSgpOwogICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5jb3B5X2Zyb21faG9zdChwcm9ibGVtX3NpemVzX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9BLmNvcHlfZnJvbV9ob3N0KHB0cl9BX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9CLmNvcHlfZnJvbV9ob3N0KHB0cl9CX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9TRkEuY29weV9mcm9tX2hvc3QocHRyX1NGQV9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwogICAgICBwdHJfU0ZCLmNvcHlfZnJvbV9ob3N0KHB0cl9TRkJfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0MuY29weV9mcm9tX2hvc3QocHRyX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0QuY29weV9mcm9tX2hvc3QocHRyX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0EuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0FfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0IuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0JfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0MuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0QuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgbGF5b3V0X1NGQS5jb3B5X2Zyb21faG9zdChsYXlvdXRfU0ZBX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIGxheW91dF9TRkIuY29weV9mcm9tX2hvc3QobGF5b3V0X1NGQl9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwoKICAgICAgY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvIGh3X2luZm87CiAgICAgIGh3X2luZm8uZGV2aWNlX2lkID0gZGV2aWNlX2lkOwogICAgICBod19pbmZvLnNtX2NvdW50ID0gY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvOjpxdWVyeV9kZXZpY2VfbXVsdGlwcm9jZXNzb3JfY291bnQoaHdfaW5mby5kZXZpY2VfaWQpOwogICAgICBpZiBjb25zdGV4cHIgKHN0ZDo6aXNfc2FtZV92PEdlbW0sIEdlbW0xU00+KSB7CiAgICAgICAgaHdfaW5mby5jbHVzdGVyX3NoYXBlID0gZGltMygxLCAxLCAxKTsKICAgICAgICBod19pbmZvLmNsdXN0ZXJfc2hhcGVfZmFsbGJhY2sgPSBkaW0zKDEsIDEsIDEpOwogICAgICB9IGVsc2UgewogICAgICAgIGh3X2luZm8uY2x1c3Rlcl9zaGFwZSA9IGRpbTMoMiwgMSwgMSk7CiAgICAgICAgaHdfaW5mby5jbHVzdGVyX3NoYXBlX2ZhbGxiYWNrID0gZGltMygyLCAxLCAxKTsKICAgICAgfQoKICAgICAgdHlwZW5hbWUgR2VtbTo6QXJndW1lbnRzIGFyZ3VtZW50czsKICAgICAgZGVjbHR5cGUoYXJndW1lbnRzLmVwaWxvZ3VlLnRocmVhZCkgZnVzaW9uX2FyZ3M7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0ciA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuYWxwaGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMSk7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMCk7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0cl9hcnJheSA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyX2FycmF5ID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuZEFscGhhID0ge2N1dGU6Ol8we30sIGN1dGU6Ol8we30sIDB9OwogICAgICBmdXNpb25fYXJncy5kQmV0YSA9IHtjdXRlOjpfMHt9LCBjdXRlOjpfMHt9LCAwfTsKCiAgICAgIHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OlRpbGVTY2hlZHVsZXJBcmd1bWVudHMgc2NoZWR1bGVyOwogICAgICBzY2hlZHVsZXIubWF4X3N3aXp6bGVfc2l6ZSA9IDg7CiAgICAgIHNjaGVkdWxlci5yYXN0ZXJfb3JkZXIgPSBjdXRsYXNzOjpnZW1tOjprZXJuZWw6OmRldGFpbDo6UmFzdGVyT3JkZXJPcHRpb25zOjpIZXVyaXN0aWM7CgogICAgICBhcmd1bWVudHMgPSB0eXBlbmFtZSBHZW1tOjpBcmd1bWVudHMgewogICAgICAgIGN1dGxhc3M6OmdlbW06OkdlbW1Vbml2ZXJzYWxNb2RlOjprR3JvdXBlZCwKICAgICAgICB7Z3JvdXBzLCBwcm9ibGVtX3NpemVzX2RldmljZS5nZXQoKSwgbnVsbHB0cn0sCiAgICAgICAge3B0cl9BLmdldCgpLCBzdHJpZGVfQS5nZXQoKSwgcHRyX0IuZ2V0KCksIHN0cmlkZV9CLmdldCgpLAogICAgICAgICBwdHJfU0ZBLmdldCgpLCBsYXlvdXRfU0ZBLmdldCgpLCBwdHJfU0ZCLmdldCgpLCBsYXlvdXRfU0ZCLmdldCgpfSwKICAgICAgICB7ZnVzaW9uX2FyZ3MsIHB0cl9DLmdldCgpLCBzdHJpZGVfQy5nZXQoKSwgcHRyX0QuZ2V0KCksIHN0cmlkZV9ELmdldCgpfSwKICAgICAgICBod19pbmZvLCBzY2hlZHVsZXIKICAgICAgfTsKCiAgICAgIEdlbW0gZ2VtbTsKICAgICAgc2l6ZV90IHdvcmtzcGFjZV9zaXplID0gR2VtbTo6Z2V0X3dvcmtzcGFjZV9zaXplKGFyZ3VtZW50cyk7CiAgICAgIGlmICghd29ya3NwYWNlX3RlbnNvci5kZWZpbmVkKCkgfHwgY2FjaGVkX2RldmljZSAhPSBkZXZpY2VfaWQgfHwgd29ya3NwYWNlX3NpemVfY2FjaGVkIDwgd29ya3NwYWNlX3NpemUpIHsKICAgICAgICBhdXRvIG9wdHMgPSB0b3JjaDo6VGVuc29yT3B0aW9ucygpLmRldmljZSh0b3JjaDo6a0NVREEsIGRldmljZV9pZCkuZHR5cGUodG9yY2g6OmtVSW50OCk7CiAgICAgICAgd29ya3NwYWNlX3RlbnNvciA9IHRvcmNoOjplbXB0eSh7c3RhdGljX2Nhc3Q8bG9uZz4od29ya3NwYWNlX3NpemUpfSwgb3B0cyk7CiAgICAgICAgd29ya3NwYWNlX3NpemVfY2FjaGVkID0gd29ya3NwYWNlX3NpemU7CiAgICAgIH0KICAgICAgdm9pZCogd29ya3NwYWNlX3B0ciA9IHdvcmtzcGFjZV9zaXplID8gd29ya3NwYWNlX3RlbnNvci5kYXRhX3B0cigpIDogbnVsbHB0cjsKCiAgICAgIENVVExBU1NfQ0hFQ0soZ2VtbS5jYW5faW1wbGVtZW50KGFyZ3VtZW50cykpOwogICAgICBDVVRMQVNTX0NIRUNLKGdlbW0uaW5pdGlhbGl6ZShhcmd1bWVudHMsIHdvcmtzcGFjZV9wdHIsIHN0cmVhbSkpOwogICAgICBhdXRvIHJ1bl9zdGF0dXMgPSBnZW1tLnJ1bihzdHJlYW0sIG51bGxwdHIsIHRydWUpOwogICAgICBpZiAocnVuX3N0YXR1cyAhPSBjdXRsYXNzOjpTdGF0dXM6OmtTdWNjZXNzKSB7CiAgICAgICAgcnVuX3N0YXR1cyA9IGdlbW0ucnVuKHN0cmVhbSk7CiAgICAgIH0KICAgICAgQ1VUTEFTU19DSEVDSyhydW5fc3RhdHVzKTsKCiAgICAgIHJldHVybiBDOwogICAgfQoKICAgIHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+IGdyb3VwZWRfZ2VtbV8xc20oCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEIsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEMsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZCKSB7CiAgICAgIHJldHVybiBydW5fZ3JvdXBlZF9pbXBsPEdlbW0xU00+KEEsIEIsIEMsIFNGQSwgU0ZCKTsKICAgIH0KCiAgICBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiBncm91cGVkX2dlbW1fMnNtKAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBCLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBDLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQikgewogICAgICByZXR1cm4gcnVuX2dyb3VwZWRfaW1wbDxHZW1tMlNNPihBLCBCLCBDLCBTRkEsIFNGQik7CiAgICB9CiAgICAK"

_ext_mod = None
_ext_fail = False
_force_ext_only = False
_disable_ext = False


def _dec(b64: str) -> str:
    return base64.b64decode(b64).decode("utf-8")


def _find_inc() -> list[str]:
    incs: list[str] = []

    def _add_cutlass(base: str) -> bool:
        inc = os.path.join(base, "include")
        if os.path.isfile(os.path.join(inc, "cutlass", "cutlass.h")):
            incs.append(inc)
            util_inc = os.path.join(base, "tools", "util", "include")
            if os.path.isfile(os.path.join(util_inc, "cutlass", "util", "device_memory.h")):
                incs.append(util_inc)
            return True
        inc2 = os.path.join(base, "cutlass", "include")
        if os.path.isfile(os.path.join(inc2, "cutlass", "cutlass.h")):
            incs.append(inc2)
            util_inc2 = os.path.join(base, "cutlass", "tools", "util", "include")
            if os.path.isfile(os.path.join(util_inc2, "cutlass", "util", "device_memory.h")):
                incs.append(util_inc2)
            return True
        return False

    here = os.path.abspath(os.path.dirname(__file__))
    repo_root = os.path.abspath(os.path.join(here, "../../../.."))
    for base in (
        os.path.join(repo_root, "third_party", "cutlass"),
        os.path.join(repo_root, "tmp", "cutlass-4.3.5"),
    ):
        _add_cutlass(base)

    try:
        import cutlass as _c

        root = os.path.dirname(getattr(_c, "__file__", "") or "")
        if root:
            bases = [root, os.path.dirname(root), os.path.dirname(os.path.dirname(root))]
            for base in bases:
                if _add_cutlass(base):
                    break
    except Exception:
        pass

    try:
        import torch.utils.cpp_extension as _ce

        for inc in _ce.include_paths():
            if os.path.isfile(os.path.join(inc, "cutlass", "cutlass.h")):
                incs.append(inc)
            util_inc3 = os.path.join(inc, "cutlass", "tools", "util", "include")
            if os.path.isfile(os.path.join(util_inc3, "cutlass", "util", "device_memory.h")):
                incs.append(util_inc3)
    except Exception:
        pass

    if not incs:
        root = _ensure_cutlass_dir()
        if root:
            _add_cutlass(root)

    return incs


_cutlass_root_cached: str | None = None


def _ensure_cutlass_dir() -> str | None:
    global _cutlass_root_cached
    if _cutlass_root_cached is not None:
        return _cutlass_root_cached
    base_dir = "/tmp/cutlass_cache"
    root = os.path.join(base_dir, "cutlass-4.3.5")
    inc = os.path.join(root, "include", "cutlass", "cutlass.h")
    if os.path.isfile(inc):
        _cutlass_root_cached = root
        return root
    try:
        os.makedirs(base_dir, exist_ok=True)
        tgz_path = os.path.join(base_dir, "cutlass.tgz")
        if not os.path.isfile(tgz_path):
            import urllib.request

            url = "https://github.com/NVIDIA/cutlass/archive/refs/tags/v4.3.5.tar.gz"
            with urllib.request.urlopen(url, timeout=60) as resp:
                data = resp.read()
            with open(tgz_path, "wb") as f:
                f.write(data)
        import tarfile

        with tarfile.open(tgz_path, "r:gz") as tf:
            tf.extractall(base_dir)
        if os.path.isfile(inc):
            _cutlass_root_cached = root
            return root
    except Exception:
        return None
    return None


def _load_ext():
    global _ext_mod, _ext_fail
    if _ext_mod is not None or _ext_fail:
        return _ext_mod

    incs = _find_inc()
    if not incs:
        _ext_fail = True
        if _force_ext_only:
            raise RuntimeError("ext include paths not found")
        return None

    try:
        from torch.utils.cpp_extension import load_inline

        here = os.path.abspath(os.path.dirname(__file__))
        repo_root = os.path.abspath(os.path.join(here, "../../../.."))
        os.environ.setdefault(
            "TORCH_EXTENSIONS_DIR", os.path.join(repo_root, "tmp", "torch_extensions")
        )
        os.environ.setdefault("TORCH_CUDA_ARCH_LIST", "10.0a")
        cpp_src = _dec(_CPP_B64)
        cu_src = _dec(_CU_B64)
        _ext_mod = load_inline(
            name="nvfp4_grouped_cutlass_ext",
            cpp_sources=cpp_src,
            cuda_sources=cu_src,
            functions=["grouped_gemm_1sm", "grouped_gemm_2sm"],
            extra_cuda_cflags=[
                "-O3",
                "--use_fast_math",
                "-std=c++17",
                "--diag-suppress=144",
            ],
            extra_ldflags=["-lcuda"],
            extra_cflags=["-O3", "-std=c++17"],
            with_cuda=True,
            extra_include_paths=incs,
            verbose=False,
        )
    except Exception as exc:
        _ext_fail = True
        _ext_mod = None
        if _force_ext_only:
            raise RuntimeError(f"ext build failed: {exc}") from exc
    return _ext_mod


def _try_ext(
    abc_tensors: list[tuple[torch.Tensor, torch.Tensor, torch.Tensor]],
    sfasfb_reordered_tensors: list[tuple[torch.Tensor, torch.Tensor]],
    problem_sizes: list[tuple[int, int, int, int]],
):
    if _disable_ext:
        return None
    ext = _load_ext()
    if ext is None:
        return None

    items = []
    for (a_ref, b_ref, c_ref), (sfa_reordered, sfb_reordered), (m, _, k, l) in zip(
        abc_tensors, sfasfb_reordered_tensors, problem_sizes
    ):
        if l != 1:
            return None
        a2 = a_ref[:, :, 0]
        b2 = b_ref[:, :, 0]
        c2 = c_ref[:, :, 0]
        items.append((a2, b2, c2, sfa_reordered, sfb_reordered, m, k))

    def _use_2sm(m: int, k: int) -> bool:
        return m >= 256 and k >= 4096

    def _gather(src):
        return (
            [x[0] for x in src],
            [x[1] for x in src],
            [x[2] for x in src],
            [x[3] for x in src],
            [x[4] for x in src],
        )

    small = [it for it in items if not _use_2sm(it[5], it[6])]
    large = [it for it in items if _use_2sm(it[5], it[6])]

    try:
        if large:
            a_list, b_list, c_list, sfa_list, sfb_list = _gather(large)
            ext.grouped_gemm_2sm(a_list, b_list, c_list, sfa_list, sfb_list)
        if small:
            a_list, b_list, c_list, sfa_list, sfb_list = _gather(small)
            ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
    except Exception:
        try:
            a_list, b_list, c_list, sfa_list, sfb_list = _gather(items)
            ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
        except Exception:
            return None

    return [c_ref for (_, _, c_ref) in abc_tensors]


_scale_cache: dict[tuple[int, int], torch.Tensor] = {}
_scaled_mm_out_supported: bool | None = None
_scaled_mm_fast_accum_supported: bool | None = None
_grouped_mm_v2_supported: bool | None = False
_grouped_mm_supported: bool | None = None
_group_cache: dict[tuple, dict] = {}
_group_cache_padded: dict[tuple, dict] = {}
_grouped_strategy_cache: dict[tuple, str] = {}
_grouped_strategy_cache_v1: dict[tuple, str] = {}
_scale_recipe_cached = None
_global_scale_recipe_cached = None
_swizzle_cached = None
_global_swizzle_cached = None
_global_scale_cache: dict[tuple[int, int, int], torch.Tensor] = {}
_dbg_on = False
_dbg_once = False


def _dbg(msg: str) -> None:
    global _dbg_once
    if _dbg_on and not _dbg_once:
        print(msg)
        _dbg_once = True


def blocked_from_reordered(sf_reordered: torch.Tensor, l_idx: int) -> torch.Tensor:
    key = (sf_reordered.data_ptr(), l_idx)
    cached = _scale_cache.get(key)
    if cached is not None:
        return cached
    perm = sf_reordered.permute(2, 4, 0, 1, 3, 5)
    flat = perm.reshape(-1, 32, 16, sf_reordered.shape[-1])[..., l_idx].reshape(-1)
    flat = flat.contiguous()
    _scale_cache[key] = flat
    return flat


def _round_up(x: int, m: int) -> int:
    return (x + m - 1) // m * m


def _get_grouped_mm_v2():
    global _grouped_mm_v2_supported
    if _grouped_mm_v2_supported is None:
        try:
            _ = torch.nn.functional.scaled_grouped_mm
            _grouped_mm_v2_supported = True
        except Exception:
            _grouped_mm_v2_supported = False
            return None
    if _grouped_mm_v2_supported:
        return torch.nn.functional.scaled_grouped_mm
    return None


def _get_grouped_mm():
    global _grouped_mm_supported
    if _grouped_mm_supported is None:
        try:
            _grouped_mm_supported = True
            return torch.ops.aten._scaled_grouped_mm
        except Exception:
            _grouped_mm_supported = False
            return None
    if _grouped_mm_supported:
        return torch.ops.aten._scaled_grouped_mm
    return None


def _find_enum_value(enum_obj, target: str):
    key = target.replace("_", "").upper()
    for attr in dir(enum_obj):
        if attr.startswith("_"):
            continue
        attr_key = attr.replace("_", "").upper()
        if attr_key == key:
            return getattr(enum_obj, attr)
    return None


def _get_scale_swizzle():
    global _scale_recipe_cached, _swizzle_cached
    global _global_scale_recipe_cached, _global_swizzle_cached
    if _scale_recipe_cached is not None and _swizzle_cached is not None:
        return (
            _scale_recipe_cached,
            _global_scale_recipe_cached,
            _swizzle_cached,
            _global_swizzle_cached,
        )
    try:
        scaling_type = torch.nn.functional.ScalingType
        swizzle_type = torch.nn.functional.SwizzleType
    except Exception:
        return None, None, None, None
    block_scale = _find_enum_value(scaling_type, "BLOCKWISE_16")
    global_scale = _find_enum_value(scaling_type, "TENSORWISE")
    if global_scale is None:
        global_scale = _find_enum_value(scaling_type, "TENSOR_WISE")
    if global_scale is None:
        global_scale = _find_enum_value(scaling_type, "TENSOR")
    block_swizzle = _find_enum_value(swizzle_type, "SWIZZLE_32_4_4")
    global_swizzle = _find_enum_value(swizzle_type, "NONE")
    _scale_recipe_cached = block_scale
    _global_scale_recipe_cached = global_scale
    _swizzle_cached = block_swizzle
    _global_swizzle_cached = global_swizzle
    return block_scale, global_scale, block_swizzle, global_swizzle


def _get_global_scale(num_groups: int, device: torch.device, per_group: bool) -> torch.Tensor:
    key = (
        device.index if device.type == "cuda" else -1,
        num_groups if per_group else 1,
        1 if per_group else 0,
    )
    cached = _global_scale_cache.get(key)
    if cached is not None and cached.device == device:
        return cached
    shape = (num_groups, 1) if per_group else (1,)
    scale = torch.ones(shape, device=device, dtype=torch.float32)
    _global_scale_cache[key] = scale
    return scale


def _build_grouped_inputs(
    abc_tensors, sfasfb_reordered_tensors, problem_sizes
) -> dict:
    key = []
    for (a_ref, b_ref, _), (sfa_reordered, sfb_reordered) in zip(
        abc_tensors, sfasfb_reordered_tensors
    ):
        key.append(
            (
                a_ref.data_ptr(),
                b_ref.data_ptr(),
                sfa_reordered.data_ptr(),
                sfb_reordered.data_ptr(),
                a_ref.shape,
                b_ref.shape,
            )
        )
    key = tuple(key)
    cached = _group_cache.get(key)
    if cached is not None:
        return cached

    a_blocks = []
    b_blocks = []
    scale_a_blocks = []
    scale_b_blocks = []
    offs = []
    m_pads = []
    m_actual = []
    offset = 0

    for (a_ref, b_ref, _), (sfa_reordered, sfb_reordered), (m, _, _, l) in zip(
        abc_tensors, sfasfb_reordered_tensors, problem_sizes
    ):
        if l != 1:
            raise RuntimeError("grouped path only supports l=1")
        m_pad = _round_up(m, 128)
        m_pads.append(m_pad)
        m_actual.append(m)

        if m_pad == m:
            a_pad = a_ref[:, :, 0]
        else:
            a_pad = torch.zeros(
                (m_pad, a_ref.shape[1]), device=a_ref.device, dtype=a_ref.dtype
            )
            a_pad[:m].copy_(a_ref[:, :, 0])
        a_blocks.append(a_pad)

        b_blocks.append(b_ref[:, :, 0])

        scale_a_blocks.append(blocked_from_reordered(sfa_reordered, 0))
        scale_b_blocks.append(blocked_from_reordered(sfb_reordered, 0))

        offset += m_pad
        offs.append(offset)

    m_pad_max = max(m_pads) if m_pads else 0
    k_val = problem_sizes[0][2] if problem_sizes else 0
    k_blocks = _round_up((k_val + 15) // 16, 4)
    scale_len_pad = m_pad_max * k_blocks
    scale_a_stack = []
    for scale_a in scale_a_blocks:
        if scale_a.numel() < scale_len_pad:
            pad_len = scale_len_pad - scale_a.numel()
            scale_a = torch.cat(
                [
                    scale_a,
                    torch.zeros((pad_len,), device=scale_a.device, dtype=scale_a.dtype),
                ],
                dim=0,
            )
        scale_a_stack.append(scale_a)
    if scale_a_stack:
        scale_a_stack = torch.stack(scale_a_stack, dim=0)

    a_concat = torch.cat(a_blocks, dim=0)
    b_nk = torch.stack(b_blocks, dim=0)
    b_kn = b_nk.transpose(1, 2).contiguous()
    scale_a = torch.cat(scale_a_blocks, dim=0)
    scale_b = torch.stack(scale_b_blocks, dim=0)
    offs_cpu = torch.tensor(offs, dtype=torch.int32)
    offs_tensor = offs_cpu.to(device=a_concat.device)

    packed = {
        "a": a_concat,
        "b_nk": b_nk,
        "b_kn": b_kn,
        "scale_a": scale_a,
        "scale_a_stack": scale_a_stack,
        "scale_b": scale_b,
        "offs": offs_tensor,
        "offs_cpu": offs_cpu,
        "m_pads": m_pads,
        "m_actual": m_actual,
    }
    _group_cache[key] = packed
    return packed


def _build_grouped_inputs_padded(
    abc_tensors, sfasfb_reordered_tensors, problem_sizes
) -> dict:
    key = []
    for (a_ref, b_ref, _), (sfa_reordered, sfb_reordered) in zip(
        abc_tensors, sfasfb_reordered_tensors
    ):
        key.append(
            (
                a_ref.data_ptr(),
                b_ref.data_ptr(),
                sfa_reordered.data_ptr(),
                sfb_reordered.data_ptr(),
                a_ref.shape,
                b_ref.shape,
            )
        )
    key = tuple(key)
    cached = _group_cache_padded.get(key)
    if cached is not None:
        return cached

    ms = [m for (m, _, _, _) in problem_sizes]
    m_pad = _round_up(max(ms), 128) if ms else 0
    k_val = problem_sizes[0][2] if problem_sizes else 0
    k_blocks = _round_up((k_val + 15) // 16, 4)
    scale_len_pad = m_pad * k_blocks

    a_blocks = []
    b_blocks = []
    scale_a_blocks = []
    scale_b_blocks = []
    m_actual = []

    for (a_ref, b_ref, _), (sfa_reordered, sfb_reordered), (m, _, _, l) in zip(
        abc_tensors, sfasfb_reordered_tensors, problem_sizes
    ):
        if l != 1:
            raise RuntimeError("grouped path only supports l=1")
        m_actual.append(m)

        a_pad = torch.zeros(
            (m_pad, a_ref.shape[1]), device=a_ref.device, dtype=a_ref.dtype
        )
        a_pad[:m].copy_(a_ref[:, :, 0])
        a_blocks.append(a_pad)

        b_blocks.append(b_ref[:, :, 0])

        scale_a = blocked_from_reordered(sfa_reordered, 0)
        if scale_a.numel() < scale_len_pad:
            pad_len = scale_len_pad - scale_a.numel()
            scale_a = torch.cat(
                [
                    scale_a,
                    torch.zeros((pad_len,), device=scale_a.device, dtype=scale_a.dtype),
                ],
                dim=0,
            )
        scale_a_blocks.append(scale_a)
        scale_b_blocks.append(blocked_from_reordered(sfb_reordered, 0))

    a_stack = torch.stack(a_blocks, dim=0) if a_blocks else None
    b_nk = torch.stack(b_blocks, dim=0) if b_blocks else None
    b_kn = b_nk.transpose(1, 2).contiguous() if b_nk is not None else None
    scale_a = torch.stack(scale_a_blocks, dim=0) if scale_a_blocks else None
    scale_b = torch.stack(scale_b_blocks, dim=0) if scale_b_blocks else None

    packed = {
        "a": a_stack,
        "b_nk": b_nk,
        "b_kn": b_kn,
        "scale_a": scale_a,
        "scale_b": scale_b,
        "m_actual": m_actual,
        "m_pad": m_pad,
    }
    _group_cache_padded[key] = packed
    return packed


def _try_scaled_grouped_mm_v2(
    func,
    a,
    b,
    scale_a,
    scale_recipe_a,
    scale_b,
    scale_recipe_b,
    swizzle_a,
    swizzle_b,
    offs,
    offs_cpu,
    output_dtype,
):
    for offs_arg in (offs, offs_cpu):
        try:
            return func(
                a,
                b,
                scale_a,
                scale_recipe_a,
                scale_b,
                scale_recipe_b,
                swizzle_a=swizzle_a,
                swizzle_b=swizzle_b,
                bias=None,
                offs=offs_arg,
                output_dtype=output_dtype,
                contraction_dim=(),
                use_fast_accum=False,
            )
        except TypeError:
            try:
                return func(
                    a,
                    b,
                    scale_a,
                    scale_recipe_a,
                    scale_b,
                    scale_recipe_b,
                    swizzle_a=swizzle_a,
                    swizzle_b=swizzle_b,
                    bias=None,
                    offs=offs_arg,
                    output_dtype=output_dtype,
                )
            except Exception:
                continue
        except Exception:
            continue
    return None


def _try_scaled_grouped_mm_v1(
    func,
    a,
    b,
    scale_a,
    scale_b,
    offs,
    offs_cpu,
    output_dtype,
):
    for offs_arg in (offs, offs_cpu):
        try:
            return func(
                a,
                b,
                scale_a,
                scale_b,
                offs_arg,
                None,
                None,
                output_dtype,
                False,
            )
        except Exception:
            continue
    return None


def _scaled_mm_out(mat_a, mat_b, scale_a, scale_b, out_tensor):
    global _scaled_mm_out_supported, _scaled_mm_fast_accum_supported
    if _scaled_mm_out_supported is None:
        try:
            torch._scaled_mm(
                mat_a,
                mat_b,
                scale_a,
                scale_b,
                bias=None,
                out_dtype=torch.float16,
                use_fast_accum=True,
                out=out_tensor,
            )
            _scaled_mm_out_supported = True
            _scaled_mm_fast_accum_supported = True
            return out_tensor
        except Exception:
            try:
                torch._scaled_mm(
                    mat_a,
                    mat_b,
                    scale_a,
                    scale_b,
                    bias=None,
                    out_dtype=torch.float16,
                    out=out_tensor,
                )
                _scaled_mm_out_supported = True
                _scaled_mm_fast_accum_supported = False
                return out_tensor
            except Exception:
                _scaled_mm_out_supported = False

    if _scaled_mm_out_supported:
        if _scaled_mm_fast_accum_supported:
            torch._scaled_mm(
                mat_a,
                mat_b,
                scale_a,
                scale_b,
                bias=None,
                out_dtype=torch.float16,
                use_fast_accum=True,
                out=out_tensor,
            )
        else:
            torch._scaled_mm(
                mat_a,
                mat_b,
                scale_a,
                scale_b,
                bias=None,
                out_dtype=torch.float16,
                out=out_tensor,
            )
        return out_tensor

    if _scaled_mm_fast_accum_supported:
        out = torch._scaled_mm(
            mat_a,
            mat_b,
            scale_a,
            scale_b,
            bias=None,
            out_dtype=torch.float16,
            use_fast_accum=True,
        )
    else:
        out = torch._scaled_mm(
            mat_a,
            mat_b,
            scale_a,
            scale_b,
            bias=None,
            out_dtype=torch.float16,
        )
    out_tensor.copy_(out)
    return out_tensor


def custom_kernel(data: input_t) -> output_t:
    abc_tensors, _, sfasfb_reordered_tensors, problem_sizes = data
    if _dbg_on:
        try:
            doc = getattr(torch.nn.functional.scaled_grouped_mm, "__doc__", None)
            if doc:
                _dbg("v2doc:" + doc[:600].replace("\n", " "))
            else:
                _dbg("v2doc:none")
        except Exception as exc:
            _dbg("v2docerr:" + str(exc))
        try:
            _dbg("v1op:" + str(torch.ops.aten._scaled_grouped_mm))
        except Exception as exc:
            _dbg("v1operr:" + str(exc))

    ext_res = _try_ext(abc_tensors, sfasfb_reordered_tensors, problem_sizes)
    if ext_res is not None:
        return ext_res
    if _force_ext_only:
        raise RuntimeError("ext path failed")

    result_tensors = []

    def _emit_from_2d(out, grouped):
        local_results = []
        offset = 0
        for (_, _, c_ref), m_pad, m in zip(
            abc_tensors, grouped["m_pads"], grouped["m_actual"]
        ):
            c_ref[:, :, 0].copy_(out[offset : offset + m, :])
            offset += m_pad
            local_results.append(c_ref)
        return local_results

    def _emit_from_3d(out, grouped_pad):
        local_results = []
        for idx, (_, _, c_ref) in enumerate(abc_tensors):
            c_ref[:, :, 0].copy_(out[idx, : grouped_pad["m_actual"][idx], :])
            local_results.append(c_ref)
        return local_results

    grouped_mm_v2 = _get_grouped_mm_v2()
    block_scale, global_scale, block_swizzle, global_swizzle = _get_scale_swizzle()
    k_val = problem_sizes[0][2] if problem_sizes else 0
    strategy_key = tuple(problem_sizes)
    strategy = _grouped_strategy_cache.get(strategy_key)

    if (
        grouped_mm_v2 is not None
        and block_scale is not None
        and block_swizzle is not None
        and strategy != "skip_v2"
    ):
        try:
            grouped2d = None
            grouped3d = None

            if strategy is not None:
                attempts = [strategy]
            else:
                if global_scale is not None:
                    attempts = [
                        "2d:stack:nk:group",
                        "3d:stack:nk:group",
                        "2d:stack:kn:group",
                        "3d:stack:kn:group",
                        "2d:concat:nk:group",
                        "2d:concat:kn:group",
                        "3d:stack:nk:scalar",
                        "3d:stack:kn:scalar",
                    ]
                else:
                    attempts = [
                        "2d:stack:nk:none",
                        "3d:stack:nk:none",
                        "2d:stack:kn:none",
                        "3d:stack:kn:none",
                        "2d:concat:nk:none",
                        "2d:concat:kn:none",
                    ]

            for attempt in attempts:
                try:
                    mode, scale_layout, b_layout, global_layout = attempt.split(":")
                except ValueError:
                    mode, scale_layout, b_layout, global_layout = "2d", "stack", "nk", "group"

                if mode == "3d":
                    if grouped3d is None:
                        grouped3d = _build_grouped_inputs_padded(
                            abc_tensors, sfasfb_reordered_tensors, problem_sizes
                        )
                    grouped = grouped3d
                    a = grouped["a"]
                    b = grouped["b_nk"] if b_layout == "nk" else grouped["b_kn"]
                    scale_a_block = grouped["scale_a"]
                    scale_b_block = grouped["scale_b"]
                    offs = None
                    offs_cpu = None
                    emit = _emit_from_3d
                else:
                    if grouped2d is None:
                        grouped2d = _build_grouped_inputs(
                            abc_tensors, sfasfb_reordered_tensors, problem_sizes
                        )
                    grouped = grouped2d
                    a = grouped["a"]
                    scale_a_concat = grouped["scale_a"]
                    scale_a_stack = grouped["scale_a_stack"]
                    if scale_layout == "stack" and scale_a_stack is not None:
                        scale_a_block = scale_a_stack
                    else:
                        scale_a_block = scale_a_concat
                    scale_b_block = grouped["scale_b"]
                    b = grouped["b_nk"] if b_layout == "nk" else grouped["b_kn"]
                    offs = grouped["offs"]
                    offs_cpu = grouped["offs_cpu"]
                    emit = _emit_from_2d

                if scale_a_block is None or scale_b_block is None:
                    continue

                if global_scale is not None and global_layout != "none":
                    per_group = global_layout == "group"
                    global_a = _get_global_scale(len(abc_tensors), a.device, per_group)
                    global_b = _get_global_scale(len(abc_tensors), a.device, per_group)
                    scale_a = [scale_a_block, global_a]
                    scale_b = [scale_b_block, global_b]
                    scale_recipe_a = [block_scale, global_scale]
                    scale_recipe_b = [block_scale, global_scale]
                    swizzle_a = [
                        block_swizzle,
                        global_swizzle if global_swizzle is not None else block_swizzle,
                    ]
                    swizzle_b = [
                        block_swizzle,
                        global_swizzle if global_swizzle is not None else block_swizzle,
                    ]
                else:
                    scale_a = scale_a_block
                    scale_b = scale_b_block
                    scale_recipe_a = block_scale
                    scale_recipe_b = block_scale
                    swizzle_a = block_swizzle
                    swizzle_b = block_swizzle

                out = _try_scaled_grouped_mm_v2(
                    grouped_mm_v2,
                    a,
                    b,
                    scale_a,
                    scale_recipe_a,
                    scale_b,
                    scale_recipe_b,
                    swizzle_a,
                    swizzle_b,
                    offs,
                    offs_cpu,
                    torch.float16,
                )
                if out is None:
                    out = _try_scaled_grouped_mm_v2(
                        grouped_mm_v2,
                        a,
                        b,
                        scale_a,
                        scale_recipe_a,
                        scale_b,
                        scale_recipe_b,
                        swizzle_a,
                        swizzle_b,
                        offs,
                        offs_cpu,
                        torch.bfloat16,
                    )
                    if out is not None:
                        out = out.to(torch.float16)
                if out is not None:
                    _grouped_strategy_cache[strategy_key] = attempt
                    result_tensors = emit(out, grouped)
                    return result_tensors

            _grouped_strategy_cache[strategy_key] = "skip_v2"
        except Exception:
            _grouped_strategy_cache[strategy_key] = "skip_v2"

    grouped_mm = _get_grouped_mm()
    strategy_v1 = _grouped_strategy_cache_v1.get(strategy_key)
    if grouped_mm is not None and strategy_v1 != "skip_v1":
        try:
            grouped = _build_grouped_inputs(
                abc_tensors, sfasfb_reordered_tensors, problem_sizes
            )
            a = grouped["a"]
            scale_a_block = grouped["scale_a"]
            scale_b_block = grouped["scale_b"]
            b_nk = grouped["b_nk"]
            b_kn = grouped["b_kn"]

            attempts = [strategy_v1] if strategy_v1 is not None else ["kn", "nk"]
            for attempt in attempts:
                b = b_nk if attempt == "nk" else b_kn

                out = _try_scaled_grouped_mm_v1(
                    grouped_mm,
                    a,
                    b,
                    scale_a_block,
                    scale_b_block,
                    grouped["offs"],
                    grouped["offs_cpu"],
                    torch.float16,
                )
                if out is None:
                    out = _try_scaled_grouped_mm_v1(
                        grouped_mm,
                        a,
                        b,
                        scale_a_block,
                        scale_b_block,
                        grouped["offs"],
                        grouped["offs_cpu"],
                        torch.bfloat16,
                    )
                    if out is not None:
                        out = out.to(torch.float16)

                if out is not None:
                    _grouped_strategy_cache_v1[strategy_key] = attempt
                    result_tensors = _emit_from_2d(out, grouped)
                    return result_tensors

            _grouped_strategy_cache_v1[strategy_key] = "skip_v1"
        except Exception:
            _grouped_strategy_cache_v1[strategy_key] = "skip_v1"

    for (a_ref, b_ref, c_ref), (sfa_reordered, sfb_reordered), (_, _, _, l) in zip(
        abc_tensors, sfasfb_reordered_tensors, problem_sizes
    ):
        for l_idx in range(l):
            scale_a = blocked_from_reordered(sfa_reordered, l_idx)
            scale_b = blocked_from_reordered(sfb_reordered, l_idx)
            mat_a = a_ref[:, :, l_idx].view(torch.float4_e2m1fn_x2)
            mat_b = b_ref[:, :, l_idx].transpose(0, 1).view(torch.float4_e2m1fn_x2)
            _scaled_mm_out(
                mat_a,
                mat_b,
                scale_a,
                scale_b,
                c_ref[:, :, l_idx],
            )
        result_tensors.append(c_ref)
    return result_tensors
scrolls · 945 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Changes from previous submission

Against this author's previous submission submission 409983.

⋯ 9 unchanged lines
pass
_CPP_B64 = "CiAgICAjaW5jbHVkZSA8dG9yY2gvZXh0ZW5zaW9uLmg+CiAgICAjaW5jbHVkZSA8dmVjdG9yPgoKICAgIHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+IGdyb3VwZWRfZ2VtbV8xc20oCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEIsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEMsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZCKTsKCiAgICBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiBncm91cGVkX2dlbW1fMnNtKAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBCLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBDLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQik7CiAgICA="
- _CU_B64 = "CiAgICAjaW5jbHVkZSA8dG9yY2gvZXh0ZW5zaW9uLmg+CiAgICAjaW5jbHVkZSA8QVRlbi9jdWRhL0NVREFDb250ZXh0Lmg+CiAgICAjaW5jbHVkZSA8YzEwL2N1ZGEvQ1VEQVN0cmVhbS5oPgogICAgI2luY2x1ZGUgPGMxMC9jdWRhL0NVREFHdWFyZC5oPgogICAgI2luY2x1ZGUgPHZlY3Rvcj4KICAgICNpbmNsdWRlIDxzdGRleGNlcHQ+CgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX09QRVJBVE9SU19fCiAgICAjdW5kZWYgX19DVURBX05PX0hBTEZfT1BFUkFUT1JTX18KICAgICNlbmRpZgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX0NPTlZFUlNJT05TX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRl9DT05WRVJTSU9OU19fCiAgICAjZW5kaWYKICAgICNpZmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICNlbmRpZgoKICAgICNpbmNsdWRlIDxjdWRhX3J1bnRpbWUuaD4KICAgICNpZiBkZWZpbmVkKF9fQ1VEQV9BUkNIX18pICYmICFkZWZpbmVkKENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQpCiAgICAjZGVmaW5lIENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQgMQogICAgI2VuZGlmCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9jdXRsYXNzLmgiCiAgICAjaW5jbHVkZSAiY3V0ZS90ZW5zb3IuaHBwIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdGVuc29yX3JlZi5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvZXBpbG9ndWUvY29sbGVjdGl2ZS9kZWZhdWx0X2VwaWxvZ3VlLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL3RocmVhZC9saW5lYXJfY29tYmluYXRpb24uaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGlzcGF0Y2hfcG9saWN5LmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZ3JvdXBfYXJyYXlfcHJvYmxlbV9zaGFwZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGV2aWNlL2dlbW1fdW5pdmVyc2FsX2FkYXB0ZXIuaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0va2VybmVsL2dlbW1fdW5pdmVyc2FsLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL3V0aWwvcGFja2VkX3N0cmlkZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9rZXJuZWxfaGFyZHdhcmVfaW5mby5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdXRpbC9kZXZpY2VfbWVtb3J5LmgiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2tlcm5lbC90aWxlX3NjaGVkdWxlcl9wYXJhbXMuaCIKCiAgICAjZGVmaW5lIENVVExBU1NfQ0hFQ0soc3RhdHVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdXRsYXNzOjpTdGF0dXMgX3N0YXR1cyA9IChzdGF0dXMpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9zdGF0dXMgIT0gY3V0bGFzczo6U3RhdHVzOjprU3VjY2VzcykgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKCJDVVRMQVNTIGVycm9yIik7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgI2RlZmluZSBDVURBX0NIRUNLKGV4cHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdWRhRXJyb3JfdCBfZXJyID0gKGV4cHIpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9lcnIgIT0gY3VkYVN1Y2Nlc3MpIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKGN1ZGFHZXRFcnJvclN0cmluZyhfZXJyKSk7ICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgdXNpbmcgUHJvYmxlbVNoYXBlID0gY3V0bGFzczo6Z2VtbTo6R3JvdXBQcm9ibGVtU2hhcGU8Y3V0ZTo6U2hhcGU8aW50LGludCxpbnQ+PjsKICAgIHVzaW5nIEVsZW1lbnRJbnB1dCA9IGN1dGxhc3M6OmZsb2F0X2UybTFfdDsKICAgIHVzaW5nIEVsZW1lbnRTRiAgICA9IGN1dGxhc3M6OmZsb2F0X3VlNG0zX3Q7CiAgICB1c2luZyBFbGVtZW50QyAgICAgPSBjdXRsYXNzOjpoYWxmX3Q7CgogICAgdXNpbmcgRWxlbWVudEEgPSBjdXRsYXNzOjpudl9mbG9hdDRfdDxFbGVtZW50SW5wdXQ+OwogICAgdXNpbmcgTGF5b3V0QSAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRBICA9IDMyOwoKICAgIHVzaW5nIEVsZW1lbnRCID0gY3V0bGFzczo6bnZfZmxvYXQ0X3Q8RWxlbWVudElucHV0PjsKICAgIHVzaW5nIExheW91dEIgID0gY3V0bGFzczo6bGF5b3V0OjpDb2x1bW5NYWpvcjsKICAgIGNvbnN0ZXhwciBpbnQgQWxpZ25tZW50QiAgPSAzMjsKCiAgICB1c2luZyBFbGVtZW50RCA9IEVsZW1lbnRDOwogICAgdXNpbmcgTGF5b3V0QyAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRDICA9IDEyOCAvIGN1dGxhc3M6OnNpemVvZl9iaXRzPEVsZW1lbnRDPjo6dmFsdWU7CiAgICBjb25zdGV4cHIgaW50IEFsaWdubWVudEQgID0gMTI4IC8gY3V0bGFzczo6c2l6ZW9mX2JpdHM8RWxlbWVudEQ+Ojp2YWx1ZTsKICAgIHVzaW5nIEVsZW1lbnRBY2N1bXVsYXRvciAgPSBmbG9hdDsKCiAgICB1c2luZyBBcmNoVGFnID0gY3V0bGFzczo6YXJjaDo6U20xMDA7CiAgICB1c2luZyBPcGVyYXRvckNsYXNzID0gY3V0bGFzczo6YXJjaDo6T3BDbGFzc0Jsb2NrU2NhbGVkVGVuc29yT3A7CiAgICB1c2luZyBTdGFnZUNvdW50VHlwZSA9IGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvOwogICAgdXNpbmcgQ2x1c3RlclNoYXBlID0gY3V0ZTo6U2hhcGU8aW50MzJfdCxpbnQzMl90LGN1dGU6Ol8xPjsKCiAgICBzdHJ1Y3QgTU1BMVNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtOwogICAgfTsKCiAgICBzdHJ1Y3QgTU1BMlNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8yNTYsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtOwogICAgfTsKCiAgICB1c2luZyBDb2xsZWN0aXZlRXBpbG9ndWUxU00gPSB0eXBlbmFtZSBjdXRsYXNzOjplcGlsb2d1ZTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgICAgQXJjaFRhZywgT3BlcmF0b3JDbGFzcywKICAgICAgICB0eXBlbmFtZSBNTUExU01Db25maWc6Ok1tYVRpbGVTaGFwZSwgQ2x1c3RlclNoYXBlLAogICAgICAgIGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzY0PiwKICAgICAgICBFbGVtZW50QWNjdW11bGF0b3IsIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgICBFbGVtZW50QywgTGF5b3V0QyAqLCBBbGlnbm1lbnRDLAogICAgICAgIEVsZW1lbnRELCBMYXlvdXRDICosIEFsaWdubWVudEQsCiAgICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpFcGlsb2d1ZVNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgQ29sbGVjdGl2ZU1haW5sb29wMVNNID0gdHlwZW5hbWUgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgIEVsZW1lbnRBLCBMYXlvdXRBICosIEFsaWdubWVudEEsCiAgICAgIEVsZW1lbnRCLCBMYXlvdXRCICosIEFsaWdubWVudEIsCiAgICAgIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6U3RhZ2VDb3VudEF1dG9DYXJ2ZW91dDwKICAgICAgICBzdGF0aWNfY2FzdDxpbnQ+KHNpemVvZih0eXBlbmFtZSBDb2xsZWN0aXZlRXBpbG9ndWUxU006OlNoYXJlZFN0b3JhZ2UpKT4sCiAgICAgIHR5cGVuYW1lIE1NQTFTTUNvbmZpZzo6S2VybmVsU2NoZWR1bGUKICAgID46OkNvbGxlY3RpdmVPcDsKCiAgICB1c2luZyBHZW1tS2VybmVsMVNNID0gY3V0bGFzczo6Z2VtbTo6a2VybmVsOjpHZW1tVW5pdmVyc2FsPAogICAgICAgIFByb2JsZW1TaGFwZSwKICAgICAgICBDb2xsZWN0aXZlTWFpbmxvb3AxU00sCiAgICAgICAgQ29sbGVjdGl2ZUVwaWxvZ3VlMVNNCiAgICA+OwogICAgdXNpbmcgR2VtbTFTTSA9IGN1dGxhc3M6OmdlbW06OmRldmljZTo6R2VtbVVuaXZlcnNhbEFkYXB0ZXI8R2VtbUtlcm5lbDFTTT47CgogICAgdXNpbmcgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNID0gdHlwZW5hbWUgY3V0bGFzczo6ZXBpbG9ndWU6OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgICAgdHlwZW5hbWUgTU1BMlNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgICBjdXRlOjpTaGFwZTxjdXRlOjpfMTI4LGN1dGU6Ol82ND4sCiAgICAgICAgRWxlbWVudEFjY3VtdWxhdG9yLCBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgICAgRWxlbWVudEMsIExheW91dEMgKiwgQWxpZ25tZW50QywKICAgICAgICBFbGVtZW50RCwgTGF5b3V0QyAqLCBBbGlnbm1lbnRELAogICAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6RXBpbG9ndWVTY2hlZHVsZQogICAgPjo6Q29sbGVjdGl2ZU9wOwoKICAgIHVzaW5nIENvbGxlY3RpdmVNYWlubG9vcDJTTSA9IHR5cGVuYW1lIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICBBcmNoVGFnLCBPcGVyYXRvckNsYXNzLAogICAgICBFbGVtZW50QSwgTGF5b3V0QSAqLCBBbGlnbm1lbnRBLAogICAgICBFbGVtZW50QiwgTGF5b3V0QiAqLCBBbGlnbm1lbnRCLAogICAgICBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6TW1hVGlsZVNoYXBlLCBDbHVzdGVyU2hhcGUsCiAgICAgIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvQ2FydmVvdXQ8CiAgICAgICAgc3RhdGljX2Nhc3Q8aW50PihzaXplb2YodHlwZW5hbWUgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNOjpTaGFyZWRTdG9yYWdlKSk+LAogICAgICB0eXBlbmFtZSBNTUEyU01Db25maWc6Oktlcm5lbFNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgR2VtbUtlcm5lbDJTTSA9IGN1dGxhc3M6OmdlbW06Omtlcm5lbDo6R2VtbVVuaXZlcnNhbDwKICAgICAgICBQcm9ibGVtU2hhcGUsCiAgICAgICAgQ29sbGVjdGl2ZU1haW5sb29wMlNNLAogICAgICAgIENvbGxlY3RpdmVFcGlsb2d1ZTJTTQogICAgPjsKICAgIHVzaW5nIEdlbW0yU00gPSBjdXRsYXNzOjpnZW1tOjpkZXZpY2U6OkdlbW1Vbml2ZXJzYWxBZGFwdGVyPEdlbW1LZXJuZWwyU00+OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IFBpbm5lZEhvc3RCdWZmZXIgewogICAgICBUKiBwdHIgPSBudWxscHRyOwogICAgICBzaXplX3QgY291bnQgPSAwOwoKICAgICAgUGlubmVkSG9zdEJ1ZmZlcigpID0gZGVmYXVsdDsKICAgICAgZXhwbGljaXQgUGlubmVkSG9zdEJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYUhvc3RBbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCksIGN1ZGFIb3N0QWxsb2NQb3J0YWJsZSkpOwogICAgICB9CgogICAgICB2b2lkIHJlbGVhc2UoKSB7CiAgICAgICAgaWYgKHB0cikgewogICAgICAgICAgY3VkYUZyZWVIb3N0KHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgflBpbm5lZEhvc3RCdWZmZXIoKSB7IHJlbGVhc2UoKTsgfQoKICAgICAgVCogZGF0YSgpIGNvbnN0IHsgcmV0dXJuIHB0cjsgfQogICAgICBUJiBvcGVyYXRvcltdKHNpemVfdCBpZHgpIHsgcmV0dXJuIHB0cltpZHhdOyB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IERldmljZUJ1ZmZlciB7CiAgICAgIFQqIHB0ciA9IG51bGxwdHI7CiAgICAgIHNpemVfdCBjb3VudCA9IDA7CgogICAgICBEZXZpY2VCdWZmZXIoKSA9IGRlZmF1bHQ7CiAgICAgIGV4cGxpY2l0IERldmljZUJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYU1hbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCkpKTsKICAgICAgfQoKICAgICAgdm9pZCByZWxlYXNlKCkgewogICAgICAgIGlmIChwdHIpIHsKICAgICAgICAgIGN1ZGFGcmVlKHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgfkRldmljZUJ1ZmZlcigpIHsgcmVsZWFzZSgpOyB9CgogICAgICBUKiBnZXQoKSBjb25zdCB7IHJldHVybiBwdHI7IH0KCiAgICAgIHZvaWQgY29weV9mcm9tX2hvc3QoY29uc3QgVCogaG9zdCwgc2l6ZV90IG4sIGN1ZGFTdHJlYW1fdCBzdHJlYW0pIHsKICAgICAgICBDVURBX0NIRUNLKGN1ZGFNZW1jcHlBc3luYyhwdHIsIGhvc3QsIG4gKiBzaXplb2YoVCksIGN1ZGFNZW1jcHlIb3N0VG9EZXZpY2UsIHN0cmVhbSkpOwogICAgICB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBHZW1tPgogICAgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4gcnVuX2dyb3VwZWRfaW1wbCgKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQiwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQywKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkIpIHsKCiAgICAgIFRPUkNIX0NIRUNLKEEuc2l6ZSgpID09IEIuc2l6ZSgpLCAiQS9CIHNpemUgbWlzbWF0Y2giKTsKICAgICAgVE9SQ0hfQ0hFQ0soQS5zaXplKCkgPT0gQy5zaXplKCksICJBL0Mgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkEuc2l6ZSgpLCAiQS9TRkEgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkIuc2l6ZSgpLCAiQS9TRkIgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBjb25zdCBpbnQzMl90IGdyb3VwcyA9IHN0YXRpY19jYXN0PGludDMyX3Q+KEEuc2l6ZSgpKTsKICAgICAgVE9SQ0hfQ0hFQ0soZ3JvdXBzID4gMCwgIk5vIGdyb3VwcyIpOwoKICAgICAgaW50IGRldmljZV9pZCA9IEFbMF0uZ2V0X2RldmljZSgpOwogICAgICBjMTA6OmN1ZGE6OkNVREFHdWFyZCBkZXZpY2VfZ3VhcmQoZGV2aWNlX2lkKTsKCiAgICAgIHVzaW5nIFN0cmlkZUEgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUE7CiAgICAgIHVzaW5nIFN0cmlkZUIgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUI7CiAgICAgIHVzaW5nIFN0cmlkZUMgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUM7CiAgICAgIHVzaW5nIFN0cmlkZUQgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUQ7CiAgICAgIHVzaW5nIExheW91dFNGQSA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkE7CiAgICAgIHVzaW5nIExheW91dFNGQiA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkI7CiAgICAgIHVzaW5nIFNtMXh4QmxrU2NhbGVkQ29uZmlnID0gdHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6Q29sbGVjdGl2ZU1haW5sb29wOjpTbTF4eEJsa1NjYWxlZENvbmZpZzsKCiAgICAgIHN0YXRpYyBzaXplX3QgY2FwYWNpdHkgPSAwOwogICAgICBzdGF0aWMgaW50IGNhY2hlZF9kZXZpY2UgPSAtMTsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEEgY29uc3QqPiBwdHJfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QiBjb25zdCo+IHB0cl9CX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4gcHRyX0NfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RXBpbG9ndWVPdXRwdXRPcDo6RWxlbWVudE91dHB1dCo+IHB0cl9EX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6RWxlbWVudFNGIGNvbnN0Kj4gcHRyX1NGQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVBPiBzdHJpZGVfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVCPiBzdHJpZGVfQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVDPiBzdHJpZGVfQ19ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVEPiBzdHJpZGVfRF9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxMYXlvdXRTRkE+IGxheW91dF9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8TGF5b3V0U0ZCPiBsYXlvdXRfU0ZCX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIFByb2JsZW1TaGFwZTo6VW5kZXJseWluZ1Byb2JsZW1TaGFwZT4gcHJvYmxlbV9zaXplc19ob3N0OwoKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBQcm9ibGVtU2hhcGU6OlVuZGVybHlpbmdQcm9ibGVtU2hhcGU+IHByb2JsZW1fc2l6ZXNfZGV2aWNlOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRBIGNvbnN0Kj4gcHRyX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPiBwdHJfQjsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpFbGVtZW50U0YgY29uc3QqPiBwdHJfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkI7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEMgY29uc3QqPiBwdHJfQzsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4gcHRyX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQT4gc3RyaWRlX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQj4gc3RyaWRlX0I7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQz4gc3RyaWRlX0M7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlRD4gc3RyaWRlX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8TGF5b3V0U0ZBPiBsYXlvdXRfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPExheW91dFNGQj4gbGF5b3V0X1NGQjsKICAgICAgc3RhdGljIHNpemVfdCB3b3Jrc3BhY2Vfc2l6ZV9jYWNoZWQgPSAwOwogICAgICBzdGF0aWMgdG9yY2g6OlRlbnNvciB3b3Jrc3BhY2VfdGVuc29yOwoKICAgICAgaWYgKGNhY2hlZF9kZXZpY2UgIT0gZGV2aWNlX2lkIHx8IGNhcGFjaXR5IDwgc3RhdGljX2Nhc3Q8c2l6ZV90Pihncm91cHMpKSB7CiAgICAgICAgY2FjaGVkX2RldmljZSA9IGRldmljZV9pZDsKICAgICAgICBjYXBhY2l0eSA9IHN0YXRpY19jYXN0PHNpemVfdD4oZ3JvdXBzKTsKICAgICAgICBwdHJfQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0JfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfRF9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQl9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0FfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQ19ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0RfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkFfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkJfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHByb2JsZW1fc2l6ZXNfaG9zdC5hbGxvY2F0ZShncm91cHMpOwoKICAgICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9BLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0IuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfU0ZBLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQi5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0MuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfRC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkEuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBsYXlvdXRfU0ZCLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgIH0KCiAgICAgIGZvciAoaW50MzJfdCBpID0gMDsgaSA8IGdyb3VwczsgKytpKSB7CiAgICAgICAgVE9SQ0hfQ0hFQ0soQVtpXS5pc19jdWRhKCksICJBIG11c3QgYmUgQ1VEQSIpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uaXNfY3VkYSgpLCAiQiBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLmlzX2N1ZGEoKSwgIkMgbXVzdCBiZSBDVURBIik7CiAgICAgICAgVE9SQ0hfQ0hFQ0soU0ZBW2ldLmlzX2N1ZGEoKSwgIlNGQSBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhTRkJbaV0uaXNfY3VkYSgpLCAiU0ZCIG11c3QgYmUgQ1VEQSIpOwoKICAgICAgICBpbnQzMl90IG0gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMCkpOwogICAgICAgIGludDMyX3Qga19wYWNrZWQgPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMSkpOwogICAgICAgIGludDMyX3QgayA9IGtfcGFja2VkICogMjsKICAgICAgICBpbnQzMl90IG4gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihCW2ldLnNpemUoMCkpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uc2l6ZSgxKSAqIDIgPT0gaywgIksgbWlzbWF0Y2giKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLnNpemUoMCkgPT0gbSAmJiBDW2ldLnNpemUoMSkgPT0gbiwgIkMgc2hhcGUgbWlzbWF0Y2giKTsKCiAgICAgICAgcHJvYmxlbV9zaXplc19ob3N0W2ldID0gY3V0ZTo6bWFrZV9zaGFwZShtLCBuLCBrKTsKICAgICAgICBzdHJpZGVfQV9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlQXt9LCB7bSwgaywgMX0pOwogICAgICAgIHN0cmlkZV9CX2hvc3RbaV0gPSBjdXRsYXNzOjptYWtlX2N1dGVfcGFja2VkX3N0cmlkZShTdHJpZGVCe30sIHtuLCBrLCAxfSk7CiAgICAgICAgc3RyaWRlX0NfaG9zdFtpXSA9IGN1dGxhc3M6Om1ha2VfY3V0ZV9wYWNrZWRfc3RyaWRlKFN0cmlkZUN7fSwge20sIG4sIDF9KTsKICAgICAgICBzdHJpZGVfRF9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlRHt9LCB7bSwgbiwgMX0pOwogICAgICAgIGxheW91dF9TRkFfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZBKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwogICAgICAgIGxheW91dF9TRkJfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZCKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwoKICAgICAgICBwdHJfQV9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QSBjb25zdCo+KEFbaV0uZGF0YV9wdHIoKSk7CiAgICAgICAgcHRyX0JfaG9zdFtpXSA9IHJlaW50ZXJwcmV0X2Nhc3Q8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPihCW2ldLmRhdGFfcHRyKCkpOwogICAgICAgIHB0cl9DX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfRF9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZBX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQVtpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZCX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQltpXS5kYXRhX3B0cigpKTsKICAgICAgfQoKICAgICAgYXV0byBzdHJlYW1fb2JqID0gYzEwOjpjdWRhOjpnZXRDdXJyZW50Q1VEQVN0cmVhbShkZXZpY2VfaWQpOwogICAgICBjMTA6OmN1ZGE6OkNVREFTdHJlYW1HdWFyZCBzdHJlYW1fZ3VhcmQoc3RyZWFtX29iaik7CiAgICAgIGN1ZGFTdHJlYW1fdCBzdHJlYW0gPSBzdHJlYW1fb2JqLnN0cmVhbSgpOwogICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5jb3B5X2Zyb21faG9zdChwcm9ibGVtX3NpemVzX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9BLmNvcHlfZnJvbV9ob3N0KHB0cl9BX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9CLmNvcHlfZnJvbV9ob3N0KHB0cl9CX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9TRkEuY29weV9mcm9tX2hvc3QocHRyX1NGQV9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwogICAgICBwdHJfU0ZCLmNvcHlfZnJvbV9ob3N0KHB0cl9TRkJfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0MuY29weV9mcm9tX2hvc3QocHRyX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0QuY29weV9mcm9tX2hvc3QocHRyX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0EuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0FfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0IuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0JfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0MuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0QuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgbGF5b3V0X1NGQS5jb3B5X2Zyb21faG9zdChsYXlvdXRfU0ZBX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIGxheW91dF9TRkIuY29weV9mcm9tX2hvc3QobGF5b3V0X1NGQl9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwoKICAgICAgY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvIGh3X2luZm87CiAgICAgIGh3X2luZm8uZGV2aWNlX2lkID0gZGV2aWNlX2lkOwogICAgICBod19pbmZvLnNtX2NvdW50ID0gY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvOjpxdWVyeV9kZXZpY2VfbXVsdGlwcm9jZXNzb3JfY291bnQoaHdfaW5mby5kZXZpY2VfaWQpOwogICAgICBod19pbmZvLmNsdXN0ZXJfc2hhcGUgPSBkaW0zKDIsIDEsIDEpOwogICAgICBod19pbmZvLmNsdXN0ZXJfc2hhcGVfZmFsbGJhY2sgPSBkaW0zKDIsIDEsIDEpOwoKICAgICAgdHlwZW5hbWUgR2VtbTo6QXJndW1lbnRzIGFyZ3VtZW50czsKICAgICAgZGVjbHR5cGUoYXJndW1lbnRzLmVwaWxvZ3VlLnRocmVhZCkgZnVzaW9uX2FyZ3M7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0ciA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuYWxwaGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMSk7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMCk7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0cl9hcnJheSA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyX2FycmF5ID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuZEFscGhhID0ge2N1dGU6Ol8we30sIGN1dGU6Ol8we30sIDB9OwogICAgICBmdXNpb25fYXJncy5kQmV0YSA9IHtjdXRlOjpfMHt9LCBjdXRlOjpfMHt9LCAwfTsKCiAgICAgIHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OlRpbGVTY2hlZHVsZXJBcmd1bWVudHMgc2NoZWR1bGVyOwogICAgICBzY2hlZHVsZXIucmFzdGVyX29yZGVyID0gY3V0bGFzczo6Z2VtbTo6a2VybmVsOjpkZXRhaWw6OlJhc3Rlck9yZGVyT3B0aW9uczo6QWxvbmdOOwoKICAgICAgYXJndW1lbnRzID0gdHlwZW5hbWUgR2VtbTo6QXJndW1lbnRzIHsKICAgICAgICBjdXRsYXNzOjpnZW1tOjpHZW1tVW5pdmVyc2FsTW9kZTo6a0dyb3VwZWQsCiAgICAgICAge2dyb3VwcywgcHJvYmxlbV9zaXplc19kZXZpY2UuZ2V0KCksIG51bGxwdHJ9LAogICAgICAgIHtwdHJfQS5nZXQoKSwgc3RyaWRlX0EuZ2V0KCksIHB0cl9CLmdldCgpLCBzdHJpZGVfQi5nZXQoKSwKICAgICAgICAgcHRyX1NGQS5nZXQoKSwgbGF5b3V0X1NGQS5nZXQoKSwgcHRyX1NGQi5nZXQoKSwgbGF5b3V0X1NGQi5nZXQoKX0sCiAgICAgICAge2Z1c2lvbl9hcmdzLCBwdHJfQy5nZXQoKSwgc3RyaWRlX0MuZ2V0KCksIHB0cl9ELmdldCgpLCBzdHJpZGVfRC5nZXQoKX0sCiAgICAgICAgaHdfaW5mbywgc2NoZWR1bGVyCiAgICAgIH07CgogICAgICBHZW1tIGdlbW07CiAgICAgIHNpemVfdCB3b3Jrc3BhY2Vfc2l6ZSA9IEdlbW06OmdldF93b3Jrc3BhY2Vfc2l6ZShhcmd1bWVudHMpOwogICAgICBpZiAoIXdvcmtzcGFjZV90ZW5zb3IuZGVmaW5lZCgpIHx8IGNhY2hlZF9kZXZpY2UgIT0gZGV2aWNlX2lkIHx8IHdvcmtzcGFjZV9zaXplX2NhY2hlZCA8IHdvcmtzcGFjZV9zaXplKSB7CiAgICAgICAgYXV0byBvcHRzID0gdG9yY2g6OlRlbnNvck9wdGlvbnMoKS5kZXZpY2UodG9yY2g6OmtDVURBLCBkZXZpY2VfaWQpLmR0eXBlKHRvcmNoOjprVUludDgpOwogICAgICAgIHdvcmtzcGFjZV90ZW5zb3IgPSB0b3JjaDo6ZW1wdHkoe3N0YXRpY19jYXN0PGxvbmc+KHdvcmtzcGFjZV9zaXplKX0sIG9wdHMpOwogICAgICAgIHdvcmtzcGFjZV9zaXplX2NhY2hlZCA9IHdvcmtzcGFjZV9zaXplOwogICAgICB9CiAgICAgIHZvaWQqIHdvcmtzcGFjZV9wdHIgPSB3b3Jrc3BhY2Vfc2l6ZSA/IHdvcmtzcGFjZV90ZW5zb3IuZGF0YV9wdHIoKSA6IG51bGxwdHI7CgogICAgICBDVVRMQVNTX0NIRUNLKGdlbW0uY2FuX2ltcGxlbWVudChhcmd1bWVudHMpKTsKICAgICAgQ1VUTEFTU19DSEVDSyhnZW1tLmluaXRpYWxpemUoYXJndW1lbnRzLCB3b3Jrc3BhY2VfcHRyLCBzdHJlYW0pKTsKICAgICAgQ1VUTEFTU19DSEVDSyhnZW1tLnJ1bihzdHJlYW0pKTsKCiAgICAgIHJldHVybiBDOwogICAgfQoKICAgIHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+IGdyb3VwZWRfZ2VtbV8xc20oCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEIsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEMsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZCKSB7CiAgICAgIHJldHVybiBydW5fZ3JvdXBlZF9pbXBsPEdlbW0xU00+KEEsIEIsIEMsIFNGQSwgU0ZCKTsKICAgIH0KCiAgICBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiBncm91cGVkX2dlbW1fMnNtKAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBCLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBDLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQikgewogICAgICByZXR1cm4gcnVuX2dyb3VwZWRfaW1wbDxHZW1tMlNNPihBLCBCLCBDLCBTRkEsIFNGQik7CiAgICB9CiAgICAK"
+ _CU_B64 = "CiAgICAjaW5jbHVkZSA8dG9yY2gvZXh0ZW5zaW9uLmg+CiAgICAjaW5jbHVkZSA8QVRlbi9jdWRhL0NVREFDb250ZXh0Lmg+CiAgICAjaW5jbHVkZSA8YzEwL2N1ZGEvQ1VEQVN0cmVhbS5oPgogICAgI2luY2x1ZGUgPGMxMC9jdWRhL0NVREFHdWFyZC5oPgogICAgI2luY2x1ZGUgPHZlY3Rvcj4KICAgICNpbmNsdWRlIDxzdGRleGNlcHQ+CiAgICAjaW5jbHVkZSA8dHlwZV90cmFpdHM+CgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX09QRVJBVE9SU19fCiAgICAjdW5kZWYgX19DVURBX05PX0hBTEZfT1BFUkFUT1JTX18KICAgICNlbmRpZgogICAgI2lmZGVmIF9fQ1VEQV9OT19IQUxGX0NPTlZFUlNJT05TX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRl9DT05WRVJTSU9OU19fCiAgICAjZW5kaWYKICAgICNpZmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICN1bmRlZiBfX0NVREFfTk9fSEFMRjJfT1BFUkFUT1JTX18KICAgICNlbmRpZgoKICAgICNpbmNsdWRlIDxjdWRhX3J1bnRpbWUuaD4KICAgICNpZiBkZWZpbmVkKF9fQ1VEQV9BUkNIX18pICYmICFkZWZpbmVkKENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQpCiAgICAjZGVmaW5lIENVVExBU1NfQVJDSF9NTUFfU00xMDBBX0VOQUJMRUQgMQogICAgI2VuZGlmCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9jdXRsYXNzLmgiCiAgICAjaW5jbHVkZSAiY3V0ZS90ZW5zb3IuaHBwIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdGVuc29yX3JlZi5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvZXBpbG9ndWUvY29sbGVjdGl2ZS9kZWZhdWx0X2VwaWxvZ3VlLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL3RocmVhZC9saW5lYXJfY29tYmluYXRpb24uaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGlzcGF0Y2hfcG9saWN5LmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZ3JvdXBfYXJyYXlfcHJvYmxlbV9zaGFwZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2VwaWxvZ3VlL2NvbGxlY3RpdmUvY29sbGVjdGl2ZV9idWlsZGVyLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0vZGV2aWNlL2dlbW1fdW5pdmVyc2FsX2FkYXB0ZXIuaCIKICAgICNpbmNsdWRlICJjdXRsYXNzL2dlbW0va2VybmVsL2dlbW1fdW5pdmVyc2FsLmhwcCIKICAgICNpbmNsdWRlICJjdXRsYXNzL3V0aWwvcGFja2VkX3N0cmlkZS5ocHAiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9rZXJuZWxfaGFyZHdhcmVfaW5mby5oIgogICAgI2luY2x1ZGUgImN1dGxhc3MvdXRpbC9kZXZpY2VfbWVtb3J5LmgiCiAgICAjaW5jbHVkZSAiY3V0bGFzcy9nZW1tL2tlcm5lbC90aWxlX3NjaGVkdWxlcl9wYXJhbXMuaCIKCiAgICAjZGVmaW5lIENVVExBU1NfQ0hFQ0soc3RhdHVzKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdXRsYXNzOjpTdGF0dXMgX3N0YXR1cyA9IChzdGF0dXMpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9zdGF0dXMgIT0gY3V0bGFzczo6U3RhdHVzOjprU3VjY2VzcykgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKCJDVVRMQVNTIGVycm9yIik7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgI2RlZmluZSBDVURBX0NIRUNLKGV4cHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICBkbyB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICBjdWRhRXJyb3JfdCBfZXJyID0gKGV4cHIpOyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgICAgaWYgKF9lcnIgIT0gY3VkYVN1Y2Nlc3MpIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXAogICAgICAgICAgdGhyb3cgc3RkOjpydW50aW1lX2Vycm9yKGN1ZGFHZXRFcnJvclN0cmluZyhfZXJyKSk7ICAgICAgICAgICAgICAgICAgICAgICAgIFwKICAgICAgICB9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcCiAgICAgIH0gd2hpbGUgKDApCgogICAgdXNpbmcgUHJvYmxlbVNoYXBlID0gY3V0bGFzczo6Z2VtbTo6R3JvdXBQcm9ibGVtU2hhcGU8Y3V0ZTo6U2hhcGU8aW50LGludCxpbnQ+PjsKICAgIHVzaW5nIEVsZW1lbnRJbnB1dCA9IGN1dGxhc3M6OmZsb2F0X2UybTFfdDsKICAgIHVzaW5nIEVsZW1lbnRTRiAgICA9IGN1dGxhc3M6OmZsb2F0X3VlNG0zX3Q7CiAgICB1c2luZyBFbGVtZW50QyAgICAgPSBjdXRsYXNzOjpoYWxmX3Q7CgogICAgdXNpbmcgRWxlbWVudEEgPSBjdXRsYXNzOjpudl9mbG9hdDRfdDxFbGVtZW50SW5wdXQ+OwogICAgdXNpbmcgTGF5b3V0QSAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRBICA9IDMyOwoKICAgIHVzaW5nIEVsZW1lbnRCID0gY3V0bGFzczo6bnZfZmxvYXQ0X3Q8RWxlbWVudElucHV0PjsKICAgIHVzaW5nIExheW91dEIgID0gY3V0bGFzczo6bGF5b3V0OjpDb2x1bW5NYWpvcjsKICAgIGNvbnN0ZXhwciBpbnQgQWxpZ25tZW50QiAgPSAzMjsKCiAgICB1c2luZyBFbGVtZW50RCA9IEVsZW1lbnRDOwogICAgdXNpbmcgTGF5b3V0QyAgPSBjdXRsYXNzOjpsYXlvdXQ6OlJvd01ham9yOwogICAgY29uc3RleHByIGludCBBbGlnbm1lbnRDICA9IDEyOCAvIGN1dGxhc3M6OnNpemVvZl9iaXRzPEVsZW1lbnRDPjo6dmFsdWU7CiAgICBjb25zdGV4cHIgaW50IEFsaWdubWVudEQgID0gMTI4IC8gY3V0bGFzczo6c2l6ZW9mX2JpdHM8RWxlbWVudEQ+Ojp2YWx1ZTsKICAgIHVzaW5nIEVsZW1lbnRBY2N1bXVsYXRvciAgPSBmbG9hdDsKCiAgICB1c2luZyBBcmNoVGFnID0gY3V0bGFzczo6YXJjaDo6U20xMDA7CiAgICB1c2luZyBPcGVyYXRvckNsYXNzID0gY3V0bGFzczo6YXJjaDo6T3BDbGFzc0Jsb2NrU2NhbGVkVGVuc29yT3A7CiAgICB1c2luZyBTdGFnZUNvdW50VHlwZSA9IGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvOwogICAgdXNpbmcgQ2x1c3RlclNoYXBlID0gY3V0ZTo6U2hhcGU8aW50MzJfdCxpbnQzMl90LGN1dGU6Ol8xPjsKCiAgICBzdHJ1Y3QgTU1BMVNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMVNtOwogICAgfTsKCiAgICBzdHJ1Y3QgTU1BMlNNQ29uZmlnIHsKICAgICAgdXNpbmcgTW1hVGlsZVNoYXBlICAgICA9IGN1dGU6OlNoYXBlPGN1dGU6Ol8yNTYsY3V0ZTo6XzEyOCxjdXRlOjpfMjU2PjsKICAgICAgdXNpbmcgS2VybmVsU2NoZWR1bGUgICA9IGN1dGxhc3M6OmdlbW06Oktlcm5lbFB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtTnZmNFNtMTAwOwogICAgICB1c2luZyBFcGlsb2d1ZVNjaGVkdWxlID0gY3V0bGFzczo6ZXBpbG9ndWU6OlB0ckFycmF5VG1hV2FycFNwZWNpYWxpemVkMlNtOwogICAgfTsKCiAgICB1c2luZyBDb2xsZWN0aXZlRXBpbG9ndWUxU00gPSB0eXBlbmFtZSBjdXRsYXNzOjplcGlsb2d1ZTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgICAgQXJjaFRhZywgT3BlcmF0b3JDbGFzcywKICAgICAgICB0eXBlbmFtZSBNTUExU01Db25maWc6Ok1tYVRpbGVTaGFwZSwgQ2x1c3RlclNoYXBlLAogICAgICAgIGN1dGU6OlNoYXBlPGN1dGU6Ol8xMjgsY3V0ZTo6XzY0PiwKICAgICAgICBFbGVtZW50QWNjdW11bGF0b3IsIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgICBFbGVtZW50QywgTGF5b3V0QyAqLCBBbGlnbm1lbnRDLAogICAgICAgIEVsZW1lbnRELCBMYXlvdXRDICosIEFsaWdubWVudEQsCiAgICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpFcGlsb2d1ZVNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgQ29sbGVjdGl2ZU1haW5sb29wMVNNID0gdHlwZW5hbWUgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6Q29sbGVjdGl2ZUJ1aWxkZXI8CiAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgIEVsZW1lbnRBLCBMYXlvdXRBICosIEFsaWdubWVudEEsCiAgICAgIEVsZW1lbnRCLCBMYXlvdXRCICosIEFsaWdubWVudEIsCiAgICAgIEVsZW1lbnRBY2N1bXVsYXRvciwKICAgICAgdHlwZW5hbWUgTU1BMVNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgY3V0bGFzczo6Z2VtbTo6Y29sbGVjdGl2ZTo6U3RhZ2VDb3VudEF1dG9DYXJ2ZW91dDwKICAgICAgICBzdGF0aWNfY2FzdDxpbnQ+KHNpemVvZih0eXBlbmFtZSBDb2xsZWN0aXZlRXBpbG9ndWUxU006OlNoYXJlZFN0b3JhZ2UpKT4sCiAgICAgIHR5cGVuYW1lIE1NQTFTTUNvbmZpZzo6S2VybmVsU2NoZWR1bGUKICAgID46OkNvbGxlY3RpdmVPcDsKCiAgICB1c2luZyBHZW1tS2VybmVsMVNNID0gY3V0bGFzczo6Z2VtbTo6a2VybmVsOjpHZW1tVW5pdmVyc2FsPAogICAgICAgIFByb2JsZW1TaGFwZSwKICAgICAgICBDb2xsZWN0aXZlTWFpbmxvb3AxU00sCiAgICAgICAgQ29sbGVjdGl2ZUVwaWxvZ3VlMVNNCiAgICA+OwogICAgdXNpbmcgR2VtbTFTTSA9IGN1dGxhc3M6OmdlbW06OmRldmljZTo6R2VtbVVuaXZlcnNhbEFkYXB0ZXI8R2VtbUtlcm5lbDFTTT47CgogICAgdXNpbmcgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNID0gdHlwZW5hbWUgY3V0bGFzczo6ZXBpbG9ndWU6OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICAgIEFyY2hUYWcsIE9wZXJhdG9yQ2xhc3MsCiAgICAgICAgdHlwZW5hbWUgTU1BMlNNQ29uZmlnOjpNbWFUaWxlU2hhcGUsIENsdXN0ZXJTaGFwZSwKICAgICAgICBjdXRlOjpTaGFwZTxjdXRlOjpfMTI4LGN1dGU6Ol82ND4sCiAgICAgICAgRWxlbWVudEFjY3VtdWxhdG9yLCBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgICAgRWxlbWVudEMsIExheW91dEMgKiwgQWxpZ25tZW50QywKICAgICAgICBFbGVtZW50RCwgTGF5b3V0QyAqLCBBbGlnbm1lbnRELAogICAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6RXBpbG9ndWVTY2hlZHVsZQogICAgPjo6Q29sbGVjdGl2ZU9wOwoKICAgIHVzaW5nIENvbGxlY3RpdmVNYWlubG9vcDJTTSA9IHR5cGVuYW1lIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OkNvbGxlY3RpdmVCdWlsZGVyPAogICAgICBBcmNoVGFnLCBPcGVyYXRvckNsYXNzLAogICAgICBFbGVtZW50QSwgTGF5b3V0QSAqLCBBbGlnbm1lbnRBLAogICAgICBFbGVtZW50QiwgTGF5b3V0QiAqLCBBbGlnbm1lbnRCLAogICAgICBFbGVtZW50QWNjdW11bGF0b3IsCiAgICAgIHR5cGVuYW1lIE1NQTJTTUNvbmZpZzo6TW1hVGlsZVNoYXBlLCBDbHVzdGVyU2hhcGUsCiAgICAgIGN1dGxhc3M6OmdlbW06OmNvbGxlY3RpdmU6OlN0YWdlQ291bnRBdXRvQ2FydmVvdXQ8CiAgICAgICAgc3RhdGljX2Nhc3Q8aW50PihzaXplb2YodHlwZW5hbWUgQ29sbGVjdGl2ZUVwaWxvZ3VlMlNNOjpTaGFyZWRTdG9yYWdlKSk+LAogICAgICB0eXBlbmFtZSBNTUEyU01Db25maWc6Oktlcm5lbFNjaGVkdWxlCiAgICA+OjpDb2xsZWN0aXZlT3A7CgogICAgdXNpbmcgR2VtbUtlcm5lbDJTTSA9IGN1dGxhc3M6OmdlbW06Omtlcm5lbDo6R2VtbVVuaXZlcnNhbDwKICAgICAgICBQcm9ibGVtU2hhcGUsCiAgICAgICAgQ29sbGVjdGl2ZU1haW5sb29wMlNNLAogICAgICAgIENvbGxlY3RpdmVFcGlsb2d1ZTJTTQogICAgPjsKICAgIHVzaW5nIEdlbW0yU00gPSBjdXRsYXNzOjpnZW1tOjpkZXZpY2U6OkdlbW1Vbml2ZXJzYWxBZGFwdGVyPEdlbW1LZXJuZWwyU00+OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IFBpbm5lZEhvc3RCdWZmZXIgewogICAgICBUKiBwdHIgPSBudWxscHRyOwogICAgICBzaXplX3QgY291bnQgPSAwOwoKICAgICAgUGlubmVkSG9zdEJ1ZmZlcigpID0gZGVmYXVsdDsKICAgICAgZXhwbGljaXQgUGlubmVkSG9zdEJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYUhvc3RBbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCksIGN1ZGFIb3N0QWxsb2NQb3J0YWJsZSkpOwogICAgICB9CgogICAgICB2b2lkIHJlbGVhc2UoKSB7CiAgICAgICAgaWYgKHB0cikgewogICAgICAgICAgY3VkYUZyZWVIb3N0KHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgflBpbm5lZEhvc3RCdWZmZXIoKSB7IHJlbGVhc2UoKTsgfQoKICAgICAgVCogZGF0YSgpIGNvbnN0IHsgcmV0dXJuIHB0cjsgfQogICAgICBUJiBvcGVyYXRvcltdKHNpemVfdCBpZHgpIHsgcmV0dXJuIHB0cltpZHhdOyB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBUPgogICAgc3RydWN0IERldmljZUJ1ZmZlciB7CiAgICAgIFQqIHB0ciA9IG51bGxwdHI7CiAgICAgIHNpemVfdCBjb3VudCA9IDA7CgogICAgICBEZXZpY2VCdWZmZXIoKSA9IGRlZmF1bHQ7CiAgICAgIGV4cGxpY2l0IERldmljZUJ1ZmZlcihzaXplX3QgbikgeyBhbGxvY2F0ZShuKTsgfQoKICAgICAgdm9pZCBhbGxvY2F0ZShzaXplX3QgbikgewogICAgICAgIGlmIChwdHIgJiYgY291bnQgPj0gbikgewogICAgICAgICAgcmV0dXJuOwogICAgICAgIH0KICAgICAgICByZWxlYXNlKCk7CiAgICAgICAgY291bnQgPSBuOwogICAgICAgIENVREFfQ0hFQ0soY3VkYU1hbGxvYyhyZWludGVycHJldF9jYXN0PHZvaWQqKj4oJnB0ciksIG4gKiBzaXplb2YoVCkpKTsKICAgICAgfQoKICAgICAgdm9pZCByZWxlYXNlKCkgewogICAgICAgIGlmIChwdHIpIHsKICAgICAgICAgIGN1ZGFGcmVlKHB0cik7CiAgICAgICAgICBwdHIgPSBudWxscHRyOwogICAgICAgICAgY291bnQgPSAwOwogICAgICAgIH0KICAgICAgfQoKICAgICAgfkRldmljZUJ1ZmZlcigpIHsgcmVsZWFzZSgpOyB9CgogICAgICBUKiBnZXQoKSBjb25zdCB7IHJldHVybiBwdHI7IH0KCiAgICAgIHZvaWQgY29weV9mcm9tX2hvc3QoY29uc3QgVCogaG9zdCwgc2l6ZV90IG4sIGN1ZGFTdHJlYW1fdCBzdHJlYW0pIHsKICAgICAgICBDVURBX0NIRUNLKGN1ZGFNZW1jcHlBc3luYyhwdHIsIGhvc3QsIG4gKiBzaXplb2YoVCksIGN1ZGFNZW1jcHlIb3N0VG9EZXZpY2UsIHN0cmVhbSkpOwogICAgICB9CiAgICB9OwoKICAgIHRlbXBsYXRlIDx0eXBlbmFtZSBHZW1tPgogICAgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4gcnVuX2dyb3VwZWRfaW1wbCgKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQiwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgQywKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkIpIHsKCiAgICAgIFRPUkNIX0NIRUNLKEEuc2l6ZSgpID09IEIuc2l6ZSgpLCAiQS9CIHNpemUgbWlzbWF0Y2giKTsKICAgICAgVE9SQ0hfQ0hFQ0soQS5zaXplKCkgPT0gQy5zaXplKCksICJBL0Mgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkEuc2l6ZSgpLCAiQS9TRkEgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBUT1JDSF9DSEVDSyhBLnNpemUoKSA9PSBTRkIuc2l6ZSgpLCAiQS9TRkIgc2l6ZSBtaXNtYXRjaCIpOwogICAgICBjb25zdCBpbnQzMl90IGdyb3VwcyA9IHN0YXRpY19jYXN0PGludDMyX3Q+KEEuc2l6ZSgpKTsKICAgICAgVE9SQ0hfQ0hFQ0soZ3JvdXBzID4gMCwgIk5vIGdyb3VwcyIpOwoKICAgICAgaW50IGRldmljZV9pZCA9IEFbMF0uZ2V0X2RldmljZSgpOwogICAgICBjMTA6OmN1ZGE6OkNVREFHdWFyZCBkZXZpY2VfZ3VhcmQoZGV2aWNlX2lkKTsKCiAgICAgIHVzaW5nIFN0cmlkZUEgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUE7CiAgICAgIHVzaW5nIFN0cmlkZUIgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUI7CiAgICAgIHVzaW5nIFN0cmlkZUMgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUM7CiAgICAgIHVzaW5nIFN0cmlkZUQgPSB0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpJbnRlcm5hbFN0cmlkZUQ7CiAgICAgIHVzaW5nIExheW91dFNGQSA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkE7CiAgICAgIHVzaW5nIExheW91dFNGQiA9IHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkNvbGxlY3RpdmVNYWlubG9vcDo6SW50ZXJuYWxMYXlvdXRTRkI7CiAgICAgIHVzaW5nIFNtMXh4QmxrU2NhbGVkQ29uZmlnID0gdHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6Q29sbGVjdGl2ZU1haW5sb29wOjpTbTF4eEJsa1NjYWxlZENvbmZpZzsKCiAgICAgIHN0YXRpYyBzaXplX3QgY2FwYWNpdHkgPSAwOwogICAgICBzdGF0aWMgaW50IGNhY2hlZF9kZXZpY2UgPSAtMTsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEEgY29uc3QqPiBwdHJfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QiBjb25zdCo+IHB0cl9CX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4gcHRyX0NfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RXBpbG9ndWVPdXRwdXRPcDo6RWxlbWVudE91dHB1dCo+IHB0cl9EX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8dHlwZW5hbWUgR2VtbTo6R2VtbUtlcm5lbDo6RWxlbWVudFNGIGNvbnN0Kj4gcHRyX1NGQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVBPiBzdHJpZGVfQV9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVCPiBzdHJpZGVfQl9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVDPiBzdHJpZGVfQ19ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxTdHJpZGVEPiBzdHJpZGVfRF9ob3N0OwogICAgICBzdGF0aWMgUGlubmVkSG9zdEJ1ZmZlcjxMYXlvdXRTRkE+IGxheW91dF9TRkFfaG9zdDsKICAgICAgc3RhdGljIFBpbm5lZEhvc3RCdWZmZXI8TGF5b3V0U0ZCPiBsYXlvdXRfU0ZCX2hvc3Q7CiAgICAgIHN0YXRpYyBQaW5uZWRIb3N0QnVmZmVyPHR5cGVuYW1lIFByb2JsZW1TaGFwZTo6VW5kZXJseWluZ1Byb2JsZW1TaGFwZT4gcHJvYmxlbV9zaXplc19ob3N0OwoKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBQcm9ibGVtU2hhcGU6OlVuZGVybHlpbmdQcm9ibGVtU2hhcGU+IHByb2JsZW1fc2l6ZXNfZGV2aWNlOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkVsZW1lbnRBIGNvbnN0Kj4gcHRyX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPiBwdHJfQjsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpHZW1tS2VybmVsOjpFbGVtZW50U0YgY29uc3QqPiBwdHJfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+IHB0cl9TRkI7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEMgY29uc3QqPiBwdHJfQzsKICAgICAgc3RhdGljIERldmljZUJ1ZmZlcjx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4gcHRyX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQT4gc3RyaWRlX0E7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQj4gc3RyaWRlX0I7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlQz4gc3RyaWRlX0M7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8U3RyaWRlRD4gc3RyaWRlX0Q7CiAgICAgIHN0YXRpYyBEZXZpY2VCdWZmZXI8TGF5b3V0U0ZBPiBsYXlvdXRfU0ZBOwogICAgICBzdGF0aWMgRGV2aWNlQnVmZmVyPExheW91dFNGQj4gbGF5b3V0X1NGQjsKICAgICAgc3RhdGljIHNpemVfdCB3b3Jrc3BhY2Vfc2l6ZV9jYWNoZWQgPSAwOwogICAgICBzdGF0aWMgdG9yY2g6OlRlbnNvciB3b3Jrc3BhY2VfdGVuc29yOwoKICAgICAgaWYgKGNhY2hlZF9kZXZpY2UgIT0gZGV2aWNlX2lkIHx8IGNhcGFjaXR5IDwgc3RhdGljX2Nhc3Q8c2l6ZV90Pihncm91cHMpKSB7CiAgICAgICAgY2FjaGVkX2RldmljZSA9IGRldmljZV9pZDsKICAgICAgICBjYXBhY2l0eSA9IHN0YXRpY19jYXN0PHNpemVfdD4oZ3JvdXBzKTsKICAgICAgICBwdHJfQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0JfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfRF9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQV9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQl9ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0FfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CX2hvc3QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQ19ob3N0LmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0RfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkFfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkJfaG9zdC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHByb2JsZW1fc2l6ZXNfaG9zdC5hbGxvY2F0ZShncm91cHMpOwoKICAgICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9BLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0IuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBwdHJfU0ZBLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX1NGQi5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHB0cl9DLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgcHRyX0QuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfQS5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIHN0cmlkZV9CLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgICAgc3RyaWRlX0MuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBzdHJpZGVfRC5hbGxvY2F0ZShncm91cHMpOwogICAgICAgIGxheW91dF9TRkEuYWxsb2NhdGUoZ3JvdXBzKTsKICAgICAgICBsYXlvdXRfU0ZCLmFsbG9jYXRlKGdyb3Vwcyk7CiAgICAgIH0KCiAgICAgIGZvciAoaW50MzJfdCBpID0gMDsgaSA8IGdyb3VwczsgKytpKSB7CiAgICAgICAgVE9SQ0hfQ0hFQ0soQVtpXS5pc19jdWRhKCksICJBIG11c3QgYmUgQ1VEQSIpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uaXNfY3VkYSgpLCAiQiBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLmlzX2N1ZGEoKSwgIkMgbXVzdCBiZSBDVURBIik7CiAgICAgICAgVE9SQ0hfQ0hFQ0soU0ZBW2ldLmlzX2N1ZGEoKSwgIlNGQSBtdXN0IGJlIENVREEiKTsKICAgICAgICBUT1JDSF9DSEVDSyhTRkJbaV0uaXNfY3VkYSgpLCAiU0ZCIG11c3QgYmUgQ1VEQSIpOwoKICAgICAgICBpbnQzMl90IG0gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMCkpOwogICAgICAgIGludDMyX3Qga19wYWNrZWQgPSBzdGF0aWNfY2FzdDxpbnQzMl90PihBW2ldLnNpemUoMSkpOwogICAgICAgIGludDMyX3QgayA9IGtfcGFja2VkICogMjsKICAgICAgICBpbnQzMl90IG4gPSBzdGF0aWNfY2FzdDxpbnQzMl90PihCW2ldLnNpemUoMCkpOwogICAgICAgIFRPUkNIX0NIRUNLKEJbaV0uc2l6ZSgxKSAqIDIgPT0gaywgIksgbWlzbWF0Y2giKTsKICAgICAgICBUT1JDSF9DSEVDSyhDW2ldLnNpemUoMCkgPT0gbSAmJiBDW2ldLnNpemUoMSkgPT0gbiwgIkMgc2hhcGUgbWlzbWF0Y2giKTsKCiAgICAgICAgcHJvYmxlbV9zaXplc19ob3N0W2ldID0gY3V0ZTo6bWFrZV9zaGFwZShtLCBuLCBrKTsKICAgICAgICBzdHJpZGVfQV9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlQXt9LCB7bSwgaywgMX0pOwogICAgICAgIHN0cmlkZV9CX2hvc3RbaV0gPSBjdXRsYXNzOjptYWtlX2N1dGVfcGFja2VkX3N0cmlkZShTdHJpZGVCe30sIHtuLCBrLCAxfSk7CiAgICAgICAgc3RyaWRlX0NfaG9zdFtpXSA9IGN1dGxhc3M6Om1ha2VfY3V0ZV9wYWNrZWRfc3RyaWRlKFN0cmlkZUN7fSwge20sIG4sIDF9KTsKICAgICAgICBzdHJpZGVfRF9ob3N0W2ldID0gY3V0bGFzczo6bWFrZV9jdXRlX3BhY2tlZF9zdHJpZGUoU3RyaWRlRHt9LCB7bSwgbiwgMX0pOwogICAgICAgIGxheW91dF9TRkFfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZBKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwogICAgICAgIGxheW91dF9TRkJfaG9zdFtpXSA9IFNtMXh4QmxrU2NhbGVkQ29uZmlnOjp0aWxlX2F0b21fdG9fc2hhcGVfU0ZCKGN1dGU6Om1ha2Vfc2hhcGUobSwgbiwgaywgMSkpOwoKICAgICAgICBwdHJfQV9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFbGVtZW50QSBjb25zdCo+KEFbaV0uZGF0YV9wdHIoKSk7CiAgICAgICAgcHRyX0JfaG9zdFtpXSA9IHJlaW50ZXJwcmV0X2Nhc3Q8dHlwZW5hbWUgR2VtbTo6RWxlbWVudEIgY29uc3QqPihCW2ldLmRhdGFfcHRyKCkpOwogICAgICAgIHB0cl9DX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkVsZW1lbnRDIGNvbnN0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfRF9ob3N0W2ldID0gcmVpbnRlcnByZXRfY2FzdDx0eXBlbmFtZSBHZW1tOjpFcGlsb2d1ZU91dHB1dE9wOjpFbGVtZW50T3V0cHV0Kj4oQ1tpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZBX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQVtpXS5kYXRhX3B0cigpKTsKICAgICAgICBwdHJfU0ZCX2hvc3RbaV0gPSByZWludGVycHJldF9jYXN0PHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OkVsZW1lbnRTRiBjb25zdCo+KFNGQltpXS5kYXRhX3B0cigpKTsKICAgICAgfQoKICAgICAgYXV0byBzdHJlYW1fb2JqID0gYzEwOjpjdWRhOjpnZXRDdXJyZW50Q1VEQVN0cmVhbShkZXZpY2VfaWQpOwogICAgICBjMTA6OmN1ZGE6OkNVREFTdHJlYW1HdWFyZCBzdHJlYW1fZ3VhcmQoc3RyZWFtX29iaik7CiAgICAgIGN1ZGFTdHJlYW1fdCBzdHJlYW0gPSBzdHJlYW1fb2JqLnN0cmVhbSgpOwogICAgICBwcm9ibGVtX3NpemVzX2RldmljZS5jb3B5X2Zyb21faG9zdChwcm9ibGVtX3NpemVzX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9BLmNvcHlfZnJvbV9ob3N0KHB0cl9BX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9CLmNvcHlfZnJvbV9ob3N0KHB0cl9CX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIHB0cl9TRkEuY29weV9mcm9tX2hvc3QocHRyX1NGQV9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwogICAgICBwdHJfU0ZCLmNvcHlfZnJvbV9ob3N0KHB0cl9TRkJfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0MuY29weV9mcm9tX2hvc3QocHRyX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgcHRyX0QuY29weV9mcm9tX2hvc3QocHRyX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0EuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0FfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0IuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0JfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0MuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0NfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgc3RyaWRlX0QuY29weV9mcm9tX2hvc3Qoc3RyaWRlX0RfaG9zdC5kYXRhKCksIGdyb3Vwcywgc3RyZWFtKTsKICAgICAgbGF5b3V0X1NGQS5jb3B5X2Zyb21faG9zdChsYXlvdXRfU0ZBX2hvc3QuZGF0YSgpLCBncm91cHMsIHN0cmVhbSk7CiAgICAgIGxheW91dF9TRkIuY29weV9mcm9tX2hvc3QobGF5b3V0X1NGQl9ob3N0LmRhdGEoKSwgZ3JvdXBzLCBzdHJlYW0pOwoKICAgICAgY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvIGh3X2luZm87CiAgICAgIGh3X2luZm8uZGV2aWNlX2lkID0gZGV2aWNlX2lkOwogICAgICBod19pbmZvLnNtX2NvdW50ID0gY3V0bGFzczo6S2VybmVsSGFyZHdhcmVJbmZvOjpxdWVyeV9kZXZpY2VfbXVsdGlwcm9jZXNzb3JfY291bnQoaHdfaW5mby5kZXZpY2VfaWQpOwogICAgICBpZiBjb25zdGV4cHIgKHN0ZDo6aXNfc2FtZV92PEdlbW0sIEdlbW0xU00+KSB7CiAgICAgICAgaHdfaW5mby5jbHVzdGVyX3NoYXBlID0gZGltMygxLCAxLCAxKTsKICAgICAgICBod19pbmZvLmNsdXN0ZXJfc2hhcGVfZmFsbGJhY2sgPSBkaW0zKDEsIDEsIDEpOwogICAgICB9IGVsc2UgewogICAgICAgIGh3X2luZm8uY2x1c3Rlcl9zaGFwZSA9IGRpbTMoMiwgMSwgMSk7CiAgICAgICAgaHdfaW5mby5jbHVzdGVyX3NoYXBlX2ZhbGxiYWNrID0gZGltMygyLCAxLCAxKTsKICAgICAgfQoKICAgICAgdHlwZW5hbWUgR2VtbTo6QXJndW1lbnRzIGFyZ3VtZW50czsKICAgICAgZGVjbHR5cGUoYXJndW1lbnRzLmVwaWxvZ3VlLnRocmVhZCkgZnVzaW9uX2FyZ3M7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0ciA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuYWxwaGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMSk7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGEgPSBFbGVtZW50QWNjdW11bGF0b3IoMCk7CiAgICAgIGZ1c2lvbl9hcmdzLmFscGhhX3B0cl9hcnJheSA9IG51bGxwdHI7CiAgICAgIGZ1c2lvbl9hcmdzLmJldGFfcHRyX2FycmF5ID0gbnVsbHB0cjsKICAgICAgZnVzaW9uX2FyZ3MuZEFscGhhID0ge2N1dGU6Ol8we30sIGN1dGU6Ol8we30sIDB9OwogICAgICBmdXNpb25fYXJncy5kQmV0YSA9IHtjdXRlOjpfMHt9LCBjdXRlOjpfMHt9LCAwfTsKCiAgICAgIHR5cGVuYW1lIEdlbW06OkdlbW1LZXJuZWw6OlRpbGVTY2hlZHVsZXJBcmd1bWVudHMgc2NoZWR1bGVyOwogICAgICBzY2hlZHVsZXIubWF4X3N3aXp6bGVfc2l6ZSA9IDg7CiAgICAgIHNjaGVkdWxlci5yYXN0ZXJfb3JkZXIgPSBjdXRsYXNzOjpnZW1tOjprZXJuZWw6OmRldGFpbDo6UmFzdGVyT3JkZXJPcHRpb25zOjpIZXVyaXN0aWM7CgogICAgICBhcmd1bWVudHMgPSB0eXBlbmFtZSBHZW1tOjpBcmd1bWVudHMgewogICAgICAgIGN1dGxhc3M6OmdlbW06OkdlbW1Vbml2ZXJzYWxNb2RlOjprR3JvdXBlZCwKICAgICAgICB7Z3JvdXBzLCBwcm9ibGVtX3NpemVzX2RldmljZS5nZXQoKSwgbnVsbHB0cn0sCiAgICAgICAge3B0cl9BLmdldCgpLCBzdHJpZGVfQS5nZXQoKSwgcHRyX0IuZ2V0KCksIHN0cmlkZV9CLmdldCgpLAogICAgICAgICBwdHJfU0ZBLmdldCgpLCBsYXlvdXRfU0ZBLmdldCgpLCBwdHJfU0ZCLmdldCgpLCBsYXlvdXRfU0ZCLmdldCgpfSwKICAgICAgICB7ZnVzaW9uX2FyZ3MsIHB0cl9DLmdldCgpLCBzdHJpZGVfQy5nZXQoKSwgcHRyX0QuZ2V0KCksIHN0cmlkZV9ELmdldCgpfSwKICAgICAgICBod19pbmZvLCBzY2hlZHVsZXIKICAgICAgfTsKCiAgICAgIEdlbW0gZ2VtbTsKICAgICAgc2l6ZV90IHdvcmtzcGFjZV9zaXplID0gR2VtbTo6Z2V0X3dvcmtzcGFjZV9zaXplKGFyZ3VtZW50cyk7CiAgICAgIGlmICghd29ya3NwYWNlX3RlbnNvci5kZWZpbmVkKCkgfHwgY2FjaGVkX2RldmljZSAhPSBkZXZpY2VfaWQgfHwgd29ya3NwYWNlX3NpemVfY2FjaGVkIDwgd29ya3NwYWNlX3NpemUpIHsKICAgICAgICBhdXRvIG9wdHMgPSB0b3JjaDo6VGVuc29yT3B0aW9ucygpLmRldmljZSh0b3JjaDo6a0NVREEsIGRldmljZV9pZCkuZHR5cGUodG9yY2g6OmtVSW50OCk7CiAgICAgICAgd29ya3NwYWNlX3RlbnNvciA9IHRvcmNoOjplbXB0eSh7c3RhdGljX2Nhc3Q8bG9uZz4od29ya3NwYWNlX3NpemUpfSwgb3B0cyk7CiAgICAgICAgd29ya3NwYWNlX3NpemVfY2FjaGVkID0gd29ya3NwYWNlX3NpemU7CiAgICAgIH0KICAgICAgdm9pZCogd29ya3NwYWNlX3B0ciA9IHdvcmtzcGFjZV9zaXplID8gd29ya3NwYWNlX3RlbnNvci5kYXRhX3B0cigpIDogbnVsbHB0cjsKCiAgICAgIENVVExBU1NfQ0hFQ0soZ2VtbS5jYW5faW1wbGVtZW50KGFyZ3VtZW50cykpOwogICAgICBDVVRMQVNTX0NIRUNLKGdlbW0uaW5pdGlhbGl6ZShhcmd1bWVudHMsIHdvcmtzcGFjZV9wdHIsIHN0cmVhbSkpOwogICAgICBhdXRvIHJ1bl9zdGF0dXMgPSBnZW1tLnJ1bihzdHJlYW0sIG51bGxwdHIsIHRydWUpOwogICAgICBpZiAocnVuX3N0YXR1cyAhPSBjdXRsYXNzOjpTdGF0dXM6OmtTdWNjZXNzKSB7CiAgICAgICAgcnVuX3N0YXR1cyA9IGdlbW0ucnVuKHN0cmVhbSk7CiAgICAgIH0KICAgICAgQ1VUTEFTU19DSEVDSyhydW5fc3RhdHVzKTsKCiAgICAgIHJldHVybiBDOwogICAgfQoKICAgIHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+IGdyb3VwZWRfZ2VtbV8xc20oCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEIsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIEMsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQSwKICAgICAgICBjb25zdCBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiYgU0ZCKSB7CiAgICAgIHJldHVybiBydW5fZ3JvdXBlZF9pbXBsPEdlbW0xU00+KEEsIEIsIEMsIFNGQSwgU0ZCKTsKICAgIH0KCiAgICBzdGQ6OnZlY3Rvcjx0b3JjaDo6VGVuc29yPiBncm91cGVkX2dlbW1fMnNtKAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBBLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBCLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBDLAogICAgICAgIGNvbnN0IHN0ZDo6dmVjdG9yPHRvcmNoOjpUZW5zb3I+JiBTRkEsCiAgICAgICAgY29uc3Qgc3RkOjp2ZWN0b3I8dG9yY2g6OlRlbnNvcj4mIFNGQikgewogICAgICByZXR1cm4gcnVuX2dyb3VwZWRfaW1wbDxHZW1tMlNNPihBLCBCLCBDLCBTRkEsIFNGQik7CiAgICB9CiAgICAK"
_ext_mod = None
_ext_fail = False
_force_ext_only = False
+ _disable_ext = False
def _dec(b64: str) -> str:
⋯ 149 unchanged lines
sfasfb_reordered_tensors: list[tuple[torch.Tensor, torch.Tensor]],
problem_sizes: list[tuple[int, int, int, int]],
):
+ if _disable_ext:
+ return None
ext = _load_ext()
if ext is None:
return None
- a_list = []
- b_list = []
- c_list = []
- sfa_list = []
- sfb_list = []
- max_m = 0
- for (a_ref, b_ref, c_ref), (sfa_reordered, sfb_reordered), (_, _, _, l) in zip(
+ items = []
+ for (a_ref, b_ref, c_ref), (sfa_reordered, sfb_reordered), (m, _, k, l) in zip(
abc_tensors, sfasfb_reordered_tensors, problem_sizes
):
if l != 1:
⋯ 1 unchanged lines
a2 = a_ref[:, :, 0]
b2 = b_ref[:, :, 0]
c2 = c_ref[:, :, 0]
- a_list.append(a2)
- b_list.append(b2)
- c_list.append(c2)
- sfa_list.append(sfa_reordered)
- sfb_list.append(sfb_reordered)
- max_m = max(max_m, a2.shape[0])
+ items.append((a2, b2, c2, sfa_reordered, sfb_reordered, m, k))
+ def _use_2sm(m: int, k: int) -> bool:
+ return m >= 256 and k >= 4096
+
+ def _gather(src):
+ return (
+ [x[0] for x in src],
+ [x[1] for x in src],
+ [x[2] for x in src],
+ [x[3] for x in src],
+ [x[4] for x in src],
+ )
+
+ small = [it for it in items if not _use_2sm(it[5], it[6])]
+ large = [it for it in items if _use_2sm(it[5], it[6])]
+
try:
- if max_m <= 128:
- ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
- else:
+ if large:
+ a_list, b_list, c_list, sfa_list, sfb_list = _gather(large)
ext.grouped_gemm_2sm(a_list, b_list, c_list, sfa_list, sfb_list)
+ if small:
+ a_list, b_list, c_list, sfa_list, sfb_list = _gather(small)
+ ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
except Exception:
try:
- ext.grouped_gemm_2sm(a_list, b_list, c_list, sfa_list, sfb_list)
+ a_list, b_list, c_list, sfa_list, sfb_list = _gather(items)
+ ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
except Exception:
- try:
- ext.grouped_gemm_1sm(a_list, b_list, c_list, sfa_list, sfb_list)
- except Exception:
- return None
+ return None
return [c_ref for (_, _, c_ref) in abc_tensors]
⋯ 518 unchanged lines
grouped_mm_v2 is not None
and block_scale is not None
and block_swizzle is not None
- and k_val >= 4096
and strategy != "skip_v2"
):
try:
⋯ 101 unchanged lines
swizzle_b,
offs,
offs_cpu,
- torch.bfloat16,
+ torch.float16,
)
+ if out is None:
+ out = _try_scaled_grouped_mm_v2(
+ grouped_mm_v2,
+ a,
+ b,
+ scale_a,
+ scale_recipe_a,
+ scale_b,
+ scale_recipe_b,
+ swizzle_a,
+ swizzle_b,
+ offs,
+ offs_cpu,
+ torch.bfloat16,
+ )
+ if out is not None:
+ out = out.to(torch.float16)
if out is not None:
- out = out.to(torch.float16)
_grouped_strategy_cache[strategy_key] = attempt
result_tensors = emit(out, grouped)
return result_tensors
⋯ 4 unchanged lines
grouped_mm = _get_grouped_mm()
strategy_v1 = _grouped_strategy_cache_v1.get(strategy_key)
- if grouped_mm is not None and k_val >= 4096 and strategy_v1 != "skip_v1":
+ if grouped_mm is not None and strategy_v1 != "skip_v1":
try:
grouped = _build_grouped_inputs(
abc_tensors, sfasfb_reordered_tensors, problem_sizes
scrolls · 133 diff lines total

Best evidence level for this revision: reported

JSON