from amaranth import * from amaranth.sim import Simulator from amaranth_cobs import * from cobs import cobs def ref_encode(data_i: bytes) -> bytes: return b"\0".join(cobs.encode(chunk) if chunk else b"" for chunk in data_i.split(b"$")) def rtl_encode(data_i: bytes, dollar=True) -> bytes: dut = Encoder() async def testbench_i(ctx): ctx.set(dut.i.valid, 1) for byte in data_i: if byte == ord("$") and dollar: ctx.set(dut.i.payload, {"end": 1}) else: ctx.set(dut.i.payload, {"data": byte}) await ctx.tick().until(dut.i.ready) if not dollar: ctx.set(dut.i.payload, {"end": 1}) await ctx.tick().until(dut.i.ready) ctx.set(dut.i.valid, 0) data_o = bytearray() async def testbench_o(ctx): ctx.set(dut.o.ready, 1) empty_for = 0 while empty_for < len(data_i) + 3: _, _, valid, payload = await ctx.tick().sample(dut.o.valid).sample(dut.o.payload) if valid: data_o.append(payload) empty_for = 0 else: empty_for += 1 ctx.set(dut.o.ready, 0) sim = Simulator(dut) sim.add_clock(1e-6) sim.add_testbench(testbench_i) sim.add_testbench(testbench_o) sim.run() return data_o def rtl_decode(data_i: bytes, dollar=True) -> bytes: dut = Decoder() async def testbench_i(ctx): ctx.set(dut.i.valid, 1) for byte in data_i: ctx.set(dut.i.payload, byte) await ctx.tick().until(dut.i.ready) ctx.set(dut.i.valid, 0) data_o = [] async def testbench_o(ctx): pending = bytearray() ctx.set(dut.o.ready, 1) for _ in range(data_i.count(b"\0")): while True: _, _, valid, payload = await ctx.tick().sample(dut.o.valid).sample(dut.o.payload) if valid: if payload.end: data_o.append(bytes(pending)) pending.clear() break else: pending.append(payload.data) ctx.set(dut.o.ready, 0) sim = Simulator(dut) sim.add_clock(1e-6) sim.add_testbench(testbench_i) sim.add_testbench(testbench_o) sim.run() return data_o def cases() -> list[bytes]: return [ b"\0$", b"\0\0$", b"\0A\0$", b"AB\0C$", b"ABCD$", b"A\0\0\0$", b"A"*254+b"$", b"\0"+b"A"*254+b"$", b"A"*500+b"$", b"foo$bar$", ] def test_encoder_simple(): for case in cases(): assert rtl_encode(case) == ref_encode(case) def test_encoder_vmlinuz(): assert rtl_encode(vmlinuz, dollar=False) == cobs.encode(vmlinuz) + b"\0" def test_decoder_simple(): for case in cases(): print(ref_encode(case)) assert rtl_decode(ref_encode(case)) == case.split(b"$")[:-1] def test_decoder_vmlinuz(): assert rtl_decode(cobs.encode(vmlinuz) + b"\0") == [vmlinuz] vmlinuz = bytes.fromhex(""" 4d5a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 000000000000cd238281400000005045000064860400000000000000000001000000a00006020b02021400d0b70000700600 00000000fd5cb7000050000000000000000000000010000000020000000000000300000000000000000000000090be000010 00001a42b8000a00000100000000000000000000000000000000000000000000000000000000000000000000000006000000 00000000000000000000000000000000000000000000000000000000000000000032b800c005000000000000000000002e73 65747570000000300000001000000030000000100000000000000000000000000000400000422e636f6d7061740000100000 004000000010000000400000000000000000000000000000400000422e7465787400000000d0b7000050000000d0b7000050 0000000000000000000000000000200000602e64617461000000007006000020b800001200000020b8000000000000000000 00000000400000c0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff270100 207e0b000000ffff000055aaeb6a486472530f0200000000001000430001008000001000000000000000000000000000005c 000000000000ffffff7f0000200001157f00ff070000000000000000000000000000cc0200001e6ab4000000000000000000 000000010000000000d07e0360a9b6003cb1b7008cd88ec0fc8cd239c289e27416ba005af60611028074048b16240281c200 04730231d283e2fc7503bafcff8ed0660fb7e2fb1e68a302cb66813e784655aa5a5a7517bf8046b9035a6631c029f9c1e902 f366ab66e84512000066b8eb03000066e8fe000000f4ebfd3806ff027405a2ff02eb00669c0fa00fa8666083ec2c89d689e7 b90b00f366a566610fa90fa1071f669dcd00669c1e060fa00fa86660fc660fb7e48cc88ed88ec0678b7c244421ff740889e6 b90b00f366a583c42c66610fa90fa1669d66c3665666536683ec2c6689c36683f80a750c66b80d00000066e8e3ffffff6689 e066e8d01c000067c7442410070067c7442418010067c644241d0e67885c241c6631c96689e266b81000000066e850ffffff 66833ecc5700743966beffff000066a1cc576683c005660fb7c066ff16a047a8207416660fb716cc57660fb6c36683c42c66 5b665eff26a447664e74e6f390ebcd6683c42c665b665e66c366536689c367660fbe0384c0740a664366e84effffffebed66 5b66c3074e6f207365747570207369676e617475726520666f756e642e2e2e0a0066ba800000006631c0ff26a44766566653 66bba086010066be2000000066e8ddffffff66b86400000066ff16a0473cff7506664e7506eb1fa801741366e8beffffff66 b86000000066ff16a047eb04a802740a664b75c66683c8ffeb036631c0665b665e66c366556657665666536689c66631c08e e06683c8ff8ee866bf000200006467668b2f6689e86683ee01722567668d5801646766891f66e860ffffff66b81002000065 67668b006639c374da6631d8eb036631c066ba00020000646766892a665b665e665f665d66c3665666536683ec2c66bbff00 000066b82000000066e87fffffff6685c00f85f5006689e066e8291b000067c744241c01246631c96689e266b81500000066 e8bbfdffff66b82000000066e84affffff6685c00f85c00066e8e4feffff6689c666b82000000066e82effffff6685c00f85 e8f3caffff66c706ec5701000000678a5424486683e27f6631c038d37429660fb606b64738d075066683c8ffeb1967894424 1c6631c96689e266b81000000066e8b3caffffebe16683c458665b665e66c367660fb600e966ff665566576656665366a1f8 5766486631ff6683f8010f87f8008a1eb6476631c08ee066e89cf8ffff6689c566a1044666406683e0fe66a3044666a3e845 66be1401000066a10046662b0604466683f8070f8eb3006689f066e8fbf3ffff6685c00f85960067668d8600ffffff66e8f4 feffff6685c00f85810066ba1000000066b8c003000066e8b7feffffa801756b66ba0600000066b8ce03000066e8a1feffff a8017555660fb7c566ba0f00000066e88dfeffff84c0754166a1044666406683e0fe67668d5008668916044667893067c740 06000066ba4a04000064678b126789500266ba8404000064678a12660fb6d2664267895004664766466681fe800100000f85 3cff660fb6c366e855feffff6689f8665b665e665f665d66c38ed98ec18ee18ee98ed101dc0f00df31c931d231db31ed31ff 0f00d1ffe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 65722027666f7263657061652720746f20656e61626c6520617420796f7572206f776e207269736b210a006561726c797072 696e746b0073657269616c003078007474795300636f6e736f6c650075617274383235302c696f2c00756172742c696f2c00 65646400736b69706d627200736b6970006f6666006f6e0071756965740050726f62696e672045444420286564643d6f6666 20746f2064697361626c65292e2e2e20006f6b0a006561726c7920636f6e736f6c6520696e20736574757020636f64650a00 6465627567005741524e494e473a20416e6369656e7420626f6f746c6f616465722c20736f6d652066756e6374696f6e616c 697479206d6179206265206c696d69746564210a00556e61626c6520746f20626f6f74202d20706c65617365207573652061 206b65726e656c20617070726f70726961746520666f7220796f7572204350552e0a004132302067617465206e6f74207265 73706f6e64696e672c20756e61626c6520746f20626f6f742e2e2e0a005072657373203c454e5445523e20746f2073656520 766964656f206d6f64657320617661696c61626c652c203c53504143453e20746f20636f6e74696e75652c206f7220776169 74203330207365630a004d6f64653a205265736f6c7574696f6e3a2020547970653a20002564782564002563202530335820 25346478252d377320252d367300456e746572206120766964656f206d6f6465206f7220227363616e2220746f207363616e 20666f72206164646974696f6e616c206d6f6465733a200008200800556e646566696e656420766964656f206d6f6465206e 756d6265723a2025780a004347412f4d44412f484743004547410056474100564553410042494f5300000000000000000000 00000000000000000000000000006670750000056d737200000670616500000863783800000f636d6f760000186678737200 001973736500001a7373653200011d6c6d0003146e6f706c00151f0000000000000000000000000000000000000000000000 0000000000000000618100070000002000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8030000 f802000000000000000000000000000000000000ffff0000009bcf00ffff00000093cf006700001000890000000000000000 0000491d0000111f00001f1f00001f1f00001f1f00001f1f0000111f00001f1f00001f1f00001f1f00001f1f0000df1e0000 3b1f0000961e00001f1f00001f1f0000d31d00001f1f0000171f00001f1f00001f1f0000031f000030313233343536373839 4142434445460000000000000000362e31322e32352d616d643634202864656269616e2d6b65726e656c406c697374732e64 656269616e2e6f72672920233120534d5020505245454d50545f44594e414d49432044656269616e20362e31322e32352d31 2028323032352d30342d32352900eb320000aa320000f33200002f330000fb32000003330000173300000100000002000000 070000006d430000794300007d4300002046000028460000404600007d430000143200003f2f000000000000000000000000 000000000000814300003e3300006035000000000000000000000000000000020002864300002d3800003538000000000000 0000000001000000000180000000000000000000005a0000005a000000000000000000000000000000000000000000000000 0000000f500019000000000f500019000000010f50002b0000000000000000000000000f500019000000010f500032000000 020f50002b000000030f50001c000000050f50001e000000060f500022000000070f50003c00000055aa5a5a000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 c2ffa6d3a2ff779c162b389071c9768dbe2ab48afc7f2d34f7af7e988a83885b99380aa91f8450ba9476a5bdca621b81edfc 5786b9b1d842b457c541a5a442acc21f757eaa554b68312ca7ab0adc5d9589d9387072f08683ff0f8205f5567997e358c551 e4d68cffffc980d8f3f6bad5ffbfecc38a7bf0dda78581eb318c29058754ac6306088dedc91134f66a67800062c037a68154 8a0641c90f0769d62cb4960675352359c5bdf4a0b0bb2c5af86f4f905f8948771ff6c1048f1b02b470a0e32276e524b4c895 82ffbf2e8364ae2514c9f9f2058a623e03d9d60cc25dbf14d4cec31819d06ce15f4729612ee24b5b7900a78df9af875409b3 fff5ff6b0ef6f27f3da319fca066d72798ffbf5d9eaa45ae042ffccfff7f085b874f6232adaeada6e011c57c2da9fcd74598 dbf8ff754b39c8b46690c34eed366d056f04752423fcffaf54b18d52283462ae5481ffe9cdcf7f60f0b9544b52f6b62e89d3 e579f93f709dd2db677757aeb8d5565e6a7eb1db460c42f2da38e85e2dbf41708d29fb7f4f854efc2809f37f75abb99f8e50 ec4359feab3f987b4f9307b3603e49a195ebe50ff32a46e6146a1453724ff35f338181ffd52bcc3d6d560d8fbe2be3ffab63 bfa4d65642f09adeb5397a44fac9b919e26cfd8691163f33946f5e912a2ca031439f365f7599011cfce4e85670e8103fa16a 7ee10782ffda1622f30fa9d2bd26b4d510b51f5c96e793cf944a51d7aef336e2bf105b581b91873922714561afc62528eb99 8731fa51c4bcce26a4b0b6354899e6ffff288f4f031125071760a97a2399e5a428e651460bafe8f651d3ea70dced59b1f5d1 941d98e08693fffeee3d4577ddf3e43b74dea743adc3a2de06abe551245991cf00c64e617ef09b3d403ccf560f169450325a 64ddd6b074fa51ee6a9ded49c9faf0447fba437cd2ce1d783d20a17c483bb99111b00d7367a9d3a4c461468aa125d49a2b72 3d5d8b882fef462bb6cea496cfddcdba738479b0f536eb6e0ae62137c7dfb0a0f0b0dc36225d7f4ad33f2d040253b6ce7be1 23cc4f68ee1fdedb6ab3364efb8606b40e67744039369b75c3b1c8d5b00260ee69de9ba5e7d879e8a37ef97006fcaffebbe1 14b99a24fe54c36535dc277df2372e591277c1dc06c8694466ef9653746124308787e8c0478eaa93e46a88bf2ac1fc0103b3 22910ec21511d84d40ee99bddcb8030665aa1a9061de82abacd8ea22b17ba74a3bdb1e0a80392cc7210a8f41cedda5213276 f7109fc2c0d8a3afbd031cf9eb955c4bb2a2f8921204730f1deabf17c80849e51766c338ebedfb64a821df7da101bbc1641a d38b541d6eb610c83b4d2855eb04a80bb82d5bfa2880b90efa9d69a71a135c74370773a731d51dc857995ede054108e10e45 2d9ca0c0d25b5667951fff7f856a870a75e69f9d442f23bf773031773dd72d3aba4271feb4601773bfae3315a3493922396d f2374db00d6e6618c066fe28b706e65ff2552d2e3a3429853e812757e65729eedf0b9c7ff67f8358d533ca7f0db343ee2280 6c37e705dd6e24af15b3dbba88f96e9dea740bc03b3ae8528c60e03c25c3869841ec07213dca5d524caac045cd7db4d08a41 30fb5906f45b305a882b48fe60b399e9ecc7da2fce453dea062b42383b77f5f2ba2bde43062450b6800ec7112d31e5744f2d 58abee60972dde85521e3c68611ff86e5992a93d29343a3ded1f2ac4e63bd1d9e087bcd43e8946d63d93ad468e3dca71c80e b846fb79ef21d459218f79c0b33cdd0dd287127d299663461b6da98527bc602c90b579903e962c2dc57eba72b400c7bd5e6a 6316b06b8076533cf5d457ce6cfd6e45b81aea14b4704b25b21b197ae7afa3daa1c2ecc8c7bbdd4d9581f539ad5ba4a031ba 6b85c671c8badacbc274fe41bc6d0c90691ec57d7fd2df3d2d21c6e9595ef86002df58103e3d934b17a0932afde9650bfa34 47c7ec48b40780c7844eb54622091d81fd97bd6f5ed5eab22a25706892ec1a12d9d17df4338f337ef80084308a38776a0f6c c25205bdb84b437d0521867428e8f3112f01a54b9bb0bb4ee8dd06bd863ac35dece1a0f50fc7e6798da5aa3fabd7be0006f6 6fe8e18ed6f68e6c6529375d6ee0b37c62a4230970800651bcd2a6a1c1ab69c8026696670472f802699f30858f903ee4c044 68b346df0b227c29f75105e94d58024d573a8ad7eb8b3066cb3c0315ee871370f965dc67c9ab615fcc69aace42bc57aa2f50 f8ec46a82dca4c0198a37b403208f4e9921e0ffe83d1e5bc5640f8db02250f1841c0acb9436615f0daf5fedc50bdf6c374e6 f3e9bb1f56c0b865ad329dc1742d18a1c41ae6d0983b021654913262015eda33024ea65ad224c68d941843a33f5404e891d3 024c5e554e432688b034cfe0a06530e64c2f6fe254a725f2cc308a647de42bbc4e3305fc95d3bb8b57fbc45a044af2394bb9 02b2b2b4410dc307ba58fcbdc36c07e46ff4fb414eadbcdaf7482d6f4bb9d7f74bb3d223d11c6940a702da98f2b84b9721b4 04d5c5fd50487ae9c14d00cad2d55d54fa589f01f4642846a1dfa0da4537c5af9c255b31193233d89863ea5419b243fba439 a3427fd4ad7409b439aac841744e057d409aff7236dcf0ba0c97d69de66928f01e5947c68c6907f28136a4cf2d0a721eea3f dc246692518cb27fe1e8c0b3082f981ba5fc88cf77e268668b1914561ac108c30e60309dbf3ee9f53cb38c98bca17c8eeea9 6aee71bdd6284aef16970e5ba2bd93ada3b0347da37c58b467c63b3a242c3d2346af81f846377fc433622b3cfa8d8660f87e 23b536f7aeb9b5a9f55ac3d9bac42ff2a4048bea808c476e9233edc243354145263b8bd70f657438bb1f2725ea2a22a459e6 39a874a89a96862c83743b1fb811bcc445d2ec795ec7d1224638b214d779b45f6ea156c1961bf219e4169027c62b409fb672 24f2206e5576c8bf9a5f7bd0e84939dea4d29b3f9c28b9c5d6bb2d55213bda0dbcbdc56768eefcedc6fd18973b10ce651c0d 9e04f6179c772986e6f67a403b0bda599ab7d440c1a0a67307a48dc9c034481d3cda3bbf23ad1b2020dff97c4ae829340bac 849e66f988d263cad609945c46f0ab20a5473a1717e54aac20805c5a64f2af5512a898d9cab82522bc5e8fa475d764cbfc6f ef6e5ee355ba422b14f5e71857e65ec9c3aa92ad4149ee0545162255ca15a138d11a7009e86caeb4b649b32350795af7073f c1eb7abff4b1d4c4bb277c75c934e8482c38312394558f081926afcef9d869567de513439945798b71e2097d76cad3ebd26b 4e85f02c8f88f6b5a63a90deaa49f272d94e6f7c04e1ad903145684003097e5212f874ed283b1acd9439e396c16739324670 63da25508886f61aec9c44bc6b857b9834548da0406b9ccb4a40a10082bee7cb2e3c7731efcb4e208d39df8a2c73c25acf1d 9f0f9672f8838c8db45e920d4aeb3f4cc9307e18ada37cb895e9ca9828819fe72341bb0d6137218416b229784b73523398f2 0f5a526bd49a8d065351e004ed7cde529b74d0ad998dd2537e9d80612adb070b9e0929310bace82cf883e6bdb88a8e2a42fc 98018529bbcd091b29428525482f45ad7208c98c570de1d7b3016485305a5138ab0a7f4bf23864114ce7c4255a46b4c43926 be1e293f6e8047cf1198274c6ebdd36385109d87f746eba8466381af381054225ff349653c2b1e36bc442d595a37d940c5f4 253487b7feb3a5c8098712bd4085b9c7385cfa60017d117e6d36772186b8825a1985652949ab2d8b00bbc2921c994c1d3a5a 2747135eaa832185d2aaaf21434a377faec1c03ccfaf0dc8889270a43fe2b552d1f0728df5c64d37ea7a0fcb99e98c7606b4 f76083555e9dea31a319f4c4d8ea33cee7ac1d85be4657c09667e9a1bbd8a0438c0eb679e86dccbc6f8342463ca2d0356ff1 63073607ad201a5f5766693d089ca3bd9bf75db1097b2f8c239d5bf2ea5ec1ff0ed15d30eadbc47521fbb7d8979e69e16d87 a296407e2bfe74a2bdd46c04f380d2dff244e60527453bd80b3e0abeaa162db0723e5981b768312247d97b2075038f63ae15 96c61cda98ce6db8b855813179ecf2c15cf09646cc075bf61181ba5806e91b366aae3abc6d75b47320a943b8675ae57a8baf b81863085d4834c4149075211454bc7165d2dbc076833bdac2e54cfaf9235ec949e50730e4c8fad52842e67dd8d191c62adc 718670b4b3a020603990e8e7a20711bc2aeae84f5a0c6d79860879dae8b49f787bbddb4082d643abc021d57f7c644f06028c 3d3844e475c624287b45de5514b2a0b38bbd276e83e253c6afd45a0393151b7d7e50bbb0916e4d27760aa6229385c6363dd6 3bb9c4b5a22fc6af4d6b9a3404595a0929f3107e071f5c4d292767af5aec9cd140a70c9df78a762e3758da49db0c84f972e5 749faaf20272f4a72a31432d477c13e7d742c57b36f153c0f55c5e12d0dd72ae6d5e05e35a63440221afdc52157768f3ad4f be126f4bf55b747841c8f956a3a8226cd93a89eb8ad3f0cbb0f643c5ddf8a39fad03fb2ff9b7ac91dc86f64c978fd982e296 7c0bbb8b714cb2581304270892806e7573060ede0ae87883f70665959ff5c052e433e831905e820a16a0093b05253b98d19e 2c31e434e43af29216eccdd3c9a6c0e12fcd4e03036c1d3aa3096a092a5616996c2685e6ac8d1ec4d10f9199a337503141a5 cd2025a8055861aae749ed885a3b0f15b4e513dedae9c33af5855337356e7e2ba09340a726edba58134cf361b526750853ee c6864eefb6f964619d1a8d08b69bdc71ef105d58376c0c93deb25ee037c48f70ac7269bb9b898edde47b6bdcc2a50fd51af3 ec595aa7d3839d7d1a4766f1c6a130d448d42765d7d711a971f033b2eaad97b5821081be77fd68d808566492a9f07e8917e6 795df5879c4de9b4b4c9a4eee900d87e791ee59dfc64a0472679f1e4516a05a0dd8b0a4075e3957ae52cf9758e367391f2bd d25331338e62484e4dbb3414f23bfe678e54839bc218e83aef7eb4b58920abd52eb4264aa830c2f54859103a04bd01686010 bf602d7332231d504c81fa01d4da3eab884aafa86f6efb675b78e7b5ff4902bfea46fb3bb42ecde0cc9845cda95dec2a42f7 85f168c1443aaf017258381c35023b382ff7ee99a16f68722d6db5729116e40473d7ff7de89d4f18c315050e871a8db74e6d f93efb46af589f1909d5507f9b09f9d9826f33a3210ee0c6916fb64ae072044db81c4dab8cfbca045e2d0c974e7a71dadf33 b7f0e85543762f06d377a504e345944e6eaa11aade6cc7557d0215ab5605aaaae6eaacba24d87dfe237a60c36be320409ee7 7073754d7559b28b7708d2d33e9a3d5af90c3fb65214b7cdd1ce8d5d5c1830ec72c8de8bad7cb07c369c6e7d955b9ff6f979 7653cb539c0af5bdad7e4c3c42bfc2ad759a6adf3cdb52cb47759aaeb5146b813cd884f5c93aab3fd779ef9bb6fd875b1771 ebf999766361a8a387ecc910518b38c6f0b82ba807285d6852b5073af314b4b6556b220f361ec3ef454469f52e9d4d236480 7347c1928ba601d5b23467f0b3062eebba6222e42584299770153db295cba5f900b3ebca0608da2eadcdc0d59cff5ed5b972 acbbc2359a7ce9ea445aef3ec1ce423a730315add23cbb9d655ae1cd3c047fc40f80c2f82fdf5e1935415ff307c2daa6bee2 ec514dab0802393e47abf539edd458a9685496346ac092bc60e62c13c0eb0966d0474378f4609e570ebd2ccd7ed4447ad987 5e524604b265dcd8c1966e33ad6572a77ba2ed7d152556dd0e56402b80499bf9038a0f7ce606e1240699681205b9ef89b1a3 f88820467c0094d5e6278430cdee0b7e6629b73269970fc85fbc768b64f06b1f3fa7c29be6935173bed75c7f6a01a655a189 2dfcea90d77b4b492735d4a63291243fdbd0a10c6e29f535d0ac7424c458e506b4d21cc0 """.replace("\n", ""))