diff options
Diffstat (limited to 'src')
656 files changed, 22642 insertions, 8863 deletions
diff --git a/src/boringssl/crypto_test_data.cc b/src/boringssl/crypto_test_data.cc index 8a25dd9caa..75c355c732 100644 --- a/src/boringssl/crypto_test_data.cc +++ b/src/boringssl/crypto_test_data.cc @@ -2490,78 +2490,79 @@ static const char *kData35[] = { "b8a29a9a10c0d6a41cf32b5bc40edd7a1d97b295c63aa62c30498f15d70e427d5612ec3f6a2c1f2997fa9283f48018435fa6092269dc2e4ad524cc6da9689302f5c398d79e2b2d19470ea8240db9df0bc0bdc911c4d53f4f24a7ce44ec76378794d16d367434b4f8b6184c7651db77fcbebb8fcc5d3a51ee9739922cf20d4a8888139fe4669a164400\nA = -e3c4a10a64b7e67d786aeb81bb7ea14655637ce963f46cce59bc0cb6b5a9cb9c92afec3d527119db97bd2605d315cf28198992b4b2206e5616d3c560bc8163f56cb1f5626a7ac6d8427520\n\nSquare = 429e4283af7f895fe732ee88e4904348ed01bf579a93cffb7aa8e135d41cb9be218f8b9a9cb4f556124105cf042de51f34c8162fdc7a981de88e005a014149c955068e87214c174daa40fbc618c536a6e507ebd313763fba197059d68c69bd39933d614b2c32f235cc955e335c4a37b9e98cd7f98c7f26ea2da932c7f82ffd95be22a7741da423123f8908cb188abc26afaf4ba6d47b56e11\nA = 20a5e2a911627544219a1639c3321bbcd6192a32129b248cf62351f85b7a719cb275a4e44368a74f4d1a307ffd27ea2cae4d8584a57070609a30fb4e365564908f3d501b53c1a54f0e37745e9\n\nSquare = 9bcc8d423c3fdfaaaabe24a910e6ac3619eaa15e23b9f317c844d39d164c952fdf5c4bd270a83f3902e54d3817fd78c96018a706c1f652025dde0b98afe35597e0d8782deaeed23337ef6b3edc9317d54e3c8a57e4e7e2695f9d2681bf82927bab193ca1f135bd0e542696772f08520faab61fb4ea6ff0d15bb91f21e68bd7f084a6b8f24a47ecc30a779ee86610387b29a1de94de517f81318001\nA = -c7b60f4c355f2ca3937ba3c124eea2cd8d3536226a44afcaa3d17abe931c09ccaabf25a1986b172fcf46fb02a0fc36f2c163b6e42cee047c54ab05e9d30f03f6943b9fbab83aa6da12d7898c001\n\nSquare = 45df25540de94883dbc182009c29fec43627d3e5758e6a07cf40064e0befa0df184528a84757b445dd079c2b0feded48b651ab18b4bede2a81796be45caad0125c3692560d19cd9a6c8c0de8383fea0bc1ab46f6aca4e9c36b26575cff88fdf1eb1e13182308295457374968fe3a9ca34c6acd24c753fb84d41246614789dfe154faf34fc684cd15035dc9c1c6b0ea171e089e0f3236840e355bd123ac4\nA = -216f8a9a3e54d4afadf368c2693743efd3eaa4cbda7a87cd07f5b1a713eefd2548343e7f091ee4d9d6ed1d4343c06a0597db0eb5194b91bf2c858210557a8288c1aa7b0e0607a24dcff9de04146d8e\n\nSquare = 5cc707d97eb107c5c40c0f19fd432cbac9855f280082802dbe4deb45bfd193ac7a9149fd12c4ae6e9282411e2f1f2ca92135424f215b800634092ed4ff2859d16ab9fb8619ece41b50f8888d3e13773d38789e19158e18396096dd57fa5470f50b391c22378d980e59b4585f013e6db52c1e24c14ad83262fd37d42f52323896f7d4cb3e38868abea8a07e7ad3f90512eea001c5147645bf00396cb0e7a553f1\nA = 9a1d1b0beea76e7f32bde9f4f2c8bcff9094db2d32c04fb7ff43624b61033646e482aa0fadb9f8b4225b47121070b4ee5d6818d3606ed775aa631e0ed42da68c2a09dab26b6a4d09ac226cc09321fed9\n\nSquare = a32fd053eb90c365e77ff47573a24add3b25b4c301f4c662dfc1fa635af8e18e7947381989b37a9c9de2713ca438b9f85890b7b160fe251933aa7dad1c3839d502debb42ddc927fa0e9b40c80dc3d408889be567699a856b1c9cf3a393b3b818432e95feea825c17d0981b942236b3779f2acaaccaf9a5817ca47bd03045fc4de454d8f1d4377e218c5f7ece369aacc35369ab57a71652dd42621491834119afbe729\nA = 33190b787a2c3327b122d1f5823bdee5c93b19b586ce1bf79d801a19b2558aafc8f6274d0908bb7a8362f7f71d3fb52b8ffc87d458249caba7af3a516ce868e8a620e3126ad43d6aeffee11866fe77677b3\n\nSquare = 74215d33fa398e21c34034af6f9c7af6a3e01982320ec8cf23074a938f1a31543f80e6aece01de247668fe67f276cb4411db27666e1dc8fb2bfa4eb68cfd3563167d1ac4efa3361f920d8dd0fbb7f06362167f5ab5ecfb72956c20db934f67ff1c75aabb594c853fa61f43d219a3f5d0d45274005e3b167cfff5493b0f26d15f85d8e906a0a6e7645eac1f40c6dc637e6d1e061e5b9071a1227469cfb2c0f17ff983684100\nA = ac6c0b9c69785f35dbe244dc85a54313ef836ac67c853531ef5db45b28835ffe61dd258c5528b0acea50f5aa5c0f5d08dcb8d82ee19bc432fa8a45badadb50693fedc1cc79a17d63aa73fe9597f1d4ce8ddf0\n\nSquare = dce5cac967c47b8a58ed6f1bb1d1e6185e849400228afa2bfa05b9c2dd327b04a86f2a4da2d02ea102868ea0c4da0f3e5a40bd02c87a08aaa5cd8d9358b3a5ebd8c9fc2dbb1268c261f46d6717b0307b993deff0adc8190d32b4f2bf695eb2cc74a6a9a712c5a621c673219ff8a24ded0997508f8f9eb1ea872008c46e71fa97f55b839950e63130c38b49c0ce3ce724a0e8faa9738d2e28ce6e7fc7eab62b3561d2981f314f751\nA = -3b735400064b15fad81b08362b8557f8318c20656839ffb4d2513512015036ab0039442032f1cf515f8c10c9933afe4206a2f309e933d1561b06bc665af2f04f4d064e073eed2280053f56cbeb137a9482c0a077\n\nSquare = 6b619bcaf632f0d8b1d715e8850c0cbbd29ac6373a9a5e93dd1bbd2b82744a8a50a7446b48c6e215911ffafcda9ed7becaf5d26b7d6df7dc8798d53239f62a482f974bdb654750def1c941c49a24fcdfcfe73881b556a7b528d88daeeaea8d62b357211a1946c81cbf0819ad8d0188f60aaaab4ea2dfef7e9012ade7abeaaa4a23d7403c1248c36aa26b43b8e7de8a5aea639a0449f50359e9b4c1b125a548383af33703f8dfbc2528e4\nA = -a5ccc69663a8712c15f96e6fc746252af89a8c2a6317caef905dd2d8a6d4fe878ac7aa66cdb3c3721ba7dd36da310753dde9801b31d759339ac919a464ab52541bb2e0dc938752bf0f1ff7a9524eb98340d62576aa\n\nSquare = 77ea5b715823045afe13d10416dfd46a511141a7d1279ebd624f1de428cc04a4f246246e65c3f84344cebfa32864de9264b2e54d4b3010c4de9d3e6a27aae8f5f9e9d8e49fe26b73ac7e65bb216aa6a42db36ac03d749b5dc04192df819631593202a58264714628686507fc5655f169483b0ffecf45995cbc12faa105895564d287a9f4b220947d6c93786c85b2ee84a0a29183483f7c241d6a67fd0b1c38c7f74421355a14c6d9ed5720e24\nA = 2bcd67e6bde3f54c4ce0ea428418fc5c97272217c6c7de90549238ee322810dcc1bb9385967673aa3f9f5a5c05d987c6445135cf1efc26b3c17e55b93cc052761a77c9dcb5c22927b09e90a92e053ec1bc799bbe7597a\n\nSquare = 40d113460ca3e70545bf3613c2ba5de5d8485641ebf531a43b6b8bb76884ff4f348727ac6606e026981d2116ef1e60d4b37b44ed7e2003410d7d636b58aed2f92e962003f28342aa5f059d23b3d58a1ddfb47833ffe1d1deee0a7e78b8f7d9d6487f22376664f1ed9ddb5ee3d17f43afda296bead11680fd17576a122c2599fa9802ddd84a2115f9fda03aba898f66e303895f452077c920a322b6aaa0965f51fbb36f01b1d412c6ccf390da050d24\nA = -80d0699a46619db033461aa6060983def7deeb976d1a71f5c6ddb85e8b46dc70b7ddb1d254971d38ca87c7ee3905e63506c6db105dd683375f4239523cbf1874069266c2c0f4b37edcdd261c51088081d25813758bdbfc6\n\nSquare = ace99f98cba0d1dc1c758dc7211aa4078a2aeb6d3fff19bdfa6981ded0982b15bac792e6b542ae48a86f9b40c6de937e402e230fcfc390b10c3e60202dee1337ab39da7a342999487b8d8b0e494f2809cd1bfdb39209da5daa590f78ded211b6bbd3fca9013300b951d8906c9ce8d1c0dd9554d5d1d352f9784f822c928dd9700ef8a5fecf3771966abb1dc6a70b301461eb6b6087d6ab80a4b624205489584224cf6578f75acd8091fd621d02306504389\nA = -349936d60c9d77a0974dc8985930d8674976db6b3cbaa067554ca6b30b1de33f2d4e1c9564ce102ac6387755aabf42916f63632a375d995913f9d45ebda54bee3fdb7cedee46ebb5c8ae7764e4de323c17c797d3b529230cbd\n\nSquare = db6c73be2a59bdd35dd312240aef18dde4231c72aa28551bb370a87dded587accec2279bea24c930236f06f24d537fcf242497aafcbf72f085fd3ecf030cd750fb382efea0f82ad9d3195680324d73fa99d48802d085c150164aec0d29fdcc3262264bbe72311f89989cc71a4afdac6ab103ab4fbb6e973a42a1f8711bee463d198f727dc7bad848ff8fa77cd3b2f612d142ba46e95bd79a86a1fe4c2b8f9181be84825d05989695842113828a83b826e7d2c8c1\nA = ed01dd49d2e5d51fd30e9c578259cf107771b4ded6bf21f8b9b632fd360e34da740e0b1af6b5a67789fda5a44025af0f1547271ca8accc7a975d98ea7ec3d41c9697018d84ffb5d49b88d884ccdb011f715a199ddc44a4109261\n\nSquare = d6e38250ab89ffe11abaf8c5d07ba11e9053f1924ee1228f834111af16ed282389d04330cb0f47dbb186dee577aed82878ecb065b759312eaf167c4698eab5ed03a8657341bf5fb14a8e28e3b443a6b657c1f4379ff2549498a33922ea84f1fb19d10866fb0ad07ce1cc44c93cd4d9ec6bbb0e61c797750c6b5d7e8d55499655dde112f4747798f0e985fc2b937a44da9b04c2dc4b0816cfc57da1f80179db653c1ce287e786ed7eff7ad6d1383fc6de8c941d4af7bd1\nA = 3aa2e696ee570160b2a869c3f21c3f223959a185cda2274feea1c829af2234c70a504c959bcc49fe0313f4f5ffd27448e28aa0fc6ce24f36943d334c626459d7e6017339e787ab074879ebf697a93ad93835d69ab09294d007a0837\n\nSquare = fc39360cc0fe040b6f8340e0728c650e5e74cf1664f7b301e79986fe066f36e8df34d38d1a06b74a1bdc76867baeb3f39a9161acd200bc7532fa4aa0ea829377659646f073db82ee044279ae5fd797edd37d3261970819589853cb320887a085c4011c23d0da9b6d6f1b5911bb3399146c2912a967ab3b3f611f0bd52e00f418e6a6f0297fcf5c4a1f71c6bb8cc8e1c76694bb7301502d1d00c8b6c05bfabbf5d350590561abf3e2b1a82e98b56583e2e4e25cf707320a0e40\nA = fe1acf3d7b54e718c901c53f365894c22c8bb4182fee8a4c2558731e01e1519bfd1bf6e353483b8c4219453fa66f06063c6c99050068c15cd13cd1648ffc42b5badfc70f6fd4a0a5552fe637e54c4f92ca45c60cf9a0163978ac08d58\n\nSquare = 9abf1324ef65c726330f64643a024c466fad37604f4dd3dfc404d31c2a430fcfaa0c78283666c15a094d494b96d3c12de6e29a34d2c99f4f8cae8217bcd2a989d59807ac68c46d60600238a86155de499eeb35642d0f581045481b40e4f0a76905f9b6bc5b9585f77f8410b99333f7ea983c3f29f3fe66ca7b793b784a5a6a4f74512aa4385dd1e996832b1f41bb3af965be58c4ac5e867cdf8dc6a4f9d20a6f1e16e153fcbb45ae5fe8a798cb06a4ffe467d6b6aca2b31f335a344\nA = -31c243593ea611dffecc65d1439db345b2e89941113f9792c", "91a76b4890db6e4dbaf1482ee812e295d27956e48d07a14de38357f15b5931c5cc08d1d248df7bfee1cae5b5ce98984c5043a3e1a2b449ba1671bf1cfef91011e12bab94b6e\n\nSquare = 66aee3e4f43c672e0478c76e2092bef33e7c60afee5d4c7defbcc5c0c86d8fe956c90a740cebe604224cc3f518463b1208699b8ea2316315474991d0f120ae905a67028492cf46fff2ae244869db2a02d06aac6ac6eb054fb3c14c756d8a3e7ca64f06586e3e86e4477f185ed527a8aea6a3c741f3fd4b64a2ee77ff140190260c431cc53f411fb227377c02f85d0258a75bf6d44dccbb8bd04ebdafa115dd55b176b6eff5567e5b1bedcae15110826574053681fe25a695ac4540186e90\nA = -a221dfee30286adc076673cbcebd24a41a438a0a7a6a547c75d33149cb1a094a8425feaa5a23cc234a722db4cca8d5912fe1dfb6db4e92bd87c12f0d06b6d954fdb9b172955412b2eb5c9fa3b4df2933390384fd1f929a2b1a8dac479ec94c\n\nSquare = e880f8655b51739e34393c3e6d69d63e0256b1a887f7e69f40c78d21133b17e92277a136f5e37da2533ed599efad189975d22ad0340005ef58db0b471651d749dfbd48b3f7b3b8a42d4677048a855e99dae6c729d8bd7eef86911feca9f5490dd216b06d9e8d1ab695c1081e72449baad28dfe113744853382901e6bdab5413c67c52d6cbbb2e0bea711edbb3a219a4046e8739c04729cf8c8210028dbc4087737bc6c1d7e0c15ecf16774690168342b1372d3646d4d8696384bc932144c98529\nA = 3cfe075d4525a3c780d6d05f7bb708b2fdf7277a0f9967e0a209fee9d42136a0bbf98660d8ee8cb4720a8042da09f6271c45ad13db24eaac465f8207f78629e9085c1c890675f441c78efa38e5022b1b80afde5e3fd08e55648f2817631eb6cb3\n\nSquare = 8d6cf4eaf58099b1323fc598b7554b371f4afef5ab501dd162ab8429333d46916fe15dfc4ed6a99ca7fa7fc1aaa0cec3533b41e291fb7f69b560259507226eca87aabd07b1ae2eb93bb53f98fec508f051cc04db4a172901e06b74229c4fa3f550a81626c7a63fa99d41e46c2cf792287a5cf7bb68946971bd43c7c0356312cdc25e524665dd39a24b6464bbbe64fe8e87ee313b860639728a9143c3a6118bc8b150dde6c10a13bea637fa8873c393e6338319c506aec6ee973b4b52a272a74bb62084\nA = -be46a8072aa44b3bff0f90c81474dd576756fca624c15f55a17e1d0bd2842467ae000b04f79f561690c93ca7118ce17ecf830a8da3678c15436876d2a74324d9714dc8ad8181904be657d7f1da3313b78448cc06e32299a09ed59bfc1961e8bd722\n\nSquare = fbaa4fcf9800673fbd3a132305ed3e14f4889518fb56ab82aa5e9b3529b74d7f9a467626d68f4709a2030264aaebcf05c0a0edb511e81f357d85b79d925a24605f1bcd4645915bb75d363654b676266329df532cdb39152fb360df1b9500e0c296014289650ff77faa78a604397a82b34d16484e94a8de123fe720e514c88f11ec276725111563db91477480c3245542ec6bd0bb2f4aaec02c6c4eb1769030a31b05da3798c224c9117f7c38d3e98a343fca03ab584ec2d7e6db60fdc4273c3d8e23cc1ce09\nA = -3f74b25f2a9c4d8d977e69a4e067f9fcec281136a508e365b282e5fc3b1d097bc6a0f59f7827fb90d4890b08840a0a1919032c67448f8f1a771f785a0f125a4aa4137c154fdb489dc1099d57bfcfc75f4ca5e69f93f2bb87ed09cc0dc620d3e76ecd03\n\nSquare = 5135becca97d93dd4b16a5a1105ba3a3e3fe02bd6a7c3cd182186fc63ed4351641182a2727ab6715e9672458dfbc31aded4781fa345054eb4c317872e2af6d4ed64b2ca7e8c25e1e664b5349df937118632a64e4ce439ffc625a5ad3358270dc83fdfa73c7afba03406094fa36d87517e5e2e1fee5526fd2dc00d9210a0f6c3745b3d4bceee5f8b03d976d696c57a09d1e08e4ce780972eca4f2ed6500c23bf5782c31f13059e48246180fd09db693d2fb5d48d51846ece8beee45cef7efc87c003b44d7b137a900\nA = 902fbe2127354a7df5cb7fd057f3d080a7bebbdb83c86a50560b8c287a37a841bb9c8421c63d359078d2948b6b57559f98fad8f8014f93c912cb70a6701c4dc4fc5e88aa413fcfb685c32975a8b72424742eeff8262d28cebad00c5fcf88baeafe8f6730\n\nSquare = b5976cf6a6560412aefa6704b126e0d987dfcedbb4da436c08ce17b1bf1b6e0bab9f934abb5c4186a5415fa38724fb8fa341d381319e7d768209ab108c8debd99075d31deb3e03ff7d23957d4f3204d543b7d9079cf337be3037b1cb4908fd8c104d92e52f041b4cb27c045a741f4d64009980e8d27af75d9493920ed98c7234777592d6577f2d1b3a0eec645ab4cee2f28d9e4efd3e4514db6796487ba68a462fa0e316e1420d6604db2b901de46553546cab42976fd0d459afd81196275cd88ec4dd448ff331bb35499\nA = 35e700e034950bdd7318d5b3c17e90a4772ecdacdb055b9391b31538eb823fc8a4599f029e78e4fe5299ba1a423a449dc257a431d189dd5dca275c02cc1f12417e111c73b731631d8a1741b907dd8f24de226ddf9e3044cf4064e8e51ebd55be774be7ad2bb\n\nSquare = b7de0f73397893a97928e266bc56299cc8d43b16a251992662646072b58fa578ca80f7be1e12619012b130e9514be803dc166b12ddfd26f558d36c2053ee6209b01458379e49469753300ef20f6b3dcd5383b121861c76ab25debb28c448ec33a81250d05f7eff80a5a4133d522d270fab29f739b607395a77278609aa5e1a55ef58d1d48492b71ee30a24a6505aab1a3ac22b9d143c9d6781fae14bbb980fe3a99dfa9a1a406611d7d0304493342f53faf5fd79f9c96b9583a219a1b22aad02dd58f32ee98146b3a8cf054bf9\nA = d8f4d3bcfc7eebd7068b851858c3668ce062a834927e165679b49132d4f780ca682876c65c7cf2e7ce34ed10e43696477da6301d13f92abb8c76e2424c4bc28a6565f15e59563d607b852dc946652b68fbfda1c3200ecc2976400ce7296b96e75fb059a4c8eb5\n\nSquare = 5ec02661f49fb9807bb73debc3c6eccdac1df1735e0d61fa7e0eee07471068a5809796a2af490c46a77d61f618b44a3168dde67aae1cf9e530382411056958d55bd18f0e76fe2c31c98b00f87fcb7f5691ed5b65424f82204156dc361ef6dec5d44cf690582599b3994ee47ef42850d5d2370a4169c5f73942657f85422ca24f66943877f73af493c865fbeb29574cc1cc730e9bbb097b598574f6b90257748e950bff867bcc01bf62f8df67d7aee1b6dc1d5db88826e86a3f9fcd8663e09cf8393ee71a09c43d0d38ba6ef643f4ab1\nA = -26ef9b6708a80d00f4d01e0f0a5546ed217085ff23519819ee89af430580ea1f086beb0eb51982682c6d3b922a2c92752dce63657836223a9d94964bd584bc8e37c6e30fdcaffbdb128344d51a92705e1c9f94205ca36452c15a08f7e62e0e02479ecd48085de8c7\n\nSquare = f6364409467a829abc2b13c93979dec84984caa12154b7cda2f4c8d91bf24ad7c45a968ffaac8d6722cc26e6aaf52dd29ea2f09370ba46d79684b7a06faedcd17136f35a58e5b550f3a2caef7b195d8409914fedd3c3154101bd735155098e8b10fbbb1b2e13555d2ab5d5b52b203d4efb27e498b240f37178f2e89b413f94859b0e8b2ec10b926c8c0b6f2937ee2d0355445364841c7e0539f7073b88c7d568edf1b253f3c10627e22c2ed731b7d4d199449cb0b5e7a66109932fe2c9cd741d75170deb9f98469049549c10a7a622bf6e91\nA = -fb0eec3246e99212879e51b17ea6615275818ecc5ea3058b13dbaba2576ef90e1519e3629b09fdaeb02661091c395c862b848f6326b9f536f7af45718c4412f09f19261b537bca36742d3ec66f964343516aae2ac27e249a15beb545b447e37b4062180f6c82809429\n\nSquare = bc4193ecb5dac900191e02be06297106155c6840c4908fbf6e41e9aae137d53c3d4ffb87f334f49837dc4ab7a66299994e4f5c9bf6ea03e7db663bdef066e94c610580a8896a9ae9c8f6587eb83d789683f5d6391bbac3a1dc1de60b4108428e6f5fdeaed6cd3e74fa01f85c6368023b61a413b69b14276b66f22653491e4f25790985053d075387cb13c79dcf963b6d880d01174314921afe1cc700c02efd2979dcbc59c417a6316db9ac45a2d60d2a036571bfbd75f9f5e42048ca086cfb4b818a9beca4a6e0ed51afa320ef3549151fb39e100\nA = 36e1f16043b4c9b4a304496c39dd63459d6521d2ac92916d348daca3f972835973fc8d21b07b09d8f5e3197b39a8f3fd0011168b815d67c48143c413e169ffe0f56ff2cf8b6596bd0a3b5b7a6b9a14ffb797f350b7e6aa7020d84d1d1b8006850139795abe2c74f03b8f0\n\nSquare = 4cbb5bc1dd7112326e2c94581f19efc8fb25339a299fa9c007114c3a22b395e9d39a8ffe21134e97ad1b87b97e667ba48b2a40af61afc81fb1e20e8e38c7ba666b146016af4dff3faf5de306591e5ce6eddc1173fdda6fe241a9f2fc6e054c41e56d296f8954377df0d140096b9e9d6a5a23a231db4dfab0cabfb11190c7a0d1c55ae35203836d433da96ca7339682bac0a7edb8b5b4dc267c6e83ac9b67a0d0d564717ee3c20aaf52c0a750f3aad94a12537c6971ee009d0f82ff576e984b06c7f7b357f5c049454e31326b952af17aa62104780e9ca1\nA = -8c279ebe466de3115b8740f3ff9c1f605b4eaa75512d82fdc8ca5ce84e11a68688154fd603ae1d607807dbfcbb822a8dc259098842c6a7b7ec350be29a3daa20fd5b093a56692e9d42e7a389c4ad2122a74205f835e268c9742d09ad36238c34e143f6e2ec69c0f490d29d1\n\nSquare = 4f771ade09cbd1a033d2bfc6036fe46ae6c12acc6f2b9bd52e7781693fa6358cf93089f23d1f0ee6fca476a43093b9b52446f3a7abd72ed0ce9b562dc438822ffd84bcd898ef9d092f1b0b7ff89c4fdb33d8715dd4a0d68ec49ad41338fbb62ca87867d847a4d99310641a37ea78b04c85606069d0c0950484ddbeedac8ec6f95124e7fd83da4e942d40103bc14474f5cb125fa0b06cf167f076979948003dd8dc3711923f5af5beb5f56c0a48ac0c5240b62738c1cdb06b87ac3dfa17befbe938ddc7281f6c248c41a1c7b99b93f69fac83a46eb298a9fd8b9\nA = -23a845bf2007ba8480e3ece0a1bbaf8bfccba6bf061e3fe1d8bcbcd6c761e650891c0958bac68618a1f55b27d2bc6e1e1b50afc29f58e2e034bdda8405e5378cb5bff0d84efcb458c5428fc607597d89d589d85d90f3da4b89a64c9d1623b98b10518a6f2e7d2295c37527026b\n\nSquare = ab45d12a4e15a294830741f4b9d4a14cc7dbed1c3454612047f890211c749d92ae0418f11cd44acbf1585b1f7323b33ac9a4b13c44e1a7e31b0dcc1c6dd4eaa12a655b5de08f3b948270a152db7d9e04dc54677075797bfad6a9a0e3958458d40e3df5e15028954bae99518de4dd3adfb2ec4b38897a8a4e4807849e1416aa4040c95a0e49a8d2889f6fb0537875f87516c3723e8d3b46da8da855929c67c0eb83daad62ceced52b4f52d2bf1c4e34f26bf16aa7da3afe0f5df76c0858ed98f21e1fc3d01e1572715b774bd5c2faabec5fa3fa59a7a1f32565a4f1f9\nA = d164d875e1f766b4567e9228241213e69d6b6c58620600166fac56938c5d", "9643932d01f1f4a2263dca4b9ad26dca1548e4b5b7e27581a63375d0e624f4e4c99b7fb9aeb25307c61142760bc4771e48c7ce38f5eb2408def632096fe40b80d488fe17a455d80edfc1c23c429775b5\n\nSquare = 5ae4e7dc5727543af39ed3d5e9ac086d1a2220421231b82f6f41caee7b9815b4049aea0d43ff499c6c9e1f226f8641351d03f37731c64686d9a9ce68e9234d6a762efcffdecd42f81044111599963d9b6873cc20bf4c8284fae03d2e4f238a14a74df4388fdc80fad0375a5d0d974da7854ede5896ed2ab25d2b49a3c39093600f73120e4fd2faf75381854f6ae80f81b977f62fc72f1fd01c278d183544052b77bd753dd88ffdf5c01745521fb8474b5c23b0b7dc709bafeb91cee0863a0c23ad7192c43cf15fc181d629853cb9b8334082c915dd3d04e3a0a81511d2e84\nA = 2622a7bf45ccd3cd567c757f4c5796b5a0fbca555bd0ac2759c24083172d82d6a887dcf93d9788fde052cb20a8963cb6db22bf5eee6151600f9d1896a7606b11a1b100cbc0925bce037bcea57e361efcc560a9abc495d7f7f45831c6429ac8f979dedc08c304f4da9c0d4d687376d5e\n\nSquare = 473cc933f5a650a4ae358c7f486d325c0e20c83b54838fc08b6ac3ff010f7c4b6a609bdf472974dfc5abda0c6b33c5ec7dc4628d85cb4276108e2b0bc4e19cba135533b3d7bb6a94332aea3165dccb230860d2353166b9905635e606185b014730e9dcf2c433e18cba83859fb2eac4aabef68c8314ef86dec2d534a184ebc4cb193643add0897341690cbe18bc2e775327fd7d71ffc7ebc49bad83cd68394eb276b2e615ec430180303010a454ef73b6a8f02bc48a1fc8a32f8150ef1b733f07da752b8e808000329f4924976bc8b8573927f18ca7c88c210845de6dcd0dee2904\nA = 870b2c4b054076d0d02877b19fe1210a8fad3422b00905a6db748239b8e807716ed9fee0d8c25496593717917edceb5db57f9960bddc1956b6652868d6ace82827bbbada5ae8c15efa26fda22657126c6300906f90e8fabfd58ddf312ce0eee760e0090fac44f00378c676115cd0639be\n\nSquare = b151124402d2f04b0e6599222d380dcf67b9716ef50d2d9ded0b21521b34a7294171f71b41762511b7cca93d9f50e9e30083ef19144882928011dbb143807d1b88c55eea6b19f0c4180023be6da63a59b6bc027aff3f5abe2f65c73b2de1e71c5f4b248bc4547040764e83a860cb3f882bb8b5f7821f92802808fa37c50f2f94d8f56daca841f42d3362762ba843aedbd03d3cdda887f75ba92423965ab4256eb842ad755aa7a2af331b488186f891065b07f5a299c807dc24fc176e085a8024bbbf12f386ef49ccc91bd4ada0936b6de78088cf5952ae6c04f6916799378bc0ede0da4\nA = -35439da9e361700152a35ebdea253378a1febec5f288e5b2bb0bdf25b84751b47e4da5aad7453b70cfd6640d5832237d2115575c738482ac6036c5fc21a981c0a7f979c8d621a92c02166b777475618aa6362a0e225dd6138ead3b2766ed9785ee01e4950a863d2fa0b7f5cb4c9a108bb626\n\nSquare = 4ed7263ae5beb0069f24318b38afe951a5a058a2e960e67f086c9680d0cc6d713f943812070bf94152f7926bdab9e5908941261244542b832f458f05ed5dc048c8b9eb84c2a85efe717e257796b4ca816948a6c8ea209c0675efb2fb5af4622b44e36066593db01b17f4dee21d7c1337ff41436cd0e5a8d01e4030dcd3d49839e59996fbbf1d39bd205343a424f2395b4d3eacdeb9ed3235d8df0dd00a2573260af63db3116a7c65d1dc69684a05caebff34e3d2cba9d4869a953a7b1fce10ebd008cba021008ac3187bba846abd7b39a1b97c9c07d8080549e313dd58b716022de3c1920329\nA = -8e1141dcebae61d5c4d81697f001d792ee2e847c589816f923f0ed42bb4de0d8f911b8ca47ffe77f80b9da6896a9b42f0030a3276218868bbe1a3fa64fb0a577704339af5dd82e66780da6f58900da3f1d75ebfcc302f78ed66ea3c7a737898a29b1f2500686b43bae1e6571addd2842cdce4d\n\nSquare = b09f5e9472cbb75070a67d025957fd5ac3be89c41e4acbcd5f75780ca459562461082c3f19c5a4a416a668b0a55f31f74cf2ec44555ddc43fde64da0ba781adfac4520dd0f78d04d9d2fd33d8b49c72663a6bc845015523e2e4e7ccc69e5b748b8b891e4089420bf0a3f6032602824c7230b5ff95f85a688dcdcfc890af3384710a9fe32ecf9ad7c6cc5761f13079b19d7b2906c7e63c14b64fc88c6f4bd7c41c0356c777d35c3626d49db8cb2d1e89ce682c7fccc3a459b08c20c4e5fc3a8eced9b37d01bed5af6ce9baff0d2b435e6e62871fcb20cf9ec10d1897a5c76e73a441e07fbcc2d9f4e4\nA = 3528e6581de547de385c93ccf1086a17614f23356a918b25bc6d73656a2302b318963bb679c9a93357f4a4f614e74f2e5e88e9c8aed8a6fdd8434630f664ed15ebb6095cbff1593f188a12f4dd6087a85b202f6c24df68ac3b137406c88c5098faf47d1eeec0743b35baaec7dae29b5a44eb09daa\n\nSquare = 5d5dc40783411475a4aac7c1a1eb760f76fcc6ec68dfebb754251cf499870654cd309422935ec841e6be4f5a15078356235c2b8cbe1ae755cd6d814e811072bdb76156b83c7d2064a202ff90af1e0f88f5889e5729a3cffa9faf33c463b74d0ad21fbb4473d4d3ebfa8a52e9c209ded5ce5131b12b69747c365146fa17ee5810e0dbab992f9da28b6c323062484d62472232721d608cdb9b5a341a677e2d7a6e5a983247d9a4001e16687b489b10b18bbf205f982b7ceee27cc3e9c6641827ab7952373f15d36e5f177b82d7eebb3f5054e12cec82c5f520a2675afdec6cbf6235d358c2fe73344002e400\nA = -9a9a19fcdf11bba84b0395088c5d187d84d69b68b77bc6418f63c88bbd8dbbccfe02917d814f9e2241fa0709817a0c85bd554fe887babae7439d96248514c12d71587c906247b3e965e954cdd57f1e51f1979f73c3237509863169efdf281c1359488daad3d9eb990a50ecf4d3fd25d4820077832a0\n\nSquare = a4d69ed4c4c9c08116ec5cc49ad458f0fb2ca00f356aeb148f18037bc49621e14820f325af39f3954bddc9cf01de7ba1e443088545883a94c04ff41a7ed5f65676109c5b711b4115775489667e00aa1b77f6dee5ac5c1789bc71c9fc797abf41c7c5ae3e2c1cf82d5b49b6c0da25190dfa9360b99b2f63444d21ec6114038b8284bf598eed24a2ab2b9802d6edd5b0fdb52f60621a87a14612844ffc71ca98180ff0915cf75f47432f73d28dfd7a932a125095655f07f50722b1673df2cc4f7566a1c6035792ff3f02356b9b9d25e905121df768dc6a1884cf5483eeb813c1c009fe4ed043febd61800ba978a40\nA = -335b12e40bfe0b847ed6ec143490df33d2e64ef4363869cb78dec008cb5cd66ea671dba964a53e48267da288ef4040e06371e1209691b81df02f2c86a79cac85fdcbb6732a1e5309fbbdbcd899fdfed18518d47258c9e63ff7f116ef4a8f5c4867aedd907ccc7d222cf8087afebc108f2a0f197c717198\n\nSquare = 74dcdacc1a4f02a99e3642f54f9d917b117d2ae8d9c392f8b6dee53fac66ebe1680c8e8cc29f5330e0eed3f63d10980060799bc37b34c93dd7b384d4ba30a5b5d42a145acc412ae838d7b9b7137637546d1118f7cf3eadf88b785f0aa01da8638f027c56faa16aba8591b64b45dae6138c9a40309b2ad29c5029a867465f9c6de8fbc5fc4b0442c8a8946272667c7622454ed6f2a236103bed7697dba20db84b5154ff3fbc6b4b9eb67ee43bcaae741d87ee2093ee67defb8eebc4a4a22d97a4e2aa7d4c31a1c88abf4a440ba4e2a5e40c4d903ba5ee4d80b4e8dffb8864bcb9806e015c1ce16490068df87282393111\nA = acf70350e554732c1972903cce269b215e985ecb8d6eeaa67fd5398d0a1b57c0db63368c0f8c2288c3a0466e2b3db081106b90920c46462faf00b5bd654f7140a689b78ef656a26b82af8dd1988f166ea04e9aa777a094d892bc7da4bc7bcf0618526f496cddea6d67df7bb0de9e99a35a0b1b210ff07497\n\nSquare = 9668b9e40a8bdde3c93943a918ca71fa0009cb05a1f592b2bb2c6c6172b2950719bfd80cddaf45d044cbb6aa99715046088f40ec6812945885679231c07f4200023548ead086b834abd8c8f8294db28b203329553242fd2f778ef5cc5ed0b48c7356d8c2d782a01809ccdb6b012896617f11d963300e7bd38ff512829514d94343476818ddf9d712bc70cffe7f767a9fc75a5630e6250ed45e6831b4660eb49d47dd1b8b6a0dddf3fb3ff0e12834337f145f741f70a2aa43769af50f099e004269ac47fab79e060800dc74da88141adbc46c15c7330931e3a2bed9b958f78b30214f81a64d121f96fbcebf7569fec0cdc6b11\nA = 310e7a40667d9d5dc29744b123cdf6a663a1b995f62fa9d4d853cbae0dd23669f4778bb2040317ebf6a06ac6299b21067aece5c5c1afbe6e789d656745ad66464991cada0eb237c6ffe991cac4670bfc90eed5f8c75073f4f846ea244bca0e9502ff56f8e9bc9b6caf275aaef38e26566fef35329ca45392069\n\nSquare = 49e677c8b052b7db97542948542449af47e14248021f8d3d3f92b9af41c803072f71050f16dd848aebb270affc47e85427a7c73f227f0d63f140d0d293157af0d972eb5b38de494fbc78ad3a4c3d1ab40197bc4427752b6102d1ced6d6cbc9d7caa0d1bcc57e708535822180055ecc9d9667e0590274b778480a3720823e931ff6daef358b1a1a9092f1f05fbb5b10ad5707a124e8be63bc696f083eb74e5b4f0e3110de8f297ecd30dfd2bcb010dcad4e387520d3d00365fc51c2a3dfe064b1ac77a9295f66beffbe5dd4333e5cd823b0f36b0b94d66507b1d9381060980f62f38a62e38e5a75203233bb8d64089bfd100f3205f1\nA = 898b5f3655de74cec3b0fde2ab03fd18cdbcfc3eeea48ba39317d26917130c2b78e05237cb0454ece268f091cab699fbcd51ce341b53d6ec0cda5d0d5388bac25c6517214a39d03450ef8502e1675bfe8e57bb6086f10ce4cf8ce65eadc865b5bd8a00dc26394f3adb2ace609149e3582cf44246184b2adc0ffd9\n\nSquare = ad00f10fed55175159b2409dc80899f9113ba7c8099d0402ec0f520ab4aeeb46d36369494a4e6fa23675adb38148fd2efa082df5094c0acfb77a9ab6ba7a299298d69b04b58011c35325f46b765e580b5c05eca721904f1fcc355dbe39faa92af5c9a6dbc4ab80e62b815b45983d9506ebd52b9efa7a6b9da352d1e4fd6ffa81d3b4596a0c14fb825297da361461ff2240e4378340d2ae529932d78f3d9f6b3c6d65d717e66122e5f590c50ce0a5d81ad8e0f24e104c0913cd8d0eb2de4c8cf62a7535bab5502df3fba08bb4dfe73d89c8b00edaa7d5f3274be9959e7ab6b6dde54f2491728a1dc11fa8e1c6a95e67eb7617e9b7471ee40\nA = -349cc2a5658fdbe9ba5c350d3b25baa38b1ede01926694bd550d36883e53d8758e8f1ebe83e2f4560605510413a7d880929e2d9cbc2730b1736dc2689cf7bbcdc68a342b6398e547a9bd67cabe298796d76b98ed4c1dd9c22e36145892e8fcf2258529aed24252a70b6ca8fd2aad8a84becf7e1bf98b1e9bb024b8a8\n\nSquare = daa3835d3189ec9ade592e6076e76d441838077a9431273bdec02379b3a6ac38aecbbd57c3755ea58d", - "def8105ac28f2ecc8598ec0c4bfc9c1c80222fffc776722eb0621cdd8a0d55f08767fc2922282a76e529d81e4d6e21a2542b8c9a403709ed1132e3b52786b81e684591438fdddb5df2f0b72e6b39cd2db6c0cc55c759c2dc1b6ccc20a5cfd10c6fd345fc766035c7478570d4ac534db3fdb718e2bdad3d096b137bfc09a562043800957e2afe4fdcfe292881f6189edfce52370c0438c2822ce3b14d73b3eff32f7e5ca97e989326b4e3a8fa35544193f8590bbb0ddb1f914894ab87998090771a0be1fd23917cd792be86ea0b98e6eb24\nA = -ec953f1b7ba7d561edaaa23076987daf86f50e9a66c36f0993290549a9006dd9d424885c0fa77295cfe34fc81c5edce9e2371b3039ea18d8f998d1956196284e6d81eb1c62ecaa8cf3fcaca28ca7e64342803c8dc3c139080bdd4a1ff30d7288b085a579d9e90903bd363b48f2072bb6fbfbd9ba2cab30a8a63784d246\n\nSquare = b33f4f3ae453058f4e865ec78f0844bab7af66a97dc2f265ca73ae2232777474bfdda39e10652d7386c16f145272192af728893c3d8a8e92c60d77722b924c30269ff5a399a2449ce15e50320c528c22655ad06227ac4efe5a993179ec61c2fc9115f89d75b53961fd16f7797657f6fbf55662b019608a1d30f64a2c0838e0018b7526921fdd34fd462bfcb2462b7065e2bc7abd57d71371e45dfd8fcfcc00a71f7e45430820747c9a060b72e4f6d2919cbffd00beb0c31a2bdc32afe2cc540b38dd04a2b73ae5ba481a6e535f37a757bbd6aaa972986213afadfa47cb7a15a6f1d443f93cb0ed824a10b4b7d82cae524a096b65ccb39be3c37c07f59\nA = 358da59ef65f62f633675764e292e5a68879df24a4727eca1fc4d232b3a6d936976c92eeb11456b5e8c11319838c145c6529d2f3acc828e55b8274bfe9afb5db241b102715f8e8164e454ef39f13ff1b37cf367a5a66c4f743c750896b7c3c29026e448bb36c6c06b0d9a3d048086ef0c3cd922a02e794223f388b5d646db\n\nSquare = cd4246489f6f221f920acbd8bdcdd17f47d2b77268f72254de4190685c123e8c5eab8517fded1852e8316c9e549d3fa355142d91b2921a3c94aafd8862cd2235429340da38a2af131b8d002f17662354f5805f6a7af7afb6dbd2f641036600614cea42bd8b24d86a5109eed29c0865a5f30c5291b1d1ef3223f9b9826dee773d98ce972da92daa19e843f84ca5f1cd77925a3c1117242ab0fb509b94a83f8de4fc8d21f856f37a4d025b3024bd0dbb6d8acfda4ab2993fd6eb7a7448d4f66ec725d37f0eb14eb242c0ff3f0c4572ba6b98a4ce905fe1b7ca3daca56c225171428c56af938fb66b37e99e54139157bbf41f536989ef813af738837afcd62290\nA = -e53ad05c88568f09f616797f0b7f2756fb543d691ec2a5b645c1e5892a247302826419a35b1348cfd2c1c569c23c31b4c46d6c57d4a488c29ab5beb77904d4adfcd0a01ea0a26bb0cc8790441cc2c8c900f030d7315b4319f1a3cf5685a140e03abe6b94730ad79e8de1f4a0cded86a3d6cfe2db267fa7dc9b2bb32872a90cc\n\nSquare = eea8028b26e0df090504d54da714a6f5f2695202e53cff479c78aedd47a8dc676243ec586740fde53b3eca9ca02b91031ce766242184109503fbe25b1b6d318e3cd5970fabd16dfa22984dd2e9f1e0f14c189170fc69c031d66663703e6235a942d51a4545bd7b0769d01d302ce2b00b83f01568a1e378f61fd0ca6201b0490330580cd9de85719e174a71915d7efbf65cd73d8f4e66f27e0dd3144d58ec09ed0f7ed7d1238ee596922807100fb7a11127944ddcdec6a9ca3bbf6df7301e354f3f049bfb7c275b43c3d8cda5907a932fba507c9145ea3166081c1b48fcc710ee32cd931f936c796b14f8a78a592e67753a7c9e428a01719c8ba82652f3a89fae110\nA = -3dcb44be1e54c5a5d7db48055ca9afa1ebe2ae648aa6e16ac497502a7deee09ffa124720fad0ab163ce8b3ea6a90f110ea52b67dbc424d0cf1e8c9726dfd9e45bebcefaa5cd5706edeed27896525f31c6bbea3d67ee97badefabf3e2532470b66e3ae3100f66ddf50cf02fc3a8e3f44c304251d3b6a7ca3a6e4bd5d16a41bd97a4\n\n\n# Product tests.\n#\n# These test vectors satisfy A * B = Product.\n\nProduct = 5befab3320f8f90542f3120235abd926aac3805a19e343f690\nA = b057af553afb120db6b7764f8\nB = 857734c4c27a1d17f7cf59dee\n\nProduct = -ab1ce167f4b2945c55ae3f87df50ad07d4be87cf9f8aa07b0c\nA = ae7a6a87ea8981a567d0b3ecc\nB = -fb0fed5f8c737bcacef4d6cb1\n\nProduct = -c2606cd48e6b075c8da79eb4668e7157f1f175c2860fd4c475\nA = -c28dc31984d4583e9d45424c3\nB = ffc4581a5c3f885cf42767e67\n\nProduct = aa6805b5408aff7f914472756da07830dcad902834dbdd6944\nA = -ffa07ff9f503511954e5dd3f9\nB = -aaa7af472ad8957763f5a7c64\n\nProduct = 58ca2569173389df29b5ce4b784086055dee821a7243db7210\nA = af417d936f4690008811a1ae8\nB = 81b26b80b43aa65aa55ded52a\n\nProduct = -a043d31dfce8bd01724d31c863d0a64f1bf013509d77737c42\nA = fb5fae5edefb6997d44a1ecd6\nB = -a336e50c6f7845a1686cc88a3\n\nProduct = -b5d6a45ffce851b201239d938ba551bab7dcb59fc11fc35fce\nA = -f918faa58bb57a2ffb8b01f05\nB = bae08c3006fade695029a1df6\n\nProduct = 6f2fde7d1a18625d727c6345ed85e597d546d9228bf7f0564a\nA = -8d108d7a16f0696d4ceb24445\nB = -c9c764cae465207097ef8d2c2\n\nProduct = 93808b1140841dc9735cd61c6f855ddbbb83066689b0d7e1a0\nA = b386d08daf3fa2154e9c768d6\nB = d2557dceb2d02d04d9c578670\n\nProduct = -ad04212ca8cadb1f7861c5130ba3a747046a2a7e4a0c72b69a\nA = e4e5f7d1311e0c5f2e404d55b\nB = -c18057a328d8c7375afdfd4ee\n\nProduct = -685e75c232f2b4a0e455fe5ee8aea52f292ad8b8178320e692\nA = -a683312f132b2320632e74ef6\nB = a0758f12791453b4af354730b\n\nProduct = 6f588c53185c503dc5b0dc3002d3817ca2e7eb2370b3e9a647\nA = -d70c9b93170261091f0c53f27\nB = -848c86c51a186ac4c9080d3e1\n\nProduct = 5e3bc5a04e054a9a244bf7c86cae215072fdb70e9199989427\nA = 898b64ef09d7cf63966e1a3b5\nB = af638b12f26aa5d12e97439eb\n\nProduct = -8d8372b235b16108285203c03a8aef6fdd3c0e1a9fd31d4f68\nA = f6003dc83818c14fbe36c9998\nB = -9343f6cbcc81fa4c9399dce5f\n\nProduct = -5ee6509abeeb7af7fc5caef40d1822ad3150c8d74f522dc7c8\nA = -875ff6f56ca72cbdf614bb9ca\nB = b375a68a21dfb1f159c22fa14\n\nProduct = ada25be404a17385af5a330da799e5909da81bfa0715baa6f4\nA = -c9b8df392e76abc3eb7d5ce04\nB = -dc5ab818c70594dd917b4243d\n\nProduct = bb24422ee4656ddfcd50ec38201b15baf679d3b75e5cb878ca\nA = f8e12cf4defe388b78510f687\nB = c07ee817b4ae95c2915b88966\n\nProduct = -93da296ba164c7220a17330647aef0980c94eddd2cfa2a3b2d\nA = bc5dc74ddf7a1363d1c2b1f25\nB = -c8f069bad7f93cbfe6df51169\n\nProduct = -6b2e1d132c4e0b0dc9b7e7de7d424fda5180480cb5ff47c755\nA = -a8048acb66a8bb88df39266e7\nB = a34e0b265d71435ae8c92a463\n\nProduct = 6ccb2cd93783576a8602ae43f41c786008b6623a4cca0a010a\nA = -b071f1f54790c951c1dd2a1cf\nB = -9dd89bb4d9b546207e282e2d6\n\nProduct = 5c742ba47d0d64bd97509927ce957deedb855766cc24c60016\nA = b44f3f252c368096fa62747f2\nB = 83439b97dbac579fa4f7b7d23\n\nProduct = -7347ba65691c913286c2fb55e45b177f031c1d86ae0e9f654f\nA = 937cf0643ffa53cdea24d642f\nB = -c81881f78243dd5737a7d28e1\n\nProduct = -9bc0649a703674e59f83ff9b8a560e5cbf51f65ca310f80f95\nA = -b536f8d9769be6f62da941ae5\nB = dc0746fb101881ae0cacde6f1\n\nProduct = bf4992fc3a124de350f9fb90ea825cf663b1fa051282ef22e2\nA = -ff7eacc7de1bb01d668c693aa\nB = -bfaa6627f9fc7ba68ae41bb2d\n\nProduct = 7c8992d34cc0b63f1c953f68d4e12a99d3f3a34d16bd76caa9\nA = 9e0d5a850d078890a983c0ec9\nB = c9b72c118b3e1f1023a696ce1\n\nProduct = -a75840c95082b9a0ae0d6e0a4eb5e09288e4e2a66e9697d9cd\nA = b2b042a21045a74ef1a5091d9\nB = -efbf8b120b384e869692a1b15\n\nProduct = -a510b333bdb4ed7479c142e8fbe2b12f7671a42acbe16c0998\nA = -e7fd5e0bb5496b9d876c27f65\nB = b6262653b2be44501af1d85b8\n\nProduct = a1c1e90afc4684754155526e307fc6ed798746f347bae2c880\nA = -b84674832b26ded0a690a8ff0\nB = -e0b7bdf2fd05a038ed3640b78\n\nProduct = 5588e0c33bffbefcc5695ca0615abd383343f21a8a0d22b222\nA = 80cad81ad9a66ab6a1c2e5669\nB = aa0453a77c8af1584f54750d2\n\nProduct = -6460c2fcd6cf3304ab163ea883ac48e2031cd10f2e9014c0ab\nA = c49ad3d7c8848d4fbf913b10b\nB = -82b3dedbe3cc7cd532ad632e1\n\nProduct = -a18717330b711669e85abde8c4dce426529aa621ba3da2a477\nA = -cab4a9c0a331a5a5e826dda1f\nB = cbfee5041c13075dfe3399aa9\n\nProduct = 8ab6282ee892b53c083d319a9dcab48af97a1ac8493c0bfcad\nA = -f7d13e47f9aaac8c25f9bf75b\nB = -8f4aa95231c1e2336aa092297\n\nProduct = 8f2d1c23c78777ed371f13155445ca3c88cbc0a9b299bdf9d3\nA = 9d8248d00defce1ad081337c3\nB = e8b479295ecd9cef7301f24b1\n\nProduct = -86d5e0c5b581fe59819730b4b71e33d1f85f9ab504c7dbe2d6\nA = b21b45e88acff48562a19729a\nB = -c1cdfebccc763beeac394b997\n\nProduct = -484ca05aefa113bdfcb1bc623f730c9f9555b462a8ab4c9606\nA = -8c12b406c02c4417163c0956b\nB = 8422b15c80c1c087b17eedd92\n\nProduct = 614c3c91f60050c785fd229a3ad74674577a90cacb654e0a5c\nA = -93d45bce155a23a397506d96a\nB = -a87e339c3fd5aebede5fb1b36\n\nProduct = 9683285f194a7e4feeab196a36bdfc4f828035fd184b9cc692\nA = f196d8fe760fdcae7eb60e2f7\nB = 9f7d88a2163ad818bf3a6377e\n\nProduct = -988a64599c19cc64f3cadc1a83fea6550185f6cc3ab82af822\nA = d0584b2a306671e4d2c9d0c7b\nB = -bb6e7559df199c68d6df3a3c6\n\nProduct = -68456814cb0edd951196d04c853172afdd5787a5bd69a57876\nA = -cefce1b0a1fb22862418bb597\nB = 80f614139947aea5e76cd55fa\n\nProduct = b4b1cbf5d6566e7a57aee0cc5c9c8ec4ad885e8766aa7662a4\nA = -d68ed1bea046c6cad057e21db\nB = -d7988b9be54f6e332d019032c\n\nProduct = 6b09212675ff5257a1384371e17b37dcc268bbb141577902e4\nA = a8208053adc20a609d5d01404\nB = a2fa927c5458c4fe662d7a3b9\n\nProduct = -8361bc26f9bcf55f677e047d822d30", - "04027da0d0455b244d10\nA = e82b6410b29020c2d6810a977\nB = -90ddfe0e7f0d6b9cdc0815f70\n\nProduct = -f1b6da00923fd513a83e32040a515649fbd362f69ebc016d9f\nA = -f9b697d9ec774a8d1ee5ea905\nB = f7ccb46a8869cb028492bed53\n\nProduct = d06206963f2e150bacdb32c823c3a47f013d5a267c3c0d0c88\nA = -ea8e63afa99c719897ad7f2ab\nB = -e36f11f55b6148d1b4f46e598\n\nProduct = af774a5eae6084df5ca499ef005642730adabf6a4f9533e2fd\nA = e4c7af7eea3ec9cc2443b7319\nB = c457bc264c8461789931baf85\n\nProduct = -76350f428bfbb95e6c253ec0f457aa84cebe8c7cb1af2a2120\nA = 8fd1ff97465775d44dee58ae0\nB = -d268a7d328f44baf80e35119f\n\nProduct = -787ae3f114f9a8dd4d249d5d3f3b0897b02564b9469416cefe\nA = -bc0b398bd0ec045b0cf147b7e\nB = a4050955c234e473257d0c641\n\nProduct = 9d6320b3d4aabac097a079b9bd2aca7f1898bcab0f23409fd0\nA = -9d7a4ebac630cc0662b816fb5\nB = -ffda517d3eb3214986b04e290\n\nProduct = 80bab8bd800ac8c9dc3bb57dca306f10af6fd88c5d8314833c\nA = 834bc50140d6c6ab938dc58b6\nB = fafee47793cbc533b3c66af3a\n\nProduct = -b08920f5922226b1dec87151ae087d8a7e5c1aea8c9be148b6\nA = bfd5b1ad323c79428cb2db36a\nB = -eb956a10edebdd658e6810fcf\n\nProduct = -6d428e08e8350bb4b0fae3b662c82df2aef7beadaa17430dbb\nA = -a57da276998c548101f514e9f\nB = a9040c1909712e1149d295765\n\nProduct = a57da276998c548101f514e9f\nA = -a57da276998c548101f514e9f\nB = -1\n\nProduct = 14afb44ed3318a90203ea29d3e\nA = a57da276998c548101f514e9f\nB = 2\n\nProduct = -295f689da6631520407d453a7c\nA = a57da276998c548101f514e9f\nB = -4\n\nProduct = -867614005cc204a8d19720fe13\nA = -a57da276998c548101f514e9f\nB = d\n\nProduct = 12bf3b676f64e5929d38c35e803\nA = -a57da276998c548101f514e9f\nB = -1d\n\nProduct = 24d8f92c68303ed0b96f91a8167\nA = a57da276998c548101f514e9f\nB = 39\n\nProduct = -49b1f258d0607da172df23502ce\nA = a57da276998c548101f514e9f\nB = -72\n\nProduct = -6fd5e6ca25c3d51b2e529f22173\nA = -a57da276998c548101f514e9f\nB = ad\n\nProduct = 1276d4705b81b82da4c7e82559d7\nA = -a57da276998c548101f514e9f\nB = -1c9\n\nProduct = 1ddb9abfc5d4017f068a67b5f4fd\nA = a57da276998c548101f514e9f\nB = 2e3\n\nProduct = -3a8b41c914b1b4a4e341433601f7\nA = a57da276998c548101f514e9f\nB = -5a9\n\nProduct = -97c0f4ba414d6e7d4c8b7ced84d4\nA = -a57da276998c548101f514e9f\nB = eac\n\nProduct = 1198739e0c23639c176d46d13f7c8\nA = -a57da276998c548101f514e9f\nB = -1b38\n\nProduct = 159150954ee0dedf541e4dbac0ec3\nA = a57da276998c548101f514e9f\nB = 215d\n\nProduct = -441d4bc44c86f02ff12c3d91a1562\nA = a57da276998c548101f514e9f\nB = -695e\n\nProduct = -64726b76005ebee27592237ba5dde\nA = -a57da276998c548101f514e9f\nB = 9b62\n\nProduct = bbe4ec7cf7c5bbd198e0ea86bb658\nA = -a57da276998c548101f514e9f\nB = -122a8\n\nProduct = 21f717d05681fd2eb1796776a69ef7\nA = a57da276998c548101f514e9f\nB = 348a9\n\nProduct = -396ac788a1748bc6955f99be4d2c64\nA = a57da276998c548101f514e9f\nB = -58d1c\n\nProduct = -54a213eb083aed1a04f3d1b2da62e7\nA = -a57da276998c548101f514e9f\nB = 82eb9\n\nProduct = 1366fb9c20fb14b8b9a9be4b3e3dde1\nA = -a57da276998c548101f514e9f\nB = -1e037f\n\nProduct = 238d65fd26da4733e5d93ab2485d40b\nA = a57da276998c548101f514e9f\nB = 36ff15\n\nProduct = -38272a99be154d531e922be405aee9a\nA = a57da276998c548101f514e9f\nB = -56dd26\n\nProduct = -64651b62b6a454c08951632c7f2c398\nA = -a57da276998c548101f514e9f\nB = 9b4d68\n\nProduct = fb272e3597b816144f8b945ae6130e0\nA = -a57da276998c548101f514e9f\nB = -1848320\n\nProduct = 280d9f5ed7243712ecb9a7c6358bcb8b\nA = a57da276998c548101f514e9f\nB = 3df5795\n\nProduct = -2fbb6bb8e1ba78cefc47fbbc20e188ee\nA = a57da276998c548101f514e9f\nB = -49d6652\n\nProduct = -57f29c13691ffa1642d2860dab9d288e\nA = -a57da276998c548101f514e9f\nB = 880c2b2\n\nProduct = 139c19d7668e6aabf2d7206cb0723ed34\nA = -a57da276998c548101f514e9f\nB = -1e55aa4c\n\nProduct = 2950ce04bf0cf836d4fe94b88fb757d0a\nA = a57da276998c548101f514e9f\nB = 3fe968b6\n\nProduct = -5175239488dad05a58414251496d2a06c\nA = a57da276998c548101f514e9f\nB = -7e020414\n\nProduct = -945ff0ed38bc6020cf679cbd3e0758c6d\nA = -a57da276998c548101f514e9f\nB = e585e573\n\nProduct = 11c69ae98f6b27e95477986f796bc67c8c\nA = -a57da276998c548101f514e9f\nB = -1b7f653f4\n\nProduct = 209afe75e8fb5ac76d13c06b545f5d4d73\nA = a57da276998c548101f514e9f\nB = 3270154ad\n\nProduct = -386d64b215e41506514f4988ed237e4da2\nA = a57da276998c548101f514e9f\nB = -5749c891e\n\nProduct = -6c13cccdb1d140d0babd52707ea72fa278\nA = -a57da276998c548101f514e9f\nB = a72fb6288\n\nProduct = 136228a8a45540372b9b3cd7f82021f6546\nA = -a57da276998c548101f514e9f\nB = -1dfc08a2fa\n\nProduct = 1f0ad3babf9d132eaa08cf5cdb8f19dbf01\nA = a57da276998c548101f514e9f\nB = 30050f2e5f\n\nProduct = -50d615ce183258e95af77319b766fac81e2\nA = a57da276998c548101f514e9f\nB = -7d0bf92cde\n\nProduct = -817d358293b86a56a4e881e50257c549471\nA = -a57da276998c548101f514e9f\nB = c84efb12ef\n\nProduct = f09b9e80be251de474d726b16e25a6865fc\nA = -a57da276998c548101f514e9f\nB = -1743322a484\n\nProduct = 22996cb0f9c60e35dce49f3825f8a479db26\nA = a57da276998c548101f514e9f\nB = 3585acec11a\n\nProduct = -2b307a37c91791a61c0691858f5f783e4678\nA = a57da276998c548101f514e9f\nB = -42cf6be3e88\n\nProduct = -8826698fcba6c30d755fc523de1cc25301ae\nA = -a57da276998c548101f514e9f\nB = d29cc8af592\n\nProduct = ae37fc99fd419809310782714530d7428d77\nA = -a57da276998c548101f514e9f\nB = -10d8059d4a29\n\nProduct = 1d544a20f9bc7d95ab67d1f65743979f23bba\nA = a57da276998c548101f514e9f\nB = 2d5eadef1c06\n\nProduct = -367897184e9929a0294d320f10278889fbeb7\nA = a57da276998c548101f514e9f\nB = -54431582d0e9\n\nProduct = -943a509076a00060a2e7fa1cddb7468d734a1\nA = -a57da276998c548101f514e9f\nB = e54bb102f4bf\n\nProduct = fcce6e42879af5ad13545c0bcaab85b690cea\nA = -a57da276998c548101f514e9f\nB = -18711db522cd6\n\nProduct = 258c49f86d0cbb14ae9edbd3456be8cede2022\nA = a57da276998c548101f514e9f\nB = 3a1562c7c269e\n\nProduct = -4a8bbce59ad7daa51136d557f7fa16e9a2faad\nA = a57da276998c548101f514e9f\nB = -7350e780b0f33\n\nProduct = -82f53ec9333275d5cc271876a7db936db49280\nA = -a57da276998c548101f514e9f\nB = ca94ad312dd80\n\nProduct = 11daee4fcc713db5b2806e47fa5dff3b5b770eb\nA = -a57da276998c548101f514e9f\nB = -1b9ed6758f9635\n\nProduct = 17038cac4f0c94dc24985ea108ae6682e175752\nA = a57da276998c548101f514e9f\nB = 2399b8a9b1116e\n\nProduct = -37e5f14394bf347a3ed061769fe8e6424af4348\nA = a57da276998c548101f514e9f\nB = -567840a7569fb8\n\nProduct = -9253d4a32a88d8f725984514d969012ead7cc9a\nA = -a57da276998c548101f514e9f\nB = e25b246f733f26\n\nProduct = ace3648371c16a931d29004e79f5b9678391da5\nA = -a57da276998c548101f514e9f\nB = -10b717b27b6a13b\n\nProduct = 1faa5b45d04c143c339b09d3aad94d39b94ef960\nA = a57da276998c548101f514e9f\nB = 30fbd672e106aa0\n\nProduct = -3fdfe246d27aae0d08d63b2bc501461d2bff3b8d\nA = a57da276998c548101f514e9f\nB = -62cef5f078a8253\n\nProduct = -5b792bfaeff04ee3d948cb343a249d49eb344f57\nA = -a57da276998c548101f514e9f\nB = 8d805ac65649c49\n\nProduct = c5f824406161eec321da5a58e3e00d393b55abe9\nA = -a57da276998c548101f514e9f\nB = -1323dd41d2e1e077\n\nProduct = 2226dec8a57be8e84e42559007e2d101ccbe67f8d\nA = a57da276998c548101f514e9f\nB = 34d47842b5d0be53\n\nProduct = -340f50f812c7420b502000940788a700f6769788a\nA = a57da276998c548101f514e9f\nB = -508836d8e1193d36\n\nProduct = -a00f1d96e19c590479625c5329a87774b5964cc78\nA = -a57da276998c548101f514e9f\nB = f798fc858657f888\n\nProduct = cb94f830cba8997331912a6a31c34f1bef826d121\nA = -a57da276998c548101f514e9f\nB = -13aec7a5c52a0883f\n\nProduct = 16b45140b048d6dc0b9fc811df7ce7dd88357fff04\nA = a57da276998c548101f514e9f\nB = 231f27f3e347bd67c\n\nProduct = -2aa94179351b4e87de5849ab619d94f47450640199\nA = a57da276998c548101f514e9f\nB = -41fe3ec2189599cc7\n\nProduct = -5489401d3da93158d4284e557d74016c0a7cfd935a\nA = -a57da276998c548101f514e9f\nB = 82c5281df41bfc066\n\nProduct = ae04d5b212ecfc9a6d7df07794d565df52991fb70e\nA = -a57da276998c548101f514e9f\nB = -10d3139229f5d02432\n\nProduct = 27821bc811f45d63089790b41d307be978d4b19564c\nA = a57da276998c548101f514e9f\nB = 3d1da85cc012b3e234\n\nProduct = -3de3c9e9d7fa3020a578706339314890dccf63096c2\nA = a57da276998c548101f514e9f\nB = -5fbcfb28bfc9044bfe\n\nProduct = -627dcb299a6720044abcf11469bdfd3f951edbb5bf7\nA = -a57da276998c548101f514e9f\nB = 985b930517b78e6ba9\n\nProduct = cc0622441497a37fddf1856d5e2c99df52b99ea4573\nA = -a57da276998c548101f514e9f\nB = -13b9b88948fb7e95cad\n\nProduct = 1a5168e1a492210591ad1ed660adde9110390e4caf32\nA = a57da276998c548101f514e9f\nB = 28b631c6e04b6ab0d8e\n\nProduct = -4d8ec27b7460ce616421b9f5cae708c2ac241daa59b4\nA = a57da276998c548101f514e9f\nB = -77f99bdf1eb09da6dcc\n\nProduct = -55a", - "fd796db7bce822a00073fc8926d3bd0c79772f036\nA = -a57da276998c548101f514e9f\nB = 848cdd6212b9bb3620a\n\nProduct = dc494b0d73e8ec07cd2bb6dd8191d2b4d48e7700cc34\nA = -a57da276998c548101f514e9f\nB = -154c39567bd8be5f6b4c\n\nProduct = 240e9301b4345b914ecd91a49a0e651524dcecb6fdc6c\nA = a57da276998c548101f514e9f\nB = 37c6e7ee89cf87674814\n\nProduct = -39002ecfd6d96661b336157ccef6536756ad2e9219be3\nA = a57da276998c548101f514e9f\nB = -582cdab09915a652203d\n\nProduct = -695f49fc891d53f396f0593efae3973082b76d4f9e944\nA = -a57da276998c548101f514e9f\nB = a30074dbce2246af043c\n\nProduct = bba2b7b45b97cb0d7fb30fed95089870742ad69e7aed7\nA = -a57da276998c548101f514e9f\nB = -1224195afc7b394ae8cc9\n\nProduct = 1910edc278515ab7d4cc09b496dc3c06c32c75bc7368af\nA = a57da276998c548101f514e9f\nB = 26c6701c39334169e7bf1\n\nProduct = -3670b7f9b661aba35ce50984d83173c84c8fa60e04d100\nA = a57da276998c548101f514e9f\nB = -5436e84b4a29858a68f00\n\nProduct = -7fa0d3e0082b37475342b7e22e5dbad7b8d4cb5d64f871\nA = -a57da276998c548101f514e9f\nB = c56e0f44fc63bca242eef\n\nProduct = da7fe3367ce640fa5941c033ac1874312f10ba5950da75\nA = -a57da276998c548101f514e9f\nB = -15200043166ff309f0426b\n\nProduct = 1871d72481f66b1d413100edd6b339cbbaa67b3b2b3cd57\nA = a57da276998c548101f514e9f\nB = 25d057879db26fa29a5e49\n\nProduct = -3cf1dd1e2df3456757d72f35353c3c7a659b2ef844ad857\nA = a57da276998c548101f514e9f\nB = -5e46be70de21949df67349\n\nProduct = -5e861cbe47aefab2a7ea59292aab1258932b9a322f66e63\nA = -a57da276998c548101f514e9f\nB = 9238670897685a6c9cbdbd\n\nProduct = f623344788efb857db55c924e95a437effa4dc8bb2bcd24\nA = -a57da276998c548101f514e9f\nB = -17cc0ec84c228225a7cf45c\n\nProduct = 15514c916b0ae7cde6add16c629d3e19ba52a101d75dff72\nA = a57da276998c548101f514e9f\nB = 20f9f925b3ed307edbb154e\n\nProduct = -460cf5b14f9d0b547c3084bf44207bf881745c409b08d07f\nA = a57da276998c548101f514e9f\nB = -6c5cbfd29f3dae1dce99221\n\nProduct = -5ddf7fb91d765af97dfda5333d8779e80837c2b51cfb4f43\nA = -a57da276998c548101f514e9f\nB = 9136aa79080defd1bcf90dd\n\nProduct = 12c1a0edfb6ab6a0caae2553fb3743827e1470a8954e0a3fd\nA = -a57da276998c548101f514e9f\nB = -1d03b512470dc3052779f3e3\n\nProduct = 28388a244214abf046488a8d95308d95f021eae4b994a5a52\nA = a57da276998c548101f514e9f\nB = 3e37dce784274962ff862e6e\n\nProduct = -4da476e76119deef291c0f56934a912a0877278a19a561ee0\nA = a57da276998c548101f514e9f\nB = -781b2f2dc40094a7f8fed520\n\nProduct = -5792496d33dd45e225f9dfca17419a04e075ffc0c90b37b82\nA = -a57da276998c548101f514e9f\nB = 87772a4fb582acafd3e4ef3e\n\nProduct = dd3a3506a7d748de16fb43d666928a87de0354d8e8a1bcaaa\nA = -a57da276998c548101f514e9f\nB = -1563841bf7851ff158a395716\n\nProduct = 24e8fb09a9ab0808ff643122479dea5ed41060c6c5b74e8752\nA = a57da276998c548101f514e9f\nB = 3918c30b5568318a58e9be16e\n\nProduct = -366c125f96b38b58d01c939c27c4100af3377eabb792b5491a\nA = a57da276998c548101f514e9f\nB = -542fb814f45924aa09a16f2a6\n\n\n# Quotient tests.\n#\n# These test vectors satisfy Quotient = A / B, rounded towards zero, and\n# Remainder = A - B * Quotient.\n\nQuotient = 1\nRemainder = 0\nA = 8cdaaa7c422f3c2bb0ace2da7d7ff151e5bdefb23e6426cf3e6b21491e6e80e977bfa6c65931a8dee31fc7992c0c801d5d7c\nB = 8cdaaa7c422f3c2bb0ace2da7d7ff151e5bdefb23e6426cf3e6b21491e6e80e977bfa6c65931a8dee31fc7992c0c801d5d7c\n\nQuotient = -2\nRemainder = 1\nA = 107f0e6cebfe22ac11294a06fed2b994d01c9b3610d50bdd254adafd08c93be8ebdd1e85e1286fe9c9e682a90cbbd6351681b\nB = -83f873675ff11560894a5037f695cca680e4d9b086a85ee92a56d7e84649df475ee8f42f09437f4e4f34154865deb1a8b40d\n\nQuotient = -4\nRemainder = -2\nA = -3d8746ae2123c2d3f1d35910b42af1f86f5e81f8e98986cea20b2a1bdb8af6cf111f1258f112c837accdf4868463fe9eba536\nB = f61d1ab8848f0b4fc74d6442d0abc7e1bd7a07e3a6261b3a882ca86f6e2bdb3c447c4963c44b20deb337d21a118ffa7ae94d\n\nQuotient = 8\nRemainder = -3\nA = -5645d65662eaac73050de06f8f982a9b2ae680467712284be3e2b0e58ef4bf4d72b5be5e12ee1fd803b47f161759662ff5c4b\nB = -ac8bacacc5d558e60a1bc0df1f30553655cd008cee245097c7c561cb1de97e9ae56b7cbc25dc3fb00768fe2c2eb2cc5feb89\n\nQuotient = 10\nRemainder = 4\nA = 813bc46ee19ffeab364073a89f96913f340d43ee72129ea9edac1beb4ebe1336450d2eabc7b26e51c400cec60d6ee459033b4\nB = 813bc46ee19ffeab364073a89f96913f340d43ee72129ea9edac1beb4ebe1336450d2eabc7b26e51c400cec60d6ee459033b\n\nQuotient = -20\nRemainder = 5\nA = 12805392c55ffa0e27e85e15f2b339872793664e9ed3074cd2600aa52459a57197130d1ea46775ef43115c9413248cc7b34805\nB = -94029c962affd0713f42f0af9599cc393c9b3274f6983a669300552922cd2b8cb89868f5233baf7a188ae4a09924663d9a40\n\nQuotient = -40\nRemainder = -6\nA = -3579fc4d6083394c691b060cf9e20318fe17da0487337f76710bd11512578830ba94ac7b587a2d5ab7cb4afe611e349cdcfb86\nB = d5e7f135820ce531a46c1833e7880c63f85f68121ccdfdd9c42f4454495e20c2ea52b1ed61e8b56adf2d2bf98478d27373ee\n\nQuotient = 80\nRemainder = -7\nA = -74ebad4b39ebaaff82cd91082408c979527907c363d8f0f75db410523f8477c074c45ff85851b6275b1ebc5279029818e78d87\nB = -e9d75a9673d755ff059b2210481192f2a4f20f86c7b1e1eebb6820a47f08ef80e988bff0b0a36c4eb63d78a4f2053031cf1b\n\nQuotient = 100\nRemainder = 8\nA = d2d8a4419fb3b1c22bfca04ca08c2ee066ccbc9fce2f41861b5eef91efd3c13eeb7eae5abea0ef1849662cfdfef7bbff892c08\nB = d2d8a4419fb3b1c22bfca04ca08c2ee066ccbc9fce2f41861b5eef91efd3c13eeb7eae5abea0ef1849662cfdfef7bbff892c\n\nQuotient = -200\nRemainder = 9\nA = 1bf534da2f4365c96fc5dd4928e73ac24b157b5136ead90cf6596033ec387a2c14bca828000ae1725f3a5ace8ad67a8c07a0a09\nB = -dfa9a6d17a1b2e4b7e2eea494739d61258abda89b756c867b2cb019f61c3d160a5e5414000570b92f9d2d67456b3d4603d05\n\nQuotient = -400\nRemainder = -a\nA = -3a172cc9483774544311a1366659d9e61cc9fac7dc11c68e36aa991ef4d5e96becf5bac3e0967c904d926617ea11bb9551b980a\nB = e85cb32520ddd1510c4684d9996767987327eb1f70471a38daaa647bd357a5afb3d6eb0f8259f2413649985fa846ee5546e6\n\nQuotient = 800\nRemainder = -b\nA = -5ecff3a3e47fa615b6e3ce2dedfdeefbfe1d437c394631820968a9650b59dc3a2dd1c9a0b06537e4e5c408a59e580921503580b\nB = -bd9fe747c8ff4c2b6dc79c5bdbfbddf7fc3a86f8728c630412d152ca16b3b8745ba3934160ca6fc9cb88114b3cb01242a06b\n\nQuotient = 1000\nRemainder = c\nA = d3ef80fca0ab3ac3432b22e2b485131d816810c39d02a9c82dcc05ec5e6406bc216026de3abe53ab103ea3b2ddbc2ea377ae00c\nB = d3ef80fca0ab3ac3432b22e2b485131d816810c39d02a9c82dcc05ec5e6406bc216026de3abe53ab103ea3b2ddbc2ea377ae\n\nQuotient = -2000\nRemainder = d\nA = 163956bc32325f28f48d41d32bb08d2a9c4ccbb0d818368fb13941e82b27da21d04094f7e897ce79c2d0ff8470505f1ef63fc00d\nB = -b1cab5e19192f947a46a0e995d846954e2665d86c0c1b47d89ca0f41593ed10e8204a7bf44be73ce1687fc238282f8f7b1fe\n\nQuotient = -4000\nRemainder = -e\nA = -3763f8e43bd05e6ffeec6d509bbe6ff9a9022ced8cb191c9abaf5fd0e0b75a53e2ad581455e3af09e702a77b164ed3fb54ae000e\nB = dd8fe390ef4179bffbb1b5426ef9bfe6a408b3b632c64726aebd7f4382dd694f8ab56051578ebc279c0a9dec593b4fed52b8\n\nQuotient = 8000\nRemainder = -f\nA = -531dd44dfa9e79a5aec8fa7c84bd3b753c146770d22d2c14a6d2125f7ab95e9b320e84c31cf3e0d883e1295a220f2a546550800f\nB = -a63ba89bf53cf34b5d91f4f9097a76ea7828cee1a45a58294da424bef572bd36641d098639e7c1b107c252b4441e54a8caa1\n\nQuotient = 10000\nRemainder = 10\nA = 900996b61f58713f0755e68bbdfa4e0bb47f034bb0304f77829847923d14715def1771f43b526c41b9667438b434d2b966c20010\nB = 900996b61f58713f0755e68bbdfa4e0bb47f034bb0304f77829847923d14715def1771f43b526c41b9667438b434d2b966c2\n\nQuotient = -20000\nRemainder = 11\nA = 179d7ede3db0c105525286551331d5b9e1f97a7883f0c13cf250afe9765bb5aaa527af7945c19cdd4596565cbc8532a3cfa5c0011\nB = -bcebf6f1ed86082a929432a8998eadcf0fcbd3c41f8609e792857f4bb2ddad55293d7bca2e0ce6ea2cb2b2e5e429951e7d2e\n\nQuotient = -40000\nRemainder = -12\nA = -293dc443c294c6a6c53dd49e84f58305d59a432afb6c7ea2039cd02a513231239571ae07f29b5427e869b9faa485511ca45980012\nB = a4f7110f0a531a9b14f7527a13d60c1756690cabedb1fa880e7340a944c8c48e55c6b81fca6d509fa1a6e7ea921544729166\n\nQuotient = 80000\nRemainder = -13\nA = -5b637eb8aa51ef15a18d9b144031c9756527fc0fb96c84b6df03700e5079ae1b3e96940a2c1e07f3b47ad8a9b2b8ca99171a00013\nB = -b6c6fd7154a3de2b431b3628806392eaca4ff81f72d9096dbe06e01ca0f35c367d2d2814583c0fe768f5b153657195322e34\n\nQuotient = 100000\nRemainder = 14\nA = 87c846f5469d4c5819aed0c7e77797209b2c1b83a7a0e2be70280b9f30946b5db9bd0f25a06cf4bdba1c7183a1b9eb75c19400014\nB = 87c846f5469d4c5819aed0c7e77797209b2c1b83a7a0e2be70280b9f30946b5db9bd0f25a06cf4bdba1c7183a1b9eb75c194\n\nQuotient = -200000\nRemainder = 15\nA = 11c2a4509f419aa977c3d37fa446fcf21b4b3b9f983fbaddeba4f51c285ac40322", - "00711a54cc6edf24297b1f3d46ad020131a00015\nB = -8e152284fa0cd54bbe1e9bfd2237e790da59dcfcc1fdd6ef5d27a8e142d62019100388d2a66376f9214bd8f9ea356810098d\n\nQuotient = -400000\nRemainder = -16\nA = -39e37ae0edd92b957e84682358039f5e432c42492a44f3de01cdf74d643760260f2837946608663e12291e9b0695449c1153800016\nB = e78deb83b764ae55fa11a08d600e7d790cb10924a913cf780737dd3590dd80983ca0de51982198f848a47a6c1a551270454e\n\nQuotient = 800000\nRemainder = -17\nA = -72f725edd5a3dd6f20b5e9ca7da08a99f8ec9214c80588182c0d42e03bcff34b488b28c03cdf41813a6193c10672a8ee68f6000017\nB = -e5ee4bdbab47bade416bd394fb411533f1d92429900b1030581a85c0779fe6969116518079be830274c327820ce551dcd1ec\n\nQuotient = 1000000\nRemainder = 18\nA = 966df62c26acab2d3d1dbe729e48d0181c68e9f5eba45f6caefa38d60e34057d09fe620abb8640cec8cac755957aaad7c6fd000018\nB = 966df62c26acab2d3d1dbe729e48d0181c68e9f5eba45f6caefa38d60e34057d09fe620abb8640cec8cac755957aaad7c6fd\n\nQuotient = -2000000\nRemainder = 19\nA = 190790727c1514b4ef83a1c6aa07493c0af7087fbc8a675bfd9a1e97b8ef80ef684219d6c6f1a5fb5b919f105fd7717cdd5aa000019\nB = -c83c8393e0a8a5a77c1d0e35503a49e057b843fde4533adfecd0f4bdc77c077b4210ceb6378d2fdadc8cf882febb8be6ead5\n\nQuotient = -4000000\nRemainder = -1a\nA = -22d115ab02f8663d8c009960086a0275d301d358cd3b250bb9e7c16cc6ebed4a8fbe43bbced856d93be64a17377d95f5f9c8800001a\nB = 8b4456ac0be198f63002658021a809d74c074d6334ec942ee79f05b31bafb52a3ef90eef3b615b64ef99285cddf657d7e722\n\nQuotient = 8000000\nRemainder = -1b\nA = -41f2e708ba47494a13607223b08e6d99c0b4247436632961d873804e83446dc97139ffaef3e25969950bd4b5bb4ff73b1a25000001b\nB = -83e5ce11748e929426c0e447611cdb33816848e86cc652c3b0e7009d0688db92e273ff5de7c4b2d32a17a96b769fee76344a\n\nQuotient = 10000000\nRemainder = 1c\nA = e4b52f78179039499c2f6b500840f41103fbd60eac0d7082297236f25189c18a8301a92f533945047fbb83427dcade334336000001c\nB = e4b52f78179039499c2f6b500840f41103fbd60eac0d7082297236f25189c18a8301a92f533945047fbb83427dcade334336\n\nQuotient = -20000000\nRemainder = 1d\nA = 10888959278661bc36089519a215bda60f9ce24ff7c0ac1f543b6e652f94dbff1f32aa40cad2b4b4d676f16948551501c29f2000001d\nB = -84444ac93c330de1b044a8cd10aded307ce7127fbe0560faa1db73297ca6dff8f99552065695a5a6b3b78b4a42a8a80e14f9\n\nQuotient = -40000000\nRemainder = -1e\nA = -3ada453530a180fda58533ab8c62beb4f693a134f512e4d23e487dac3b575e5390c0a90992400e402bb47aac93d46ded55f54000001e\nB = eb6914d4c28603f69614ceae318afad3da4e84d3d44b9348f921f6b0ed5d794e4302a42649003900aed1eab24f51b7b557d5\n\nQuotient = 80000000\nRemainder = -1f\nA = -57879eb5d92d565daac3ac5173639bfe44b6ecc69ff770af57bd79c9b93841c5677042cb362b794f3d8b24b0d3b73ed1cba58000001f\nB = -af0f3d6bb25aacbb558758a2e6c737fc896dd98d3feee15eaf7af3937270838acee085966c56f29e7b164961a76e7da3974b\n\nQuotient = 100000000\nRemainder = 20\nA = 89a2f1792afc54467955839eddc9ef2e37d391ce7a1a4a205291220c1f49f59ee31fc7a7a7f7706c199bf5c8c951a0d0743d00000020\nB = 89a2f1792afc54467955839eddc9ef2e37d391ce7a1a4a205291220c1f49f59ee31fc7a7a7f7706c199bf5c8c951a0d0743d\n\nQuotient = -200000000\nRemainder = 21\nA = 1c267719338a4562e934bc57fabe6da86ca534a34244bd38c15032f01f47c2fd498c83f644b345c5c661ada0e586a096bb63000000021\nB = -e133b8c99c522b1749a5e2bfd5f36d436529a51a1225e9c60a819780fa3e17ea4c641fb2259a2e2e330d6d072c3504b5db18\n\nQuotient = -400000000\nRemainder = -22\nA = -250249f2185d4b428fa9534f03ef3cbed535bd31c56c0b273e6c3d35e0266f7777a6e59a99da5738b8e3af8ac60061d6716ac00000022\nB = 940927c861752d0a3ea54d3c0fbcf2fb54d6f4c715b02c9cf9b0f4d78099bdddde9b966a67695ce2e38ebe2b18018759c5ab\n\nQuotient = 800000000\nRemainder = -23\nA = -710b30c23c3c4e646ba90da33d2ce35af2ff181c40b02e3ffa607966730c6b6e274dd4c3c78e578e0b10f431f2d832274bf6800000023\nB = -e216618478789cc8d7521b467a59c6b5e5fe303881605c7ff4c0f2cce618d6dc4e9ba9878f1caf1c1621e863e5b0644e97ed\n\nQuotient = 1000000000\nRemainder = 24\nA = 877f1caf75e7166ef18484d0718947893fd1ec016984387debc55c19e378a487a5ddbb03a80a88316f6fca16ae148933e719000000024\nB = 877f1caf75e7166ef18484d0718947893fd1ec016984387debc55c19e378a487a5ddbb03a80a88316f6fca16ae148933e719\n\nQuotient = -2000000000\nRemainder = 25\nA = 1ed1b7d9e4cf3d44ee98ef69850e61a39f54cc407c6795c07c887374441fd9ec258c21193f8a8c55802fb8f8c579cf94cb0ce000000025\nB = -f68dbecf2679ea2774c77b4c28730d1cfaa66203e33cae03e4439ba220fecf612c6108c9fc5462ac017dc7c62bce7ca65867\n\nQuotient = -4000000000\nRemainder = -26\nA = -35d324ba37d2000f960ca1c9e1ab96e341a2ae6a5ea5cef014c73a39dde000d8ad9606b817ad67e4e4593cc5894d354854898000000026\nB = d74c92e8df48003e5832872786ae5b8d068ab9a97a973bc0531ce8e777800362b6581ae05eb59f939164f3162534d5215226\n\nQuotient = 8000000000\nRemainder = -27\nA = -7039477c3e0a6f415e25e9f9b1dab1edcd8a23f984e7e3bc149c206a3b756b1be001450af4049cd4535e4243d7032afcf6790000000027\nB = -e0728ef87c14de82bc4bd3f363b563db9b1447f309cfc778293840d476ead637c0028a15e80939a8a6bc8487ae0655f9ecf2\n\nQuotient = 10000000000\nRemainder = 28\nA = d6c59dd07409da98f7bbc7ee471b6e06c4d9e832e9f4d04ed9da63564d37d3072a950564cf549bb5d6e7dc85565d3cc8ba340000000028\nB = d6c59dd07409da98f7bbc7ee471b6e06c4d9e832e9f4d04ed9da63564d37d3072a950564cf549bb5d6e7dc85565d3cc8ba34\n\nQuotient = -20000000000\nRemainder = 29\nA = 14d27a16a9cf2fdbc85b88a604dd8f0e57b5b34a27089d75d805e05fbb367dfa61c085aa98b896e3e53b85ef774a3fa52417a0000000029\nB = -a693d0b54e797ede42dc453026ec7872bdad9a513844ebaec02f02fdd9b3efd30e042d54c5c4b71f29dc2f7bba51fd2920bd\n\nQuotient = -40000000000\nRemainder = -2a\nA = -3bd0119619fbb5b260c44050d61e6b1925a49713d754ceb06bafb1d730a93f199df654b153c40e75096ebbaf5a6ce3c801820000000002a\nB = ef40465867eed6c9831101435879ac6496925c4f5d533ac1aebec75cc2a4fc6677d952c54f1039d425baeebd69b38f200608\n\nQuotient = 80000000000\nRemainder = -2b\nA = -61a283fe41d965ee770704bb453f689cb82a81089422d6d904a91776a06d32857220286e6ef6327807b724062dda143b46890000000002b\nB = -c34507fc83b2cbdcee0e09768a7ed139705502112845adb209522eed40da650ae44050dcddec64f00f6e480c5bb428768d12\n\nQuotient = 100000000000\nRemainder = 2c\nA = 87bd03a64d9c56fe340137065ba36bd07b556119546dd1fc3ae087ead32bc79ca7efb5c7230ea7bfb00ad419096d9279fbe10000000002c\nB = 87bd03a64d9c56fe340137065ba36bd07b556119546dd1fc3ae087ead32bc79ca7efb5c7230ea7bfb00ad419096d9279fbe1\n\nQuotient = -200000000000\nRemainder = 2d\nA = 1eb7cfb197d19f56ad994eca52d1af6466fd09da07d68d63067602046b2d42d3063ef5eda6b58afd69fd92b0b727a0ecde1420000000002d\nB = -f5be7d8cbe8cfab56cca7652968d7b2337e84ed03eb46b1833b01023596a169831f7af6d35ac57eb4fec9585b93d0766f0a1\n\nQuotient = -400000000000\nRemainder = -2e\nA = -3ab858b3329e5bd0469118be52a867b2febbe2894d962cedeb3a5be1738db1cea106cd0710c9f6937348c2c63b109ae623d500000000002e\nB = eae162ccca796f411a4462f94aa19ecbfaef8a253658b3b7ace96f85ce36c73a841b341c4327da4dcd230b18ec426b988f54\n\nQuotient = 800000000000\nRemainder = -2f\nA = -6137bae6cf7573afcbb6fd5c066ba37648cba8db0ecafe9dbc66959b19deabf42f3083719a2268b7602bafa2140a1ee8ce7d80000000002f\nB = -c26f75cd9eeae75f976dfab80cd746ec919751b61d95fd3b78cd2b3633bd57e85e6106e33444d16ec0575f4428143dd19cfb\n\nQuotient = 1000000000000\nRemainder = 30\nA = d00fec043edadc093673e5f5abef0c6bacdf1f3faa49a831a645bf80db7539d657f69403b122a5c6f879eb8e63be54d35ed7000000000030\nB = d00fec043edadc093673e5f5abef0c6bacdf1f3faa49a831a645bf80db7539d657f69403b122a5c6f879eb8e63be54d35ed7\n\nQuotient = -2000000000000\nRemainder = 31\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -940693131e2ba7b2af531803794983337dd526f0d84d08d58723edf002a388d55c8502d88c2a2a6e78233a2a1b1c8d339a13\n\nQuotient = -611b743a0e2acb1043bb33de50a59eaa0405b37bf6b622075dd69291fe5b53305dbfcc377d1f3082319c153d0c1ffb3b3346\nRemainder = -16e346b6a4297\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 30c77f3380ccf\n\nQuotient = b9e34073d5e6e5b9e5d2d7250150f8ad86870faeb88d5aed5029fb25c176de216e2388e0f5d33f7c3b56102873eb40b06f2\nRemainder = -16ebc86eb88339\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -197b6f6ad5b75c\n\nQuotient = 141bc8752e846cd63743e6fce4a22efc3eb5f0ce46ba81b8f578c94c516288ec3610fc9923f45d4af2b94c0b0a20b48ed0a\nRemainder = 9bab19f12d81c3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a67342", - "6000000000031\nB = eb90162ecae18b\n\nQuotient = -381bd85c951e1dd775b0d7fab344aadf06b1b592c643b5852fa44aa55159eedf3b3e47fe0d9f399ad92da85ab2bfd18240\nRemainder = 1e4f817a2f52b71\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -546c109fa8a9d7b\n\nQuotient = -5e385a83b56830626cf8306acc232f955178080e86384bbcf92eec3a8961360223c4cfc1d8d118022972e61866cbfc46b\nRemainder = -292e149300fdd1ad\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3246242094394c8c\n\nQuotient = 9af0246f4b49316df43f61ae3795a764fe9b1d071ce227982ebda7988a7a7a98129c94a76635c6913cb15e4f75ea1608\nRemainder = -dd3b3e32ddc79cb9\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1e928618913898b2f\n\nQuotient = 1fe40099811c648aa4e84e4fbb8cbc19706774a11391fc03a9667d8dc72dd0b26c4a46d0bae56ba90fe4bfac1517d241\nRemainder = 16e021603d30dde2\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 948887c1634f4b08f\n\nQuotient = -3f4fa4c179dab02ad461bbea8f890292c934496db560f72878323a4463d77ae261363f4dc8f53eab145fcc3815d3253\nRemainder = 407ccb4f0b814dc5c5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4ad17434071e1ce664\n\nQuotient = -4d17d19f7f6861189a520776339a1e425876808111c303e391118714370111151ef4ad2e6e84250f59b0fe09ab3293\nRemainder = -36f745b0f421d16db7\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3d71635bcc25183cdde\n\nQuotient = b976d544af44e711351c6618106d3a002c42ebbe22fe939a2457d24e8dcc35c95dde5c7c77af6b4545344a198be82\nRemainder = -107334ab98e5099fec5f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -198a54e35fa0cfa328a9\n\nQuotient = 1307bb8e89aaff7466bc238d32672fbbde7be19d15423bcfa14f9a23fe85af9739b72807fd4bc420ad0b0fac37a42\nRemainder = 170ebe9b83d4c43b79ab\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = f8e923a8bbc0242eafe3\n\nQuotient = -3925a167c1c4d2fae265f277302b989466e309a7211e0b7173031cbbb91ab7fac8dfe43c9d832764e222e9d8581d\nRemainder = 4d404e93edb435dbd60af\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -52e36cee22274556059ea\n\nQuotient = -4d5a6ef346a872142b999ff9a5429198b3c2a97e968f55aa2c01583efe30e9687c57e2bca2372db4d3d443052b6\nRemainder = -3a2ea5f9d204dc31f21833\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3d3c79a115d9071b573d2d\n\nQuotient = a49dee54430f1737a04543d5f549efafab25f0f28f5e304f1bbca191f99521c2c4be1b9927bde19e1ec2060bb2\nRemainder = -17d02758f8fcadca911a95f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1cc65a75211f2826c9d0811\n\nQuotient = 1808ab7c0ccac2ff8f7cb61248bf4624fb60352a356fdd1408904f8c6fb0cc52b7642ec59183bcaf5dd89ca0ac\nRemainder = 5c95323f3b8861261dc31ed\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = c516e6e3fa6e3dc52cf5933\n\nQuotient = -437e04d7076794850aada0cb4ca7a1055df103e74e00766be6a2fdb2631bf294cdbf2695d0a2f8f9eb5587aa5\nRemainder = 1fc63797594c56160536faa9\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -462ee529b488d1db2b6c60e8\n\nQuotient = -5dde5497accc4575a412e7232ce75bdf7905936e09e382d5c9f133faf82a05ad9dcc94ad858aed34cc14c714\nRemainder = -15e79293d5e055f906381a899\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 32765b0a34c88864d39bedaae\n\nQuotient = 11ac52a9287472e1d3b8577b3d50c95076e190714796761322b3ce869d96b44387e190e824849ee345d0a22b\nRemainder = -a158ccc7c055d64e7df3fbcf0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -10c061a37f6cbd11bf0c327643\n\nQuotient = 1ff5cda1551867577c5ca72c86516a82fb8fc5f59ce967b73c6bcc1b85168389872c9a747ddf044d6dba174\nRemainder = 21e766a0020ba429b330a325d5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 9435cd2dc2a92c950bb9e69b83\n\nQuotient = -2719c892fa3f4dbc9951b2095056a16159adaf32dff902e20a800a0cc2e858ccae408f2161aae25d3e1f6d\nRemainder = cafbe9caa1f83fd0dd3d5a6881\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7924e4dcf8f96da61f54bf83870\n\nQuotient = -5080dc99dba295f4a2d9a474c2ddfa3b232a82fe629fe62177514988983eff8195b37d3fee3afa343b497\nRemainder = -94ae72f78982ac1ff83f300cfe8\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3ad70d4b6b9b5f5b2eb65da67e1f\n\nQuotient = e475eebcfc53d49ffad2e0c2a4ba48fe7ce02c42ff107e01ab3fe5b26eee45c83c4f58c181d77c259155\nRemainder = -c83ac7582a02b47ee734e0f24dc5\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -14bbcff5423a260b21895327b18bc\n\nQuotient = 201308a421b85291d23465d648ad2a8d6f3393efc16fb675a42ea7bbca635ddd8c2449b1b34e5db30a03\nRemainder = 8e07efb8ae4c9df39533042362081\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 93aebb72a81ba68e8881fd1a56a90\n\nQuotient = -2584cc534f88f091fe471c652ac66a695906a7cde1fc1cde9be3ee09026b690c1a899378ff31f6acb90\nRemainder = 794801d9d5770a60e312b99d6b9f91\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7e408caf387a0ce9bbf4309c80755a\n\nQuotient = -63f7bfc0fe5a5421bc0a19fa6c87713a72eeb2a33e5eadee8c2f32c20d14f403ab8bdc424b9e8e0c68\nRemainder = -24227c242afedee2473c1a66a5cc29\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2f622c665af7f8126eabfd90df8e9c5\n\nQuotient = e557e6d2180aeeee5d2cef453fbdf38e84cc148f4608ade8836045498be2d318520ffadcea6319432\nRemainder = -dd290149e0e159f9ba6bb9f5a4b003d\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -14a7623d1d9dfc177e913d3119d0d30a\n\nQuotient = 1651d852316d472b41ba0460566e43fabb9257861859ad0fb6ea5a6433a4164299e078f4d50c58afb\nRemainder = fb60aff5fdd2a2b794b0d973ac4d92a\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d439da27b5e70342aa5cb365ece15665\n\nQuotient = -3ae357761a8ff43d3b1bc53eb336260342a39d22f8fac44eeeac96c2f6de32580dd6a688faa9c515\nRemainder = 4fa6f7ee4faf2f6be99c5ce4b65cd642f\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -50700f9c0da59482165a47a3eda2bf07a\n\nQuotient = -543b4390e4e254226683aa0b83b2ca176ec27a373969fb88f766ac72adc9125ff83b2652e46afd3\nRemainder = -12ff398d9a7d9e97a7f63a0bb293c8fb0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 383c5a4f1767e83fc382ad4f1c7c2b7ddb\n\nQuotient = ecb72c14c59d49287fb6b2cacdf04619ee617d5f3f0f1b2890fd4e79746a4fbd848613cf5eb437\nRemainder = -1035512a2717a89062d48f1bfd213333ed0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1402b751a1e5f3fc46e22b43240d6ce9b27\n\nQuotient = 1e800ddc5d5126f322298383f32fd593623eb88a91b2d68c5d9f56e20c16ffe2cefabe873570ab\nRemainder = 72935d534bed5ba557b91ea023601f50b1d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 9b4df766c608ff3efe5ea1f65cc850fa73c\n\nQuotient = -2c2dc2378abceb983904cdf6728f361d279b4c821710ae785724a7251", - "c43fe4f705f023afa7e2\nRemainder = 249f6433af4e8e224eb570fd438197af62f3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -6b382f812816c77d65c94c0c660b31a69b8f\n\nQuotient = -5f3ced1e42fbd3c6b2c6f1e16953e0c1bb6efb4e49566f974a968f69a1a66a3d7558f5a802a8\nRemainder = -317a7fb1af65982fe4641fbb1e5837e6ea3e1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 31bc97372d17038fd842b72eaba2abb26df62\n\nQuotient = af3fef8111c449b9e0858e7e53e1d00b764232f7a077d75043249c387ece30af351c8a40335\nRemainder = -a1493bcbf57a8480461d62796aa8f8541ece4\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1b076b2f7b78b4a0f0e24ba3a05d6c697efab9\n\nQuotient = 196734cefb08f09cb32ffefc07da8d9545d3451d5a08736757184bad94c73be71311cf1e01c\nRemainder = 273e33521f4d74840a96b3fffe169f79d32855\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = ba7746f4400f812919a3dc86b00642e1487691\n\nQuotient = -3c5989cf33145057a9c8e904435d12939db519cc6b9ca1c0a11934399cb139a73613950f2f\nRemainder = 456ebf56c636d54e37709b9e799e83b7a08cb93\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4e7d4f389423f42e980eda55b4a6a45f6f4bdc2\n\nQuotient = -8432cf3338bce1d12586f83025aea50cff3864af3eb2103a36bbb0aba10b0ba4831641633\nRemainder = -4f62c678137df301c4bef216e6aa910104e76ff\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 23d4c57b5a8162aae8d937be12efbcfd7b96ec06\n\nQuotient = 9f94c4399eef16dfc65a1e015e0786c86470299865932c4d564b71c9b1551a9c0308af38\nRemainder = -168b74a6073b4a5b54fa14aacb5c3bb7897ed0fe1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1daecf01ec633610373b79e04c22cd7499012bc66\n\nQuotient = 1d5b838dce6c0324f157ad125adefde6e1045dce9ff97cf8d1d39b79bce02128e3433ffe\nRemainder = 3aa816216d55fc3c910a030fd10fbda1e12f2ac2d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = a1598a12a84e9cba42ea0e200e88d4599c9f615fe\n\nQuotient = -3edb182b53890ca8762f3039d2d71a8a27c36cc884d0879e0635e6326af0182bc47cad7\nRemainder = 4610b2b1305220bc0de584dd3f87d90109012a8077\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4b5c2f1ba3a82047c9de61d47cbf1bec86b6ef90d6\n\nQuotient = -7571ed4c509630886483f6ca0923859e644063acb38cfb338bf3a681fe449501262516\nRemainder = -21c579846594fc3e5efc53ab01576a7b32d69faf41f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 28550e1f7c6492f4cb682c37b105f92b049c13fc03b\n\nQuotient = 9ed8fb31327a110ef4377258681c5287de8ef9dbe62aa4fe84a7f2a94bb69607cbdb2\nRemainder = -1b7bb759dd0ebc346cbe216e56be8063f063490c17c5\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1dd1e61caed1efc07d21ce05d889de1ad65808cae026\n\nQuotient = 1aa716227d1ca6af68286062b2d6dafd7ade16abbd5d6fa4ada0365832fe18f73bf35\nRemainder = 32e714b0c4ecefb38735cb88cd5e07c21c81be858cae\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b1b959a7b3262d7f4dff488315903aeaffd982b726d7\n\nQuotient = -2a9979a530046939e0b43a25edfbea6775784eb5cf346a9fc3a2d22e1aad473cdada\nRemainder = 4edeb91a2472e80068b1883cf2cc45d68ff9bbed1756b\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -6f31bbe097587a68fdf01d0bf93830bd03a23920ccc0f\n\nQuotient = -566ff76814e1c7d31ad53bfb9f3c0607ef1f7d1cf9bdee6e1cfb78b3ad7018f8bbd\nRemainder = -1eac095d6d84021c33aa9b219d191bd0637f20b5920eed\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 36ccf5bdece624b4f54c729a8cde13325d8dd764f44894\n\nQuotient = aee4f377611179d8b6315811dd94639aaaee63e99bddcfa8eee297ce1dc04daf8e\nRemainder = -59cb3ba7efa1637c46b21795872e8deaff90f13402cfaf\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1b157ad838684b45065aa77ca3238a4d8c5427f719cdfb7\n\nQuotient = 1c72d32cb83cf4a9043d3bb5002f61b03e29c34e44a9fc5cc4d613726f5e618546\nRemainder = 7312d11fb5828c7f1a0060a5152a7644fc1e6a59de28d03\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = a681444c4d47d829f7b629b561ffaa0c3be1232346c907d\n\nQuotient = -2702afc4095a0396215e3ca36e2a59725f743b30de0dd8d4ec4d943fef6c37162\nRemainder = 223dd3080ede3a64744b14df8742cedd71388b0df99073bd\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -796c9ea38ccf516a2054a1e584c18b64b996c9679960585a\n\nQuotient = -805585c6a7badc933bced6f8373ffdfe9796e963d3fc90e85b1a22c38f842062\nRemainder = -a6ebff3f651644915d5c466cc2915d104f0f85a44e08fd6f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 24e8fb7a6a3057ddcafff92916c46f7e4038b98c3104ae831\n\nQuotient = 10383ff8feeb180d4fde925b534be97ec3d5f1f1dab5d8cd9ab5d8ea646cfcdf\nRemainder = -a7efdd0401c74a69cf74442fe3da907acf92e8edc51668828\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1240a71ed8d81e86fd9b16e1d64f438b35d6f8eff672494017\n\nQuotient = 195d95a520fd22317492117dc756ff97806c48c1aac67a41ae56fe503a60cec\nRemainder = 8b8692bee56f8a1ada9ffd8b3583eae33a0df9b73a7d8585f1\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = babe02063b61cb90634ac0493174073d2419e00728d46ad2b0\n\nQuotient = -37791adae674b866e4791c107a697363847dee4a58a37806391426ea48b8c9\nRemainder = 33986fc6a5f5c4f4e31458fc7de55e08a4e9320509d90299b93\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -5563bb852e7338c65aa21c516eecf47f498e5788c608ed46cae\n\nQuotient = -68a30494eceff55e4f54a556dd9b30025ccfa22c0952fd746adfd13d31d00\nRemainder = -1b511d0ab81d528d00a1058850bef48df2e9ae9357e779bb9231\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2d44e919fd27bb3fd2093062d11830c30fa77febafe0a2082cc6\n\nQuotient = bd30999592dbeabb8871b76aa04cc1c6c3794a83f0178c2ad505d8189485\nRemainder = -b0dbce286df5faccf0bdb40ca60f508d436f9410c5e49c3f1360\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1909930e2d16fc877c15895a3ec8b2125858bfa1c5a1b8776bedd\n\nQuotient = 2171694ef4a9d57b83b09357a511d4e11cecbab5e9387928b480d686a0e9\nRemainder = 29abc8898d5ef85f87323c2a6fa36ab6e1bdbcc0ca742b1a2347e\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 8da37bc9c7c9bdc62f49cadcd40e156e776b7f4c8f7ad543f463b\n\nQuotient = -267d470f32911150d9944e684c14e1834734b15475bee968748dd5f6502\nRemainder = 53a2ffef61709bd7143c4c876e021f20a99ba481f2b11abcd45da3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7b117ddccee97816c2ca2f1a612cc0d94ac67f5a79ed41744c8fc7\n\nQuotient = -5a21a3bdd3a3d4f1361a978706ba1cec409c296a5b3c369e91fc8317bb\nRemainder = -2cdc818f1e445fb3772d2a56833aefb2f5565a5fca80662e6fc1845\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 348dfba3c793f0018d7d3a70c4060c3148b4a3163ba60af9d6f8b04\n\nQuotient = b301b4050fdf4ede8f9c746b26d968110e1eb119ca42cd9c9bd8d4fab\nRemainder = -17993daf81711fe59204ec82e363d2b91971129af9206ff9506d3cb1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b11", - "85454dcf046745436391a673426000000000031\nB = -1a76328184b9bea8770c91cfccf8ab98e75b2224d666af58022aca80\n\nQuotient = 19c401336dd43c221a61264f8b91791d250e6c99c61850efe6d1e3532\nRemainder = 6c9e547a77c98eaba1b021777dbd98ea88f7fd37c95a2b182f2b9067\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b7d7b1f95f4fe2f267af88b81af88fbdf603e54ab6de73ccd000c32d\n\nQuotient = -38a77853de88a8db14612884b515e3cd7c673175779d4ab71ba58f83\nRemainder = 51851549cfa00dbfae388cc3b46fd4824268e00e12fba288acceab339\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -539c0171f48e4160e5c308ee9e74f35d8b6d032e946dbcf748b1335a8\n\nQuotient = -79a7eab82e5b65f4f6734e8803fa7c30852ea3ae56e801c5dd11778\nRemainder = -f89592eedcbcc68d5df80663b3cdc638d9d779707d4ae5a552d97d009\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 26efac15401a945ffd37066bc5af23191292765164a0f1e4fd537fd64b\n\nQuotient = d33afb58753a21581c5b2351a74f3d220599ed56ebeacf1d43eeb2\nRemainder = -f699437f44af44b3ddc080f5b74f753d35f70baf3866040ba3c64b30f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -166cc6a3c60facfa0d8d318f26c6514c7eb9113f6b625c1de804ad379f9\n\nQuotient = 19e55bdaaa5a375c36e6869700f8677db563e5cf985be2a8d1b012\nRemainder = 7bccc3a653f29f3f45b52b8de2449c868c64d976666c01bff2dca03a8d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b6eae7a82b5dd1554795573cbf558d7cfed813eec270c326bf290adccc2\n\nQuotient = -297530094c3e4270ab5cf67e60fa5af6a32eb41b18b050fa6d46d\nRemainder = 62d8b502e172da7bce53fbb7c1ae376b6c21b3a3a47523aa0023406e353d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7241ae5f1aaee9340d437ad2dab94b70dd29fc6fff7fe31b100aa5001644\n\nQuotient = -640f3c38230962c6d6fca459afe0e46137525e8d62dd9b84da73\nRemainder = -16fcadd5155910764ecf0b4bd0afc3707e2ce49cedcbd5414f1c7d860e95c\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2f570d2da7a4e62097eb494ca43f7bde33e36525308dc864ffbaeb5d48f97\n\nQuotient = b3895ebba13c8f383ac0482be02e1f5518511420cb4513426bb\nRemainder = -21bc847fdfd48c7a4c36c778681ea20481081cbb7af6b281c8b8ebf2b2c3b\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1a6233954b3480af5f911a6bb8ad33967d5e0446c3e56f521e892c986b6b82\n\nQuotient = 243f3fbefbf842c79c5e96162fc42fe4f177a59d27681c54b3a\nRemainder = bbfaf15a90e744dc4a1caceda3cb339e5491e4507a1118613c5e9739f976b\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 82ae783b8a13e2e65d52dd3a6d6b057163347872f4d72245ff364dbf2421ff\n\nQuotient = -30f7cef2948c9ebed8fa3c5ea9a9bfa96ee4e9729c9b18e9d3\nRemainder = 1feb3fd887629cca60c664e385dddf538d9bf7fff2d34ca9e0e7614946d807f\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -60bba60d69093c0134fcb90aefdb9c190e7bf037ecc13dab3cc7915d7893046\n\nQuotient = -6b6f0183c1f598a68683ba7435c05d700d74681fe472669a1\nRemainder = -1f4d58f81a8c18523918d31791a00ea9aafbbb87792d90a5392273ec4e405da2\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2c17372a5128d7c403a3b94838072ecf9aff88d164764b12bfbf6261df957e2f\n\nQuotient = c4347fe42b2a7d9d5a650b72724369c5c1f59262a7be3fc2\nRemainder = -1103ec9c4a15373949cae4e34b7b42e242da41edbf5ad8362ce5e5426d3154a1b\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1824671758069b7660bad819f06c86fc76a9344ea38412058380363e5c5b4086b\n\nQuotient = 15e8c8d6847dfe974cefeef5fee93da9e58b74d640c6c413\nRemainder = 61dac240f2b39832903d5ecad9cfda5162bf8ebb0610545f259b75c3dc6ab8771\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d83386fb9682576cc70cf84520c53169e391b414f5421cddca6e257bd77753c40\n\nQuotient = -3572711bf994e6ad48535cc4d65ac323ef1ccff530b4337\nRemainder = b5899d4cb879e37022c539962959339d055900cca16153da09b54c658753cf50e\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -58a05faf5c61f85ac5a090b6bb045c851ea17332d9bfad4309ce2b7a79ad3cc575\n\nQuotient = -6931ebfc6e34305e5d7cba5284829d088d1ec0abdde508\nRemainder = -1b09eafde481064bab3a5c7fd895edceca40b1e62a9cf953eae1061dfbe00936391\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2d0769f392ca9ec629ef1bfbdf08cd8cc9219330ffe3c05343df792dd94b1147714\n\nQuotient = 9a4800f0cb2bfbe8d234410deb510103b7da30cbac7d9\nRemainder = -971e4a529e439a1b96b942001631027ff2fbe40b8939e224adb7f2ed30faff64d1c\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1eb3d7971125a036c3a67d9f5ce580a4ef4c469a492be53a55bafd2eafd4032b5b9d\n\nQuotient = 23116704b7a1a86cfa2ee5707ee46268634db5d50dc0f\nRemainder = 467c6b64c8121e4f250492191ea36a27119a0a6d19af519bf7ccdc2436c885c99d85\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 87134e98f73470e23a96c6a9139af3d4d21574de8aa9ea1d720df8940bcbda343694\n\nQuotient = -3b7f72ecf4f55c02366c52f38a827f5773b7cdebb9ba\nRemainder = 194b334b2046a66be3ddd7c6df01c88967fcb11e97b8206d000bcf6043c6e9ccb13f5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4f9d0341cadfb1f0bc38184d93503faa196fb8170f8ba2b5d3b512c09d39b7f79a5b6\n\nQuotient = -6db1d69019dd4cb26fd65d5b88a31bb6413b30278a1\nRemainder = -2042a060391e181882dc0c8d91c3b03c1ea35e2eff01babb3ae876ba1e57a505d44856\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2b2e8f445c0c3aaef0285945e4ca37a700310e003086f34d02c891b94b117f3d3032fb\n\nQuotient = c0e5b9a5853bb21b5e2e37f469764579d5cb2bf984\nRemainder = -154669d4bce7914cdc8d79f2b8d1faa43e8cc3b20fb0767e1c9a47c9e1daed4b665cfdd\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -188e619dbb719381e701363de874fe168529c10f30d3ff184e4356991fdec1649f72235\n\nQuotient = 180054f8c36833d44cab9dd61e6d89d28605c564af\nRemainder = 59192ec5c6fbd9773b8b7dd7d8ab1800dfecc8eb01c29997d15ad75b79575d9e26e1fc9\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = c55b5eb165c63ac2794bfac21980ebacadb93f1e059309fd2b855621572e8d9b3f29018\n\nQuotient = -31412e97045c19ec38951b0e3884c66d1d7479437\nRemainder = 56f1425227bfc6eb1ecda7bfae0e5cb59e92a2cc5306b28465c8739e40893dc5c1e94cbc\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -602b8c25ded1ab3877f58cb048c733649c7dcadf87b2652e35c4e5544d2306107ebff7b3\n\nQuotient = -8da1489ccf7203ecead94c67a5750884122b6e75\nRemainder = -15162026586a1e55dda72785f31c9e6140d166a1fd34c87a7d8c78f8d8f87bbdcf8f75b1e\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2171ee4a6f7f67d5a33d0a08c367184d70ffe39da28562655e75f6b66c866b1c2ac93e467\n\nQuotient = e635f8bdbf80e99723aa5718d3fade4e573be2c\nRemainder = -ffbd73bfe05f95bc2b135f12682288c620215eac3d6d56503d93a90e06f236e597d1df975\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -149375d478a096e724b84faf795c589ef0d772c4623f5be38da99006cd833dc5b28363faed\n\nQuotient = 20f76f5c6d0c8284764a10f6936c22bfba5f851\nRemainder = 82e3fb3f7252dd87b5370d26d9e8b9e98c7d333701f0ce8a05c337054c7aeb343d04d7e342\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054", - "711aab90a05b1185454dcf046745436391a673426000000000031\nB = 8faf8c0a3ef94ab1069394998e5412a7d84f44aff97edf63abc46d96f897172c38faa0b13f\n\nQuotient = -382586dfe93872abbe3a504fc62a8973913f96\nRemainder = 4d407323ef56093eea2f3993334215950f4e1a85ba18cdcd77d819d92b8b292c3ec8edea425\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -545d81ed25602b158bc79aadf98a8f655fc399fb8652ae94333bf54c8c9ffaf8c6b3f2a9d52\n\nQuotient = -7d179efc493eaceaf46572a1f3a62bdfc4a38\nRemainder = -3de3d817a9cf7d529b5229a503e8ebbbd2c53215ac3c584c010947f780198dee16ffbf47791\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 25dddb00f65d6a1ba8caf7815a8063c5da656d775eae9e0108c68ce11dc925183810888dd04c\n\nQuotient = a9f7e5f235bae0e3e29393ac5c99d510b009\nRemainder = -150478b4a0df3eb20dcd1be8da283a00636c021c5c6337e7732aae9c4b49853b95f6d2475ea7\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1bde6cae7f5ced9006c0b1a61fb50982a433e4e2050aa486298f456556d8e909e96933e2ba3ba\n\nQuotient = 16de125df5936181981b4c2d0051a8b4d211\nRemainder = 29ac7c8a11f9beb9ad649257994216146b663bf4f237c561bf315d95778fcdb1010283475ebf1\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = cf24735a60ff5906410be5c4d98e3c9247919b57e404aeabc7eaefbf07bd64762bc61b96c9040\n\nQuotient = -268a52cd10ab4814268f66d9f44f71a98eb\nRemainder = 20293699f12fbfef2e391963866fc082a7884cd13b1c9bd8d5d203558feed2b889720be936451a\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7ae7d548212830013b7d653072c33f0dd54a6ebd8792bf75809d29a8c798dbc67c3edd99a69b85\n\nQuotient = -8f051067ccb82b6a3dffedd0ff2ee97c46\nRemainder = -100dac0d3bf5aacc5fade281c071eb2399560a65349566567ce1c0c34e43f175a575ed1eeeb3b07\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 211ebb5dc59a051fdfa3b18ac491971e863f2086cdc099672c1215af4ec877e29950efa4f487be7\n\nQuotient = 9b7ee4c499386f922432fcb1a453ee2ec\nRemainder = -f410122a74386d724cdd45b2e548645ac5ee4a44cbfecb82aad34ae470526674da44ebbf557bb75\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1e76750814dec1ecbb1af0fa2281ab3185e94e47fc16a77fed312f23f261ad7709ad7c9f85862c1d\n\nQuotient = 23efb26228d7bcf281cd45f54572e2b3a\nRemainder = 65bf2ef1c2f8e94d98060aa305f85e6cb869c74eabad99877010d30654aa2e578ef6aa3c5f1122e3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 83cfc25e90a61cf8686e3d5857b2f958674d478622c54cf8427275ca5e9312ed24e44ed4a1b5e413\n\nQuotient = -2cfcae0e922f2d884bfa0a3346dc9812\nRemainder = 14de2725b11a9c6784d9608c52770d29b9fbf824ecd4890bf28f3ec0dc6c52e4df9be540332b8882d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -694b057ff381badb37c7c15c81e74cbd6774e8d61c9e7d450811c36262ea834fc1287fa59708ee072\n\nQuotient = -4c0238ff3c18d4d58e543f020002802\nRemainder = -2ddef796c50817e82ea6f64a02a8c6b30ab40070ff5401c2d39ca14b9c4d99de33834bfe566a0c2efb\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3e51c9ab14f522b55e8f9d3ba995c0846a864dfa2d568ea211b0cac1463ce6a1da72d0a15746fdcc9b\n\nQuotient = d41f9102a7785ce64f76b7d7b870b0\nRemainder = -106eaafdd518c658bd371164ee43ccd915a01b513fc7d220900039ff840ba36450e16ce9987e08e7141\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -16549c5d57b531528dd4d781f03cf275b66cb94eba038b782b739c3ab30b8631c8706abac06004a942d\n\nQuotient = 1616b432b3277e774aad92b0cf544c\nRemainder = 2c89373720b834d718ff3df985ae47c3a7cde0e0309f682f5fd48dc97a1ff3d69fa0dcaa1245e956445\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d6721300e877a8145d05f4f3d8085697c2ca5f34a5357fed0bdb7169f83b6f8d855232eeea594846b79\n\nQuotient = -320fd6a7375a42a3961362ae196d1\nRemainder = 5336711bf81237ea3449f4e9f4e6358dc250f8ebd86082cab92a8079f2c8f835bc783082efb0ed7e3f66\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -5e9e8e1d446fdd314d487cac1226088696e33161d923acb67d3c75e87e428bdbc193e02f53200610fcdb\n\nQuotient = -4bd06daed3f30345d269f51e4381\nRemainder = -1f3513bdefa40662f0f50a04b418a833aa2f85522dc6c399298b1b147662ef2164ddbfb7247ba9511b8ec\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3e7ab7ffe5f63a6c1e109b95b83af470ff820cdedbb3c90c398ec42e44a45e1ca894870a7fa51f17ad5c5\n\nQuotient = d6fd01a0c5b55fbe36e58bbe77b\nRemainder = -c51af3e8b430870388357cb366ea888bd7b4ccde09ad3a1d2ee1426af060245c6d6b5980ae87fb66c4642\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -16086df3dd5e665f2631a294563c68931faa19ee67d6a2153d262940a648ae71bb3c1745daca5ea977331d\n\nQuotient = 18bd9a8f5678d28cefd955cf99d\nRemainder = e193f2fece67b7abe16373c3f84f18dfedcf654d951bf47585fccfaf67ee04f5037354d057c9f5eaa8eef\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = bf758acacd11f3f3e6665cd740517c9ab2384266f3c7ff9afd0888cdad2f6c9401c24d6c11fc3949aabbaa\n\nQuotient = -371239db55c79521206c9e60c0\nRemainder = 93773085af7582dd298b09d7098835787978d820289ea6850f27d0d77eecce8614785e32b228f46ca4b371\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -56033fd85be464301f10177b58d895fbb6df6154da5c2a2a7cfc3a24d83a96f5295fb17a08148a4e51dde91\n\nQuotient = 696d8e378d12221e2d970c53bf63a20ef381db8566701972c22fe067cdba99c57b68706a5c6e52f21bb3de861e49ed2141b3036f07d1fd0ee\nRemainder = 9f0e50ca76031b\nA = b2668f5fbcf4170820ed3fc9b12a61862acf8e3cb17175482efe23c5cfd3556e77634d407b6d1f98a73437a8d6066319a7a860afcab2338a1b1313037e30f4d9\nB = 1b1313037e30f4d9\n\nQuotient = babe271ea266bc7bc16d193097903037819f82366c7e9ff8f2cb14157b40433c6ee327038d5dcc44140b070d823befaefbee5e13419f6f17\nRemainder = 93d7c547a9ba0a4a\nA = 74b1a591f449377836f378e05d2902b29964df59c6926e5a9182cc09ce3111783cb7021a185340b4880d56635de268d6f3855c4d9997373b9ff8df899ee3b3f1\nB = 9ff8df899ee3b3f1\n\nQuotient = 890139fef28aa3b77814e1122b9c7f26e746ee3c507e6082b508fcbe380de83b06a01f735239c6847c30eae44749fc8c5e3bd97eb40ba297\nRemainder = 6c97aace900389d0\nA = 7e89adea82b4cb6feb41297b6dc8d948e72c3d5554a987900e7fae48cfb38fb5282b13d9a1f5793cf7cbf1ef551865041c3ffe0e287714a6ec7123556af55a48\nB = ec7123556af55a48\n\nQuotient = 1fdeead441e2d7a6ce3cce2389b2a22248ddca7970ae3f7e7d8453052fd08534ff7c46f6a4537fb6f28df6c5fc8a7d384336e679b74205315\nRemainder = 2903c7cc2651bfa8\nA = 9ca66de3d83f0a747fe986464522bde5e42aeac20e8ace1ea13fa6bc9514c58517479a4281d4128c6d775489b85dfd114ad184613f308f6c4ea484a22ab0ad1e\nB = 4ea484a22ab0ad1e\n\nQuotient = 12f16c8f9f898a08853982e2ac5a906d784c5ab8d74007ba3ab311e861d7c1ac115efe694cab7583f75a4a59ceff2887dab53b2f1022aa452\nRemainder = 4bdaf1f352e87aa5\nA = 6e6a97b358b591b78db43772378dc084a11836ddc9dd4607f263ce620714e8fdf6bf67387c163b6f2999f84270802b4bd5c0f0377e949fbd5d42fe145e66ffeb\nB = 5d42fe145e66ffeb\n\nQuotient = 14e0c06c8cff1f9f5dd8afb6fa6c340f0953a18ba7d2b26b22d8e7f946ef20fd5ac277ceb59cbd4ce3e8213803c3b5b0452ed449e22bf2c29\nRemainder = 55422f1caf4a9a00\nA = bc9c054ff568af73e301e0751bc1ee055e82826cdc53449f2d9f45feda2ba227bedd6df9b74fb58a85917d60b087bef04a156a571716e9bc908ae83784ee35c0\nB = 908ae83784ee35c0\n\nQuotient = a457ea94da3237c0dd15ee30e9c13e7b4ca1dc90fcd67951b873787206babaed837a3eb17e298d74cae92d1059636f9aefe11aef9ffa31053\nRemainder = 124768541b600598\nA = ea6dc82b1906c277526ed867fe8b0fbe32feecfb935dbab860aef59a7d72799fd4e952e70b4c9304c7b2a06af8badcd6cfa12d0b6c9db38d16d2c4a24099ca14\nB = 16d2c4a24099ca14\n\nQuotient = da0a37eece8972a0e2e8817c54e", - "67c4d9f92373340488539d5051984bce0ae3300ef6ca9d0902daa4d485dec3b4db6c8b1ffd2c5d08b18ae\nRemainder = 1ba15c46023500b9\nA = 36ca8763e20e6ebf07a55cdfdd83892bef0bab68ac092093bfdac1a49c1da015541196a24249bb2262e70f7ed53e0fbae61f02ebac4b61f740548136ce50f243\nB = 40548136ce50f243\n\nQuotient = 3d8c433daedfbf681b528f88d610204d33bbe74d0b13978c34a617ae94177e07a757519b5a8f1a93a73d0751c7b5b72b4bdf475a9708fecac\nRemainder = 4cdfd72349c6110\nA = e0dd7e73b2a64dc017da65992176e2535c43b6fc14f2f7b0a7d894d768bbc77507eac0112b2dc3ca83d70989a1b949ccf374be6a012d80a23a74bba39671fcd0\nB = 3a74bba39671fcd0\n\nQuotient = 39d084b444e39c32f2883e9968301151802da15141f65893f37b8b834eb01c074aa1e1a978c5c99732c87ae106bf8db09e1728c8bf2aae88\nRemainder = 2950443357cd7477\nA = 16df31dc290559c3b6a3d192cf15d825cfe79f8dbd5c9848eac7fa90eea5d87f8b430cccf9baab3e8e4dc33467a4234d8551ff25e33af175654686ff1368e96f\nB = 654686ff1368e96f\n\nQuotient = bbead8f70c8e61114f22d36e97861f16037efabe1347613e78c51d7f539065421a66c907faddaed13ad2a0f0b00f8fd594e917799cd937e5\nRemainder = 3013136f5f728b68\nA = ba5e688ab4f8ab5c25592bc4334b6dc2b7a06d491d0f919b716bf1cf109b62a30d9dd59dd4bdf870dd2687894edab303277a5f3e3a537cc8fde3ee3bb61767d6\nB = fde3ee3bb61767d6\n\nQuotient = 42aefe467ff2a5614efef1edce25a1acba9c476b3abbcd680140a3aecf8f51c1ebaab8912de217451bfaca2842c0bae717b8a030b6318c0\nRemainder = 1f130dd2ead0d35e\nA = 17bd50b5322c51ac883852ad2a4446c039dbc210ca3aa0313065fc88cce6819b324e93b036bd0c71be58586cd2b243d01a4a918c10ea0cc5b22f9d795df09de\nB = 5b22f9d795df09de\n\nQuotient = 13de73dcd72a3638fe2a907fd7f6574bbb228698fa60e4ecffb082911c5f09c74bb4f50564d3d4035d07eedea38b634a3e3acc26c8e9aeff8\nRemainder = acb8702f0113e0c4\nA = e0327b2e59236a3f91ccf960490cc69b2afc854de9299ad2edff9618f9fe24251886afc65f5c581a9bc86013f356d599e98b8b10f5236a51b48a6b29025983a4\nB = b48a6b29025983a4\n\nQuotient = 27d11481f00519b786eaee96220afd45bc51700f7366fb5e7da35bbc84891aac3d9d2b709dddae371a6b78439fef810c68eef586e1d68350d\nRemainder = 3d1890c5e1555d74\nA = f3504d5d96c9e27a1527725ced337f1cd0a183531642051e166507432c01e8d44c4e8918701c2a05eb8a9d7e26bf04993f9adeef2826ae4e61c602477f849121\nB = 61c602477f849121\n\nQuotient = 10bdeac209c67b023044186704735c7291423054bcddc24b731ad601b49372f4d5ce6e9d85002f8dddf0411efce943f81a5e42cee2d0c9fe5\nRemainder = a93a0c5bd51004e4\nA = fa29e37b0d0410d19fd180149b14f94ec2edccd347da65f6832850aa06a61b7b78c96faf64dcb347893c93c560b8043466419864a382c6f2ef1412873b2d8cbf\nB = ef1412873b2d8cbf\n\nQuotient = 1c9b6cffe44241292320c0660b89f2f77aaadc8d36e33f5ac3da0f12b3c114a156870a92079f7192d237f8bf49aeee6282531c929cc56d75\nRemainder = 1ce3e5eb13ac7958\nA = 144325a641463ed6bddfcbd73e50620a44c606d71fac38efb1c9d2747b4903f7b51fdedacfb66db022aea09b43c7c2ad7b851035165ebe59b552d4f7eee617b2\nB = b552d4f7eee617b2\n\nQuotient = 1b4ad18dc0e634053beb3cf840b53e35117ea06309ea8ca22e37123fd7e1d391c96c792e5125e322c27daa73301024080d73ba3491484b659\nRemainder = 3286bdce6dc3a828\nA = e3a2b90d3ef446f6bde30d3e726cf3e78212324054b40deb0b18fe00645568fb0a6234b6bded6240977373731bb30d1349e25cefd54b7a9985735e9b78002691\nB = 85735e9b78002691\n\nQuotient = 28f5e8da6733240cc2f18e3cf4d42a50d92816062af33a9e1871fa89bdb39a0d905c49faf51cc1c1378741bea34d25ac2c8e522881a6f6087\nRemainder = 135784870eb40c68\nA = 593206f9367b72f9cc59b3e37d2eb23b2061422859162ee53656899c2471017474f500c6e23efe1f6b1e57852cd4229329dc182ba01a257122d76a26aaf9b844\nB = 22d76a26aaf9b844\n\nQuotient = 1ab276448d16c533b6e90b5b5ca266e13ec27b5a58c80b7657df963ec2d1fe4eb1c1d24873eff6408bcb3d0cf97c31e85240eedf0efcc1e5a\nRemainder = 27b105741264f875\nA = d84fde3d851b52ed3b2a1268e9b765ec6c09c5768bba709b3b799802fadac30a6c3184185e6d57249b1c34619f3c9d2b90bc0c348b22537281a39fcadf738083\nB = 81a39fcadf738083\n\nQuotient = 84a87678485b3e60ee1cae3701ebdf0a29ee44115a492c34a0c8e84090e14070eb2ad0abfe2c339f26b5099327515104fe3d1c5546feea98ed\nRemainder = 95f7434941f9d8\nA = f79a0643bcd9c28cc22cc7b4178b3340e4685dd2672792516d6fc08567d2de2d3e25d43f100a58826edb146ac94acac4213bb09bdf8a258001ddd0ab110b89fe\nB = 1ddd0ab110b89fe\n\nQuotient = 516a2ac26e5b3afa502c7f3c6f15376f7a380e5842c229443343b5b74dc3de84db3ae99a0c57043e32a504ded19943c0310cababb3e92cf8\nRemainder = 327cf78eed336523\nA = 17c0d5814e1020d5d69674bdf6b9df193a16c0c8567a589d014e8eb7f6c9c36560791f7acbbbacee7c456eb51a4cdd7ca88011e9d8d9f2d64ab08ad74f7be5cb\nB = 4ab08ad74f7be5cb\n\nQuotient = f0da0beebcfaa716f494cf3fc81fe65117c90adde3b3942e8e66986fe8050fd5c9ebe1c88c5db04cea4c4c14779555d70cafb53870671f95\nRemainder = 3b2f844440d7be00\nA = ebba8c393c2a22b094d824ed95b4acf6875719fc165f73ee6d359e1134949169fdacbb42d5deb8cea96e11e3aac985635b5bcc6c02a6778cfa8e03d9ce6fc680\nB = fa8e03d9ce6fc680\n\nQuotient = 56527f07593774f0fa642241400985d0bb9b41d3dc9e025ca069130d93afc972d75e3fe0f798e127c3e1b4e925000459a3a5a83b15186e516\nRemainder = b620b7a3b752b78\nA = 5d6cad9e26267abb480b2b9ac5ea323bc4c3c53e0de8ce40c89c85accf0499aea5b11703a04296519047585ff12f8795f98da0546c20016a115100eddabfb468\nB = 115100eddabfb468\n\nQuotient = 294dca3b56ce9529aed2c132a9bd6c0c61de7a58ac50582f396b4fadcf7873b502bb869f801a9ab1f12384631cefee72b3e6050a7f69eba4\nRemainder = 53a0fcf5486c7a6f\nA = 24aa73803f270185d23310df2cf3ef67b18d7800bc41aad2ca13f372a27ef0a9217194f3f512e79f545a903895def195a5eb9a1a1b6b3f4de340e9da9b305d3b\nB = e340e9da9b305d3b\n\nQuotient = 16bf4dab1c29bd284c9b6649de65a4ee58f21d6a8b51627ca133fa817872b1a4a9956662db0aead5898ed0eda08511be7c47449638f2fab95d\nRemainder = e7751deb047d98\nA = 77b04d93272491322ed2fe651044e28cadb2ae7825f02b55aeb0f73b8b8a8b336802416fe08c718ab681581ac04d87116323f61f50bfd2180542fcd4a46dcff6\nB = 542fcd4a46dcff6\n\nQuotient = 388ae1c243bc9111e663c0c80495c36e8767bafe188b532b7ac84b5160d902af1b638aec6e4c66955d16bd8ce94ce6027a7bf95910f705ad0\nRemainder = 7c667ea307017c2\nA = 52f357e9a57722a867d8199242e100f06e8df810ee913d6992bfd9dc03ed78bcf44d692aaa7be806df0c9e0802851d7ae8405f76114e6322177907198f85cb62\nB = 177907198f85cb62\n\nQuotient = 33dc2fcceef7dce92e3a9df58566c6e28d03b58ff6ecbbb31e43936cda6380a56788285d37b5e8f11487afd78c39cb2150cc98d9d78a0c6cb\nRemainder = 429a380c9f8eeeba\nA = d99cf9a0bfc347c9631ae8c69defe1f1509c3ecaeeee5dbc61317bb73fa5cc6e704f64c865cf4d898f8a2f63214dbd511f61aa6e09856222432376698f8d2f67\nB = 432376698f8d2f67\n\nQuotient = 18ecac9e5539a014cffd8310ceb1170577cb23aa9cb3c523d57ad83069d1609ff743cd3c275b67097a038b85afcd7105ad21672f9ecbbc7df\nRemainder = 37924fea665f5c92\nA = f87aa8b6e62b09291e0e9b832ad71d8f85d60501a8d89d2638dccd4022e89bc4932c186a198557282527dfa86dfacc2f90fe0656695b61429f8220509f5106b9\nB = 9f8220509f5106b9\n\nQuotient = 37c0649a53c8cab91a7458702870bf64cb1de9fc1c6b9a3b92444119d368501b62d3a5138af72bdb7752eab8af6bf4e3bdb9e3beb1805b88\nRemainder = de179463e3e91ad\nA = 995c04c1f24c4efe88393bab7a7545e39193662d5db7c8e557d6c554ed4367f5af82c463d0ba6bc3148620481140add5677937989e03fb52c0323980d8841d5\nB = 2c0323980d8841d5\n\nQuotient = a6d193cfe7d8983768ff29908ee6e07fee99927a4bc4ef41d01f63f3b4a2e7029630b7d925d0979458cdaa903771286af672253cd99593b3\nRemainder = 6bf69921db298b3e\nA = 55c856daa8110599cc4fde0a44acbd69a68eb177e0438f7d843ba0fb74caab2a7e0c8a6f176f5555779e65c555e9157a16a1497edf36ccb583a458f0372a57c9\nB = 83a458f0372a57c9\n\nQuotient = 63f379bef9866b59f8bfd6bb0120a75dc03506b0034e7440764afc8ec14d8d735aa6f03a568ea98d0a74ab9bbe9c6e11b288467e5f79a2539\nRemainder = 11c077beb8667d88\nA = ff1fc3ea60fb37ff23e2f2f4e207a86e055cca41eebcc5bd6376904b51fb3d233cb04666fdc92be33239b5ee552870e45717890e35fdbe3728d6ff55d5662419\nB = 28d6ff55d5662419\n\nQuotient = 285ba8cdfbf00b112e496ce65cdba2271c82a273b3d30bed82ef2d360790c5deb97f3311bd5eb9876a61e33b3a37782d00c2d5ffbeec752ca\nRemainder = 1672a8aa119c3a1d\nA = d614352268930d301aa4046cd38e2eda4dcfcc52eac984943f2c863de5c4f8a44473a8ecebf12cb8f4da4722d305e5c9c3eddc0109d416e854df334dbfcfdd4b\nB = 54df334dbfcfdd4b\n\nQuotient = 358178128648fa9ea28dcfe68b4cecc7071e129e3ce4d113f5d1e387f7e5a412e9d2dfe5ff16d9987a544004d213ade9c134cc240eeb6871\nRemainder = 44c3fdb374bc0c30\nA = 18b973dd011969e29a1f4a5b8f118313f715c2e31dfebd9fe0957cf23cf36eded89c38637a8d3512bb23324ff2a3627d5b942300200c823d764b7a6c12d1c91b\nB = 764b7a6c12d1c91b\n\nQuotient = 19ea7212f6604d423b308fe3f2f4986f31aea9d6a117a3e207e38ce5bbd8d7a866285ac60433630de547fc84e364c451457fbf864a82c6613\nRemainder = 2718de2dd0796f08\nA = 83577f755a448d5586e19486b04de7836818223ea920465c4eee979a9ce5", - "696ad8e2fd5253b5d5dcfdf355465e8c0819658ccc5580fd29b351169b54c62b779c\nB = 51169b54c62b779c\n\nQuotient = 13e0c5b9905770b60a6f978d1c983cbc84dccfaed0f4222f534df80c7d3d129f5e8f74f19581332a7f6d383915424c71db4ca19bde2591fcd\nRemainder = abf5f6c8ab6ed4f4\nA = e2bf43c91cdbb244790eb165cc13feafea36f5187cc9bf8aa8cf202042efd5441e3822a1164992da5be750aaac0bb11f09375bdfbd4a39e3b682c7ee6ab5f5f1\nB = b682c7ee6ab5f5f1\n\nQuotient = 3919f31521e87f90df3a4463d0c83fa31e3f569449009d307962d26f07d854e8d3f0badbf55311c206bf34e6227949327a93b1a5ada7a930\nRemainder = 6c3802d44dd4668f\nA = 2546880cc6f97fb379afbc4a2664115ba7909414f35a5bf88be2ed5187bd1a24afaf82eeceb0b438d4999ebf9b7ec752236669425bd3cce6a71d9ad67ff2ff5f\nB = a71d9ad67ff2ff5f\n\nQuotient = 121d5ad4115c2768b962e51d09f426d61624e0f203ac6c923289b4e7964e165b34f3dc1ff938a7cf37478d407de251c64db71d3ee629c1035\nRemainder = 660a35e1c1245910\nA = a36d3250c123697adbbbdf489e6cb40be57febaff654ca951c9fa0b396b1714c55ed6e05e468153ac443dabca29de9b43cc0cc4e62cdf24690593662c86fb5ac\nB = 90593662c86fb5ac\n\nQuotient = ad81debaa02f6e60da58b46e76ce041fc4da64138634ea7b3c165b8fbda027eb64b6b5339e70babbb83430d60383c2cfe22029e617fd03a7\nRemainder = 2e4aeafa2ad76832\nA = 8992cd131757ba5cbe54aa58be115723ea3438ddc782a4d1996980b7b312fa76e4483584df744b10340e5fc9e468690cef538920a732a8f0cafb4e30846cad1d\nB = cafb4e30846cad1d\n\nQuotient = 67a71b9ebaec91121a8cf6bc2932b6be01af7954eca69c5202d771c2c2d13683cdf90ec942a3445771ccfe484f947f078de825ea88b3c05a\nRemainder = 8395953f744cfb31\nA = 4f8ada84096198175174896167405b85cbc03fe0642f6b263a70f9a22f19ad6c9aef38da8ac036d409e6fd925023c95312cebe04eb653e0ec473dc8dfed98967\nB = c473dc8dfed98967\n\nQuotient = 9416326e2347a541b777a0fa1b0c35d8fe76c940d24c6f6806d6ae8ac1e280c16e480786478bda3f780ee92f3f3c361574efc2ed5ca98e26\nRemainder = b8ff45f31bdb58d8\nA = 902f5e48b96b9b1fd16c3b21292ed495987ddac4e1d92b2ab10378f2966c4399d6a41eef622a4991ccd1f647531dcd145de4ac99b3036779f9414ed2f4ba7e08\nB = f9414ed2f4ba7e08\n\nQuotient = 403c651b4e571e8301c4158fc185396554bf61d900708d2af5c2bdf495b3cb539b0b9b5acd0d71654b3aa68024961d5a7bc9e2788e6c822b6\nRemainder = 7856ec047cec8dc\nA = bdd6d846983fbf140173a26d2b709b9f31b4fee1eac9d25fdf0ef3523be0e6afb372acab470cfe1806b36d84017ec99302eb9eb5eb2862222f4916d8b6201d14\nB = 2f4916d8b6201d14\n\nQuotient = 1b6d967173f9777cb6194c8f69289b91da731456fe5a1515a49e4463cd906c84f97381cabdf9f358d97fad5d3cb140e3a3de397e7f9f683157\nRemainder = 83649246ade8bb4\nA = e3da80658acd53ada7c2dc57178e697f2907c5b0c64f4a87a794ca7521105a0568a32874207646df3768ee60964b7d1d2e29ea6bf7fbaa7e084eabd4ea553a72\nB = 84eabd4ea553a72\n\nQuotient = 27b8f1e49e404455cc68217a20766590e749507976a3a6de25a7cf2c32593aaabb04d84deba1ec6bbe048a2959ffd747243c396dc53c9c811\nRemainder = 3daa032278ce53d0\nA = ff3ead7c7b27f607d16f1ef4ffa91b6cc28301b9256cfcb0c22b6818371ce648ae8812dc50a86e4bdc0d0b1e5b0d55c6ba07b240886a6d5766cfb3ed0937a543\nB = 66cfb3ed0937a543\n\nQuotient = bf987f58700508356fb6274f64a9f78d455e4c436fc6fcc980ec0800287ab3789b91c29a8a72b16645ecfeec926b6f8242f3c7dc3adb40cd\nRemainder = c007da44faa80584\nA = 971aa67c9af10f70977f600e10f9278b8e66d2471956da38e5f4b3fedce9a5fc7ff42b800bb4a78314c70bb59394d0880383f5182b6c1960c9e5b47ef8e63be5\nB = c9e5b47ef8e63be5\n\nQuotient = 7332104442474715d7c4cdac15fc1731240f8b4dd0e6ff3284a15a62a8f9a071dedb87f2220efcc5839cb7e6933a8f65d767819db26e134dd\nRemainder = ef65a7789f54174\nA = bcea2ae4b1edfebf905a5820f0481b6c58d76a69df9dbe84764add3f49496a5d7005d645eaee3754e0ed105c13a114e6a0eae5cc4efab6aa1a3d3a0050fa86f5\nB = 1a3d3a0050fa86f5\n\nQuotient = 3f6182804a7ff12fe7ed3c8521b55564559b1a47a78e1fd56597b9470e7e0f6e7e48c58bc8841c9d118718ccd5e0c0bf9a08d8e244ae60da5\nRemainder = 398e30aff5bd284\nA = 2b877181a960c5e29ab1b2672ee22539256a82369e8f6cb5bcfb69e5e4a41f782e89b58fc0ef6ca336469ff929729f8492b44f12199f0e1c0afd12b2c999e787\nB = afd12b2c999e787\n\nQuotient = 1a80a681d2c42edbcbde552323dac3a1c03b43251a99b5549da6cb39ec6947daa0d574f0df68512984fa8e269b0b27a5576b3aaccb76ebc23\nRemainder = 378e44fdc7a5ec4c\nA = d37e62f44de27a1418f348139eac5ab9fcc1ada21ea6d7695273daf638b4d7eee6745f54b99a9678cf742d304736ee356f66d16d874f8cc67fae9be5dfd41a3a\nB = 7fae9be5dfd41a3a\n\nQuotient = ee982a63816d56758c29d284c19b9b984908cf0a9ae3f1f926e162a2cae4f88703aa477c5c14042247635c103494d11593c2c3839baf4d93\nRemainder = 39afe3275c01aae6\nA = 9a0b0476cd33861d2fc3137df292728e1f636f6fcba5105f384533723231a3104e7c77df46f7f34a4bdc63d5c67b418cafcf106b26ad020ea547d34edac1d3a5\nB = a547d34edac1d3a5\n\nQuotient = fb3f4a39a661e5c31228a6b7b4c27e6e52d1954e8ce262b98b61650efffd762cf2a1aec228bec5d5787683cad6b2e6e49a0de91c15c81874\nRemainder = 63e5ed36ff73a42\nA = 4453712f56467328401a69d4d749a0771732734a760a74094e50a62a030cb604e735bfe0bf0641754edff94ac0e0549e8c10941255f0f21f459e52a6cfe4d9ca\nB = 459e52a6cfe4d9ca\n\nQuotient = 7af60a7c0f995178be76c070cf49eee311e6d1e3afaf50c8c93ff200c1b3fe742b23259b4fc0b9ed0947be4fc9a6c212d86de9a0f7dbb5279\nRemainder = 19657d8ce516a138\nA = c9c92a31ad0f3cfb56a294c42a26eaecb77edf33ed40a7e6797927a0c996a7c0a701b484741163df388bb082e3daebf4e1b7a99002632d6f1a41c1d517238557\nB = 1a41c1d517238557\n\nQuotient = c890c55a8e2a3105b9bf9344a57a9b9fab5fa1fd57083d52431b695553bfbe7a44a9b6cd1f83958224f351f8511b14215d1648e88e938573\nRemainder = 1bab5b03c372daee\nA = 88341550e470016c7ab600b9f6cb410071a77f907a58cb6da4ce3e955d1e859534c2c1098fcfd91b9fa66926e51896733c36a824c3a20844add94e27f30ca651\nB = add94e27f30ca651\n\nQuotient = 34c240c42da400317f66f5151630493a2f200ee418d5ca3300cab10dfb429c2acd7280bf066fe19115f86db83d8f5b93cda714533b16abfdc\nRemainder = 18cd326996ccebc1\nA = 7e96d7b90ff09b114dd4393e9bdfb13d8ff517681126c566e18dd6369d87d248734d94bd02a1f19cca90be7642822b636369c51dee441a9d2663ec896e1d6c6d\nB = 2663ec896e1d6c6d\n\nQuotient = 10d18159e75efa8204e325e6be830b4ee8d2c07419e8276edeac6cc286488fc0c888300db3ebb5f935aa82654d3b932540f0093d1880e1d6d\nRemainder = fe9b6b8ba7c30f8\nA = 731aa6e2fb2ad1e1f80d7668c7b0642203af24af382abd207a5ffb588209e8b5caf953e9a96b478f39ec03a397d1433998e3c95e382d93376d80cf0c957788e6\nB = 6d80cf0c957788e6\n\nQuotient = 450d1f4a105ff8d1a3efbb12165ca98c67ae70404472e4862db479e03313b08783ecc42104780c9d57df0ddf19c5b4547ee9ba52ea82dd0c7\nRemainder = 169e15b4d5aa180a\nA = 902bcb1904b80183656dcbd51879e2982e2b46a547c9ae3119ffc12c6a003e4321b519289b7f22fad19d16480182d1d797c3045b2d29dcc12167f9ce5e233d89\nB = 2167f9ce5e233d89\n\nQuotient = a426f71cb3d75365cd076a6c35c10765bbc3f4bd317fb83a70083b0f7dc43a4e0b95508e60dc1dedb780e9b485f4f7a8870960de669b73af2\nRemainder = da381ae5c97a506\nA = bd59dcdefcbaecd9292c4c3685fb87d3a94c0f0ed01e43e63e1f36fb65d6c5eab3b584f3d1f76d31458c9f6b4c69869d96e943c61df102771274c5b4d821469a\nB = 1274c5b4d821469a\n\nQuotient = 26ccd4b7be090af22221729b0ca51a5e66435c2d33f8d88f94405f6c0123ccbbbbc8080cd8448a977946019ccbf5d267ac3f151ebe686720\nRemainder = c41f9e7bf20b376c\nA = 212dbeff03f14b5825f0d7cf8a7501db21b60581a01a26d522ee44e7fe69545cfcaaac64dbc76c7e3027ac39ddc2d80af6f3fca1824c6ff6dae90967d9ab48ec\nB = dae90967d9ab48ec\n\nQuotient = 801df28f4fd987b4e980760f4f2625276a2a7191d453095c82aa98a2253324ad2873abae70cd98c28ef3ce102fdd53469b9f01889f3ba8b0\nRemainder = 8e435da582e59809\nA = 48341b28138dd04807e522e341f74ac46b0449fa45f96d7fc586997c056a21eb3c399752a6a6c023509f042cf9e879f397a34af9aa2ec2e8904674f2ea3ff739\nB = 904674f2ea3ff739\n\nQuotient = d3857b72b70adff9b5dec3cbc63de7c90ccd7aab6595339b2de39bd6b9789045141d224aa4e6bf9a06e017aa3edd00e716a771b3f5b97771\nRemainder = 14135c686d2e9f70\nA = c1cea45dd46409d5e24fb7ed7d849dbb079247af2d312e01083754ed07f65f090e4dd50d23a973488702ef00936c5d78af603ec0fdf03dceea8f939c922b1e7f\nB = ea8f939c922b1e7f\n\nQuotient = abe20c90896e261e7d31bf40e7f3136d36b0b78006d12225a4dbef6aaf2062b609379eefe7e5af5bcec17126286f196f1330da8477096763\nRemainder = 230307c44cd55896\nA = 19a637e4f3051be0f7c4d35513bca4a91ca9b8082fe3c73899b70b6805a7aa0458512495cb6ee1ade55ecd5851be1dba96d65202f06bc7122633a0d905017545\nB = 2633a0d905017545\n\nQuotient = 5ed3765c4a777a903e182f7c9ce39d19c01460f389b904c3ce1d3525edf25ffe7dc0f4d9e24f0bc8b7e01bef19c83e74f17884bd7bfabb2c\nRemainder = 40f5346f8775e20\nA = 546578393e914be30581e24508a33f6560a5805dfb1c675d1ff1d6f5eaa7ee638b9e0265f543413e04e3f1f3b0895dec271c9897a48d9ce9e3d7df32c15b75a0\nB = e3d7df32c15b75a0\n\nQuotient = ed73a67932746985465fb0606fb0e81595514f1647c911c303d4d31eb0306e3b2aece07320f6fe", - "a57a7071d73150591ab2a82a7d53968a81\nRemainder = 2e495a881876da00\nA = 8976445bc318921f7e12c8d4e8e50596849a1503b5efb65e939c291de136597c05a1fd16137f0bbbd7197df943cd612118d1e55a50ee097c94331c1cfb1e941c\nB = 94331c1cfb1e941c\n\nQuotient = 5dce24b7a16d847b0c43cf365ea20bee9679fa0e8732813e827cf6ef3c9bdb7fd8846b5689ce8b80a7dc0dd05721cb06d2700aeeb7ff04d6\nRemainder = d8ead1ae3126aded\nA = 59b99e5d028e6771d27004bc19830a5fcb347f7ae04c0ba7c49130bfb198c5b16821e425c979e6d2dddc14889ae58475bb52c6cdefecf2a8f4dd6e462bbc8f47\nB = f4dd6e462bbc8f47\n\nQuotient = 170e10b399a4c5fe354b536fe59d53602102f215d5107493680ab6e181f67d75ffd45bf49ffb23cf9269b856156b5ac6b1c5def4ab1abb18a\nRemainder = 57131776937c5df9\nA = aeb35966e2a616762768b7f63ce3aee5e81561080617bbabd7846b3ca03fafaaef83dd05b8d16cef40db0a56f3b0ef6eca5e236681cb57c8793dc0907d9aa30f\nB = 793dc0907d9aa30f\n\nQuotient = 1acdb88f047f9bf679c50ed67ba01dd24dca92103f8ea2677215b6142083b64f9fd2a365499dc8f2bc61e29fa176f7d76b55557fa58e34f9\nRemainder = 5065b726dc6b3758\nA = 15a6292c9fb66c6770a8dbc6fd431d2a4b57338581f78d0860fda90182cca563eb2272a79fb4f5a6fc72c90dc23e8a95713b65988b5b3f9bcec4f0466c1c47cb\nB = cec4f0466c1c47cb\n\nQuotient = add8127c0a27c961203ea0351aed5b3c75aa816e9c2684574e55f55c7140adcbf69d2cff843e5f53c157bd60b43c45c8b6658de72062fbba\nRemainder = 67f48d3584cf4fe5\nA = 4e8938c8cc46d34e3369c5d8536b18c963dbde56020678f77cebac5f8777e0afc62ca2ba4f533cf6cf7561bdce77b6f495bc1b05f1416d1173a6a288012c7c73\nB = 73a6a288012c7c73\n\nQuotient = 688ddf883a0bcc1ff9bd582119c2fea7c059e19aded8c048390a1d8fd7d769666987418bbe0d4cf4b67009a342958928769375c1c0d558acf\nRemainder = a5356d04b64ee12\nA = e0c9e32056977aeca72e229d83f0d320fbaf5cd8bf3e033289f46101c75ef59a854982f33bcbcfd200034e8ff439d669a03fa404e7dbfea822664967d67dd5f1\nB = 22664967d67dd5f1\n\nQuotient = 39d4d94587fd1445f31457c275fd6294fcb69ba155e7da3e6cfef38ed1272d6c95755bca49007ca62cc101b038d264876f18594b8fd4c329\nRemainder = a34980d5046e2ed0\nA = 2efcb12fb55c923f5c6ca7ae076765059e15d9e75240a6e5fc3db92de184143fab1934c7450c3a380a9851846c9f43d67bc199a314e82e72cffee795d695f82e\nB = cffee795d695f82e\n\nQuotient = 145ea82eff186b7db4b11fa1514674fb9d41c698efb33227eb1abbc4eb78bdb2a280c0c4c47adaf4e010a4336cbb5650becd1ef544e223e53\nRemainder = 36052bba2867f5f4\nA = f6a6c7e33fd4c664652d696c495df387b85b132cfdfe34bbd35759477b4a3c052f610df57e49e85720489e4bb8dc923696400a4a28dd000cc1bd491446a50b96\nB = c1bd491446a50b96\n\nQuotient = 35d0c9d870348b113868282aaba22b21ec87cf421519a23b288b150604729356f924090ba038d7400c0ccd4932836c65902b4d3c46a202a0\nRemainder = dc8c7d087bf24b0\nA = 22228c8a5966ebdec64007704a373b0596ae702d62e29e468653b21a890ace2f02c27f26b043f48495687ce8c2ca8092ead21aa250ce0f6ca26129615a2432b0\nB = a26129615a2432b0\n\nQuotient = 52fc995a486c4bfd17ed9722948e9ede1c4ac2fe80e6bd7482fc47944c4337a185a506a9ca473d49073e1b813ad742f19b13d57914888d5f\nRemainder = 75c703f654ad630a\nA = 3473041ae301dd2806da30dcf06b9c09600086d6873cf3ee9d5a0be638849afb56bce2664f797de4123f6f8fe3e12acd32e33a285bb7f493a1cc13a7108327f5\nB = a1cc13a7108327f5\n\nQuotient = 1744946730b2789977620f2e7439641125dd338d1b31fc50813b34dea70b83d209330bd17fd527db9a402ad9752c26b8823082ec9971f4ae65\nRemainder = 453a3d59303ec3c\nA = c0f592d83649bcafb7e2de1a8a71fa863c1f51b595bfa638c8fe30731c6fca36da975b6f19c657e3ca29efff6febfb311c003ec68189998c084afe4979b5bb19\nB = 84afe4979b5bb19\n\nQuotient = 468f3eece20aa9d6473f3c559760793e702758a3d9cc19d7817216392c7cc7c3968778cf2fe0c3f0c1424d7512cee19ac0717952f18aa287\nRemainder = 5904e71034e3a02\nA = 1f0c99a128c757d76ae6dfcd01012f0453c8f89b00476ec46321ecb872f99a48b4da29a4abffd0bbff2b727dfa182652ca85350b4ce100fb70a6a40ab6c41d95\nB = 70a6a40ab6c41d95\n\nQuotient = 12198913ef16c1cfc7c1be13f1cc5991a61ff74935e09f0c46d26456b7cf2825403b9851d07d27e0197c1fa2ac5e32e836979a184f14cd94a\nRemainder = 33431c3df719f946\nA = fbfbf5494a9c5384c7ae3df6c02a5e1f9f32dc31cd7f437832696bba164bae1a9d95daefb8bc08e0e8e637436fb747084460697b5ef5ac9ddec06757dbe61aea\nB = dec06757dbe61aea\n\nQuotient = 376c2f902566d83c21eb7c3aa3a6fa0482ed52c253f67f00d5b915d0183c2d9a2891c2ff837fcb426a4c990c48bda4f90e0bf69d13558696\nRemainder = 31540f5e05e8b4df\nA = 2527f8cafaf7e8319ca53104229199188ab1ca5fe592bde8ecf605e17ca6446414e06898a85e177d6985b5cc6d4eeabd6b222b5f44b4fc1baba050665c090b5d\nB = aba050665c090b5d\n\nQuotient = b8fdd5cd7b2d9295258bd99e2780921cb2ea70627a79088039fc3ab1c62bcfc6307e86db4a7803f18e5339f152063f9e41d370e97b1ba2f5\nRemainder = 4ed4f2d12e4f4ba0\nA = a25bd113c5a8c67ef65aa80f1512de43c9441fec0c41250048d29c406fbdae80912eb3970457d621c552e3af7ef2d6bc1b5448e7df5be724e0adf6f71df7eef8\nB = e0adf6f71df7eef8\n\nQuotient = 5421daac8cdeb6acc2b8b0dd85b592f255ee4fedb3a9e90f2a5bedfb0f9f033d7c562c96958346bcdda4664c67848b9d9fa7d3892bc4e9af\nRemainder = 7e5661558c345eea\nA = 490aef65c81b32f5df76dd58decdec3e3f73bc1fcbdb6aee0c93cd98725056153b572509e75d2cc4b042bbeb0a77d27fbca1e39efbc765adde41a7dfc5c3576d\nB = de41a7dfc5c3576d\n\nQuotient = 156a8a24e7804c5f576cd1757dba44cb4185bc13cb56603b54ee3b70fa35cd98db1992904d4f7d99a63b3a486e6fb31141a9d39cc0301f897\nRemainder = 29e9c1627537e5a4\nA = 5e4a10e772de8dd2c96acd714f7d3880ae8ab460095a01038f3aa9b8ac8165889403b42019a1e70e0e7f32e77fb388eae3579dbcb690729c4671868b0526aeca\nB = 4671868b0526aeca\n\nQuotient = 1b0eff2ff0aeb2c02ee3cc9e0bff808f4d616eb290293b13a6b58a84127972bb417d55e1d001a9720ec72562ef3ea688e64c4f32c7e26cc87\nRemainder = 664d57c57d4952e\nA = 806b8504abfbeec4d5923f83ddc071be88e11c4394168854448df96160b95adb1fd9c288852e2f3df3e36916ba5118815ca2e83a6a7d9e074bef9c961e2958e3\nB = 4bef9c961e2958e3\n\nQuotient = 2e363b13b0457a0e9effc2d7e297df78f35e5d24d0f8ad4525b573fb2f66f374871291ee8a8ee3d15a823b560156d474c678f79ee480bbe4\nRemainder = 5ba8f49e0ca36ab4\nA = 2e1bb261d98ec405dbb068daac5efeb0a51f08149181864e9dd6bf6cfcb617b76d8facaee2ef468807e0403bc550d58e8ad9e5cc0f094b02ff6d0277fe642f44\nB = ff6d0277fe642f44\n\nQuotient = 149a5b1a81b9e47ed36be76252055bb202dc25f8fe7beaa1ce59c279b32941cfbaf8fe4555867850b2fba43b10b74534db82398320f9786d25\nRemainder = 1ef621737e81780\nA = 63de892cf5df40c98de78c755c99e94e0e76cd5dc0b49b8856fe69dd0abcdc535bb1416f0d02b4eeb54e8a939cf7ad4edfb7de4dac87523e04d8ea8637e50920\nB = 4d8ea8637e50920\n\nQuotient = dea8a9211974758752d89965eeeb93cc616f88ce757ec2809f829cbb8d99b4ffdc3f0f643779fc5e0bb53b5273a5b15965f4a364863592f\nRemainder = 9ae7de3edb6c7edc\nA = acd5cebd069f7febc38c318867ba3a562bbf8ea9b19a6b33538ba107e49439f8ac6e880c6267c29b39141dbe2273d93062464de307efdb7c6b738c0bb282c3e\nB = c6b738c0bb282c3e\n\nQuotient = e9149b347cdea84d740be70060b239af000c4336ddf36fd5159083b795c4763588c87a959df0104212a04cc928baf60b0ea72e8cccc6d477\nRemainder = 3ef5c6ee67e6f5da\nA = 6ccf1b8b406e6a106160e73ac4122a04c0814ef5a47708a6776eb52002d52772d3fce3fc05398172bba191390aba925bb23aa1eee626410877822f27d1e3cb09\nB = 77822f27d1e3cb09\n\nQuotient = 1606c2fe44cd0b780ee474a9c7daf0b2bebf62db0ba8ef5a99fe22036019890a4c7dff73e678965bb0e2a6e61d00a74a1d33dc1106842115a\nRemainder = 7cf920ba2897f714\nA = ef9a3983f26237576311a871e4a3df0538593dd0cfda58ab90b889fdb35c700f7d158abafad127605057ca0532e846992c41ec06902ce58cae0c1fe238c726cc\nB = ae0c1fe238c726cc\n\nQuotient = 8ccf17de5068451fef1c2808c62e19997c7f920d5cc0fde1f5a247cc57c6d730df553cf33094b786597a343a0ce9e4bffef568247e904343\nRemainder = 2689c40a54df34bc\nA = 8435babd279b7a3833d01988c58005d4557f7689ea9b7168ef42ce2b31a1a3c32a982aff654f271a651085335496dd826ee4b3bc27f58920f05dc6676e51c662\nB = f05dc6676e51c662\n\nQuotient = a9e78c48c779140b1d15843089765ce9ece3855537ce88cad3eb7aa7bd6ec72df65adacba2bdf6c491066406bdc3dd3dd734a70e93eed958\nRemainder = 53da0b15ac079ccd\nA = 78550cb7b58b58d6878b615dfa25a5b90a1ff631740e631c7f8829962446903c686c810c46a1551b6c1f7a89ae898435bb8e36d1bae24a80b54edbf4bbc9af85\nB = b54edbf4bbc9af85\n\nQuotient = 1e3b41304ee07f6baf1ca061e0e28a3740991c6ca2749eba70d3ea1f9cba8adec45cb69a31cbff22784a9e056e884713c0812e8c7981e49328\nRemainder = 3d051148ec43a72\nA = 76b9453d315e7a9c592e1f2640f5b6b90a65e7f2ff8ac24b9b47e35abb76fa5d303be6d501b341a882bdd9d2a1c81a9280724673f87fbe9803ed5a2e7edaeec2\nB = 3ed5a2e7edaeec2\n\nQuotient = 1921410e1a538a71d33d9c5de95593fada116200c399fa7590ebc374282570477f5f4abdd5166784ccee9671a1a23b96378df62168049f6b8\nRemainder = 1a1f4aeb882d7546\nA = e4aa84f782a65d376b10e7789a7d56695885aae274db6cb37e0a34414397a57b4a5f76dced11376af5fd11d31828203e685861a6dea2397891", - "96fe73d0e46116\nB = 9196fe73d0e46116\n\nQuotient = ed2afbd2e63617a651911017d9d02224d521e99275ab642ad1a941827983b17ef0f2067b5405b20e8e97f2ae6099150a1989df94276aadee\nRemainder = 4578107045b9cb81\nA = b547cd987638ff7e3c30fec9b728bc10c3b8cf16e7040bfe0fe9a26e44d2898c4c4d28ef525cde2b4007b2ffb3aa80fc4514a99b9aa2e112c3acc56b72ddbe9b\nB = c3acc56b72ddbe9b\n\nQuotient = 56181509251931afca3bb9dca21eedd6ed4226be67497d8d1bd0ec052af146993e7358f132e842f9b6c4934cf1b4501f5d6c5912e65c8d3ce\nRemainder = 1b9861df51429a6\nA = 32988a4e0769a5aca200f6f6f1498512e13b4904a9a311cd8a962fdd688de0c6e50b04f42cdd2cf8bf9b0a6922657f9ad195773e1250f85509672452618da9c2\nB = 9672452618da9c2\n\nQuotient = 1fa45bb973dd1d2df0002772afba55284a1e41f6aa4b0d1a6c6a4beb8ae00b52e88a9889037b8bfa9b7ee38036c57b713b48af156c3f9e8d8\nRemainder = 2525d52ecdec8814\nA = bda657ddeabe24c82c883e85822941bf64448b7cbb368468078101289b6fca36680b3884e35edc1fce5a5cdbdfc11359a1ba8ac0785c09ba5fe5cdbd30726df4\nB = 5fe5cdbd30726df4\n\nQuotient = 63e21f5568d07976aa81a2690b9e81b76fc3291cdeb010d1693d0e80191186815c7b2f83551a5f1b172640425d4733f06f4df1b2c8a7e6ed7\nRemainder = 14781a368471ecae\nA = 9f3dad0b3b56de15ac46cde1d79aba6a2f3b34d685cc810e9fa3f2d865bea4afb480d58653630319a258e9e8ded9be93cda3bc52b80a9359198221221724cc3b\nB = 198221221724cc3b\n\nQuotient = aae37878db016dd758003b85ef52acc7288b7b74c4723e3876a710baed4751d3be2ae49123b248f2b2c55a5be702c4428b1dba9b8a6ae8a9\nRemainder = 6c754d5c167e1228\nA = 4b93a98eb7b92cea0a4f5c2223e77abdfbd332b39f295b4ac40f71625d88e4add7e482adf3010082d8dd8854cf714a54fba0887de87946e97137cf7eabda038f\nB = 7137cf7eabda038f\n\nQuotient = 9881f551c4b7e67611f37df29e77cbe4e2d9fd5e17b7da3d013d6f3d4312e53dd26dfe3a2a12525cfef1ef81e6ebeeb7ef8fb4f918bf15ee\nRemainder = b14595005716bfe3\nA = 7737f8e7337160c14cfa8411236ca0354d8aeabf389b9fc4b14bb2ec3bb68286f3d82eb394dbd8062862b955e9fc8e86eb646317d1315d09c81ef51b30288cf1\nB = c81ef51b30288cf1\n\nQuotient = 4c8519d4d85ccf845fc5b8f31c27c60f0893ffda29ba86e8a3fd5fe67de5d29cb29362679abde996039b8febda2ecf71f6b9e1c1874361464\nRemainder = 10fae644af084f8a\nA = 900f7846e927760d9986894de6489e53cbbcdd59f7707917e7581422508f2ce79b77bd2c56d964a41e60baa927ca679faedcd9cd8102dde91e1f583ae834b092\nB = 1e1f583ae834b092\n\nQuotient = 16ef17b40bb73063f3cd0929cfe2405ca0ff2d3d426ac05f8a8dfadc85659105f7f728e113baab59247c4c7936ab975c08d6f1c72c12c532\nRemainder = baff11e6961c72e3\nA = 130b212cb6f3d854e4f17524953fd8592f5e59dfe92fc7d955e2899d1dde1ae4aa20d749caa349ca8d1bda7eeec2310532a7af54660e2a1fd4929335a1623bad\nB = d4929335a1623bad\n\nQuotient = 1cdd7ee2eff733b83beda5b862673177e2f2151ee0fd9ac0bf0ec5b7e05516f1d1b59ea754b0483d0e4bfb7668bb99117907a58a8ceb78028\nRemainder = 29e33e0c2a515780\nA = b0131ec2c1ffe9a523591a9453d2fc740bf885e7efc1a0158905da1e646745ef1bbf39b406564cb3da2f842bee307b36219bdee5991c969d6199279c25d4e380\nB = 6199279c25d4e380\n\nQuotient = 20bfcd06f9c54c537ae563e33dab31047aa30a6bc4e7eb0902bfbab3bbb7e65df442c46625c39e08c88310116348e9ebca2450ab463727f90\nRemainder = 11d8f2f6d4c1f55c\nA = cefafbaa2990eaa88184162ecb118d20e5999e5a8fdd25ae7f6248650ea74a8cfb92c58efecdd5d31eceb618f1596d7a6bfd31d092cf86da651f629975faf91c\nB = 651f629975faf91c\n\nQuotient = 37204c5735e4ba5e47e845d8b652cfc2b1dc715abf21ea0ecf5b1c6c8b9e596591fd7a7f41787be1a028c147a721ebb891b0abe3bd079b589\nRemainder = 1ee700ffb0ea02d8\nA = ce22d36b3cb913b32bd0e25cc14c7270d3f7b8e600a9b6732377f846adafd7fbd8a09d12fb7011f2283d988fc29aa25948dd4a0f24512b4a3bd460ee19887d35\nB = 3bd460ee19887d35\n\nQuotient = 191051194e4362bb201f5471d4bfaf92f79b6fbd119ca3dc1afffba334869ed9f8acd14fc42a2d8f616d652610a483ad90f5140e9a5ca4172\nRemainder = 74785b6874d8fa37\nA = f3c79f9a6af1c5bec72218d969620149afe8bf068cf7a7aceda977076665bb5a2c30729ac3aa976c9be379c6a5458f1501db8802652ef69d9b9f4f097027ddd9\nB = 9b9f4f097027ddd9\n\nQuotient = 6c46c17fdb03d192f75d636e1e2ab4e858d55f0f205cffd75550c4347726b5cfe036c6c901782cbe5a04f1985d9fd1dd39d747d25a6a7a88\nRemainder = 9a836be71a24e72e\nA = 4f6cf6e357b4985442a25b5c84e2cc0a5e685e2f5ff71ceba439b81f4123e16db2296dd4333fff23eea92bdbb812daf1d27c721412fa9847bbc9a0bf08879b1e\nB = bbc9a0bf08879b1e\n\nQuotient = -4984390f93e11c9a77880cfbe157dc41d43fe901c8895ac5091c5367a77370b16d42e8cc260058adf4d3fc8ee8cc6c0099804f4c319f15561b0a2b1caa7d703db82a726c9eab569c\nRemainder = -19374dcf21822188d720d6ec892bda2c084e8af84f38012da7029a3c3660c7e813fd4f7644ca80373575ff98ab6d743e939269c51bf62e04f\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 330af318ce0ffdaa92448777ed117de9c104e0f975651322c8e01b1c470f3cfb7a78b11f7daeea57614cec37d18b89155f19babeda0016171\n\nQuotient = 1a56f7d6c06a316a9a466319cbd558a99f06843782673a54775d859768a61933de3fc410068d00d5f6ab13fafc9228fd40ad41434501f8827bd7461441140eb6977f18d102d446\nRemainder = -3c3d566cd48a909292be2ce30f88ebb68e9122a3359f52d1d7b0189c467b829a9f226c0b64845715020dee12d179913ddb7f17da2db86d854bd\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -8e770450768d07ce20ff8f5f6af464b1ee5f1d0e8faaf927a19d3ff801f6089378133e822b8e63cf29c4c9ed721adfc91d3355a3c7bbde77bdd\n\nQuotient = 42131cf8f52a6a3f189697ce402a8c9439bf05cb3dc1cf8bc49dc2f07cef15b3bf0102c941b5b3bde6440abc6eacfbf77ea8da06ce932fffb226b33dedf001e9657464b0f06\nRemainder = 4cd483574fce075404dd22072abe61200fc455c15b382c7f2962ffd82c38ec1e2c60f71267cbc35fcf77fe1f9301d6b5f884f1c416304aa9f4d4b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 38caa64e74b29a7e9bbf341edbab112a730b17103831a9ecb70ef077e9660b2dd1fbf71d7f6bb4cdae2ed7cdbe9070ec9fde996c91b9bca5b83450\n\nQuotient = -11d6883fcd705ac97cae5bb7f8a2929d6f636f4f232ae9a4af9769183dfce9a9296fa0714c3f4fa1eea467a5c96a484a59d0cdd87496b9398e7a818daf89a58add3a39e80\nRemainder = a6b7984fd80d719ffe2e6eb756e4e3bd7ab51f6088e04ac8fecdc744b0385294dd23b5007910109abf40cfca814c10addcb5330e422b6f5eab6efa2b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d25d50f53c694cddd56aadda2654ae5888603b39cdbace93d19c117af5505750aa24e615f95446862bd693f5b444e2a876eb2cf49f6c7acd007eae02\n\nQuotient = -3fa898b02c621915f44b213ba4e80b8e85c7a2f4c78df2bda7d99494bbca3eb2d9354965d83e1c9001f10aad9b3f3ed837a630b329f5a4b28935158fbd9d291a120b08\nRemainder = -320d41a3875da2e83ea9a83947f5abb1a7026c84020e983381722bf7aa87d5987ab088cb2c37fc3781c82c81bef3263fec560023e236a747030618e9d2b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3af2721aad4b18db27842b5e539d8cada9dcd7ac4c5b885065dd2496a6f76fa73c8a51b239b5c068ea6feffda22d8ea806fb488ad5a94210264597edb40\n\nQuotient = 179307c3e14de14a744d082825ed723b996a4e15f156ac473960583138c43f4275b4436c50ef8f21a7b450a969819b81c15bc355fbc5fb55cdd8e124d931d142851a\nRemainder = -9c8eabd36a25e995c1811b79a2a0357f6aeef4477cac0ffdd130046cb2a647f928a34d91d9b489d394965719cd58604b957c693a93145328e5568d33d88a9\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9f2d3da1da77914df66bc889a40847a0d705d4648a11f282e09173d170e96d84b5a45092d995318fe7a954b54b88b784423402519a38bb521e84a4f6c5485\n\nQuotient = 6c0f316406afb4cc2aebe34f7948422de0b612a02dc47f4ae59419c579fc465ceae1980a3e524fdfdbdfad4862f168a9851664688c9ba01a8", - "bc1ac156a6276643\nRemainder = bf52a2fb6493eac22fc8b334ccd8e8fa347620539d9189d535373f94503310a027c5423197c7279bb51ab8c459e27f548d57b55740320e80b753290d077aa7f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22b9e55639ad3ff4f071a49c8bba6bd9047e162fb31882421db8ec5ce46f28fbc35040bbc74ead5a948c47c43e9c7adc32fa52046b53f12b07b5224e0d8e93e4\n\nQuotient = -1008fcb6894d8c411905136fb3e05b38ec5d8df35db06379fc2d6d3e3579bcb34fa6e021b98b899d9d082c111b1a6ac8e50418fcd5968ade6aff8828d8e4777\nRemainder = 3d7dca387b00c677d855fc4af4d86d86331fe4309929039e828765f0937990bffa964d3ffc5d4f2f4b8bea978329e7cedb847c7cc341ee52217f903ddcf9446ce4\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -ea045323f406bd7ce25b3ab4993b5f6dd92ca80e3a02607a862deb13470ccef229fad67ae958cd87fecf4f08d9609595077d0d1360d9fe48c4566e237aa877e7b1\n\nQuotient = -42a50301031962754ebf9c4b1e125e6df3dd40ffbe09c044b1cf4b62ffb4f92d298b05933a450bcef65e86398da80740a610ba45928000a5c12d26e9f6a4\nRemainder = -c5485b82cfefb3f980e0fc7c6cd89b1345a8fb942299bdc36ed4ff8916016315a0da84ca0ee2824dce3c7e5ed49d517c45173c9c8e30b224940af6cf828c73db8db7\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 384e523d5a687bd1a90101e43334894b6a27e8c6809a8bf5bffabc34d558a8309997dd6f2a3b7c1a63100dcc0b6647b444ef7e5aa4a9c52c7caba1ebd096c3fae6f95\n\nQuotient = 1054439945ccb5bc5461fed04e364c7a36d5dd2c0428872676debe07654b2ce31e435a90c81f2bac1032143acb0c49ad101398feee8426bf270bdc0229\nRemainder = -7bf919e14b2559ab82b3c1bf428d083a4c851a7a1fea44718377e9e945caa5cf48e0b1ad727e251bbb330292402a75ecd96a56db4ad07146533a3ab5a717d0a25a3a7c9\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e5cd83a644ec86b94f5e33d4dc307a2f14ee8653288145dabb2b5f894560c164470197fb9e37749656f47df343c245258627aeea17965fea10a57336bdc6b4a47443492\n\nQuotient = 62675274798218da426a54ed7158f8f737b7b3c328a9c351371f0cf61f41712f9b28741f187eb635ce45866762fb5fc5051776151d202e2556c5845\nRemainder = 1aeb5d1fde3c259917e430e6790b00484d0d9508391ba6ebab0f6299190d4b34f5f7d8ea2174974471a1e28ee2c15e05da645db971f699d5d0e80569b7eba7908ae579f5ed\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2622350611b486e6be7a7c1c073c230d604d782c2696038a3233ebcc3f01c6a711969094e47f49e294f2c5bcd04fb1b7c0934f19bf6e7aa519a8d4ec2c172ac59cc1a57b26\n\nQuotient = -12970cdd96b92c37787971cd8dd166999ff241be881eb9543ff29165a9c1a3beeb38b1910a5724ffe2b73ab95ac1ca88d3989aa531374d4ec6122\nRemainder = 627455cb555398150e5b4c1c53ee16dac8d80d9616ed1ef40031424287f8028a9cad1a10bdd8430f6f65368cfd00390c8d4355aa5ecdbd1ff0266a1ade235f33cb5309446961\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c9dac93cfb7abaa3fcde359e09a92ab0b5c06359bc09ae9bade3c6783064dba90b233b4c8d5c6236a13ef96c7a223e37bbdd931eae61e845e5a10088f75b3ff5f1158e833b15\n\nQuotient = -6742b3871dece5986d4e219bf5f43c101da8896f247521fa286fde696e0b71ffeb3b6a3e4f33710c9ab150b7a1f747cee76839c5e7f2509f62\nRemainder = -203b2d6eec9d485f7b439fe9d4c640bb31170af38418faf4daad577c30e44ca06efda55ceea4fbd959b3809fa2002b6e2cb891decb09334ed89ac66ff05502036b2155ff62f8aeb\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2457088096865cd052e9cd9349c6e5e34e46c89d6e860a36f8e2a0bb1e5d983e07d05e6f6b31edc67e4793cb4d40979c029c80a13e654b66c8acf6b894f615a3ac800bbd09ce020\n\nQuotient = 15eafc416460d757d0abbda8d094eb535262a71dd033c25e704a6df54265b6123247e5625da476e0c220ba88582a1ed94265135bf8bf1fb1\nRemainder = -64ccd9a0ae0b0abcb5507d51b2e6c8e52e67907474605c439796febda06eabd8a3185fdfc0bd088cc49fdf564b5b45890b07269c15b1aa2f993cd9872b97aa6cc37dea2f03444b3ed\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -ab34d3906d8a2b806b22c73d44948d703c1e05a9337f75cb0b5df5205c5e2d23f8a92d8381372f9398c9ac2f7b9302b83e48b26512ccd0b06e6b8ef1b930ec2678d71e2eddbf7349e\n\nQuotient = 3b22916d9fe3145fcc3b8872bebf5aee4e14235f618e0aed09199852c6bed80df39256d8407d334c06f4479f230913370b7d451fad99d\nRemainder = 1b02a7b97f9ac1f6306aa00fff0e59f55fce463ffdc640364a950df29474e08b67cdfcec0628e973d42fa1e4f98e988ec4c47e4915651a1731b71d5e36a10a0d1b3420427dbb79ba7d52\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3f74cafe9ab0c1b307cd7571fd442665fa3205fb2f45b3811b92d1d38b096a2025b8170663a29c52ca84da102e62048e583fba96a594c0b23952fec587814857c25221ff2cd0533cba6d\n\nQuotient = -12ffa4b6fc369404968911c17358012b993c18c2ff34122e06f450d3d441926b5f5638b40efb012d76d8bcd3c0012d0a0ce5d55c596\nRemainder = 64548684fd5f6c816bd296234740a4eed772570bd4a48852462f9cddf14f1350ce7c7c6a58aee8f66ad7df87927458db09e3af08eb5376de08444f35e5171cfa0992fb27f70b81574f6e8f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c58383afca9e1c480ee75d3cb6b0b99ea42e827d39fc96bab6b0dddc97e3eaaaec02a74847f9f7d49937f5ade3580bfcd491990737d172d4079437067251ab403c36a9826e974b113e2d2a\n\nQuotient = -4964410c2b038573107b0151b36177cdd62495e0dbef536b59c8aacb8836bb45e7bb014e5022360621e8e82a273d0d462b8eb6fc\nRemainder = -1250c42f8c9b129a5c477be446b86356edd1b19409d362c3a5fb5d59c30f1c3fdc1424a88a0d6ce20bae885905d98c8a5a6495931f73edf4c60112ed78834e3bff6de3ed54c867fbf16a1cd53\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 33212ef4a8e80daf1049ac6f639f8e1990142ac32f7ebc97675ec90f8eb1a2814dfdd295ae67317253d0187ad33f3932a3a7efb056d0a3c87d28e64e23e9f1de751ee6f0f61c6f39d08d72f0a\n\nQuotient = 17f77efddeed52ef2e423bc2c10d2ae15c97384b766f4108474964c2a44789e61249103d9f5fe00b4d612772dc6ea12a42e395\nRemainder = -1ec95323b7b95169d5ec0667f3cbf683e98c15dd0fe44df4ed9de9586e43f1f69337e41a6d11d889452665dc0b03cf8d9ef2effe0b350eeb9f6468751b8a2c42608ba2a33192b770cb62381a966\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9c91fdf2dd1827ed103a102db254630c278bf8b47bb12a342a92f081acbdd8ae5f5476ae194e24b187011ac25b19fd09e6e690777f9d3efb6b3a32c8f5905e1478a27fe4b1adf17a70abb4e7571\n\nQuotient", - " = 4f5dec525ffc737094f40d27446ca0be5b7a2aff02d51d99609165c4cea0dbbc1d92bc0a8680782b616c149bbef7f5ca912\nRemainder = 1bc84ce56a9a0c74962681c02ac927051c81f3824d9f3f0f91465df333ecdb449473d9c26ae3abb9509add5795e89ba5eba6ec7c89b114c86e6991ca0c185b34d6e66925a14fd82809dbc4936d273\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f47be01e6dc6a86097676fbd472c2af0c83a2f743fcaa885e44fda7e9f350e9fb7a8cd07fda59ccb7963f1e95e6a1236f5f94939decdc85afc0e523c711b24641c844cd3113c17fe35ca988ba407c\n\nQuotient = -163cafed5bcfdeda88555f30bd4cc2da2cefe2bcec9a7c19c36ccd04a45121a5a0dc28d0bf6ab7fa4b78933c47a5d5286\nRemainder = 93f856077f5b2907cefcddc4d767ffeb0acb7af64bb9dd8a15dcfdda6c244c24fb8404ff9ea2fe1dc337faa05930d33cac4f61e171d0236e222374cb3da76396ae1329a407fb4ac652fcbdc568d0fafb\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -a8bfcac452a5e48fee9132b73bc2fef771450143ab80aabd8690ce54c9b52c2b5a669076a7a35fa6d926268077bec6d90b722b5d074f28ce3843fb0147e567c45f4e91a11416c082762e71b5c6129c08\n\nQuotient = -617dbaeb8c6f9d584e8eae923c872048f9f9bf039ec6b50cf8f09c061bf79acc3311b37c2502e560848c05ab316fe8\nRemainder = -1ab4613767c4f1f7d127e848f2bb7c72a3a9e1dd6173b63198b80d3bbebce6a31494f19b53ad9e3a77248e6f9b26fc59060e2759a20dcdbe785297bbd912da9a1819527fac550d64bfd20ed1f96450c30f3\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 267d9397138fd0374a7a58593d41627ba1203a646ec2c04997acf607e9d217b8f40183d2f9304447d6f7e727a476e636ded4697a5ff30a9ae3d249baf97969658209c1b32ddc0edf920b0b278e9b5464313\n\nQuotient = 10ad85703fd51870306c5e36b51512341d6d39e0bac47a03732787b2f62e49c76666f7f49b2596de6cb5c5b2f31b\nRemainder = -846b4479713bb19ebb8c1f1b75d2be0f39fc1095a3d2ca149b5565146bc19382b86e5ab0d098ab1fca1ce701d582400190fee34b602845c3c0c498925710f0b9e3af2412ed5ead1fe03d77e9b2b407ac83823\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e0ffa4e120f2f46fd1430b6022fd03f71a22f9b120f8d40e901279be235b32d94760fb8c2403d23cdeb728ae73e2b16af7322d6ebd5f5673187668c99805e700f1e997423886bbcb851448dc1ed4cd66d6598\n\nQuotient = 41567bbf616ab41da51108d7edcb5a8a4877c5a8663b3aed7559421b1fcf4b535a54989efedfcc935b3917fcd\nRemainder = fc026e554a0821e0d36b796fe6a676fcd7383a55fd6158d78ace4edfc3d8aa87c65f0eb41baa2aafadc51218b0562ff4b5c9b17bbe84afc491d9e309217a5138ad48dd51e1b1a9aa51d69963b608ec47d63fcd3\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 396e9b45ce43d3f89386cfad8ddef4b483ecb5173234530c67447ab74629d246c18b9da09522c77f598957e3fd2a1c0c9417399912fd547fb1023ba6b90d63d223bcbf3e7ba155e51bba7e8635aa5c39d2b9dbb8\n\nQuotient = -18f1f395347ce8df530d9330c61c0e30ac9531b50a0af2ae7809db1258285c15ba7a436121287990fcdbda2\nRemainder = 51417b9e9995de34316a66a2f70c146df8e36952fe64124819607bd8691a465f4fde98e590dcd56f0faeb95d1b67751081c2393626713c27ec2a2123aec2a4ec3761e5ace4aaeb612d46e52e16d72a186d2ec8a7ff\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -966dfc779cbf9c388a84e947d1128e2392399ff45d9491259c7cb19589154f82f41e852e0c6bb5a728f6e87ff4ff95abcb9b2b57af1b6b7fc125497775ecc1338e4bbcb5315f7afde4e283347184b908545211afb6\n\nQuotient = -3fd962e88dc1d501fe9335fff8b6b2d50eea967c3035a3dcbcdc9599b81f9a445ed5a6ae7413b8865fd4\nRemainder = -97f06f6155f8d0ee6850728192e0b4fcf55fbd9ba982c5f1d598ddcbc4e1c4be0e209fefa6ab3b7eb2b4c645e4dc40217202285ab0a7270d085dd9d4fd24e5293faf6797b4c3c79bbf3ec63fd82942549f9e8f862297\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3ac566d6b2d18572360fbdc626ec488aa316a74f33d71a17a2d0e1d2bf26395623eb91dc4abebf2f944e9bc3d669fae2e4332088e9ff9d9f43927a7888b1390ef60f05efd6e63ec606ecb3e164ed6dbdc9d088586aa71\n\nQuotient = fb5ce21bcf28490afb64e6746a1a81792c90eae17407c0b4c5ebf2464eeea43e516be2c615f84901d\nRemainder = -3d255bf94c3d610c32266fd472d070c0f5e7dddb88d32723b2e1a20709aed2faf28701e0d0227c2b33ecfa9e708e5ac354a97be732b786210d86f1f05d191513386c580b1ad1f4ac6890f87fd0d4270f23cc5c2064502c6\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -eedb64a6e204ee3d6df508830704f1d5b2d2e627698d38a114c07458ea0befd593a80dfd2e08fcb1893adf57061ec4fbcd3130692de7c46f5ca51361e9b79bb7a91963618b8e5b7591392a5f0e3be954e8b9978c97f12e9\n\nQuotient = 6933a3123d0b32693351a834751345300c49324b861a663e8700bdb3b70ad996747b284a8ea5c02\nRemainder = 13849ef93cbc77460c3c496e8f31f7e01a98c21cdfcd6877547161f9601680665b394933d3a0824f0d32854508c89f0e4a0873280c779c7ca636cd89cf6ee5d42a917b4f382be3b9654039f623c11b43164827f870fa0f0781\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 23ab6042240a7709d43de7ee17332a9710bd0d913c42b3591341527bf48d5bc30abb962482292d45a15cb03c9457cc8d78d1e00aaa63358427b000e59e4260bfe1e2cc603e175d7fcf02bd9f61fae3740cb8e10a510ea3d1d5\n\nQuotient = -10e67cbb33dc6e24765893a047252766c2bfad8385150689dd4fec9ef495dff63ede1fdf78bb6\nRemainder = 9dabe2cbc734b910fa1bd25616daee5657d25b6e4dbc2cd93cf8549715c87974a8336fc5070d86c11f6b670d4b3bd5ee8ae3af2bb321fbb4f8fade3f5c6c2d6c366b4d800dd13ce897f13b0d3fb79f1d9ca525b4e7286c56ff29\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -de093dba98747499f2876c8b6b7a6b9587284835ae35f0716dd594c826cdf5b9179f2c6b08d800a77a6936602ff2b64ee0b7c94493bd5009633f5bbe423454b7f018ae96c21230510ab4bf5db394ff153b0e9eda3ef90eb4c253\n\nQuotient = -521f5e35300b9ec2742ff472cf61235dfe2e449772afa638b1adb812cccf269afd164b7602\nRemainder = -2ad10e8758e1d358d4744ad344ce319617027107c0b8db195d1b58c6e6035450c9b377f026fdf9e5737750af5615cff2ac3ccee623c060d779373136d48a735b353d64bcc5f2e6ea1e46083fd799b5f57dd5ad0ff3e6df9764af977\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2db1990ba1e353a1a62de1b914ccb691380b6ea937c13621a29f0a40ecef460cea52cfbc77d98706fb3c9939ceaaf962fb8003b0cfb40535e0dee22e8e7d04b5648fce2e58803242c199421cc4b26cae776d3603f2ce410ddd1e0da\n\nQuotient = 1d45aa6fe6837a1b7ac95efd55d1690b66487202949a286fc85da7ac0b50b860215e44fb\nRemainder = -7984639b596f1d4e6efea9d8b4719215588620ac959034b303584679a44fa84a4be0c89fd2e29f54e62959f9b7a858c06b0cc051176af82d4b85e7334555ba11c39e6cfa1829995c383ba81dbc220e527e90a1d440c1d0", - "69703cc1370\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -80316fdc405bb002990d3ef7d0e98defcd1f0e370d1e51db2d21ecbd96230baf69d00b168afcb7b8da9edc3ef7f6621ae5c5a0d7797e5c92283342e42468dba1036fcb2ffef1f493ff97826477364f6b5a41dc56d6389a01b83eee041\n\nQuotient = 3c0c3f7a777e611d1bd0d17d669a1ef7920b72ea8de06d4b415a73b836e37d6cf0780\nRemainder = d8c77134a75584ecd5ab29e97a909ec139464901f9cfcb1d3d9e29a63d204615b6845d466c8710873980f107c40ab54eca9f8933ef6d726f9bd0f3e9e97eade5eb1a9bcaa7b01b6ad51ff3ecf67d6e4d345f128e990494a2db434fcd3ab\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3e7dd961be36c0c286eb9e78bf3b33e6f9bdf2c2137a0c660f1d21dea31ac9a044e526bf47ec8190e137a60f1f55e947046b9cd04a2485679e48cac80a1bb064a915208889289d63a6e338cf7069ad799861c31ec6eafe02a4ef2c2641c9\n\nQuotient = -178d749de2dae3a2ea4898c59aaba98ad9f340762040f5aea13cad45a793f1256ef\nRemainder = 6c5d9b19aed9f099255b6e3d251aa50d1e534e6c86d82eebe097dc8dd0748201e48ac62eec070a999c21f5c7684e5a700212e9079b5fb731321dd1e16ca82ce80c1f5c17fd1720f1353bb90997f47f5fce335a43a6f59facff0b3724423393\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9f52ead13916f9807d0cf0c6699578af52c54816828f22de62328fbd7b4fd6c3740ffc82af4e24892092c7ecac44b5e775944445e6615fce25610984030a345731f944128f5734e6e315a0ea97aafd7563105695d026880d065761687b75e8\n\nQuotient = -4fe43bfa9417839ee408b254603c3dd176653b6915a89de5b781b400162fbed6\nRemainder = -1c15816e03751a203ae23c48965c8541849b09996bc81d28e28d7871fa87d1c3b2d383c056d3084d7d01d853bebe270fe2c0839e71851e169d417c47caacab2aff8a8e05f65dfb20eb17ed8f67475702fa83087bd868246cbb885d52639797b85\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2ef8419306ebfd215d9079c7a2b959a53ca2f4553845e3cd32caab2635c0e77fee8c5c016c121e3cbedfac57f810c132486ba78df9e719a976e0112516893f14cf9b89f95a89aaabf31cce509ac8e7e62ec3833f0be4336afe6d7d73518141d39\n\nQuotient = 127e8c06e12943017f9dd57ca24dca0ead230092811d307386c81b6efe009c\nRemainder = -24f3431858d5aee412443feab243b465b849f5dc97e4de4db88c7adf774d9bdda65fa0a28cf6b18eac6078b00cbeed2ac406f8426aef868d4b59ab045825d4b0a18af6c9105e32abc72fadef55b221278d329ff6fb9019630411bec143c4156df7f\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cae6399216401dec0f8ff5eaca884ab061469082ee3a18e49e0b4d5f9cfc98a598c373249a8ad2374e0b3de71370e93a98650684fbb931aa5d8b4482cb0be142492bb71743c251346df66896806f926a4a5dd4c16ca3294f01bb998835e6583d29d\n\nQuotient = 3f180694e59df85f48ac02b6d4faa26278af9641db18d79f198da5d802f\nRemainder = 36cf82dcf8c7ec783b4de68e0627a4a4b2a508637c176de09feef62dcf382bfa5d8b88539b5ca2cab6cbbdbbd0e54c092f00ee13f4a352cb570034cb0a012cc0fbdb6ed32967f3b81d146f352139bd3d9a5c27789468b7d79b84d6a8f6085f859532f7\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3b7983bfaf565c5ca444367654a07b8bc2bf7fdc04ef12128c392bef2f6b67d9475b4d2f0ce1c380913aa98616fbe1d74dc5c9d64df15f5c9b87a8bfbcadf335a6e8f863c7a01ac175a7d79645ababa5f961fad7d1b9926f7284e254fed33765339e0c\n\nQuotient = -11f635baf7b7d613e84dc38978a21ade2f4cd741d0c4f6ae592d93af9\nRemainder = 4317c686dfd56216bc4865f8dcb6a3446e13d8b33861e74d6c4a3223c387ffb8caeea0141049898609ed1abfc2adbd21756cf64a72272aab6c0b8f2177419abcbf9086635dfbea80a7b884181f2f2ec9a402cb0505e8208909fe062d5e6dc7094d66af62\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d0ea50558197566f22704e66a70328cacd6f4b7ca9b00c16b7c4b4e7dcbd47c9b2526b3858ebb4de7a571ac570872f3b44ba1fec655c0778a8a87ca24851f6072c5c0b7591b5e67a8cdaca78fa46f201e02379fcb9a8470e4a4971acde36cf501d369751\n\nQuotient = -64a078497f85588d3402355bf3e83d25ca1f0ed2c24a395ef6de6b\nRemainder = -87fc31ac66a24ebd629a26209ccac1b2c85e52dc83c5240269ae5a27333f33d31152c9470efd41472af034e8536bbe94b0a49e892b1d23db3c13fd84b7395d7e3f19d7d4cb4a4c07dd1860826696cf7202483446452aed2b4980388e7eda0ccac792d77a33\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 254a85bf512d9159b00a70678239902ee7e15ac2790ce5747c4a4743c6a0851e6a179b64c75acf312dd37a7b82a729246f79196b8a399ff476c48a05f89c29fb106bb06ef0300c4b330a7b2bcd4ea1e82584c7a96b99ec2131c885c5851343cfa6ae4d384e8\n\nQuotient = 116a06b1d38067cef9f55875fee1254c8ce39b42c19fb232a287\nRemainder = -c15a797fed3810e4f536e9509564b2142ffbfc0c961ee5aa923d43a824765c05d2a99fef79bfcb6310c77a91d9bc6d0762bd687493865de270c99989e891fbf6da7ea5c7c7a1032449457eb73222a011bb755ff44e4bdce8e86f8aa9f687840c0832f7fd8ce48\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d77c14100d19fbaff6334ca6aa504001a1d56f274632dc89d48e1d517935503c26b60c047cab9e186a55b72439761c884f63fdd2a38ca1acc653f6ccbb4b7262e6215e6d00c8829b448b7ac8716fe0bfdbf8088c8c61eee8f8db43b7b5551f6278081ac2eb1c5\n\nQuotient = 6fc9533f6d0e6c55494cb1b319ec47bde8e621aa92d91155e\nRemainder = a1a70f674cb141a896c4adace0dc58cdcbe2503fd0ad36ce348dc5b8afc96d0f2f8c65bbbadabf2920012798b7ccaedbe8d896dd2674082ad3cc75b54c5c190ad56ff34e8cb5dd29c031656497d48571295d6da396d5f4cdb652732d874a79a674d06a1d7b979f5\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 21917f48bb8e65646c618068fd9069c06e22ce8c679a845f9c4ec843849010abeee12e2d3c61fb963297abca30813c446f2ae82e909ca6ac7839fb58974fa65f3b5d91fb8b3f99d948519ed56653d50026d694060208cf48e3c757f64885b4ed4328c6f071e9f5d5\n\nQuotient = -1abc689fd19523d2e295f260d248041bd00ad3009cc7581\nRemainder = 1ab5af1478fe7373d012befb319b53ff9e36899c1749ea763fb74f7d24624e70ee78faf3115c2a423629528f45295e4adec7b122b993b5c29260558be4831df06468bb1c63e8afcfb1b9b533ec6acf754563d2ae25e2adb4cfe5ee3024611e03a156484a130ee01f3c\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -8c5a7b6bc8ed6ac015ec24efff607b0446c1b736dc8b409e2f433e69d0ca015d70c64b4c924175d0e0102ebc3e1dd96dd4d5bb01cccad229e699f9d8f9ad0e04339d70cd113e93d50c10c03083a81264396f5db2d979d272798ed30efa15d52289d0c72f42582ea56f\n\nQuotient = -4aa210fbc0457fa7366a8aa9a3acb3f9fce812303ec9\nRemainder = -737bc4fdd3d5496fc7f936ccf14bfc3d93f5b7caf4718c444db7a3228b41015c67aed304fec7704ea8238ba6cccb1e94cac3bcf4764a44bafb49e5fcb0339ae44c0114cc304b9c4370363657cd2bec09b", - "f962ccb21f6091b081e71d2bff8556600576e18d4f78fc68b12\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 324774e49bb429553c10156e8db122670d6dcaf6ef5291f515c517d7ffaee36ec5ec5ccb4d12dff71ae7a05bdfbb03ebaf4dc6c4e8bfdc165b77cae20153c27d53bf27d92ff25643b4888cb586e773955a1c02ecbf0fa6958a8ec0b832332eab2e449be6e72c48d2f1ad1\n\nQuotient = 1c8631a18d189f1fb689f896005f2dd2098e0dae9e\nRemainder = -1a1ac9612fc3354056a5378de5b315f12591ee71f0fa9d8a6b2ea2b1c4eca9947e5c4f5ed3d4b78e69ef7a1f5a9894b9c7d85f6e2244ae76881eb06584eaa98c78b60b46084b517f4882758691f91d9e2acfd580d5e901dae14ff4a4fd6b0d7c73450e4928fc6f02fb5463\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -838df2a27bbb033fa0e581073b879d6e8747fff38539801a1870f2e52d91bc84cf10f2560e93784650fba080304244dbfe9da679f207b6920be46b0214a1e490537e56d99beef3f58b30f311a12283501ad79a5407ff209d19a6efd0421aa144e0cd427380d89bfae5d1f5c\n\nQuotient = 4213d04b9f0b30026bd355404bee887b22b2cf9\nRemainder = c2bc097d1c20f050e88912f066b658446cacc7a4d510343a8d88ed007a8c0cfd5d44fe5f067a0e81536d121b39f2d0feb8dd053bb5632e3f9c04be5f6bf4091d646860cd38c96271cdba466ef8b7e2377a51d5669117e664269fe3c08a51b10e1e019ac063d670a3c7db12563\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 38ca0c2f03a5c56676a2f95cd7a69d4aa2085343af6b1d2a71e0d1c54157ec0e8f9125df2a499cdd484c04feb23b1e0042ca908db74744584036c79f21c25c40401d551a65afed0ef35f1ea000fa1a99cb29e6307f6ca0304145f7e483d008cf9efb028ebb654115a8c6b87a08\n\nQuotient = -134e043b3b88b31f89ff4bc709cfa1bd2c1a8\nRemainder = 99c1c846cbce5e9a26c5afcc0186bb1e43b2501ab3205d13fdf01dccb9b1a935bc1cf8adf74d58f1c316381577366b6d126da49991a0d5e02acaa678085f335ff8b8e975e5bf2e52a05488ebfc21a3e0d0bc5bbe67442f77bfc3c1f0c03b7f7ce42bd0fedd8a498f018d8cbea47b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c261a6c562fcdd56e67fbd2b91027f17c95da43175eaca6e4069c16d240ebbd240582dcde953eea739a4668fbfcdc6af8ff3ab58674c95de90fdb43f64a61108b030d644a44b0319b912bb563f61e520dca9c88f411b32e99c872cf00a01f5badad584636352913b7429b99ecfbe\n\nQuotient = -448c4922b7a7d5e1efec2c3f41d0264b76\nRemainder = -2599e928027d10d3a11056eb719768e5edb1a625fc0b8a1dd4439ebd30a82bfdf89e617ac7c71622058cc64ba32dc242d96fe3ecb856f1b146f831334af562cf88139a99410dcb869b9ad6ac4826563b400b59f55d8fff262dc920fe525b12b2fa167ec237028a098c9117cb77bc3f3\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 36be11eb72832f8ae7b6bdf689f794f62cc1c885e64706d14a77a11df9761c2e9cd81d8f6a0ad0cb1696c69afd80c8bb992cda5100cf1162d600515568b9dc9c81a518da9d240888d4984df65c129ac0b4c557b4e63ee5be79a27473ff5bca58e559cb04c4ac93b61545e7351bb6514\n\nQuotient = 152474a1a76700598c18d9301866ec00\nRemainder = -274a2f9e2bc5f9d75f9897b28f840b71bb10a3e4e7a35ee1dc1150be61130b4e0e987e8742c5edb75a1ce3158eb8bdb7d657b8ba39436d7c88fbff160c7488ddff2f13b3b95ffe149a3d0d2d406b1737a7671f69c0e5d7074a151cb2776b2d13ca24bec261662f2967fd22339ed6c3f2b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b17c79a31d5085b49793b6a6d628109a6047e3b1afc947e5212d0a9ae32b1955cfd6fed07fc60634ad15f32a9e402d7d5f750fb6d1ad958211f9e8ecda8990689e5212cf72b24e9b51bd07a6e0477dd4c02381d0ab6c0ad3cac1f620f723ab004880800736804751349f6bb19d3db48da\n\nQuotient = 5665f53d5a7405c83a5ff382ec376\nRemainder = 252d055186ec896cb3142c9e4e49c441e2ddad365b86ad21ae4ef1c522d3306c2834d6993a5e1f8c64a1ed582bad8ab746f7e773fc004b1c47814f73560db72f7237ef6e2f671d3b19a8777be2e4c662a76db87ea64f32c48ea371b1ffb15df26726854a417e18afcf49054c6d2e0e337e71\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b6eb2caa3ca650be02fa199e9ea6c48646a76434e268713753a547e49571f9817ad396f2cb7b16d307801fc8892f0af3e7f93ce08f7955a8acfbc0b56add4b4c7ef7351f60e402b9a8ef7fe02ccdcb4b00b7ffe78c7009268dbcf1d606c3a1b5307d9a8ee6121c6a635a742b8bf36b56cc7\n\nQuotient = -eeda035247bb13860f228d8f2c\nRemainder = 3976edf710ab42bf069e5829de7e16962d1b765f6ae6ad0ffabe723e21ab01cb9f3f5f4edb1d8c13cafc0556c0aa93d72dbcff754ae9260abd294647b71785bb049bbb865a26bba22defc458a14af019a796e942e77d03484028aac2b3798fa730ae0193d89728bf80a8728715a0807b3c497b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -fb5e55f261aa96f54983869d58b3e9f0757d363b9c43aca5580b7c0380096f396ec79d1b30037702c19be5889fc6376793cad51975100f33ebf43e0897dfabcb9adf3adf8d845aa7589ba1f6d155b25f73dae3b2f835595ad6050401fd4e6392012d06194af415b810b0c10a53bc56350bfcc4\n\nQuotient = -5b37eb0c3e3f8f8d9ac6f4e4\nRemainder = -28fde388257b9a11441c592580cd38caf2d69e2ba57d43151c77d26535226e05e08a9e6d8ed470d4354e9f46b7626e5f2b22b652a2d78f817bb51598c727a765941fba63510b58fb3dd5f30717f237da43b42d20bc260b06d488c9c912bfcea1e7808544c58960a3e1355c50c889cefe75d4d9937\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 29232a3fb059242cae6e0b419ff13c479048cfe46a9063188706c6a3842674b16a1aeaf771c5b0ef401d2dc8a57f6fb4fe1b3c7bb545c18ae763e39421e6a07c4469d234f9fc737ac21ca67a5553c7ed693eede4325dbd132dbd9889d815c02f426801eff1f46e7a52f72845234acc6c153f34065\n\nQuotient = 1c7ac058af2e7bfbda9484\nRemainder = -54d7aa6dace87e61e24d87053b9d094bd160916b720d7cf4f740a4fc5a7f03909773d0456c530ea0204427146fd44d3ecec51d8627b5768de1494bf42081a8a4fa97163b0b93b59e70e533f3257723e441cafa4aab471ec4086601021c4462e1f74bebf298ef45fec98fa8e6ea97415f84c93c12633\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -83c2cdca7577b32c20e9e20fb498a2bceb7174ea9aca09d4dd2fc7a1d3b922797b4e9640c7eb9dbdb4d93c7fb9daadd680c1c7645d8102d77e9c877a9f65b13239f9a650dceefc1fd41ea9bd2b38a622bbec99cfddbc6e88f377cd51cc29fd17a27f3d0d970403a2aeeac6ff9fd69c3bbc5c2b0fe7e\n\nQuotient = 472df5f4393f33cc382\nRemainder = 16579a289cc776a47611353e158c43dadf0a78833396f8419fcbbe47d90c7e840e2c90e73e563e6c505bfcf691120ab0f1e9ef9c31db608cade70eb8e487b1113a46e2b5c7f4a172ad99b502eacdc0f91c295fe608389e61d030607a94d09d349fe1a0cc46d1e07c8db533cedebcb4a3b89afd8b924993\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 34b7f6780620246f5a0a92a768072185f02e57a52db1d865c21c952f4386ddb7e2dc1df076316cb4f2f394397cbcde1af0197fcf33e6428e6f5d42a9ccf623f7", - "5fae5940873097d4591d9b1a4cbd00074d134272700ab06d901742da695c3ca9d4f917a808113336f883e769fa8051cdcb0cad7cabd1cc\n\nQuotient = -12b4e74d76bd306d9\nRemainder = 8768fbe8ddbf60b548938d8b4a74c4a326ef335257e5f513e65a7d2cfbe9d456425ceb719407bde3cbc74c9c978970597b5663a0ec61962e77eb351adaee2d2d37f1fb55b5d2ceccf282ea3a0d398be1dd1b166d55dce04a39ef434fa392893618003adcfa61401276ce4e599051ad93152e3477ff524f0c\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c898a753745f0fc178227a7004d917557cf3dcae2e85e95aee51e137b29c895755853ce2d61f214b80070174cad8ebc2795a7d070790acd335b383f9dc88c01227eeab85f1f29d76c1136ffcc7b9fdc073a3a03d8812c7c561b32d8e69754fff64acfd64994b7e9574d2a7cae6bfd5a6fd61dee7ee993bb7\n\nQuotient = -548c97fd02eca7\nRemainder = -939e90e281f97a433eb1c6510668d0fc448f03d737d92693b6362c692167add7e4442105d60ff3db29c03ed06c3121aa4a53c4625906519a4092e4821c918d2264ed0cf088b7da43a222877f3ad9a9fe8ec06fc66b9cfbb44e0fdca1dbe4e461dda9b85231b5b9733e0c78852da83bae557755de3680ab61d4\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2c61dce04200e725ab0ecc5016f66044218391bdf650bc0bd31f3749ac06c24707e79526ee459ccfd4bc22834f8d23f391f2e99135f92b5abd0b04079ab75a263c0e98e46edfb440cd865269ed7872e8c1ada312df1bfd6a5fcd2ebf548d7b7d1d75bc36f62e5e9d15262bb8652a8041e5c8f4d673eecb777d1\n\nQuotient = 14622572f311\nRemainder = -6d197a84d2ed486327790059adb5c073218c56345f48c15caf6892734fff0aa7af4782738bebf24d984bc8adb3056f67e57f9960001a67fa462afd8c57ac9d60ae6517d58ffb4773b637ebe6bf2473a5490511fcdc576a4c40ed03b3afcb2fd27c57b66a26f6d3f9b2bb101502b1117ba3ce7214c9db6302fe20b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b818674faf69bc92085b7230d9335d7bead0413f2905539a54e8d1233843ef13f07cb5538e0787097cb24f152cf54a92e62ef143e31cfbbaf3c09650b14229a4f61a783eead26430949c88a87f1618788abab9728aa52dd8419f5d568e6a109f278b2afdea91cdedca43e562d4bb8fb7f1b7aef13992fa7edc320\n\nQuotient = 5cdbb03ee\nRemainder = 1cfa68d5da7a600a7ac598b9ca1a0759f972fd9a46ba62e5e96d8f6f00fbccd0ab26ca03d14470b43793411ea9803c9409908625fd74ef8f9b2d7c2064b2e3439adcb684e6f01432a1feb0f492fcdd2b8b5a6cdbd0bf460272218bcf763974be8784e5306c219ee535baf5541b8580952e3690b585fd99f77c46d69f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2869338cd16322409d3efbd328b27e2ba53cbf71816ff5c093849b1d866b8cdecbd6bd8ffea0b7787251acb760f85c277ded21e56acef05d29bc728cf44f55be87cb4c8913408a01a1ad53461058a1cf94538f05ec14a6d3eba804264df957de7eb1a61b794a1141218966463dd42402c260c229241ec46afdb5a06a\n\nQuotient = -f16da1\nRemainder = d8b66b622b5a54963c2c84aa186bfde5b67a3562e07a23a5f6843bdb615a3c5d4f007ad8b275ad7e4c5b1436252efe35699cff2e0546e6dd8c7230d6ad560c51cd54db6d312be32ae4c708e9047c3a25c211e2566c58d6b9291de31612006d4e847c6916702be99b3f7ce40e1ac842908acb7f03dc120aa8998c60737\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f8af8fb7002a9d2218dcd0f0c139b8e3dbbd48e25a5c910f6d0b6684bca224f62768b64955580306bac6bfd45b99ad77483563fc7dbe015edc06bee3ff93b0afa8f5866c23c7a7570b366550490c97ad84062c2495cff30717aaa965a8e15e270b504dbd4fa943be4f97a7fd1f3b589bc9fcf4f907a7690d99c978a374\n\nQuotient = -71bc\nRemainder = -13316e9b053a06520526f579718c326402d2a9686d51a340375cb53d7cebba99c8d1ae93388db0a41cf55d5753dd1174014ff3305fcdbd5b02de9e90c45ec0d2900ebf6ef847c2a045eab7f80f07f01c81b9fff093a779a280ae42239df79de8d2ec4bff6723788c86786fe276ae6a4dc1472442b552258e1e5b597305187\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 20fe256859a2e4c4f77db6adef78b2aa4758b29ad0787ce7e277bc68391d5949bb4dd07a9b1a79fe890c8a760871d81adfd3858e27d1bd6de33fd31b8aa6131fef9130a50f995c3be1d615d1bfb9878804b7f6494237d8ad78ac219488f17335ae54b494532f03a3fc8e9576cab6facd90c662658878fec86db66bacda3a7\n\nQuotient = 10\nRemainder = -23e09736f469c83f280052ff01071b1bdb52b7e2b061e8a1a8c6a4e091fcd7ca0b33ade885d928a11a3375599aedfe554d1c2289795daba08f07327a19a8adfc219592bcdf9fc5aee5961a48b3b1b5fc380eff5ed2ba7d7e564462397fb6c6187254ee41c74602b141d7adba99205d2e0b35da57efa96397b3a5d112751cf7b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e849bc0bfd9560cb90e42c8e4e88df175133c14466e530716d89ad0326b660b0e617b4efe8df6b000f517d3cc24d9dd4cafa2773dafd4c6bace0aba54e43c17e8e3ff9497a97ed83e6408aa0aee0e6485dd1d89d52520d1acf4d587422b0c5cd2d5e7e81fdcf842d6331779e800f96628206e8be020ad4021789008a641f67b\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22004040a65f9b6f120bb7243c638cf3a4cf6fc58c230da932c79568f68e31af7a7b8569aae77af671f8335ae68d6dc1698baa9d6ba9cd633a662101b45bde51d55098b50fabde8546f317ecc2ae7a39521bc075942e3751a349f51ca3c371f3b8a6cbbea3e11a334d677c07612bcdca767194c07fca78ea8a06cc3b0dc6dcb8ba\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cad46f410062dc33ad4d712c3b743ae2b7613576b2bd7c346a8479ed679a08e3644c7ee4f23b95f1cc9111905714b170abc37ee1003956f64f0a7e876b38d524fbb2436ed56069479d8d2e4029770f7801a7278fff99b3dc76280f35c7d43ee594073f725554a92eaf4f785c18a7cf6669dce5adb0995233241f3294cfb5bd8f4741\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2fef69f9745646aa13e0c38d77951161a1f881a7ceef032698da3fce00764959f11140bec7d7f53d6777c3622453d4525fb068da48047609d18d463a8fbacde1d21035963b668ca11d5b9ae66db13de7a7a5b66a40608dfb56d9f9f0c8880426641083a05b5ff9e6ba0d6da3a04af1af01dc218e9b4f6ad7b1d3a4d1d26a5c906093b2c\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90", - "ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c50a24e5ddafb768f64677233c5cf09da1b4f06894bd68e194b23feb5c5d6844320a12a02d13ad012f13b1438eedd6313bac9c1f9bb4548fcd314988d8fe0ce6458306735307afe08a96a0c2bcd9cf126f529e48b7ff4b8266caa28c40b5c3d2a473ab8805c860d27d7ee9c032423148d96fad019490ea019d40679de7a2a3323e80979f9\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3a8682d0e5a4efa985dfa8bbddc2c0d72a4400b8b070a8cf7450aa8f831d8a91c9ae3542641b7a4ad793e232a0d301b82664fe2c7f20bd9bf8275828a2a20027d6056b211638b9b0220fa4252d058bb485dd3c4622b1eac97d54b9634b558ff1bd5bd11085d4f3d288f7965af52beaa922b23ac0207d5763c24c085076128e0ef7370eeaa19d\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f00fb238bc9383079c7ecad9b9f6efc622d58a76f2d5d40ec7cd7c3c083c459fbcf3d128df4d20ead5f585505515aab11c36584ca622d28e0cf037419a649d598346063a07e29c61b7a8e76d1949dbce3720d45576763aa0d391b39dd6b694c7cc60a1b4f4f107d87130402985695e1847e82cce39b8d0fb5c88bcf3b37d6dbb90baf5a8553c3a\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b809f6baacecf61198856d9edbb768ca2df2abe9b7b8ce1669fd9259732c8569c0cafde2e32d253094480ed281a8db230f84e780c6e8bbf3657c0b0baaf19ea973fd8daa2870c9d79f3695d78e063f9130fe07ce806a088ca267fd2820f10dac34b5b32aebec20e4362dce26eee0c29d2fedc1e020d452bc2499234d07a2a6e54314e3fd6dd85fe5\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -90ed75629073df816ec1d6dfedd1cdbed9239661e362db706288dc4d774d806bfacfd4b32c3013ec67d8c2af133b46989f12f809fe202d33d5ba53659bd2a9a85d3fa542de4a5c656aacbbf8899aa66ba816b809f2629f37b0444cd3a6dfc99103bcf2a5ee87790b8401be806b5d7fb7064ff0a6fc8ec769d0ccbddbc3d35f7dc4d388d8d28021c95b6\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3f60052c9dfe0bac797a674ca7f11377a24c28a1396ffa0f46acab7909543086aee1995cf51852ea4a21ff4bbf6e7309cba9848a7b2e3b33dbe660bdc58d513d16bc709f1f2253648b46daa7aa037332552db1da81b4ab9850ac4ec66621648fc856a71eee3cedc6617071600ecbc5ac8636233f288ec249b7ae0bac942a5fd539d03990c4fb28a46653aa\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c12fc156d9345cdfcff94bdd324429530ad8caf8afaaa1a82297eb3a8aecf2ac021384036749e489fae05e8776da0deca7e4325436bc8f383bed579c2d67a456c4e23871489780d760d63d0bc0d1d0ab41f06a091b44f602bcdc0bd4e817202e39ca6a934c0c9405adb5a14d24da895c58a81d1c7ce52734183e00d80a414ddd8869998822364e029b3f42cc\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 205dc6227dbd3adf8ee49dffd43f835882822b1c94f92cf38f5efc62f943075d80b33588973a0e0a8ff5e800ede21d394736ba98d4eedc53a9122f8c262cd09fe9e91cedfd0237003b0124d757797ee13cd03e7a3a257bd8df756940a4d22face9287edca00ca23e7d5e629966ef710b07e54241dbace041aa6d9f82687c3ecba818203adb376ec0b201894a500\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -82c30a9ef6a83d81b77825c71ddc563939b8508f1b7e44c725ae0f61006646ba9b86507ec9a4dfd3755ecd8bfb451c2d43a61599732b8aaeedff7a304ce0a9327e2333f75e9a010556ecbc3abaed02214f25e1c8373bfafc2c288ea36b8d5f848b76295a141d8f633609a6656c07f3d98177f5fa83833476dcd111aad179001f81d6013ca3a54cddcd8dc0ce7eb24\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 33aeafda3cfc20710f0b4a3d9ace4817eed80ca57ce6c82dc2e7946058a40983c9204ac95a1399fa633bc96cb10af3ddeee3ad2337c64391a42dc7794fca629e3e1e4e03a2ae24a000e7113b91c1b6230cce9592e45b6ee7984680b45aa0aabd7f56cab1a64ec310cefe5211821a75deef2e0c8e43eb467dea79dc8c03d2d523734498d079d5493", - "c904a2ebfd8a3a9bd\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b897bc87a40211ef8f93645b1f6c981fa00ab3b12e117a89375400ab5f4c64bfbba01d265c7bc6f5e3a8e26de5de9df3b8f70f4a39c0eba577db5e4b7a68f751b4a69ff4a38915983cbf70dd7e066779405d572f5bbe0719c978b6865ea1a72d90d3ec8a8c146f20d98595036b3de88a7500d7b476644913e4b63e85c4e2632048e9600d553e560759770a902cca680b17\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 20604e080549e1c503049ebf4a56cf9447d90fe699a9773915b0a65588890e15bd58f55ad7b52bd7b7992a8b24704f1dfd5fd07c70aae4ccba5646405ff8a9cbf542dc334cc0c27a790c05420b552539fbf0a155861bec0e4d9e3fbf045720ea3aed58307d5738b64252a963f3fd5ecd0587cb4d7e159b4980dcb112e26c9c34f10a192e090ade157eac1d7a6f970871eaa69\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f11fc9682601cab97c25533b2599f50edb1ac65d46f1969bd9c3cb3717461627621c8cd401a0a0b91f3645b8804e095aecab31c1bab0c26df556adafdd7e7f4f0510e0bceefa3619e26b8c9a1bc613db03857f53e9eb5d4b8f75a8cd1429feb81edc705e5a779d5f95373d2243368ce17ef22da79a6a2672496bdf629171b7973fc4659c8eae9ae867cf38d6d7617029bf59d2e\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3cb0ffbd9ad21d0e86e4e4dab4d237e2a17d97356bdd305fda772fdd99acefcfb8309d813643c852f66e1c6c7fa41ffd44f8335ef7333b2b3e846139fa9be2c4ea762afba4e11263c0b5fab18c5efff2a18d83ee89844f5f4db2c1325f0f55e066a9e01030c07a85e2c9bbd37b5e767ebcc9b95f474ecff24df9ae52a19edeb66546a3a28980f616eb5a351cd399e5f8436f17faf6\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b8aaffe779855c6ae51807f8cba780aa64bc22e8fa5e33f7f1dcb084fc476791565bc33eb37b4f791ef5cf46d64576f48b5fadc9f096f20c798355861ce5d24a7be1450bb871f9821099f98213d74a5e5cf83b895ae65e0e0fd096698463906a112e6e169a1cc0769df7a5ba6812300fdd33611761b6339385e1a70f8f8b2be7679ca216f5b183140e69586a27aaa9f2fac118118875\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b7ee3ee34347dd89ba4a81415aa1269d0390346597b07444f0febb71d490a01b6fee174634bd88e8aa180409549b2726d044b4690353de2fb2294c8f69c612485aa066f68fdb89466760a85901cbc7312bfe5a6f656e67dfd2d4ee099ff97694b01d6d5b8626ab1650eac5267be53f5f3ced5dda1aa86bf42ae132a28fddb94902a515da40e0fd0586dc8b17a34af8eb03d06f70ab89df\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -bf8213944ba785e01b8d37a12de77b2ce1492f34bf6f67406cb51da89675b4f70f4d4f314f30ca8d65cbc48ee2fa1f0a3e4ac0de3a87d2c4c589b6812e850623d78ef2e46fbb555f6d3c69b211892c11a4a2dc3d8a9a19e96a07952602ed5ffc0232c140c3e828acf990e5425d8dd9ce0c1107ad1c6f96c8fbc90ffa457abab0d843094dca3c8a45ddad81b7850190625613a4851485f38fd\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3083421e375f0722b9397e156de47f77635d62ba1d51794469371b473b71c02e3722841bca2ca06b5d1cf1492bbacfa0abfe394dfdaa7bb8787550ddbd953540e9c97631d9a1efe0c8f8e14f395c82d20245cec6d8021f8564b4d66e7779c3245734c56fb74481172f4e349d9a113cd0ee5263c69ebf746c5285cd4c0fa91d9531f769fea3610c2972ccfe9a22c00aa62ebf52b3a4c6135f3069\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d736bce537f47ae4797faad797af8cfeaf8a4fd42df1f7e61febf8ebf6e47dabc48252ff7948f3dbf8cc369b6952dc58f64cf09b4c53447d135c7a753c21b6052a9726a47a61e13628edf0f2bdb357f2e780ac1ae1f28f211296c8961c2955b773d7dc2904dfea96780b2877af133c9591a0dd54cb20884f014f363862478ee7ec45236bfdcf0321af0692e68f744af28fbcca827ebdc7b210da38\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba4", - "66381056a3b92c35d9b8b71372b\nB = 2cf1708f1e675ba688c0d19eb61a05d2c8642528ea6b1512375faa732acc59ec04ea0aa55e0049144be09eae1292b6cba6db7a9823f1e912df6a5032bb9674f4f26c0c8244ea0dde7acfda566574956cdc33e4a27bcdea25fe255c19f218cc4316ae8428ea61d1bf865197a066b959c5fcbd7c9596207997d05fc38e32322aa189ea06cf5139522571661745c0d72b740dc6d842f1dd8481e318b5792\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -a9180e44a284b5bbe72fff46e55869f749b626ac33c8cb17be1fc260d7c6f460f24a89e1367112e00d0da4d213a821d09f103f35bc4eade5605bef23c5d048b1cfb45dace8b9c637af626a85fc773cf51e6602a7a5999a030030cf114ed6a4ed7583465b9303a72e7f60824c12329517c6763b0f64abd8ba2b9b26cebe882a51f05ef8076e527d53a213db910a5f42be5fb78729a3dcd08d69a709920a2\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f26e156b3b1117f7cec542b20fcc06ca66cec03a19b6f5eeebf22b4c0fc265df5ff06fc9dcac569735135bdc142b526b295225711efb71577b10aacda2fa446f5208487c725407c2188b3185237740c813e4455a6f1dde4f62916237f23164a3471aac0fcfe24ad1ce1dd81a6144f5861ad0cf22dc337abe10fc4a88b36116dc4929602ab48eb971fdd7a5ff747d6b9e0b2bff75c59621550991966a0a19f\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9fe18ae697576dd36ebdb621d14cac1cfdfd1f5cbb7cfa8962c5a7dace96f9f54fb4f4cf2e650dbec5d1ba89ba53d251ecef7dcc1cab8c2ff3d77903f5fb5f29a4e8e3a2a3c05c105d5733b5132f2f8d88f99d17de86ca1191c32ad8ed469bb649ef188306f69f183bd0fcc32759e4f855170f88c0a3f6745aa98f6225536821bfa056a42b37535a622f42b009859c974cabf2e14f75c749d0fe5a01fb3ab0c0\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 33ab185854b20a8126884eed85181b14e75d4ee452958cc1043b099bc16c24b9c2f3e0b792744f230013907844496e600389800e45fd55133fff0cf19c9c152b9d031039eb90da568f9c5212a3ba283f4d1353ff8ff9dd04d292c265bdcb77c3e411716f471930bccbb8ddb819ebb0e0036dc1a18457cd97f4f5909a725baabbd15e8ce33875895aa8dce77a4dbedeb0271a2a4a17f77f5920c3776caa4a75ac650\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e7ca0c037bf8bad5f8d9c5a2737e044d9f7284c616156d142612a53eb217f57f4aa00b6daa424e6c0d9163939e1ad0510a1cd64fbd576f3e54c59d7aa6228fb3caaba7cdcc951e00ed141ac3a68abb9780bf46bf544fe0e347f677288e962fb69782741df49b27cbbe8720c6f8f2e769147d89df6e17e3c592bede2e696d384b9f01b99b31c505d67eb6193a8844f8c4cdadc9fe45dd446a0dc572c9da6e58ed303f2\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22b76d6973e37aff4a09216e57662f186c0a0748c4375d6bed370ea61d1f6fac2d9bbe04487a629118b6b0b0c8cc4179fff7bedcf048cc529498bbd9cc81ef3a103d6cac49d58bc41c83f961b6df7f00c7171fb7d9359e03c76e4364cffae5f67321ce646e9b05f9c04aa16ea65389e940022eda6dc740ddc070bfc7e589b86fd1559dc320701c39de20d54d0483fdeef6c4fd012850630b982c2e243ac1ff918377ceb4\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e6e4d69a82b83e26ef8ac0f4c3a211153ea6655b7ca12840e7b866510d114693049c5b8b22c3a097eac832bbd1986e60564298e54dba3316807ad64bd6c18903a0f22660c9e8d5dac180f57cbb90b176b842d5b58d6dd9f47499a037833a92a18f397238a8bcdc4afd129382fd6d200d3d267ca1e6bcc2cc65950831cb8e30bcc01665c8149b874c9f11168153c187341afdc43e4d8652ce4fbed9f9eac75db40d64344ade\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 319a81f052db21ee213c536db2cb8a71e0dcd0a9b2ce780a9588c38b717c5e487a337f82b5223f638fb552e92b826192e6a1c27771d1e86584bc6c7cbc5d9a6ce6edf2ea2ccf6939485959ccbf3183b40e410768c4665adf90a0ae2792fb4b5d8aaa06c6294e31893620decc3bc72fb4eb68f1e56b48e39c59abe869d07509b7564268d0b7f178ef09ef5dcde6e7dbd2a20fd1d4fcd707943dd63adf590a117ead1ad10ff85cb\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -eced809145e696ceaa0ee8f831eca67049509b31a1b15e7fc86cdd97a73a2ca05bfea5f4b283d287e49906463ef36f2f8ea23c2aa12d5534c08e9769055e04822be0f8ac85f404f5c025a6833b4115f78da9470451c852ba0f24062397d20385f58c5aca10f3f09072b2592e5672ffb989a390abf86cbce74268aef1f4ffde730b3b962df1088bf8745105a7462379ce142f819c253", - "8d9bba99e094ffbc4478625bc54df16c5e1a\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2c1ffbbb30e71d5fa77b5473392f95297b489c85f83013262abbe948842473154e00c86b2e354278844083f960fd746a3b7cb9baecb9c66932774b3a28f678d50dd8fe52fbeead43d8c8adad7c0fcdbe5e02664b0feb0ce214c5fa007c5fa2d08c5fe96787b95639311cc4b7eb2a7217c9c38c6d93444fa60c1f52ddae9bb2ec1a49a593e210e47377d3623cd2c4994ad9343863443911062e12233176f4a65ec715b3c9731c4a0cec\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c3bf056b905c0392a7b5fa57446ed350f325eb67d59f1784c744b04c7f4d8f5397db913407aa8a7f1dd0225c1a9673828db0d8bf3d4908ef53307131bf5b5c4c6068ad73b874aab98e8db33b0a758532172acd8b2c830d0679a8226537090166317b8eea91e8ee4a7282c0ab0ab6f2b7b63d728d22b534fdc88294c376a8d036ba9a644c2489bcc84f6aec83afbac08067a7b93f3897f8dadfb68c327b751841927a728faba47dc44ec4\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 23fcf9510caa531a304eee8d0b2d49050fca83abbf287b6b6dea06501c5afc6d87d2924df1d45b1bf6c4bf77b563a3013cfb4ad9094f8ee9892d33f6ee1c70131cd5721c5af804a9da7654510e8591aa185ee723f8caa78046d9e6fbb891e6024d2ec70110ae61c3969995e35941d2c7f3779d5bb71ce5b693bc9ce4b087068adbb554acc4ab23624e060f7cea169ab512a06ff3d2a36c2b6e3bd9a75f1a9ad30a6a16b0256c42eaff2c3f4\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c32d5e643b12db6616554116299c1da672efff1eee394378c5e9e5f702ea4ad64f0dac8904bd2751d2cef91adcb283599f6c661967dbab27059e94dd50025489cf74c6897a22e95013669aa3063fcdd4b73aa6a9a1ba5cad3956bb26346e22df6741cd0ba1c0ab87fbe74035618a394383823216df47b910cae495b8fe7ac5feb3b2cf0d0ef6c75db477160b75324db8eeac48a0fce72b9abbd7079ce6f529a89025a03a3777cc7d1deaf3e4a\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2a8f2c530342bb6ce683a760540e956a1155c0fe065476e400caec59861ca97ca71e51a11b3213b2baea1a41a29449998778e0f533fcc181698d293f05e28bff2750ef4095170de98a19a36ddcf59a65f3789a3808ead51680245070262c9544e446f23652eba47065a2bc4701c55378bd49733619ed2c213f8ed12a4a317c465f37efe07ff2df8e88fc33d3eb42cde9408dda28215702bfa607030839285a8bbf89b5e8842fa7d7f50d83fd4ab5\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -bcd2b2362aa146cd120b729e81c98ae598804006d046a7ed0f9782baa10a85e37c7c22288dc61c24830a1b42b123d63779e88d7555028292fed5ada1793264b35e961b608bdd7398e421c5474c33a65059ef13787e0cedf4f8f032beac48c4b5e5a67417109142a43b198ab617d1de1a38d6fb4922c6ef70a5aad3faf6f8d5da3af9679c94cf61ee760ba792d2972376425e2ec9c4109e969e3d9c3dd90cdbaeaeb7382cb7bd024b75a1fd6d621c13\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3940430ace4b5b87bf4baa2673582db3d27307ca4cd8e55e976ea3e10da72b6deb7de932253bc9228c85cd4ae7766cd0264004c658a66d81e60bb9bf4dd66e2afe11057b7f7b53a1ec222510748be53a93970fb056e8082631b2b77413fccb6e61cdc6f224b7903d75345afed8a4f194b4bcedfee1f16dc256c2bb9f4a129fab6a9fe752895a93937a3d087ab7ca212991ff34f1bf1c55987a574674af43986312bbc3bad3280bbddf4ab0217440f851b\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f0dc20b88450f45381791e85d080e4f2cf38837391e16e608b8cb5e0ac0ca75e9f72cc04bf2f56f130d46aff31efbabc0ab14f0c0ad680d6899797297152be85ac012644c8d0927b5b6c70dc3e5a8d79ef92a0873ec22af3d9683bb5db1ffd5ebfb698c5ea64cbe2b6a8b9f14d4c18624be1b78b19eca14942ae9542012692cd0d5289ebf75fcf5486596f92659143e9f952af3622137e633376fb95e628055e0fb1ba3a37ccdf0af69a4c0d6b0793078e0\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f2135850715f623909e41a745eaf7b37593567fa8be2d1ccf76d10b93a096e244b91d8700cca37a2ec1bff7c3d21cc3211ea8b03a3594921dec32faa185e7f3d9d17e98cbf8d881fd2abb944181659242ede21df7e5e8784f541cad678df1ef6ca4a5fa91f7856c62fe593c4d24436810cf4fbd11125bcb571f6975d82afeb81bd0c7700e053fc175fb5fc7b329c438479a863b8d5fbe6b4436b67355c51d0306e8847a27a30c9e61f0e08232673cdf0ba4e0\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4e", - "fb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cf429f101a2e19a65af1e238f6745215cf476ff2609c846f10289f1ef21b89af2aec53def3f4ec07ea42041f8b5862dc37fd03b2df12adaa8c9f1933cc69b526d47797b40f49545fd093b8ceddee3c55721d1fa19b336218de0cac56d410cc6cff4e620578cf820f5cdaadc367dc4d6372aab1e0ae3831a6d153c14920b1dcf09e7629b7442a06385420d79742e409677e3b82ec58bcbfa668ca072e981e20728a983d84a432605389c855a6668e0ee0d2b67449\n\n\n# ModMul tests.\n#\n# These test vectors satisfy A * B = ModMul (mod M) and 0 <= ModMul < M.\n\nModMul = ae2ca2ce7addaee2e2b7752e286b2bb6a58b51cfbed5c924f00398e59ec36fe6341cd83da43a33a12410f45f6228079c4aeb3912be87e2e81fa1799151bfa0fea29873097475b2c3efa312145d0bf7e51b2a7c9bc961a4f4dcf0c883ff90b919b87c21099fba40257645be31f95a3a277\nA = 6b18497fed9befdf22a01d988d34213f6687d8a96e86c188dea4172e7c6095a0d18d3c86c0f5a1af9c6e3aaeb6baac2a510930b3ed06ec78ec2e12b\nB = 1a058d99397db0d209f01212dd4023ae01b15da04fe62d1f76f21622b2695558c67d706c535ca7f19b36f8ef2d508ffd6cf6fcf25e5\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462c7cdd79b7604246a0cd97b40ea5a9a77408f13cbb548b56ee713c690dac0507fd988bf28e77462832f4307b08564a51510d4a951c1ad7564316dbead2b53540090827a8ade8092a6133af0e5fac7310f787dc1472836178ed6992b9f71224da3e884bef8e8379a58e6d4be0fbaf59bc520f786631857213305e23fd5ca65\nA = 16c92f77c139706430f396f72ec7adb045745cd9f5899b0074d9955bd32de66f57c05c7929b575312a7f1c04f19e724d64744bff7b31ad0e6171437763\nB = -8734c4a2361fc530f60b28a5f1c7e93136c5ff6bfc7553965eaca54c61e6befb3c0f8cef4280e780cc5940d21a740debba31f863ded75\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462c7cdd79b76042469eb41a7a83115eb84103da4ba438c3e33227631dc185054ba4e607141d1e60990d8aad4e0bb0ceb645ce9ccdfe72d4738cbe1f6a73ed3e070194fa4feca6001c4a853940a227d15c1f1cc153d8c96e90e24805929fb11e0665e0c41c77d5a97fc5903a8b215360e26f6a19922d650f460f7056274ee92\nA = -6715098ab2ba3ea1e6341e89936e3ae913cdd450dc831c8534071f3c362841e47d88f2cd29c0d1239aa0949f3685f12f8519625bbf10b2c7a515e6d00942\nB = 536d4b3e4815ae5ed55bae6950f5a8a61d52439d2800ef1b5ba2285b85ed0f6ec4af9fa0e364a6b14f6f6b8bebce9200467804e787f9f3e9\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 309b3e30f74c58beca8b2c23f64fe1203830db8a7e306e1fa2e2022f0d6d422851da509d1b2936f088f0e35effe12a7463f47ca369bee2f2980bc48dd8e696b2d8c6f35cf55fb8baafc2e613b4c684de26129cf196741aab873f81e498b1e03018a539b5eadffeb5953029f31f8579df7ec0ff3f752491910\nA = -11fec955948e007b59fc50e729941ee9d43d552b9411510b73f6b4faafc0465f261f8381d96f647267f72175883172918b5c866cf1f1ffc43c55f3c96a60c01\nB = -2b3792f39499767e0a8b7a6a406e470a78f97ebb36765beab5fe52e95abf7582736db72a2ebfdb2405e3954c968b350a459ff84ef815dbc5910\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9143ec3e9f74a8eec476cab17ad8636eaa7c60e108e89ae0702dbdb2b255a217ba2530c6fd52658cd931b962054a9c20c8713976ef3b7989c40611cd25b0a9ad0635d61f6dc95dba6e0c4a7d53ff539b623b97ba3d66344fa324f905abb861c6b1e830c4b0fd5f6a4b01f09c8e1408941291b2285c4625267a108c\nA = 7713413d87f1e50840255927ff27bad79e5de5898725a876e4647913158cda9f5fa031dd7fc11d2e8130a0ba99e8706341c1a98d5fee3218763ceb1d131e9cdcc\nB = 1384e60753dd4bc20cdabf398525e7c4aa40065255c5058cae0b2ec90a3821bea8de672a712431aef5864eab719ba621cbbd8b46fe86fb31286091\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462b3b4a0432890d141c0f46a28190a2e30ebb2e4ba90ed132169cd72316b290dbf5c261984d98e63eea6525fa890bf52185ad7f164cf49f67ca91c2f35511f3bef6eb7f3da31a602a78e4752e326d79dea729f4ca6438f2aa65eff44bc60979b42e44f6a301cb5de8fb42abb47bce5633c6ae9479d39c9e8b507d96161e0fc\nA = 17d806d7c76aa8acb051fd9c0c782443f1b1b6387455f7cfb737c41658d0459bda5d13587055eafb87ad8d209bccac1fdc392aeca0774ea48799511c1fb9141cad2f\nB = -d7c9b6574354e131de4b8643d766641e98554a03238ebfce1112c3da5f049d6c410a7f05758571aa2625f7190b936a214797570539317b32fb94cfd8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 16c84ed15ec6352a8ce6d5c2bdc0d9f13b333072fc7041146e944a29391f83e346b8ac0bee6dde98a420ba4f8852801d7c5bea6f1177a6cbf799edf2146f8297013e0e796917cc967786788ff12d9c1d07d9ce4b897bd22a1b8a391d3b4ecaa5b5c85d0a03aea5145db6350c42a964a41ee5f83e7d35e14cf442e5d99ccd0ac8\nA = -6d84cdf18a2f53fe496248fafef183914d55c42267af3dd42a39515e80cf29211fd58454986f5fb6afb56170dd9865d3158249090270bb9af341c830522a4dcabfd494\nB = 6f6f3f74187b7d74dee92f79be864d0a2c56d4bca3283742e9cdf15112c8f4208e3ac8ecc98b44b4ad74b0671afa4aa9e48dc31d34224a1f66bb2b4658a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8fb782e4883ccf3aaa2d3e020b08993d580c69ec8fe66ecac152c5babc8aeffafe406736cea492450fe6adc25dfa2e12723a3f9baeb02fc0f785b3db760ed28048e1710a78a2ae0c96b67c109c5034375a512b6fc7906847253f66316baa0ef90facc9ab992235153684d49d6939ab9e91086529494d7386f604ed69aca2f53\nA = -1f745c8f0c8fe6ce3f893d77fb274c61b72b2d9f9c5a2eb2467bc00d1f496d0ad469d76bce318bd64ff1107ee5fcad4469f84d658586a5789c068b0cb9b866d8fdcbcac5f\nB = -3a2347b491813252e8ebef1bd181534b074a368d076b8c80bde2e54ec3b4ec99001f43080c7857427e069d99b1b65cff998a141ca6963aa5fad1ee632986ad\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7c0c1c05ae1d6420bd93596a01aa0153000ecce660a8a14d6fde7d4740719cc495fe6681a9a08163b2dfd51659b3ae7db0fbe09504370bfc695457d7b32665a4df53e879ac817bf715d5bd6ca0e242b1ebacb1ffd6698ec90c442910a92b35ec103b345f9a9e5c7b005f8028da4dde80f36f6f6e5675040d19e46aef06040eb3\nA = 4c09264420a9452c6f0b55baee42c076aae5a73697cc6bbb88b7c922f236ee4c18e477f88e2c40cee03f0bbe87d3ac8dffd75f635315f856a3881c6373e8b9a286c813325d3\nB = 10474ece7ddae5c53c4df5b594439124370932dd94aa5d5b4ddaa233b1a55634fb7d72e33bf1b02965fa9d1538f97e1cdb5ec0477cec8ebaf202aff8533211169\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 481543f1556df756ae2e422ffe35aae020c9bde9e9b1f760b43043a4654de363dc67f381c0df1c3c1b90edb4343c47ffb8345a1aaf5dae56f446fee08a0b9ee8c42fff57143e10846610a9925be96418c4c957b4e92af734b96fd6f21974877dba52a0db1fec4aa97640e357434f95ba74b6b8323cbe", - "17118dc489552844602c\nA = 11bccd165d9fa2d8b01a48c0ec549a6e600396cd2023f0240056193ad27e971c604eda8aaed6ff6be8be1001f3dbdc8655f1ae84eceb963938ae7bf428eb5c968f584798c1bd8b\nB = -cfb6629ddfc98a242e3290959f4d0726c0b1770b52393bc7488a471a90f7f0951362c03e67f443c9ecf4987f5303a789bf65e0fd59cc5eeb9f5d4f40d3e4a14080c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2a770ccfbcb2bad207d0e2dfaeed04b6e7509daef00a1df88e57509451739a8a0f15106ce8b53d280a4b4e09900420714cb6961ebb0e00e88567c5df50d2f2908b4bf8e0a9a5a8b3c6120503c14f16a99297459543c467dcb67915e0a10e19f72ed5b6891a6121b66abaa602818801d3306630bb04ea57e6b31b2c05e368d398\nA = -442c80289bfbf00db06eafbf06109b55f99786a323fc2c6db5686f99094cc24aef50475841243ec3ade2a1e0ff28b4032fd8afb8bb5e28f3b2863bdb9fc8f033adbaeb5f2ab16fe9\nB = 6d43e3c46f4a55d49e78f40d34033a7f5fcbe50873930e7c5452b6b3b176534e6e70033868c85b4d63052964093214dfd0bda6a84e893b1aae3cc72aa83d039e51c014\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = ba0e8c91a86af1001b13deb115c77609a1e7a3736a6b807255aee898e3100f469ef6222be532dedb1b8d3db4b3b55aa4b5da5629c83e9b2bde76bf2f2a4119a5378b5cde000980b3e58595d988ff776f0388fe025625ccf368e20914fa90dc771c826e4a836b2890e82ac2274471d586b4de5dab3278f0e70207562ac6e6493b\nA = -14be403d28c8451cac4dc83fbf895a9d2b74f730c39b0fcb33d7258f99211dde31a78f182ad1d27a559031d67d6f2f94a741f141bab80fc692afb452ee2d502099ebd5760ccec7f7ebf\nB = -2742dfd02134594edc6d3025aba5ca4a34dfeb43821ad84164510b43be4fb95748f8d0eed7bbcbeca14efe843fb676882784bb36c889be29bdad9270e0956286552119561\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 20c691d6544912fadfd9894cbfd42745991f39a29cbe3a1cdd302bd0487bf70c0179b9579b77f8481bee13ddbe42f32d734b6118af92884c946ea8576f6dec867c1c251c73777cad7c7c76e90da00ae07f96c8d6a751e5b18157dac4468c05d32eb86e74e0e8312bef85905af8193a3f5c799c5875badbc9eb7ead1258e56d7c\nA = 7ae9b4d5151b11bb7bd4d1569a6f4804f3b4d77948e0c6300e4f28d51c9a0afed2ae7503e53489edca5359e2b3d0c82a9cef316cd7e1c1275c31fc9c51a8c1e5fdf23935484e467d6460d\nB = 1f46f88d39fbedffa8501fa1268bdf3460aa98e12b629da59676e61852a4d3f8c59f72a2fd717fe2faa09639bc651ba516cd39297e0cac67444ec57c0db47c2a4e250033d02c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bf21b3cd55c0df8d4d568d00f757b10ef3de782ae71b289cb2b59d36df1341382bdc1825ba13199f2cf279a72968b3bbf5f7e3d13ea9adeb96d81132788231fd988eef04828119dcca21ec1fe844998909cc95a8d01720e883df27f07ef4dc3f09081015dbbdf019b96707c18b0b1db6e689e8f86466a2afea4a9cafc576e10c\nA = 1243b14aa3d16a55935f6f8ca49295e35e7f75b03de7192e1e8a479abc0a430e0d340acc05eb9a61a5dcbfe3ce3a4c5c940699f5043e924f282bd21e341edf8b7a6741c6ac72d7587a9e7a60\nB = -bcf08b2153e8ca911096189e35dbdb21b77ce89685484f574c89f1747612f39340bf1b204a23530abb36b2c5e195940b86ef1252d6729393c25d4c73dd434b6dbc3057b05d3f15\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 460539d96c07e72acba5b59c88fe904bf7f1e1648612908444b0b08172d05968b31b43456918b4287dbe01afc3cb4860d9c2fe549a580c989b6507094f6c241eadff910d2603f747f8e289e7a8176ca4a978bba89288a4cf875bf3e03939af966c54e77c28119a39d34a2b7055465f58ef2efe7c82ac547fb675653198e4b504\nA = -5a44cb669c055ba7c28d49f84bf8d12179aa30bbb9db2a48d7a6b09e44dc0e0f7471e3629cd2fb51e5a53346ae025fb49f9591ed1d71bc79daeb3f1254342d8a2b091ae07a758c1555efe59e78\nB = 646cc0f766346aaecbc5147a4488ce157a6d844045b80884eaee9d419087285fa71108b5ab4a05689aacc8d2e3dd0e6714c55eb8f77487a3fc5e56c3c2df0c4acf28a457051118560\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 79b536f4f30f9f7483f90e65e6456ef8072d9a7430405cf8c9377ceea2c676afc338837643436d55ac6af2326ebb362684bccc5092367209822581700d641cb8d331432b761e4c6e22639a27335f45a25ec019d180fc53dfb53d69216d7cfaeaa07db8288adc35b7bbccf2829631c1eebb821e4d3299015c3d462dc17aee5024\nA = -167529b1e8668938ec02a68bf4d76c22dd018c41e19be25e2f821f63c2046085d0af30d8b4212ea0f3f9943be1c14fb2d2a944551107cd2bbf8dda5bf258957325f06277036282977db4575b0deaa\nB = -378e1be10a57e03b197bc2b1287d643ba6d89da4bf6a6170816691fb6529c602eced237863ee39659be3729825f032a57eb5de0a87b0894d1a1244523e85b6f50a3d9976dbb038490e46\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 658169197ddd0bfae101c10c3e6a2b10dbb456048e81160b47b197fef439b1e0ed710399cfc80ead8e436f1c0399064f92da50afc335847515686e055fc7bcc0ca721184435955b896b0af4f4d96672ebed2f154538d49fa507b945c0a6ae926793751231980274213c80046666c28ada213a2f87509d1466b8d1b2122e93f8\nA = 49136d37ae8f3da71a6114327833e8aaf3dc8b5a9a27e9d04c953988456e525263f86ba94397321c2093803b789f8db3ed7cdba19c4b796500b979e02952e1625246f8e977e01fccc133f94cb22832c\nB = 1dca005663385fc00b4fd58c73adc7589d15ddbcb8cb2fba03a737a320c447a2b21e576ceda73811a31d8277883fd31e22f776bff3261a098ecf8f40f2855b0c723d1265eeafb43f85323e3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a49fc8084f3e780537b4038bb769b8db3653a3315298a99c2ede6739a1732a636e9787f2e8b09d0b9bea08fac43cccca71a315e6f4a7d6417d171b4693dbdbee8cd9f95be0847ffd40ff027267125d67b89737e1d0365bef6c4429504d13cd8ddc7810f456d6293c0c57c14a307b94010d79d5c13b92a907f923966fd3c5c8ea\nA = 1e7d8de2061cca59d1cc19b356a8fcdf2ccf917e0d81598f014167c5a8de027ccfc8f2cb8c37c396ebaac83ba862c146bb2d551d10ce03de9528f97725804e8a6de57b9d9da811200604c2a032462b6ac1\nB = -e38592f3acd75b575f64ced439d5ef2377d21c61bc70625639b01bf755fa2c6de803ce155744993493debcd4de40860bbfcee86d0b117d7f8c3f8ace68b67cb6fe7a81a145535553896424f7a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5a99c8a6afaa97d8e7d84f4899803c7786b1bfd2ecabdbfbb3bbb92247ff91ac213a72f6d23c24699d60babe91a7d9cea751e686c027fa1c954474fa5680f0059118426c71299462b11de5f2817d190599cc4b352df4d2e80605f9ad1e32eb13712d3027a2b6a19d52151e37e7fa057d8fe59dfc8a943a42a1756a38f103a75c\nA = -7df29221e6a102e32757c18f87927cdc90ecb012ab0557e0ab855daba832d76ddf595b9c5a62988ca968b64fd5bba2a147a5991810c17cae7edfde38bdbb7e13a1fe5206724c05a9fc9276c8d4e503a860c7\nB = 5c586d1aff7dafea3b8ee42e0e8854712c95385374b5bd1fc8ec41a72b296e070940c4160509a4a1699a678533ff3d12299338fc441b0f01e29a48677bfc5aebc644555285756e97c74e1af6aaa8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 21fd2d881b6a52332dceea42664aeae1ca110512c13bb33e25ba4ec0f39f80eb73b1fa08", - "34c998c23a2453dbff971eadb183c51a30ba78d593f23be9cb6b2b33a554ef31e4a36e0314fc2ec889f18debb956b89d1bf8172553271bd56d89ed0b30abb70e68abaa2c76f73cd5a3de93433747d09c845b5f8843f9fdf9f6c975c8\nA = -19fe3bdddcf08190a037768b77666de803ca4f7f0d7dbe6aaaf334a486dd0da7ca024d1b3df11e0406b0326595a171be30b04574c1a7d04f4d2ccd334663690fd20e4fd168386280510a00a70c1a11e99483048\nB = -33b2400173c057980b0e0cfabbda1a5cb5b83b7ae80708c199f28142237f04b071c6eeb63d42e80eec04b76152250c9e4d4c4f19a048cb9815dce6e66710fad1d27494db5c31d9af37d2aa779d12d7f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1c45cfacf30682a876cfe253f05b393a2cd4dc065ce73126508ce897a99a723cf5145187643ee62d746f6edf70269ddce3c348a1432316286a648ee9ac31ef87feb14f25c42f2dfc2e84bb5bdb4ec0124e249c526c55ff2cd0ae938555c5f86d856eb181572ed01dc045f1ababa52d249e56aba0ecccda905d7d1e64bf89bfe8\nA = 6a40d948eac2fe5bf6db15d7f6b89fdc0712e32d39a881c21859e8f7722391ce05973efc7c40e2c0d7f56c217d8a986bfdb08bf87bc0435873cfe4d01967c46f7d39464bec411d0369f6f5d1d83f42596fa47451d\nB = 12529775e8253ba220d890d4912fb95f91e4edb59610e889431208b6bb42b089cf2aaa12ff9ff98c2482e7f4cbf35b22d15fa28aa288217bf766e937a706fe1e600143087b0a67f668cb7b762c9b9f38c0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3b3b08e8eda8be3918bf648227eb0d569dd898729d9cd54deb32b1a1dc69cf7b2c4184c8ae9641f0f75950df263a5e236f428ca86244e617b14a04edd0f31c02bd4d84f25bacfcd4a2786825f0361251475eb6c7e99020dfee4298a1f1bc260d4e364a332bc6f651dde7ce5026dbeb0e5aa75ee98874da54c7930108ad28e3a0\nA = 149d36918fffa682cf90c4d3f3d48e6408e7ddcbeb44e78b9cc7fbb08108f65215761a61d79f37ec8f67cc51e0a9b4bcb3834b0ebcf6734985153f29a2778473b80147eddc813b4fbeb98843f5c1ae6cea68f88dbb4c\nB = -ca87f66182e271a69c0964eda92a009d438078b584c3eede28ce1a501838c5f497186d305c09922f32ba858fb55f2a0dbfc9cd0f93b789c1f800cf092726d6d33db19e4f26c7dfca69b83925db14544ebfe2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b199655160d88b6b4157ada0e5675f82b33b5592408bb57c46e2f7d8791bfccaa51436dc3b772b83e907c20ce7edc2835ce96595b78c0647d244e9bad6f4184e0003eb0899e7a47ba0be888b9bf795eba95e5073a85c4d20416fcd4a8d4e1e16b403deb38845fb8bf9e9264d68807acf02d579e8cd104cf2bd555e6cf73d0450\nA = -70ccbb73e33a7cec30ef2071f3b1f2e008e70fd6d00fe8b7aa4b9146fc6d0549c57d984cd014c7e0a4ed6d33376998b7c2c9778fb9580d8ca4ba795c88612721c153c186740c58df3fa63b6cf7a4de76e049217218c05c\nB = 6cf4168d44a8da8e8446b4420466fefbdeeaf9623a40e10b77547687b25f36916f2c18cf6060c03b3b40e0959479f6aad5e44dcff0ba799262ef53e280f4a7f667d262d472b2e573265774deb5ff8f25dc1822b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6ff91af444c61d2e2fe8ad73bdc5377d5becd55074eb60f0f98eca3d8f4be8c02f196b3afea12c36f78b78ae6a5ab677ffb7d9c0bd58987cca816affe468c7fb4b56055f5d2326532d6ed1c00ca2d052ecd103994e8929bce04e067082b4ded7e1973566f99c514b4e0d95b9a8a931ef4f6355066940990fead70208a63841f8\nA = -1c924bea12ad6f8b65abd1796e381fee2cfbec15138191bc22d57165928794bb080c83878fa5fd19a5d657b2fa91165459966f50aabf19440f7d75f027b32e999ff4d3f7a7ce878fe0f33a847d644d86ca19713ca9968d97c\nB = -3abd4b281b8f25f5957d1f2fde904457d49a3a7eeceada26b454ceb4ae0e879135d376571f08b5038b7b3d73a9a9fecbe265b72375756a715a523ba66737085e5ef7a4ad988155adc93eadd5d95a0faea56914983b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b9076229b1a1241e8b4da3fe143ac31d060785be6ac1e841c2fa9683d2bacff2e2b5dbac33f58b0b1718ad2053c37ee55ea54a9d258ddd8930d2784852844d85db24e4721762839a5c73cfe588efedc8932ccfa585e1b5975083919be9e32a86dbdf5cef84d3d4b2ccaf7a006c0cadca1e35fff2da9da7d7e779494d8f85bf4c\nA = 75eb0fe6c07559c2b0c7b2acd7d29b5798f6c4cda64a504ebabdf54bdc773ab28b218f0defc040016178958d5561796230b71edf49bbdcbd3f14494859843c8ca7a0f777cb05827f2839f3982832f4f3e3c5e50af17ecebbbc3\nB = 1b8aa718d61447003fdbaa748a9d86befdd2675a677cf34a1be7c81e4577f665d71135a8a243976a4f6ffa1636695567bde522f8fb1948033a7e0941f833d827e957781cb4349a08c6be418befc8959960fd5fc1b288c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9df82b7c34ca97a3a5d4efa28d5ed4f35484914dd73af9090c4bb31ea3496ece8ec650f4e7b07dc779c97e597e76e43cdadbfc6e72b61ea718c073be1cd204f8ad2bad0df1e530e75705f3d3dc285e9d793c8d42f04dc20773d3fcda8ef3ac1cb10d33d20a91add0358ab8658f49d2fe51d0d2d72684e31c0eef85e5695bb4b4\nA = 1fc2a171445ee6add5c2e4d29e50b91d83338f8d63c111e4d3e95f16d2a33be02bef24dcc3d6ce6bb8f1ef980dbf8fed409a0232c0566153014eef840aff58ed8c33e8d463d408f93e2f5381a26fdea63676c4e5397eba1d39f928\nB = -bdac7a177c77451104852bb99004ce8e617036906667258d85adcbe8cda21ab7d03aa7dcf62cb210a9db8fc750c7e1ad290b35473be0fd607fcdc686de0b78fd9f258f5b25e2ed43c2ad1a38859f882b9f6b293dc258659\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bd9f3d2e8a1086b177698f87a9860e3a5f030e04a0bf4ee9436ac55e005bda01ff4ac662cb85d39e98a41c723ae542a83a936c3bd0280c6801ffda080ec0aa4230b45dcd0bc5eb41cfcf272028bce3572847637a92d1543bb2b8408e880f5b776e1cf14fa28d15cfb584f025596ff10c9f091c837a3aa622d9e5c856db8ac207\nA = -7fd5357cbee7c5e31fb62ad03bd47b705b574d915200fc7f1013d836b9cb683db020b152ae9464de6aeb8baf14999ac7025dde6173fae6ade325c60ec310eff6dc4130a8efffb15ddae90d760cb7f76a27d0368175d4a44a22f7f223\nB = 5894a0223e4aafe4efd4572752fbde4952c8b09cdfc35137e7e6ed650f8fdcfce9de673853dbf73730b159b2656047e69377d7c5025a6b346fb08831e64bc8bc34b75765012460d8135a4f7a0f41d768fb85abf17f5e2f5c3f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2c61867bca70e8662c7e5435a5aec020faae86fb079b992bf49d8497fc5f96abbd38a6f04f6ca8510e0160e546b3f68b7baef4ef0f404e881771cc12ec5ed3e3787c2d2ad6bb957cc59f8d56f0afb4bea49cb671cb42f4e8a0ee1dfadb6fa14f84a5b3269dd33e20d658ea4cc39499c7a39a4b5650ad7018d32f97954610f676\nA = -1bf5ae15f24c7c14eb59605136a3f679f303cd5b81e4a27465281d17715afdc2c231d7ccbc59f80ad176f4e0326eb757b52e3695e27c6776d7936da47e3a8a904f735b151422029535045ef489e61ec93f02e6d588491c8dad1cc311f52\nB = -3238dcafb85ce557036d19e42e7e7e473de9f9da6f920e18845dd010546868d2652decc94596cd2c36bd16b02c02559892b9f573bf21ab18c3c75591413d046b385d08aa66d849ab8adc9fbf788e837b047a7ce2b9c63f7fbd263\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c1d04b831b712d0619db462c3f3fb5973f5984e9a48493ff273a5abe17a548e185d751628899e2851e425a7d4b2c72d4d908dc813cd122b8f497e08e299dca9166f19752ff8cd9840a70155ed9e8c063a3840838b3679f96f1cd5f1cbf0e037d222029e02769dce7fdaea0bbb5417f85497d77c76a387c6b970eac15dcd128ba\nA = 7aeb60c134e84f289e419b74f99a5ce", - "5b4aed5fc630d5d591ac7643251ad32d6ca7f052fdf8857f67138262d221de644140e9018f7b84879d74883f8f251303f65e06bb52246ec6a912772cb698b47de41c1826ddd065359f6b9f1ccb0cdf\nB = 17f81e53d9fa6201e4d3eeebb32267929cd5258d10f053e7c021c4afd17094f8ecf433b1ca752f8740f6d6bd84f801b1b9fd64bc4787b9ae5e5aba0b4318a63dfe27e92d5a3ade192af7563c74c9d6006ae7701240efdd6021a83cf6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = aef89874854ed34deae1b77286f9cb0e3017e3ae77fe050bb244acf4f30dc03504c73c1a4d44b769709bdb53811a5d0f8a76a08e6a66fc2cc4e98537ad6a8049f02494305b89a49a55e71fcc3f5fc42d6b478456ada9b19ec0a03f5ccfac5538c0040092771660312be5e51996073ff1a506d7460c57d54e10dc2991c028606a\nA = 18d3af14bbffbfcabdaabe44074b407d69abdd80a6eaa5954f0e45fac85af7ced1715c78da872f7a8fabaad3207e31f12b7195cdb25abef0a1e54d3b13349d997f207fe130d7985e2033cfec899a0af310c9827749cd22bd062eb0b1faa254de\nB = -85a7d9f08a60031e689b0e611d7f7f46e1178eaa2e6459602e738990c77f4d3783ac43fc04d53504cf67fccbeb02f9846756f8e32fa4a9316b6d3b45f644254077bef096a72bcff17ffa17070a4355121cc5daa2f782fc0d0bb48101db\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 14a85edc6297763547702c212b1a8274b8f85d53ef35cd1b01ed51039bbe030d0a1b9626ae2f571a43f1224d723847a1c6708f2238f6f6fd75db6656e6c703a5acb57f69717efe8ed58a3713ba2720d8c001d026d83de0ce5e24b67c41daacedaadfe404aaa9b672f00562e6901fbd0710c4303fec41ee3338100beb36c9b1ed\nA = -44414ec207060d105f599b9a66aafecc5b232b55214c1a5e1922f6b59439b3ff77cd3a327bce4f7406871196b90350e6dca9aae147ce03027dc4de7563c734f111d95171f489105de5ca80047cfa43f7e932917b816ba7d41fb95b4106745d700f\nB = 45f2cea1b9b75880ac3ec206740cfe0ecceb488c9155cfacf5885a8cb49be78af8cf221ff8de2328f4880479c031f830a3c9eaebfd83f7de501b7c5cde03c4720c56a676d331b2a13c4689a2e34a43fc11f62825b8776e75d31225ca7ff65\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7670c1e2e141d8f8f5466de8ae2e0ba2eb3eb7634699eab8415d3a37f8df291d00def88361e9fb64a2f116433dac3ac2764fd62f3201dce4e48a3b7019e5465f82241ffda29d5eb0462fde74dea3168f8993ccd4d090b9c31a5a6cd7e05f725bbc89479836b89379b422250ab049f31c860110df5ed69089716877fb0ad7b0dc\nA = -15b4a2f808a85a5bd466a342c4853c04ac0ab73f8e53a4a0477f73dfeb8d7a911ab2eb5d3d192b9b084d0e38db491148947c66f838aa5f460c37341b129137614259efa531c0e6ffdf163ec6851737037a5299060418d96da035e6f583e6ba79d0414\nB = -3e94fdf22004384f7881875b1d8f58019ed8afb1b6a31f5d591e77b0998f3100b34174d6f3466da44b4c7fc8b92ccc5679c26c146b704198a65a88554d24291adcf897bd758a035361f671a82972b5962002c6a828792980f86a64547165327f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 35b49beccd8d2010a8d777c1ff69e28e01a1bb78c6466e717f0a934bb62f9bbcec5ed29f9cd2c14d240a6c33b28c986eb9c8912a4927605532483dcfd31a50876e1819f3d7a0f49bd276ced5c4110470244fca52d2611ed7e31cd8b73e749aa70743b39e92810b3b52320342a65cad3180f6e2966059d15f79e5574348f5f66c\nA = 6fd078e3cbcda6a71a710e99204da640edc71a65974fc765999a74ab50a0e4b090d57ed0ee869c8da2cf694b6fab56e87c4af62fbe73eb8890bc066ec3460beba04dac3b8fae7e4f316e8f954c6e8d934e946dfdc9f4cde0f26bb3d40d5c444b03bfc65\nB = 14d8041a3b83468d2f44f150ad8d8d0a1a22035d630f2a17b70d5c3d557d3abc7e4d753e1ebfb3a3ba465520b84746073d211a67e079ec7f47c2cff9c06da69bb5cbafcb6cabe7e0018867c42e07931d6797d4499463e3cf786c6d5d6c8cbd600d8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2f6e0fed8a9720fbd83ce950d7545d2c6d5b271582194570424f90309227a51777cac974bca0ad3c1289ceb91cf75af73b0645cc20d71e7789144876b8c1bdd550328d9907accc316189e8ad81310848cddd2dbe362c9398d814a048f93f9368fdbec0f19ab87ad2a59d4066d738c3da3cb71d4716f2cd2336ad35ea1438276c\nA = 14bda9e4aac85b0ab7abece728f61450b7779d3b5fb83be813758e742d2ad76597f132aed91e20a75c554f0d61ec4dd118eb733d04942b2548b1efdb4dd22fdb543d9bc1e4bf0574ae2cb2c46fb98cc4835b6a074d6df1a3bc5443beabdc784d542e3349ad\nB = -efd765f8ffd72d041ac3244078b8dc4482233e9411b289cbc2cfc26fed2cf28e286835010438ddc9e7021ceb098b10c68bcc4732608ec1f4052df9362176ee14812bbf09ccf7c2882714ecbbf92bbff61c06e9dc35a368208a05dde949fa2cd091ce0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1f0c436379f6dff55a59093ff2a0626a9b959e3e3e59365afc33c7a7893f04bca863ec910c446957baa8de4e35a1f4e9c4a776ef41b053f03b775f327eb7e5fbe68bbb478aa4339ae703ee4b573d6931e47e09271d40239d527fe77098a7fbe519f5eda1f26dd6a7d0ee6833efe37187d8a85844690fecf9fdc3a4d80b921130\nA = -51eb34de29ba24d2b1fbeb0a1c324f4ebc69cda2dff971a315c0c2775d988b03ca29891ed0790f3dd507a1d26ead461dade9284613e45df338dd83aebfb66050465d8aee554970b43f7d4e0428e1512289fa1f9b23867b67095c455b66d536b91207b749189c\nB = 55259a1122eb7eb611a69118d3d42c2f05dd228d71c0e1e42ae3a8d3d180a95b74150d844e916ac85105805126e4b995f2ed1cd3fcdf28e1fd241dbe3125dfb3e4d90556256eb513a2f7c9b596719c83b26931d92bfd3573560e8bf054138f5d6b9cde72\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = ac321a272d2206df4dcd6ed8ca194a1049c1e3a20bf325fa44809d302170f850721c077bb5d792f86f7ab03ca259567397cc2fa1429771190bb632ac2c92d3fccf6e05e13cd33149994cda5f9c57da155439663f6a13c66f9da553f5038fb92fdba186ed9ca04b8ec87cba4c5a68c8edeedb94e38a6dbe293340dee1a4ecc768\nA = -19ac99d7d51456b00a193b3b04693c7e5436e05763f0154768db078ea5111cfe9eda3451091af213b9c8cc649d341de66c12ab2803ea39655d3d7de182a77355ca444c5d2778f791d39952a7a11839e497f5dfd8a703df49ec4d7628bfc25a992e94a6477e6be39\nB = -286d1d436f113308be594f0f43d7a05120639152b7e2f93058cf602cbdbc016512bfd23f7aa937fb358b7b602d15998ecc150f2b9224c58527c0c1267739e065e24236771e2c683957871637468181e6e896b513569bd004b9845f0f0e4c26a5ca123365e1c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3466804a1b7d1af8b6060aa93a4c325d9cadb33ebcc8bd991f9e44cc2cca8918411efeed0f005790d649382ec40278c8cff903cf3db177d24466c58cf6a56ffc14e595c36bfefaa2327d37f616b1466eb702f5c49170598bc361d892e18051b8233dbc5b3fd6832befd9a995bcef3b0f3beda6efaf09f7306ec203172e78264f\nA = 6710c19330d3f974fc377e28039e0c0ee0a558621fd67fe724c326537c18c66dc5eec60980e07d401ad5556a05688d2dbe7b271f9d5eda3032bf7cb7c420e7b5d65a195bc037090b6fe83064ac3731624ce2baaaa62a6eb07156ca12ee51d4321988026cff573ede9\nB = 137ca18f47a151363a3e8c52dcf024262ba525ec8852e8e406f460fffc2cf88f1999b17a5821849317fcd84d09c88ebb6eb0340120f113d7ca5fbd91c6a40cd790bce7b422552cc0cfd2a6417add2501db1667f2802e5d0f4df824adbd033a90a155cebfbe0b53\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6f248a70b2cddd9627b32fbd130f05a604866799365f94d97f1eb582b28192959692a870be7c2614536a8de84", - "cd8c1364a75a3927ef9dddbb8c6c87dbf526f2d3a7916384f2daed96002831173fa4a51863c28b4378f99b1b201010581d5eabd66ad1e328cc4e647bf5e0588bb775e130b4a4d029eeeeb5852c5742862ddbc3e\nA = 1f014cdd87cb33ffee623cf454edf2c476e91df279b4f0879637eb6e8e5ccab305186de67585595d34ebc195fb150408c4620cf6c7a0b0d9695ba0e0e1d7552ca7d0be3dd678b1cce2beedd11939891a6804770f1c843e16dc2ea6aa8e4043940c37fd3d950caa122845\nB = -8d8d9dedc80994fc5db04d8c935301e47054250fea9020bde8d5fef01f2307cbf458d5afef5210a369c396287c5eb453637a2d721085af3de0d75a5dfb5dfd22fde3b229d438439af7b296b9e68ffc982efc6c825556c52a735f8be12a214a06c4270824d5268fb6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a35ff7e232f047e575b200b9fc4c9253de6ac04c612b8a82c275a951075eace5e7d6664fe8f78301d554cebe7b996c1f4ec3ca59d8d12d7196eb3909223de94c220f0445d24233534af1c93433b05c5924799d2c781fdb88c4537bb8d442e6bf76b2d966827bfb4f40378a3f135103513da056bc0d375b1339561700d15a0227\nA = -58346cc8a9a1e5b8babaed8e7f59415388e0db654ea7cd465d96781c57faae7a8af8e7578e46f3a8de7bd1027188e1cc32fd1c0d60be24fa3289a12cd822a6c9a77dcf8799624856c27ba88fbdb047473274e651760581b44457ed048cf76c166d38bb9b2afd3416ac7e45\nB = 61951a16dc6466a9fabae99df29b7229f1ab96b476092dca1e4f8fc8e7404e2fba56ee66486d1f27f89bb3f86f271307228d7d6cbcff943961e177300b6acec1eeb46af1c5725f745a2d2af0fd9642f57a09c9ce6742114be0aa6e939e638bd5c7a92a7c206b2d36e35\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 90b441d8277eb1ed454964acf567067925881b5db0b446a7d554dc61ae87ff979bfb0e58ca1706123453e62ce31284a5a2db1228d259e27abc7fb5cc5848dbeb9a6808fa1b4afa844ab39b652abc41423c2833e1209a1674db518b6df7ebae315dd7f416df54e73088762ef64cc2cd0a08b1cb01c49d9299d149cbe84145a55c\nA = -1ebb693ea7d18e0ff4a9a51124ebb78bfa3a4635b75a6387e9fc745a2325409f927324d1289be8a4f5cf2d5c04adc7ead20564f97e453287f03e5ab59a6133584f970446652d05a131d7d382c47b7cb97580ef6710a532dd4f5a0369dd3db500ae5a3c5efb587cf0cd2638382\nB = -3916ebc4653e7d6e0a4f1e234d765d41e9e948b5acd7ebc73cb595559c1b20b037a3c8da0a7aebfa5fd327bdcc922551cdb8db3fb0a581fa0620ca2d2559ccde3ebc44542b4d80926d061e2a35c08c09547e0cd587c396ff2959ee93ea64b1e6b7e2b624cdf445988e1f42\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3ac61c3a028f4a2df6645acbd36818a2f76a3229d229ce22471760807585a909727411e8b68bfa4e76adc459409a101a1ce83900d46918e8d0903a163de87c07bbafbd60c7f536a62c59370ea53b6cea4384345343146bbf529334b4201ebdc7585b6e5eee42696400c9be9f496406a4eb51d2fd1b40466224f1752b181774ad\nA = 5a16d5fb9047949684b80805e5d962bdb939d0d0368b48517a2a826679c37ee0ded4fa83e657192d9ae84294e450f7e2f2773d1f13395169582cbf95860891b9fdf8f3240a16aadd1198e884f22b2718219d478e2410fd4bb98ea534a3626201959af099fa55488f5390791bcc7\nB = 1f67066dd06ed4a49cb556dc2fce22814754885a7cf6c13915d974b46b0e6269c0fafd688f45ed2deeb026a7cbb772c080dfd577d21ed2c81e50e7537a70dd550eb94fcdf626500040da88c43dabce13c82a93769a9e0ef66a471661292dfd3b3af07169e2dc909e43678400b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7087dd62eed6ccffc7e1370cca9444dccc4ff160458941aa9f49dec1a2e9ecce4cf50ac2daf06994c5010cf225cc92238cd60e1aed9edb2befb0fb354ffdde94ef5e8ad0415bc95851d59095a5c4850ec52a74c78eab58309f395d3078dc481feb9d30bcd9f113af7a01611b94d085e32193dec738a64c5fe9bdfbf5dbc98cda\nA = 13596eeefbf06e9ead8d883113d8ae6cc3da8b6fa13ab66681db5a9c083ef9e49d905ec19c39b149cc09452eea0446b29cc92d4e865e6f681827336945282fa6b276ef552363229a976c503b822e6e4a9862d3fb30dd0c3627ccb97a7046a6a679050a39166388a9daad5ec5555dbf\nB = -a4e574363f2e5982cc087b38110d257019962fc166c2d6e6d396220bb308a8a0dc7d90c5cb2ab85faa19b07ed7dc11eae9bf2abde0a5fed279e77a717b43d35e70fec4e18445e37741262d0b0c20dc4375371d87d839d39934f1dc41122e815f3f37352d04d0cf514738b351f02\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8495eeee238164082240ae1db1e3c1e36fb6621e6b714c9de914f9de8a587d7106b8dc5214f7c60c0ee231d7441e03cc26462e71adf8e29772ac95d0395722d2756f9f64daa8ed41d7ce824a572d7f9fd419112ae823b5b48b8aaae09fe093e9ed05918c4ec88ab159890910837ad0691849b44be95993682b2da2b124de39ec\nA = -403f21e1a7911806747bb78a4f20c4e6572d49c6c4ce071db0c8c91ee985e68a16e60093e4628414b2673d25c9f13c4c43600633af95017e3846512197c9515aaf9953570ce5861620716b3d80eae7de0f033772fba82652484cb3ce7cc189d1fafb14e044e07a88da302547f2e623d8\nB = 689d1b4a968b7c00082ae3a29c8571f826c4630c947a7767fe4a71af43a5de84db9b5baec0980eafd0019e09de1b5c56173ede68c9a6acf260bef3d9a03f4c83a33106c94ca7e1a8615b3553088d1d05a62ddab0f1e5a126df5d960f67e3b92981022e1f0358c7970bb2fd5dce7a7c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 397df584bcd3b2e1ec7ed89de624e9d104bd6812901e38c5740755ce91bd54155c0b624c590ded199590be5d98bd1ad4acee56a62d05d6b5fdd1ade12f7db8e3eb08c4a5996450cc1204be7ba61b768af0efd563ea478033324731e24fedada1ad6e564238c891494e85ded4feb2165fda22f75bf120856034a9206511885fd5\nA = -19cc480d1e07523bac502872a971d78bb26955c5453386f5d51767150e229daad3ab2dc85e0fa0cf6e72389391fe627fd2d9f263f105508642eae5a095ec4d88545dc9d0a2c436907460e1ea7db174673000eb2e0b60d57163ced261bd0f6cd8ce54133cfa10591f1fd27996353110060cf\nB = -39c45512fc7c9620194fb7ad22abea8f6dbff4a137dc4523115ad7e262934143cf1f320892f8c097a400d4099e787ea7041d0d69b6269d191fcdc8ea28340ecacab71058cb39a9c7362c848826b35ab560c27113fe53c497ca452397891c81365b6e7f07f916d47961e50b8c7c5cab38f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 263ab04c98efac12210beb66b13fec7c260c5b1cbc20cd732a511fb3786b917a617d6622847f4eed70f25982ef5d0b0d13848c62dcf447e3a1d491f4c80e69cec03cd318f6f93134d582210bfa81c1790562053a71091333348c6624d4d793fd6ef971d284a4ebf0be0771efad302015abfaf3edba017907f10ea14a46d9fdc4\nA = 7a354753e39b9ad1c0ad6b65575fc7247487f3ea320fa82d1d333ba8dd5d0ff925331994a6961c9c603be5775ef1842159551f0bfb34920b93d90ca60e6abd514650f77ee8ffff2bac0eecd0fe8ea0fffc6ed0285c9f3c3cfaacf338043975457d62f9c8dda8cce1e99f34529435016fe2ed4\nB = 1a4384f9620567c698ced05870b4dae983d8f0df6aec888353f9dd6ac8ad54340c3ba8346bfa47bac38897f3963fce972f6d55f3407ae03f5c7637be1a34e483e50dcc27148b76ef079f117104162beb191d146ec828ad5c5bde5ee1683a031d554c276d837bf1f2f622cd11baabce10212e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 91cf4d1899e170bf75dda0d51a6481f79eb94c333b876382c9d04681073e949191223926523f6531f0a45765d7f382221eaa080d7bd05a3c19220ebe18802b15d8009714e8e4e9872223049622ca02040eb041707c7e525f698cc361847c66fe3673a72e4d701466bc374f55fa5437216eb59375c0e2c4f7020149d0118ea72a\nA = 12f35c48024e8271e8f9a60a48b5a214bfb6595a837c041b230e6ac87a4c1d4b3f93a2d3a193c750c9857c8627d0f7c454d6c4f224dbf14a865eb83e990b1d9b8bfb729b8d3dedbbe9c95032e4d60676c2baa2aabafa698392590add3b83b521a7a5e7d6", - "f8af207e44ebecd735374acd01ef5822\nB = -8fc18f92c0613d085cf3ee6f586b39b99ecca864bcbe60fffc63c585e5613df68f3534ad46e244916b1f9188507a3692526c9e403b8e93480b0a5a6297f65215f1a5d8e20631a9d559fa1acc15a98c9397761ce18903f393b10444ba51bc92ac44df90d4cf0852da9d75902230c6de6f26dfdb\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9af562a7b61c6c84c91bf979f32ba5d246d2ee2050f07ec2dd5cb3f9496bd37c3922ecb2b5b17085a13e93ab2dac6022077cc18c621cce3a2d2247e5e89de8692a36f596e5dc7a6969a4f3ff0d1580eed380e6550c6218c1938caa2b7ab401ae6f520063c811088504d60a19da3b5018d640ab8d340f35d1337a2ede8bc64bf0\nA = -63bc10b8fbcb391dea305fe61b404d3bebd035514a812d0e1d38daa3d67f9f1bb8f02d2979270cb9147aa51d66ca73d4b5787e472456a13fbe0d568e92b622439d33ad3c357a56dd26806ebda7b3bb592385ca5dba7e5eb5d85eed0a1746441e8d56e22decdbf8f4296e30d222da5af17c427e832b\nB = 57a602bbdefcdd00f42ed1e2cbde2ba858d171804da56b0ac87081424ad1569df1308fee7c9ed349eb496d5409c4c46921f09ff0830bc9f57e920e17df16523598fd90314141955ddb84a1522ff3ebfa812cfeb6670525123476a739f64ebe6a5f1fc805a880f8e5a71b908c483a121b38d05cc2c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b395c9f264172a3653af6637e72c4c8e564d1ce68032a5d761bf546e0c4b51b33cb026bb4256fa639ae98e54e5ff7d8921ae411497272b53d97c2c44b5b9ecc5aba43dde201f64f1d033056f19ceb0cbd04decb486a1d07ab1c64fd213d7eb6db9cd11efd743462e137f368acc4ca0b49a7f85587bbb5ede4be1616889e2699d\nA = -1e71df5f04001f6468c3a192086bda948aedd19c5da9a5286856f30524238d95b0ae71940f2af123315ab5d2fc61964d3e970d5858b7c1a78d0f2cfd10cba7ba4830a8c19a09b59794ca5d7da32cd8376b5ab06079b51cd9819c0021ea41a9e43aee147befdbb17a92cac7c7767705fdd908bcd291fbb\nB = -394c187308320ba1b14d91d75b8ff993dfd57f9c84e8185f12bf9924e046629ffcd7174879f9925bb643988259cbe9dc9277fa83a25012f91159b012f1964aefddd5a94ac6c2a55a22bbae93085dee079f84cea1d53dc4771901db9a3db5a14eb17c25aaf5377e2beaff6276cbce7cee97a9b8f32737\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6602ce0fb5002eca37e85b60cc871b7b2eed13d38c20a37a6e0886ee4814f3ce2515f8714c67ad81e8c3abf6a00464e6a51b15e55b6c11296ada43cf459e15915026d3260cce8fb796241fc2b0bdd2b65ec04bee3b7ab6626e10597f3b13b43d16c34afd5b43a219917626c88b24c6f8392bde1b2e65a50b7f1a8dc5eb096702\nA = 4855ce75a3d7dbb72a257f6291e9f6ccc158647aeb2f8beb3e8fb32f6f59af1a46617b77440798562d6f58bfe826d3ea7dd28daee8f5162d7d24ae6c24c2deb2669b15898689ca789e2005903f3a94e991e7d3c8f3ae6181029d959bb15e71d7ba94d2dfd3ddd10f6fc49a65798b5f6ffd64682c78b5d91\nB = 15b3e9992aa3f042fd58ff97a8c04aaebf46b75fdc38caa9224394a1805cc26e4311bfb498d5a04d19396e98d11c8810620979362df82b23a115fc1711b57c7a56b8408e2682a2edca36cf9311addfedd2d0889a78cc1ab170d1379245de6f1f6f4db815fea9130463dfe5283f195e6e81486a1d39634aa\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6a81ccd82f00d829bac186fb38b85097d52afa3ca83a026856bb83f94d6af6f6c6f3141d433f8fc159d11397df8d2f44c769f255cf8148249d8e9fc4f59ec3bc8e804d7d5189e71e20b8d0e540b59a2854ddd7feeebda5a95f17605e8bd5f311a63cc2e4ce23a51229d0a49ca04982c1bff79c201de6cc6150b690c98106a39c\nA = 1f1589c9b5ad9d878631cb03c23ea7e94680220856285668838452a63b726e01709588b38e578da8a4845aa5cc2e4723beafa4f81a1a2e463f67d9a3e432de7064ba8bfcb943cd9efb0e5a136649cdcf5e85a667917075804991b997f318752304f4946d69abf161625ed0c03bf9abeb4ef28034f818e2a643\nB = -909dc7fcbd27d0bf7d6a3d0e2937ce725b5cca0acf78c103d633206cb431e2e2c785aea4bfe2042df32417143de76b71d21587112f36d067f878e556b94ef63d59a07d19647593efdba7f3f5324d64c55f93a283a0dafe080167f6576053f9beb326994f4a1d53e18e3f3e770e69450bb70f276d128e48ecc\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 69139f2e10726f83300505d15dcbad5b5f284d1c06789181683b7b8caf35dff063dfa4968c35facf32a3628dcfc19b3fa4c30ba0e030b06773832a2631529fe0c0c402e05a0c4e9446a8b6c22754c70ef540f90d903d83a2e3592169ce6b5edf939ac5ff25b8bd48aa2425321602a9571661a1109e275a3b3039ff0c2f430b18\nA = -5d02cf3969bff8789850ac898c00fcb3ff1fc49a22cb243ad18703bb8fae25f83502bcdd885417fe46e8237fd0b444712c4fdb8f4972dbf9278a83eb305efc7a8210ce55167c069d1c4136a9b66d0c4dfadbf036c079d12aa082fbb42bfb0098006136a61f3da43aba3d3bcf2f5ac2d7884caddd0cfc28681d33\nB = 50b369234d993721288662d83298d99b9052a0a66336a5a31b76dfb20ec2b5be3aa76f78b2c17c63d78402a15aacb585be5c8d2e7083145e316e71e111fd34f5c79363c4591c247b1a94b20ee042d840c42a3001d6c8dc7cc1e1348e0e3ea8c6551f9d24af2dc2d0c38a54ef065ff048b148ce4f11ed2b549c50\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 87de406a6c957e85c759f8ff684023a0f98e93ad4ffcbc6fb0038c7a7ceed2486f15f36555d286338aab3283aef677118f7cc3f88a7ff0ac9fed31da6786ce895c3c08d3edb652bbc9ac2b44c4cd24ad281ca3a8e8e6e4d730f4f0c25487cfc1b2afe222934eca8b1e1572780dcc149422a88eeb1bf31065c929685a0a97ac3a\nA = -1878e0497aa1c2942a2e6956957c876dac73c4bdbf42bc92498f29a006bc92f788c24a4624b87324a7c8aedc6b2c0c8a1a442aa91557aed9bf2c02b6664979e8a9a21330dd839f4ba8f84515fa6f7db9287f7c20f31732b98fc09ee7796dc524870dc35851814bc57e1a8ac49d8935fea04bb08b8760df33a98149b\nB = -32f4e94bd073cf3f70810d9af7a873996a0510109bc6fdebb855f27dcd012c59507491152d30849d75f95dd868992c6fbbf29b1d899cfd401e9e7f4e0436732cb4cc9e6a6d6b0cb63fb0bee21e422b7f7b7b14dc5d2b6d10447fc4add390fd3c8e7b06f1d9b181adfa8d04459ed051bbdc9666623b00e3871e597be\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b456ccf9d066dcf4247a21c7f3820e324ac9cf004cecf8dd1f6c3aa40c2a33e24c423e97190fc71bb9fec21d36c5a687065a7877237a2a05e64cabfb3b20bfff0b1f5ef2e9adb7edcd7140d1047b0919a2c770579ab44a08e5ad9f63a06f90ec7d5885b91de5e524b2e187937609b4b81d40a0b33e31a48d7b9868add75286a6\nA = 6c484e3c6b530dcd3644b19fee66c41c7c2c1dbcde574d87ee13cabef9dccbe5b41e25c32c6a56df23f2e87176afd28249e5fcb918723707fca94d7e2c9623a3493d395db802a1b49d550f52c29666f785652fe81afcab00a60a5b50cbf523cd13dfa06d5a5b0809c68ff7264a2cb35b8d52284172c62ee658e8417e6\nB = 1b4fc753d0530bd07094bae09a02b1ea684fb4e8519086b1e2ed9d59af011f61d1b94ffca6f354a5b428417b328bb1e8af3f6c7ac9121dae58de9f1dcbaa9c73a357f408b870e62b0c7db1a72c4c440f2e6fe90b199b9dab29fc23927190d3f2bf8a7ee926a152e64474283695614ad696c85ea547f5f51d02d1b823e3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5e7c63276f350f04816a6ed9f98507a78314f1d99081fcd906affa3b8395fb58d029ec657af82e77ef45611bc988095bba9c26f25f8fd404432fecd02398e69635f3315a824d6a98b33eaf6a91f12957a5e80cb48d5b086c795eb3b1e04da5432a7e8be3d683addc586a44b6243ffbb7a979bf9664cc7ec41e75f267d58a7127\nA = 18efe267d4c62576294f4ba44c67a058cdc0bb44c48f4035682b2d6b8a63106081af43d99098ce133f8d7f9cd04d4dd7414f704e32871d43d6e5d73fa9f447873168b43b32d6ad19378d74a967f92ec7629a690d29a62a5a6e734e9ccf5b84857a00d97b9db846b057004b03d88b827dde717fc30e6a", - "5246c752d65dd625\nB = -ebaa580d3eef5361547c692e107439c8391ac0a2d1cec0cd275d0be69133eba8a94bd186ff9a129af3f5a015d5ebd30215643554d7064635dc11ec7a8ed2200fd637b099e534237f0495d2b629abd4c8f84aa1d925d53e98490d02f9fe51bdda08b043f67f0903c0195fcb886c04397d3612e4501ab8c7b7db69f781e169\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 76fcb39f94dd2756e8266c025cebe8e801524a757b976e35ed45e3da3db720061cee9037fdb34776c704ad2059ad8920e400bfbf10eca9bb157eca7750cc31fda06473bd22d4def80189c47ba32e2824c721425f225563df2a2ea1edd090e01c0bf980677db5a5dcad37d21a68e2832d1012586f506480e929b2fd9bb4aaddf0\nA = -75f903ed9bb0b6db8e3be16e797258f6c18f6cb7b16f835f04e3045f7e4974d7a86a63f2ec351c88fadc0635b6dc83a797cdcb5cce1a1674f89e44190991e0930575b19e2aa1512bbbf2ef6f8c3e707b17516756fadb635d8c6bf9caddeba14834b5950a4d1e98bca79a4d15e5fa5fa3c1727d7a49b33d481d32fb14ae4164\nB = 4ccc582c8460f7def2d26167b68788a681c41bdf6dc805dca83127a18bff6f5ebea6db75cd959beb859637b200ccb5c7644d571f436e46a357d027edc9769da226278f7ab947963f7caed1e7e70e572980e960e9764a40c6db67bb526694b084976142471270b2331da563a10427cbbb38e76203d7da5d67487eff701d75188\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5adef30c67aefea4da3884b8a1d0ce6724492bc76b477f1053621e7d19f3cac15448e9401d34e05ac4b508b9d1db9a8d323cf43722e0af6e3c3b6d463c6007449c3bc3236d156cdf988dfc308a1b4911554ecace52938a7b10f463d14f917ec3d9fddcf6d33081745009c59b58aa22bcd7dd8c3bbd489997d4e0bff5473ab9d5\nA = -174e8e057a1d66e22eff88de26f43fde1c8efe5611f6ba4f318f027f5a5818df02ec3f014dfedcdfc8c143c5005c3c5098d409710967c93474f5854c1113fe4030e6682bd56d389ca8b9a4587b8b9262d146bc92fcd81d75c3bfa4281898f394f45d5dd11cd4c7344ee7a933ee346bdaeb6f5188967c388b919a0ce6730c0bbdb\nB = -22702bcc4f9d5bc6f803af6af8072780ff7de7a346d6b9293ca751d6ee3a81493fa86738c44cf2b7be4bf14a55a4f8179c35c09dcb1485f4c08ec5e9f9b1efa91f4b5f15a31a46e1ed71cd934ba6bd271bb22bb5703aa468d297f360ecbb48f9fd6c572683e83ebc3d432203347dc62e19fa06f93e087283347950829d4256bf5f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5c2f67b1607776c10fe2c30b112e541c4d8229f5f99f615fa02cf715d3f20556a28eff5c233c58994e9c6c1fcc37b3416b0875b9a62fa5a09a4b8f9e216487203b387ff97fad1f39f674ab19c5e34cb2f162e6b0b0b0084f0618e64928423b73b189c744e3de9fa50d66f45975f68b14866cc16c8c6c722a54420adf027880aa\nA = 67056e93b69e8a7b789f1f8b835d9c6ecb7762f844d656b26df9844a60bfbe0d55684f61debeed31a24ef4246485e8a1d43d49eaf97ed9e7b9f2d2916a8d85b8c9e8ad5575cf5a3fea42392e5d1dfb23f7ad41a7b56a4f21e2828aab38a602d560c99783a4f807120292ceae366b1fbfb4be8e5d4561bc8944e7f17ebbcb0fb6296\nB = 1f874f244ed6cff9f910ba9a58db0dc0a7435e8d99ba6412e976b8f64d4106d3c5c57ba079384fced1c261aaa538e131734451fe84fd3cc5cc8b3ab46b2031f888d95084cd3a35a61092672a9118eee4ed1a0df0409e3613b3ef45a8b16b71ec892755dc3f83c5492b67fb9a143ee6102d053078f4875636b20b536d5cf851768cf73\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7850019c6712f18eab877faa8489daba23cf34b512a3193852508185b13cd5a2e9f503fe8d61b74b5d3930021a5b8c38322aae9b9b1b4814fa4c2c5bc409b58f11fc8fd7854b17baa94a6bff5f234832f9468d90d148fa2bfed774ac03f2dab6a506a70db4ce363f932adcae202f04fdcae968f632dd674416c23d4e21345ef2\nA = 1e378a0f27e6259763890d29e112e3d8d2bdeb9994c49fb67ab680b6e71a52fa0a7db886d3baf52f36d943b5430ae8bcd82e229f4197239c35678eed254c5816722b995e9c311be942f8124e2f80c1e59658433a57f346adfcdb83202e55457308161d2f928b60efc39538a6469f90f1a868cf6077568c8241623896ddc2705cf04e4f\nB = -f4ee37e39d4cadb692bab5483ceaf0258b068f2c0354c540438803780c983469ea28324ce7e209c3bf55b91f0a2f4544bf318585e4514333eafb9b8c2f02170c620e9b5280a828ce1d8dfc64ae9c28577e15071825a85a59656c5b47d9a382af6b78a5b3dab1078dd647e0b473174b8415d401543d30a4018cc3eddbfa546d0fad9cbb2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4c8f8b671443a3af5ef5749885ce5de8e2afeadef9051bc49c0d7e72922d049b1accdb79d82288e472b07578e8b6d2176d6cbdd7f0caab593dc0fd9224a94920235410501fddd6001b62a7f7d8eceaa7a8e4c0de52029fae68656e8120972b5cc1c2e909c2742e836f2fecfa51e12e4f8a2ec7e69eab061c81785374ac607fbe\nA = -5769eae759dd6bf94468eae94189d3396886d4569b0ce264c22d39b623be3abb01bd5008b9fc86701a3373f7764118becadcc69481cbb134c20f669cefeb376dfc489dd4ee91cb333d06afa391dd322abe2b3b715d11ee372666473a473e29dd90fcc97e939049b455be52b3f288db306999019c1177ab5820d94859a9d2f050b7ee1d4a\nB = 44adcaf1e2afbfddae19b23cfc0f0ba1f940d32945d0b541db23f3a0a9d06fb1f67ade9a8e620bd96f4005ced99430c7a55eb7e93a701c829fd5b9e55dbb4d3833afbcaa0d9c946916b1a86af4a6393b1155c6439b8b82260e09ccf0ce5d1c4856f4d524983e4b0fa123267694a1c6118beb8be26113a02721a02d7b0ccb01ec6e9c0f9e19\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 51e25767b8d4d7b2b0c2652d9ca6bfdbfea06acba543b1bc8d3d25b2fe5f2998febe1a6e742abc3f482b4267854c2223a5918a9b5c84e0864278283bcb5bace0c046db1d0240443404fb62d70ebff3ccc655e5f5977958df4c878d9859a69731744f3d33978ac31551487270bb4fb56ccbf59402ef9fee42cbc329420180de08\nA = -1966812979042198f70b3f1238c93ac5c6e5749f1108c2bba869b1dac7680f910e56318c9b59be9212e713a348767ba6e75917fb599e929ea2144880d18d4fbda4f4663c7abb49b02245169f385e09098a4e01b56dadfca8c803acb7cc244f3c98bc17440ab2afce318476b80e1d0b4ed9a8d6f2a0be64633f8faad5eb48de2681a38a633ec\nB = -2e4f5eb92fc34c753c61dcc826abab6fc4f427c6ac7e73ffdf65b1037464b2a9a0b0290e713d81ab57c0e1dc30e76fdf96046fe10a34cc4511398319ee34bcaf73763a9042fcacf59a100c43d3333ffb3743048e8df0dc61fd0da3f935fadf882ffdfa9f0f42980c1af6edfdf161c4b16087e2b14277f655abe54582de79c51193e13169b55e6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 33539b5f38a9943b15801d449adabe02da6e21651d96acd9aa40e866bf65015fa40178399254e8af6bb082d021e2a05da0f45b699d193b70112e114f0d25287476dc0c733c5cf9df57667ad0d3ffc4ea2f85b43cd10459cdca9465b0974e578c00a6e275e0b97ef2a4c9886aab7b5947b78a88f84a3f1d8c5f26bd07bcc59886\nA = 531b891fe9e8db322cec59a2115574c7a304c423e6b11516906b840542b2c608785e2c18033262ab9cf68f63edb40ad4f073ce8841db602cf8fae0a6771d741c6392976c9b333ecfcd0c8e9997da40616ae2a9e0c6be93fdc7af0dc0668ded1e42a9f729c70f74500ee76a91d3d993c075c2f645b35792a20edf17c157459e35c0a48da6c4c6f\nB = 1a6fdbfed1054a0c5758f92f72db7e5737b0740c4d8c3ae4713366ef6709b21eaecb6b74c92541a9a0c99ae18ac6ef7de79d4c84ce39ad59cea9c203734a99bbb895916275e8778cfcf7fbb7b7d081a677769e4ab96bc7bcf23303100e629fa8e07f5b8fc2e39c7b5724c72907eaad09d3088783b3118e57c9c8ad1799b43a13f73864c5602c478a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2eab6018361f557ab06725ad90f6886d4b468ab1a193f8fdcfb4ad15fff781c8681329a27aeb5f03a81d7c404b8017b12fe23165e941ea767c733513a07e921aedf20596763f6f977316e37bed70f6a617e5c2757c229c59b3d7b1fe8755b5f65f7f4", - "07f13634aca7c8a267e661ae2f77fc5a95f56cd6c8458119df587478b1b\nA = 1cc779145b2b7bf9ef4c9692845e162329940f96eb43e04db8728bfe736698082aae6b6a1b3c32867c293b08547a0941cf4059d2d567840ab6ea526e3724ad59e715a3782ca656cbb739dfdf0c113a18f0dd62423d4edb60057fcaedbb852178d38f1b5a232842b4fc645cbfd97a8cac0b094b870064302dcdf23df2c9e9f736d93409cbb8ce9ab3\nB = -cbba16086b51bd83d3460e51cf193ebc79b826e4f30978274eac3b2dcb04e9d7b56a1449b7cb128bbfeff5c4720bae45271fcc64085d3ee501f0f21fe73cb7db5f275d88be55c339f9180ea21a8cf3755a875331931b75d23f57c2030c89c6f9c1ead431cb4dbd4480564c83f8470610e5673c7eb6c0fe7351ffd7ee460df5db7872c67041aff0227f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 96fd93535728b961b4167be8b304e570cc34e787c12a9a5d76e099b336ed6b837cfc246c5bceb04b0f4744c5da7071fc01d70e342509473e5bd7c60d6046c9b4f21c5ee71c4e678447f837db3a7694fc3936ca733efdb7d387f0f6e263b3ac0b89054a826da9716691c9d580ad38d701d08ca090b6c59be466e1b9833e75d820\nA = -6791fd686f46c3773fc8d7f4753d178a93f6fa4941f4305d9689c2a305bc67840bbef80ff05c7bc6de3a595f73846609327d28540cd705f5aa94a3ae5915ef55304c37c4c43a4b46906889331ee16585629bb303673d439de9c0236f708fd19a977e6e1032e0576a921853f7dd328979ad1f1aa945905dae93a82b3af9451a541f544c18ed2546b66e\nB = 6ae062b39c77bebc2fef05743e6d35e14a31c6fe1fdc42d8de2db94ce70a6d60d66263c7414b1081ef2fa6ab511b361b8baa9c71ec628dba5bfd772c440baefc2fbed68d40897878232d9715c4b7e7c9bdd41cfe7b6986d825f68be8cc16d04afb0cf593f3028f3dcd91bc94923f3d7211aa5f0f12d3270e8df8bc191808f0e266c4fce2af97ac7ce06b0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 949ea5f645ffe5d0d03359d51a663c7dd6e6013812a47be309575e036503126f48677c68c4ef6e7b3f72d76657fa282ad5881263e649b5297da82e24298300d032af3f5e8309ac7eb597b16e257a6f7af3476a264415aa7783433e83be57ffb3fdb404a9ddc3527d6a9c297f8cb7b6674961b3af837ebb65f218147a46c39cba\nA = -10f59ba073126d92a201529a5374500612bc59a9e66322c6706b422d35a4f82d97e668b268f5527b4641c6099c80bcea504234f3c1e3fd29eba0f161da97c50aea542becba499f29d4ba5571873d4dd9eb3f48cb26fa6c929a704fe8e49791b2ca3293c2428d9cb453263935c9c90a4a2b39d23a0baa12535845f907d42b729033a0a1e74d18da30a88ed\nB = -34fdf9ae6760d4f434d09ce2a7760ca2dda14bc256015809745524dc49d841b07102aefe5a1d0182e3e09d4d45b415e46f653185742b9b8ea6960160752080e5c9577a12182ccf1a293407b534ea8ddd33ad16cd19ba537d8db5b542f86a2a292423d452bf18d82361240a7efa831518184572c5a8b73b108a81d5036b3b530d98bd47c7fb2123418f12e05e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9ab739ddae55a0d71b39974628d4601122ba6c5035c3ad0439691317f23dc33c0014f3e870a105e4dc1432ec79693bac658433b21cfc218ed411e003990b94ebfa87767f3614ec19f5bc30704adcaf85a9d3d15ea764c8f0bbd52ff388659637746d39859398c79016ace8c6f97d3a5616711a235b85f334fb889b9280ccbea1\nA = 76b15a0aa0f59ec804a5e9a627e1fed524320b29120b6789f8e71b1ac4e00a9a8c826919035b84f87d291e2f35460bee181342136dd9eaeb99ed00c6328b8e44c49ede3921d6275f6e7f03de179fb2374ae2fa6c58852fbb2649e214691daef945ead6c8bd5a53ad2b130e9eab6ad046ddd6b80874ca6515322bc171ee32749333669de0d9c883058423579\nB = 1fe2171056ed4585a143b6b2bb5f44047664f64d710dfc05c18be5840ef9426ef05b6e92e4ecb5544ee4622e9030153dd9827f2f01ef38e62b88ecd6c46b4457d16644ef6d863c226acfd6928a40de614a5853137124fe69127a7f05463eaa49bc742d8f7be300d06b302dfb0ba86801119bcdc01b516afa360aa8b22b7c6c1839cff859ca1bf26e3f7e030512d\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5631048ffdb2767aa04d59d8a5750016b38b983a2d53743ba4de5d93bcfc8ec30183a84bb1e290ef9c72c7ad357728acecfc613a6f9b3d712456d545ed54a337930937f4589fe41e66ee930db3dc10a4fe41481008c69eced65b9d1c46b8574c5ac8f7d94025d8fff00ced17a5e17508527681bf94c2dedd51502a2c4652538c\nA = 1aca12b1933f25ea081e12ff4a4f6f9ce379f96d976da2ff7b8eb8ad791fabe31c1148fdec22dfd67828e540c955a1e13f40c5b125e1c7e6bd839bfa84e5bfb58bfed76058c6db77af7a34ffd25fabd60e19f65e1faeeea6371d7785f2e5bddc8650a7492e06691d61f997483661eeff54a30656f1daacf31182486bc40647975151fc05d2f64b50e632f5d5c4\nB = -88ed894287043e7e5cd2eda3c1e5c97f85809f7a246b0c20891fa9a024f3aba4ec1f3d112580fe6ba6b0bdcaa1325ac7ec9508aa88c187af08e4f37631eb6cc97e4481b18f747ce6d35ff355e425a4833834ffb8d34a818bdb015fb818ac9f58feb87020234243aff912da5590ea3f6cba74f1a9fc3ffa2b4aeea25479c55a3b572621e75d86d8c8f6ee4f587e0f5\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6ce341aa4a571cd5bc110dd436acaa09f409661967de0bd096c77c60db58b2b0ec95cda50acd7fa20ea4266b2c579eeb6ac214a75d40abbb70845db74c4d6c93f8c545add269d45fb15d985e7e630d0425565d06dad4a3ff9835411e51fdd9780c24f466dbf29244cd1b8c3445af181d0928db399bbc8632f7ebcb9d48c0b754\nA = -52c53999b02a92d6254557203cb31a21dcb896495d1f29f3277d19129ee43e521ab9d5a297204a844a9537d63b74686eceba72ea2e7b98ee8895513395cf7c44c99348f5c4eb657874a8115f0027d6a416b8a04a1ec0e6809b7701ee7d41e99996e307bee9c295ab3df1faf674e0067d0ab3bec4da998580203e33760870ae472a3045bbd66e352b8f4d284efc00\nB = 4329d110504caeb71ce0453b0706ff675f646e70a6bd9575791a38f672eff226f4958f8b1fe4123c0001d8f8595d8030d0e9798232942725a9b9d654ecf50546adfba7103fed796b455ffbb4c153e70f941bef7953c8a210d6f2f4ddf5d9a79d9938503ae8f24d69d5d7df1c988630ed960e12dd877bb80a1ab0bcf6db67e0c0578fc0c40408f72b19052534da8d31ed\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4b9fc1e0eb4be199427c48bbe1b53948d0135bc1965b8aa5421a4ec704b13cf934c650405ba02ad611b0f29d46d82d4a1fc5a84651a29364524e37be2fc7001cbd3c792aa477802999841ff19620cf66dd2453c9b05aac349b9094d43b40e358f32805d87cea3cfa98e05240ff95ec57d88e0a12917628ebd34946eb1ad6799a\nA = -15a223b691d8b3696306b0ccdb52c1d62c7c2d1ac71e5f07cd8fba960417b42fb5ebed5eb9469be67f231b5254bb0fcfadf5ac5d2906769e8bf8292f0442986cabd88805a162c0c1f60f9ff0bcc2029ce33452d05f754375c0bd147fba745bf8a0008792d4f90d0e0f2cf391f2d7865705544f4a220ded44732321473c0ae7870394d4e625df11bd0923340cb70b995\nB = -340e5ccd644849d982bdd455ddb3b9a23ca14e168bb87256bcc370ffb6b7fe78fd062b3bcc1ad3c8c3b8cb549f2baaf1b7f0f6522aba02fd35b651f7de52b3aa2e0e40352bfd6ed0f84a2bbc3b3a396dc8512ca1db01cc69611925f1037794c82a418f10e0d994f458d1f19051e8bea32b90ce744d46718f42e711c094ad0a1ee96c88920188078f1b044ccf307e4cad7de\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 31c090e5160faff9a136a7a482b42a43ae3c7d00c215cbdad28804be0e7b12b0b3af820c1350b1622a22c8875f24d48ff16231c826d1a946c66f70aef92d4e6582e3ce9213d907267251ac74fa3cca9f1c8fd53fe9898aec19936a2b797fc345d68f0791cc740199be39c05053d5591d874b415e62653b04a3f41e263d00f230\nA = 5419e87e50b28b6d24927934b541d8de548a8f4ec7e9b00aadb6d23f2d33406177d3fc72d29ad2c2e141ab2916adfd30ec4791c626af61d8d192276d632aaf3b54e2ffe83b44f6f1ac441e6823b6b58cc08fd7a0af945a02eabb5aebb2c7ff0622a17b38077cd0cba906ce23e71ac7f4da40ef6066565b4cb3a62ebda28f3629eaa251dbd9979b123a5447ea20331723e\nB = 184782ba4daf429cbd13ac13fe93fe5833f09915cbbc707feca3293e505ce9cf0b4b12ffc8b178e0a4617f809be53d4895a4182e7a8a65043361e654befe8b0", - "1429ba4b7420193d1d7d90930ee19cee0316f33a5795335f5fa517e1ffbc99b95101b0f936353afd3bcfec34851ebff1ef02fea991a01b587d28640c935ec91496d1aa3ab8d38a6ac75b3a4198ed27b9019bb3e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5eb9f3ca660de481968a3c7321281f22fb9273b16fc10d8eff1fe34842364dabcfaee4993c1c8ddb7c8d6e509a8d2afc005075d5fd3c4471f0622753c7797aea900e785ceef905e2606f64f34e47239c40b74f07e2ca70bd5a18cb0a88780489f3e98232221f65ac9c5ce703a256b7b75eb1dd38778d8bc05a37ac9ad8d36b35\nA = 1c73d8e3d5db127a81477a5c4c6d61ac62af446981773ca15a9a01fd5175a2826a8763f91d68df28ee606e8ffc203305875a238d2095345556f12f3b5e10c5bb6ce3f90342ac74b9ac057195c863c4b9d28ca1d958a98649c7f8897bc6abbc39becae963f61b33bab4fd20d9d0e5464f21c2cdf06d00f597dfde45dc5919f5124f26888b12d72cbd2f57de3f2de7c014f891\nB = -e406fb60e35f0abdd313b8431f4cc89fbb034daf71fae0cc727e9a93cdfde53566fc74e48f4cc2111fad158c63293bca0b21b98416381b81d2443d0e91647679481cd6b6869b37112d3b6e575eea7fbb5bdea422558d817b49ac36a829926553202cf9dcef09423c085d26176a89be741ae20a434ea461def090dbffaf2e2ef97bbd4ec779041ed69ec07d125c7b85a2d215bb0f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = acf9d363fc9b76ecf7e61c33270031340e66595e559dd1c9dd4d2243819b660183521a4124558fd4b216dcf5c52c4127fe517c48cef428b9ee0f1bebabab487c968a80b9815e82c12e807c096974ea3893a8d5597f745365c352a6bc6ce92479176092f02907538c5e784bf26dcde7672338f402753b08de8aa21b9480df6955\nA = -7c03ba6e3939ebbeabd35cca277eecaec31f326ab75f1a29e05af50c4e62e0175d4d6a57acab87cf1fa3a51791e9a2b2d4d5db570ec3941263902b0c74544c323c106557cd5139d2a25f3c3ef81ca009d4e3c16f1abf6e2b5196df1b30def46d61eccdcb3741a6dfc8e8c5e6db68ec29c82b0adf6e35ce7aacef8da806b3b58bfa489d319869b20768f8eebb604a9624d048f9\nB = 4e021959da96ebeaad17f9896ed53010d80ed3fd4c3a826a266e82b80ad81b3032303e7c0e58034a652b8aac00c08d42a530039de60d74ad349438f5ecca1256342ded6f30e3bd2aad5bf2b49124cb27f45f697e157550dbbb37f5aef0f04839aaf1ba43bf1e77a1529818d0fa91d940904eda6b748e5c86cd1b37592542c43b7b4afe2b8926fef6dc01784fa431d43900edef27f8b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 24124c69aaabec7a7b4e7a82245f6cb14b199852a8b314a7b8d9049cb66096d5ac93ac75eb58a2004de8b0fc8375638c0878fb6a45be8bfbcc292e3571df1bb8d6e346d5595fa395fef983a365e4e868154fb3e337d47771419e7f1dd5e4220900c564d7cbe8e7792ab288f99d265aeb296c5ebfdaf08b88d9b30ac660cc3ff8\nA = -167c959417e9566c93e7e05d2a410f4850e3a313e516ec958c3d2fbdecbf58072d05691c68981e176a867d7467091dfeca11f695f750c8c44ebc4d08e39e679d96c4791ceb1ea3b89fa3ce26f7ef214c5368c03ba694f7ae592bcd8ae53a66cb3eb1e0cd3c105faae6eb7e7a8fbc88248be722406f2d35e46c751b5ceabd992091eeba15191ccf6dd61a7ee0c624d43b188c42b6a\nB = -343940f3b2a5f73a51d6f609e8af306f44ce7b5c2e79edf6f4dfc07866dc5c4b2e0ba48099b5503af87762a44ae451d166f8914ba25b3cc41a766583bf73d27e40784064582fd9fe952fc00e9aa2d4e4f1ef35818978e725e69c1bcf267fda4d635d1d292d54d3ad10bae9763dc5d7f7226f371184465695f2d384d749fe07967a1bb64df22f294ed88b13600c7068d881f713cb8e3ce6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 50cac148215963e58cf6d2ebc36fa518c63a0ab8fb136ab84c9657fee459043ee9f42aafec89e8ba5fd1cc5c4495a41e80590ce197e12c087ff7e6ea88ed798735f55a1634562b82f8514488ada526e5dc10700058980885000e266cad55948d1e080f6343f84b12a3698d9ad5427fad4017d931df77ed2e45e2fb8380b7fa39\nA = 6a9833d768a22ea46aab1a1619f30283a1ec254a2de5652981d73146aabe31041ed04d271c6f2e5e2d090cd615518a06563a94ee2b12cf9f142de3f15599998a712974d0ce9b122a2aa65bf8750f54c6324f12e321a888154330f0f9e1e5b7999acd70d4e6da95c2df1da2d19544b7abd2bd3041e3228c7cdba44f7d1cbfbcf968f8fe87fab523eede0485efaf5cc9e56095cec8983\nB = 11e782e2b3f469b1e3d14ccd1b8301ffcde7e371f6e9afc99af5809110c6d70e1cca5c0bbfeb95fc3ef8352581c11ba75c0f8c445ce2aea903769a24289581c95ae5ebd9553fee61a30d155bf6011278807833eb2ce7ee2a98fececa23fabaaa259409e88e3c4f4eb1e04176d44878ad3f6961e0615ade2fe86b6eb02adeaa7c9019d63231a28f84b7dcc8bb0e71e2a717db09301e1dca20f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7cd49d72bcf5ff4fa2c686f21e1f0146c4f24b9ad2e900dca1c0a5d2fac5047509064e65ac582946b251a3f04850c9abd8b80c92af0fb11ac13debdae8b94927f1de0e4bb217e78f5d04897c6a0762667d3d883cb754dc610442c9dbd44228a7ae4f14fca145550d813655befe3bfeb52f1c76f989ea8a1dd9c10fbc7e9d6574\nA = 109fe33568598972063279b71ba0efdc2e03f770cdec331428fb8ca084c9b20d0fdb5cf9ad7ce90c8cb8f0fef10d219d7dfcc6b4599440db8cff9971da7852880bf004266886eced8763b3569720df3a1fb0dde2717ce0183f2250034871146628430f206c12f5fd87574c206b203d90c0f2c705cad3484c73da8bf4e9f7e1bd433a6f7fd27df63079d30c490aed7161bc594eefad4bc0\nB = -b95da952cabdebe0194b7fba519768e1b56149353cd12023b97397b59e0d7f4dd1d27b65b833948f58e66d3f6928cc3140cced835dbd612cc82a7e9fae1621986f71ddb6707ad57926b03e87e165d30fb145795a70627975bbf9d9ac9bce07492de5227c666663cc28b3e70b19dbaba7f16849535ce5fd61e91cd2875e0a534a10c60d21f919d566a3469d108a35ec3f023210efd5d318c7210\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 98a89cb3c9602fe503c32c44609bd4487b6c8323737b3376dafacc3eff96efcce7a31f1b61ee6799dc9561e77ac058fe5195cc013e72a2864f7e492d9f35244b321d46270a582f6f14f15fa8203d392e81b183a1d64d48b51d70e38d49c93869ffb9d7509f15ccde547d2d9c4dccd50eba49190b6e831a9f4f9000a95dc83f3c\nA = -67d7fc8f1766c40bd476cdb65d4dd161c3d4c2c5860a0c559f0e87ada213c9ed33308c36bb1c7d615fa69ec53656bbae6b57181a0134af23ea2a75f8fed3290a2f483392a3745fb57adf2121738c84f6d34325121a702c8ccac0090ea27fe9a5ebb6ba9d4f397e4a7e3151850b3d7d25643398bd3e4c1da081471389799245d986cab825a2e6ca72b38ff978a2753c835299ab4597bc65fc\nB = 676ddc4d18960817ff8fd2adffaa68c87d234d62d445d6ba3847ded849356d929d9e4ff01f517d7b1c0778bf90f475923517d855956f17ece1e032e2fd474d2133d6b8a591995454d8b587cb4f6fdd0fa29305f146d340cbe6b6efd28a926c73735621be0c5decb792083b3f063a43dd9f635e03f78c1bb56389a5cc993c8f36134d755a324d4fccc2ac3bafa270df67db0a4ee6ea4497aa33b5a8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 76c31404854006a7d55554762094df6e11e0393f5b0451d85de2e5b104432df72023a35f44da10dbde01cebf77b8f9d3ad582373c5d32232564729af0d03c5450e439045d96a2f0a38871c922af2bd38c545d219adce0ec80fccd121d6a733bac09253604a8a0b1ecf0f24e44b818ab9e9974181cef10e9eb17684c57d72257c\nA = -134e8784878a8f3cf49ccb952075f9f9bcd24a20f8883955f262867045c11a9c566abee00638927e5de924872fb98f6376e321ebf3f567db6cfeede62e04f839617d78b7c9d3487b60a0d3897b3fa49b14c12511d04854bde4a9dbe5f31424a3d05cb75d23b46f6c0819536020880afa5a2c173f6881754b56f82a2864c99c820156f96b5cc4665d603597331d98d90a52f4a30c6215ee5eaa2\nB = -3c5c0d35de5fb21c84d2db228829f43b31132b582556b92b495f59df502a6d00584bb5bacd9b8c1a8c7eab91db0ea24b40f07e62a712842d5c2e1d208a6412a068cd5c6394d715260b67fbc03e3ae7eb4862f74f4d7484f747774fff03830c65fe022d579adb6737f6dfe297db750e6a58d1004e7e2716838befc2ea97179ecd53b7f36e3540e1c3a0f3e044bfe2d0efa9b89d2d308cbd0bd88ab3706\nM = c462c7cdd79b7604246", - "a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5b704b3181e5d0494937b4d6aa8172eea82919fd1d884493197a6a85ff047a7bcd5dcf072bdcef0287be20d4ac49918d1df550d184f86d7220f0a84fc4da3ad05e131c443fb529df01fec9fe4fa6fa2f36e791f9e16b4092759016d2f9b1ae7c3d071c57edf26386aaead767a3109c12a5004c7b9fa595e6d592daaa2dd1df04\nA = 48a0ccd2d14e14e2aa862d306501efe5de239e8ef36ff6251c861a0aee9f739411f402491bd99aebacdc26c4f30306f9137ffe4579c2f13efa81b979ddfffcd23675ac6307c0aa3ba8ee77a2e3a3c8e241bd2ade6484e6ead32ce8d752fb3584d14688f223758c5cb8705cea9c56136b219d87f9904bb56be2ea1c9a035df33455206e6b7972cba32ca4c3db41991117d88da3521780fe65c4023\nB = 160120a35ae3edac3edbede9ff1c6f317d95481227d87785b7ee46cfb80fac9973e418244884caca3211a3f6cd3bb419cf70fbc22d82ba5ab98ad80e1f6c2cda753aaf7be78613ef25577107a47ad1ee3c3645db85c4d29bd77900e99e1f439cb23c6c68662c05322f94feffcd9e37d8665cde984387093a043447de590e7874e6acfa37ed302040df4d5c3dcdf9fed91b3d17ab5c141d4494d0f301b508\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 448c3a64958b82ccaaed3c74706ce0a48c5e059c3610cc03a6b5a03a7de5d4f1d1e4b08a31478fa8edd58401f0171697f0662146ce2b371e335d695f9e4a671255f29fc0b9b7d1b2eca4cc7f8357aa0920b5942e31bcfae84e909828fbe5d02251ddf10dbe4c15351f675e96e2eae6d044da1f0858ce8ba9b7aa146850b85d93\nA = 1b2a52aefe44170376df29d17ae2dc1501c9c296f72f271c21f53db71247e72c3eb2b780190c45343bcc8f548507559ced3bd4a6fb13f9174dbddf965b9c4a56c3d88727736d78be9db2268cd02382e50c6fa28ddaf8eab9f44ad45d5882a5100b3027c150a7f3bb36f29d24a76e40f3820ba116d645800459f06c20679321cf5be72450879462f0eac99ab6ff8d26b464cd0e6d78621c9263394c15\nB = -b7d9bd08d7d8e0e9596851b7e03c78973a502afcc7b5fe5b0db6034ebb8a11df1ef7ed0ae1371eb4111cefd61c61935d768be3e3755e481daced219874cdf0d07a76e7144be626cf1fc21c8a0e9db4389ee213193775e95d4d86741d8d8fc820c239b7a90937000dc3e89b2fcd61b44e1c38c655bb3d31aa7e422b4406c9e4a88e6a2c18ec7c048f4a6b5b270c90d9fb378f64be3b5b351621db48a6c18625\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2192157490ae044a26c23eea6da51d3a3dd08c7fb67a9beb76d37ee24ac0089863aa7f00849b81bab8259f3a0e1bc744d841e07aa413c286e4bef2ff3356bdbecee756026915894584b4fcef7e49da4012cd9fcb5dbe3f3b867cb6a7ee959a328b0fd56a9eac1f4e40a22bf0a30073cd2d48f99245ac03c373810c54eaf3306c\nA = -598eef47b40d1fa1ce260edc561bd1c1ab286a7e068af412ec2baaecd07c5b9cd596505ea1bf0370ea961c4ceeb9be76baec74e6952cb846f20e5da406bd01368b85d59569b403b7a305cd7448f331f10a34def43c738fd633df9a3eb194c32d53aeb567889927271d71d3929d43fb9338248b64f7d23cd1b053239e09cc2ccf5fe9c9ce240f1a10fb151a8583e4b4cbc70ec3082dd20a9962d564544e\nB = 559fc917de34bd7dd7a23a432142ed79e3ac4a6caa357eea21e423eb9af7fd94f1eca735d2588ec4c2ff013520c3a0e209627217cc69bd5a07ca46a43ec1f1bdbee5f09ceb1b2c18bd388d3852e51070943f16152a73da624be680c671057677356c6f281a4ba1f7c60609125d7fd9086c907ca5c191820d80e483886b70c1074e2963c49996ee92577334881edafd88270bb967da795aa4fefb739e4367390ae\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3488bf00f67b852592922fbae64fa56d2e4e7081678e789bbb3b4f48df62576d537da2e99c9bdd721c725b9a828194662bbd51ee20ba73d4ed5562482540880686d9fb1e8ae62d08e39fdbbab1d18e399ebf07b3a6559dda8b043fc25a8152858d39b10ff64776e00a839950e7a9ed5ea95b594b6e9e9d4348ceae08071ec5d9\nA = -1b135d8cec9969561be396323e2f8be0c60903ca59b6c418cb19876e9e3cdcb9ce4f5251eadea11fd6e785476c70822aebdc94617063d161ebe55584a8a774ab230b8228a2b65bd5a6c873bb6b261429eefdc7d0c64c7e78133e739efe57f835ad03ef8f84601e1a2310659db5e0ee706f23e3c5c38c9f8c36e5b15b654d1cc528f1dd392f1b08921af8be6fe4e4e6db774392441883ef867bc729338943b\nB = -34fb63435c90018e5843098e379c76ef3ba0615b6b500854b3dda3e77fc5646228fcf3a6e1cd87a506e4959ab05e24474990ad98ad0865942737734c03dc289307f1b1f424b9a8c2264350943449b3d2b0f71f989039131e23095d122ae98c0089a184dc530669e804140134e5b602861a5e61c030fc3d3b3eef0a59f8c0579fc9b0afceaf16698de3fa07c43231312254c04ab11ad7a29efc4597780c2cd1b64b43\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8ea5fcf7fd41803606c95729d2d910941e43b222f9b0c93a1a803b197fababbd653a92ee34e805906fde29b307a962a294aa4dabebf0d181c046653ad0fe6da1295eef817f3289dcc6579cee8869198c39a9f79992cf6894162d35d812df327a64470c935994aca4985d0e6a783b853ad762338dabd575ca71034e29d768d014\nA = 6858d029a62b0f75e4c59f3ec067e3990b2304c90a097daccaf554abec49a9d297ca14648471dba08f22ebbf8e238c89ea06f188203599aba56611eb3d4df09ea795a7e28f91f4a9a582c6b949c6ffc584a076de653446aff9b24e87202037974aede37aa9a121b5b70a3e9b5ca376c9056c2c91f5d5484baebb64cccb6a09b4f40529afad1ed64b4cc4aca586892693fb5f92edb6b4d5f678f7a2441e51410\nB = 197d6deff7adc30b025e7e418cca0a641e1a1b35f78fb56b9d8847f0690313475e6fbc6f73c3a718b10bf37434dd9fb1eca33a99bbba674195b20d35e3b34ba9d7c8438eede24ebb48e6d39eecd93fcd7dac44235ad32f208919f57b261da70ca378f9b03ae5e5a733f97f0b3f4102d971272015bf50b6f3e50c7b36cdaa14a8a580366c9cb0118ceec6e627827b0b8f614656292675ddb66e1c55355d5a1d78e69ed31\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a25db977e7a8fa4578fc530995335411432ced67e131fee2cd7ff56970df64a6f0f4a7d225d2f4ccec8e98273ec9a0f1aef01dc0b866e425d64e09cafb9ebe3f80bc0ad71c769f1ecd5efdb4a990ebd3a94303f52f4a97e3a1d615918f8b2df5321c4aa9339b4453d7a710a803106dd0ab49c6cd9aea431f97fea9fcae0bbd90\nA = 13f97ba15ce46ae32147a0aa4c1639b6b555f4d8a1af15ede4f1103f7a0b06b4625bf456d667720adca0c4e26e858f008b012fae63cd89322b33fe51e87714519e7dc3cceea27d968b46ebc04024d063b17901a7ae978591ca6ca41afffd81769f04b714134cfaa6700cf23bfda6ce67313988bba5fd3782bc62f76cf551d140c978dc002a779ae37400d34cbea013a5d1338b203ff267861edd88ab8ee1e4c4d8\nB = -88d8a4c8c680fb01f493f73753c70ee753951d4734627da14962e36449db5490b8c575729fafbd203a125b500b96364e6799d9cfcf0efb4ec877e86865eea5e99e2fe5e7655c1ee0eac641e73b71c66d7a72c2934d1ccfefcf59781035b2c7b89e5de3f7d1e9128cac57947d22e7577832ba374492a2f53be37e17733d8bc625fa77fa5cf093975049a5c477f792fe75e85da26cceec820c8b255df0292824b4c3a8ed455\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c1f2165a402fe9becea284dae60453965ce327f540bb8969562485fd1bb60372b8689d9c9c97c91bcfd699dc370117ea8b704f06cae3d972dc6e5eaac971597c69d4dc24a68b256f97229e643706aa6d2d844078a5fee2d08270820055ea58155d7bc754f09d0c6f804e55ebe53e3ec418747d4130cec68533f6f0c2f8fd2409\nA = -626a1580e52ba52a877cdcd62b34cbc7f949148671d4a61201e03e98985d704b2975b9a2d9c4557deae065becd662ce8448171ac582894bfa2c59d4ed20c6d0471fcad1d0fed1291df5e4556aba72f3645486580c8bfd0e3c8f6cb34fe17ccdd75fad4d4a2db4e00bb8c2a23ed17a31e95631320590f40416c153efdaf897e3b278a1faf1917554d9292f90c4edd5992748b58492289eecde1af34976ea8ff507fb9\nB = 44c336d7739118340048939d6c198f73f90e13030b69be286ef920902391d87a58df3632091d0ef25340eab395203e8dcf3389e95debb7432165147e145735d2e3226637b4b8cb7d85d68308be07f217f57fe439b31fddf3fd469869a20f1f852e1645b0d4903432ecd1fb6397db4c11f6b6b9c0fd25778b0ff00bab9ff576b16538a6b7da40f01fa7b987af8ead41ecb66b", - "8940c0e8a1208d0026773e711153d99348e92303\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 98eaf476f11168bb63fddf7dbf3347e619f9b580ea6804ab893214e94ebc089cb652e307f1f37ea7ab9052a352e260ff7d1e8c17461bae68c52a8a8f1a57a84c79b2c8fcc2d504ac4f553d2534f2a776ca129ec1942d83c8ae24c772f6a8429bd61949ca1aa714cc3881ed731497b84415c88ad4b9be34197a549737edcfeac8\nA = -15897a5a986641fc2cda42d185d72aa1552eb92f788bb71cc74c0e424bd038e02c620d0686ff88ebdf0bc1632093c0d89e724e7d5b526b0ddc4c7e145aa90b36be0d8574901fdf286df84a6b52674a78cf21ae4865618b4347bd905461d878537b33cc41710ddb290964c48e44d4d2ce2ed82847de75938d23ed418bb9ff1caa03b5c1ac5d65692dd1defbc6013b3270c4314a45dc67883762fda5509b915e8277c1924\nB = -3a7141f54a0bcef68cbc3006166f7e15a5c2394892a428fa417a485981316a537cb3ec757d4a2473fdec2cd61010a9ff865852af8f43afc79a97d394bb6c58643858e2b4dc5cb958c33781b5c35aced7882e8b8d7b4e4249c2b82150adfb0c8f2bbb1cff3d2ea27ed24eae030ef468ae4d6b7462f0b072cd2a2f02426b3290b87b14d14b34e91a94c5bd69e9eda53335cdfa7df90a57f97f3d023ff85537fe0a8bc5d8fd7901722\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 34464b7a50713d17b01b5940b5acfaa7006aa6b9b083bc17e0535b08783761391eaca8703af2edbe13dd0fe9036d38aecfd9faae08c0861042ea1a25b41fa8a15b7721909783de3aca127e955e177987518dd010306a795bb66466fccd55bd9e2bde17470cbd36b1e8f8b63805229754387a5fb40f3ee9a8afb2e51e25c8bea\nA = 701ae8c5bafab7f41c999e492f04a7626b2b1054e6dce1b83002b2d3de46717225b018733b0fa8fe3f973202da8a090ae3fd14f48b27097513ecd4ceb1b9729e7783c17fee9be5221fce4ed3860275b3b36b7416594d2b65e198ff564e82301cae23756c878494e57b5ea8fd22ad800a582cae32fbc985d122cbc6e0eac77c1000d3ede45ae7aa087534adfdea8e9f924efa1b19c43dfd3b7bc83d7c40df7c6578a320a19\nB = 18e0256543619a750384d30b6a7afbbcbdcd9a2ce644dbfc97a8ff699e118032558f706502c9b956695cb25a46d7526596b3d0b67b69611009265838bec533a9488d24583e7d7f2284e23c3cc4ccc5920fc57e24f60da0d479d41f5b9c6ad9152903a4f37842176c6257fb1e3e0681d6d583e704c1d1b24cf616fe638106638fe9d79a0c74f0df67cb2df9d99185324ebb037d01ba0066ba947d5345cd3201b19769d438c43292f572\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bc57cbb3e1051d3a3035f77c2e375c7e3221dd472edb1a5ccaa7521849fc0ccc7568238aea9335a733d839e89ace6f2b66ef238267e0050c065c3d9553cf50cc5cd93d34fb43c3ea1c31b8ebf0b751f595a7e5e3e860b366229de4286b9d3f0267f78c6888ab3f208c55d9292079116ea0eb9f4ec2934c97149aa132c03336ea\nA = 1ffb0aac11f6d1d257ef7aa997a030e2a12b0615fb11ff04f344f6ecd550e8e77e9883c246e009af33a51204e4066ed4249950e022a61337848dae17c88317e15ade5b5499c0d7597a69a02b6c18db0f975c19c16d2167c583571e947676ae9c15be60e69d76e78329aed5fa57dc5e616795b5487f3d52bfe74b54bbf93ceda093c2e14104a6d2f017f0d200a9fc89deaa283e04b0bd9015ec67598425312868eeefeae9c996\nB = -9de2d82e25b449b8ca4b02b2d2fc0a023fc5804ea553aa84674a815bd74193a2e549070e2cfa0b90a53070646875282fdf855940905f834f5a07f073093c658cd1813fc5cd7092af592092d789ab5481bfb14b6683139646cff8eb1c5dcdb6a33113d1c97d4b587f15f972c06046730b7e712a8e3dd5f4bfd07cfae289047de31776f222d11510ab6b70a200ceeb6802d6c33f913c509b31b96e2b8dba9e25b0d2250c3b102d814683f1\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9f7f4e010370ec1d76fa83f73c80825c3b71521855fca5db06d7ed830c910d0430375bf319671f6a83bf6b57d9d53cfaaed5bc5d615c5690df0067b18791c33cb9f0ac9fa5f0473e4f4eb7840b0b660962097606b3de5744089ffb37d9c0df1123a91a5896d4deeab8aebec469b099a3a9a4f6d822030ec2fc4d11636706fd0d\nA = -7f56093243ec2399548ed95df79363e6ff09de211dfffc314b7cee526535def0f9a8eb9aa6f1736528ee7aae8be55c06645708d576111766ea33e0564c12103edd61ede3128a7a642f968eefd0d7f3768b1325c2dd910d459b15e54145a234225fd29932234e59d3ff5099ec4d5b5c6075f56382ade1101115c7b94e1e2a7bf075dec210fdaf2357c735416dd5d616335002d1cde6056bf7c478f810b78c661a3dbe6e54084bc9\nB = 4df1a6296428d06f51f31a1b0f66d0b77a04db3bb8e1b80d64da649899a1a55d4041bf0bb47d3e3936ee0f3740e1e8c2b235e1b8944d28c7d617d1f968abcde9dce10d6e3c27b2e3607d8df815f5a39da9b5569e95eee1fe5532c0a80011e7415800d8a9ec175fb1d13dad959becf04964b70dabde6d37072dc9f6d914309b850cda33a565515dd6c0181fc48bc7033b314ae0bd5872480e02ffc08dac4e3030d83b33488cf149e19b0021b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6da5fcea305cc6eb47fb17190889e6a39c339da1bea2d7c95e997fc538b4aeec8b0edf7c109faad7fb6c656420f4afa104ada7a0d3d14d3ef0fc6774b59aa2687c0b4efe7c3fc83194a89c832f7168346cadc2b1fa6fa9a23a67c91ad731b4cfb9943738c7f9951945b2eabb3743473d9c0444ade756291f53fc7641501597a2\nA = -19dfb98f9f7d20fd331ea749d2019d8367935fb75ecde45d6dabc815ab9e593e51178a72816f85aa678304e6ff3a2c24079a59aca253d76c4ac633fea1070753ce770765bce47428f8f5ae40c26a3ac91ddb551b3d575bad9a3b6fc7954acc93aad2131b78fd212fb0db7cca4195b41651a5311bbd4d8c64f1c93e6520eef8e6308e98caa1cd0d3c9b4041182cbfa131c4948257f1200b1c5351bee77ac8bc8e44680ce64ed0648f3\nB = -2736d5038c60553927f389c0650bb1355b0ce745a7dc5f52c9909039465344af910a5f6a9cc4ec130b9877c1cbb52fc08b20d672e42b853d26a02bc07eabb9e3f91399db8465b6a8b1c9f4a4b9eeeec6e9b6180f1a770c139c8f29ceced61cc7ba182884ae01d14dd85bc924391333e8ef039b586b6a0ae18db3570aa560c2b0226d5e23e7e753873637c25aeb19e74997da4f5d0755571785bebbc7dade57446e0df4cdb8df23c1003533f60a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c0265805aa8ab52da5aec06ef7cad2026fa0b18edb27b4903e3c068ca6464465e34d3f3bdb4bcc10a19441040deaf5569645f7e09b36c56631b3a6144d6206d39c9bcac53b54210db6d484cd6a2780bc68c07272de03a9bba7e51c9d86cc8883cd2e1864a2ed711d505930143c883c57545e9c40851c6df8b3314a8c9a0d201c\nA = 5622f906b077d243521325be82a43fce321412bdab1f15e4ff0c11a7066a288b7939afc01d30243c8a4150e74286611ac1ca4daf457aa23508a7af869d2d55f54f2746afaec477cd7df0d5711dd636802ae7f673b3f730236ac3899330f89cb71d48c2838322fe856d9d8b4053d9c1e66acdb5e43614ecff954dbe37c5269d7ffe00b34e682c0be3d7cf653ef212daa3d55dff92b329126636e440b0bab55f4810a2849f77c39ebb93e\nB = 1ebe0d1800b1fcfb67d7d54568e45dc604450c1dbe103ee21d48dda300c1d9b9415dcd9f5a56cf12c2ede3c862e895efb83621435377387b29b882b2acac78386895c7daa90810092bd3062a3a4867f92d54622d7f0b89b40fabc4709fd507d4002ca80de231596630c234fa418611ede0ae4a9616d570232c1b03329bad02220ef64e455c164aadc16190ce35b78060a6b117b4b0641fa64dd8e8cddb5914e7657573804e63dc7b216b1a9aa175c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 606d2b6f756548568013bdaba6e811dbae88fb01f5f36d30d15dc1e099d86bdca9fc1eb3a785034ea14cb7f4776586327d57ca5a52ea1b30f26e2a76140bbb0e930c7780673770fe22c5ed443c349510e1494ebe402f2621b1e6bde39b8691edbe5c7242efaa6634553e6af146dd40666edf4a3db5d1e7f9347fa1189c1e5168\nA = 14ea5e6fd612945c71fdb17ec44d95015773edc908a85a6645a8eb823d11226545d05b81791401cefc81ce9765eacea7a619cb482f29d38988d355ce731bc9009969b7487a3acca2d2065c1faadc5d6dd8ca1dcd3f3d4ff61d0a75ef75272e62193618f6b802f70795041de26d6ce367ba996dfb91167cb1fa16c8977f982e1718de7d60275a7f66e4ad72ee55ea06267cc4e8b08f488579825cc674b0bdfd34a01bed08b62004fda", - "15b7c\nB = -8a542280f6c8bf4d9fbc96d5bfa6ee0d16a09dffdcbfeaa2dfa1097a760dec7bc540a0b5b2020bab1eaa594117a40a9bb99c3f16fc340c262b29909608740b8e77fe4706a88dc0fc3bcd47998e88fa02f617062393978ac1bfe14235d43f3d5edbdfb9f140412f4fc2dfc05a700f47b1f0f90da7ae07ae781d9ccdbb951f19a8b8a9a7dd8a65942842cf207f3baed3a0b2f08a06ad0d9ab7ad0110346293d51ec53ff8165b925c0e7906be8b7303252\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 512220042f151479a6a8b7c743ba83366cb7733caf37164e9c823422ccbf78b0b83f426a7230f559d50bb0ed3d9486c6a6e25f4cf96c4fdcb2c861566c6a73215b6d08995a14569710cf9e54abded1d77fc7722d06fda4557a3a99862e5ce963e1be25336fb42a4629391cde3aacd47ea5f5426e7185c5df27d9136a6df26f54\nA = -4d108217b778694931088bc255d1f69cf8f5a14252156163f948ae58d58f2ed54f518177d668e795474952c930052c1bcfcae11bcd15af168ec2e881e6ddc8de257d0cff90ff3ad409bb3a080d30fdfda99078cc3ad8302a4bdd77de66ac082b40fddb3cb36c75a86bacaf60984a74a0fd575d751ed2830650d85844aba9e3f781b2dc6b515bdb8d9459b083e1aa653ef177de76282e86c99e97dae9c0b050c9e6456a051e7d99adad7be4e4\nB = 7b9079504c635655a588ac360955fceb10cdea5f3de548ca2db681da38c17a70df5798f72cf18691d14a5f400ac69fbb47e64115cf071466c54bc7077a228249209542683ba57791352ef3409f6a947865d8f234ea9d39491b5c001685487b32130bce9aeade97d9537afe3f2f87e8f3315619ef7f215a73cb724f1adca99b90912aeecdc81485c0d00a74387ea99c965118fc6a9af1163e60d1ee6a1eeb12d7c2bb9a54f747a415beb5873d616fa0eafa\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = e36899d83a143c82e19e11494ba18478c0a9497fc89fd83df38adcb6b33918645a416626409a156899c6583ab9a4426438d9c32cac54b78df579cb7b6b1feb3f39ca4a6183743a4b823082896a89f9f1722be842cb2d2ceb605f84a9f9b61cdc7e184593fc2f9ff2994fe6cc4860d255809d04ab47e154eaec9ecc807ceb298\nA = -1422272d9e91a14b38b3e81cbd9411a0cafca23addf4f33c94a1bca70603db879dd8a9c0b95f5986bcb447731219c4f9b32a1e3253b027b7963ce40279dbf4008e526adc0bd7bcb2b533392a105c6e8e1bddfdd2bde7dfa0d2e3b1c6ffa07fea07ecdb9fc828283e93b0ce4861945562478b1a56de32251b7d31f9a2309488f7cbdcc38cd6b1c951570675ef0d61e1df69fed78979dc755f160d93ab5a3e65dc2944d3333cb85aaf87a153a90fa\nB = -2424fc1e71286ce3be684a10dd885e4891b52e9009c3021d90ebcaf68b6db81130bdbb74869cbf142e0f44ae72684fc12c85abb5157987428c7812889beecfd7bb43fcac2eb6298ebf1dbcd2e70e4274841c2703b8685df18f6e5bbaa1422004797defc6ba843e77f891bbb46699a863bc1d77c5e3cab809c247e2975e8170da00fd9c8b232abc3fc6b16951ac4e6c96f9503c1ff2d6832ff9c35b2c8aa408645849c577d2b8599ef520da57fe2a9eccfcba6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4e8a59476d47ee2cd0217bae2981cf25a2c38e5f5d5c30c2d8bf95856a6e8f42429e565f1836365e550d85207246514624e7ed932d6f5802a50ff9f15d500dd84b27729c1717a3df0f2d6dfd40f0094208445193ba6500ba03fa3f4bdeaf9251aace8729b32ec3215bcfa170575e26265fe523cf44a071470e3b1547901e9227\nA = 452cfc78cb9597e67aacd4ec83e5b473ab8b7a1dcb6097fab37e25d5a6e25c69c73a6c20de0e2a744375bbfe7f612036e69c7a503255d9e17c6ec1dc6cc6f634d4c79bed4764496e5c7c026fdf9408242d3b234195e67a5681e7d7b861f58eb631ddb9aeeb0e5b3ff7a7657a7fde5975b8a9e1f643893bac47debf7918c7ef8f6d7439320dccaf63b80ec9761559078baa8e35d98fb9dc242ba83536eef7ba9901395ef02b19990d8312203df7dc1\nB = 1dc222e7a737e6d97a703fa232defc6c0a4fb2bafd247c8e547b9c474421cacb7692ec98f94be19a5e40269e1f5713d06a6d081a943dbc667bc867e481b99c55e437061cd44c4482649faf870d9347e0252ba9dbe116fb4992dc2c2a0583c1351e9e01e71e9324f5fa942322485bca93c2d95cf304028e68224fed446966073ec7326c93ae326a7a533a36e053437910418bf1761abd9c4c5ab7e6f538e9bf963903e6c80f21a0a38a683e8166e4626a8d8b743f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a4d5e9fb7f0d75ce41ffecacd2ee1e4d15f82dfd4decf5ab1bee75fb97792d0d574fee60a30b15af80bd38e6a25b1821e61628dbe456e39fea3f8a9ee6ef3d2332412be1500fada0c1728a1457656eb3e9d94c64fb2d0ac89f10f2b9ff57d73207274ae7e8c7538936cb7241615b830cc9011d4363ef88f51c7b3ed503c25179\nA = 13eeef030b3110451fcb1a258434aeb51d3dc805b38c72ef7c79d4b0e18d600e5dd28b552b59f3dda1898367ec7da5dc6d9089a585cf52002eaf8f9ec64b8d3ec50d0bef7dc3faf203c48583ec89757cfeaf888ec4a91470a6b8ec9f26a6b07f3311b4fe972cac2f2ffe47f5c11d2dca87c62680e2229120cba4de9cfce9f7f5c33af8398c07ffabac1675de1845e05a32536329647214e54e5d9216fc0cbf2730898eae19e425688bf184d16bd1d655\nB = -ea324da99252edb03f40100e528d9a5080c43be97fe4b7e03d9563ba48040d328e57d0defd4b7ffa9bef3ca0d2682aefd2a0ffca8566e755b11f2e3c6c1b707f1b9465592aba6181e583babd5c70588e7123361a8ae77d8c398e33f894ee288babea1d7eb63e2f3de469e502b5048417043c5a9a9a3eb921cea1533162e3ce9c79e6caf62bbe7e17b180b72c59b9ef5fe1a001b733d909a8278029fb4a63077ef9b3545f1159ad73dd75030aad599ea4884677e01f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2f096fb8fe2156c41ab695956f13f0fd9a084f87ea5f5b1acb6b60c62617b8d7079f4b072223ba18cde474af3942599fe070ddb0ac1a99f42b9506a2648e1b8f6106015aba0bf7a824842403bd3f4ac8b6fc4a9861bf0e8ac59be0322f0495e4b515fd579dfef273160ddf96e453f4ab663e703609c709fb1f016ca919fb26c\nA = -4212bf679cc00adb2ca502604b71dd5dab99cdfaf55ae92aee6bcf8b3b6354a384656c09eec6175a95c8cb4591ce118e783d6344525c25e5b356e45802ea3ce1fe764833132e6b7bec434e4481c9cc2986904988bd8da7dc2e31cdc481fd0e359674bbff524124bab1ba4379885a6cfc1b73d953e6d1aa1b938129d74fac9dc597c31383f2f7e02fd995f7065290a9812ba8e205316ad5bac6fc65c6c7310f1a6b033503ebfe85bf6d3851bea1b65b9c15\nB = 7ad83f97f40d5be508cb394c128764532f0aee9a108eb02840ca1c635860b6d751d5f676e8670e2f61466397e1bc68f97ea52d64b335d07aed22f20bb1ed19e3e42e4205d650e6d37714c2f80d39b111577725e3bc7ce75bd7ed5e44f8377d5fc2b97f05c3c1ed5ca1ec90ba3ff7935a25a8acbcb15fe1fc7aeaa1e444cc2f06c1e6711721d24b8969d465e4958cb87924b3e0fe99ccb371009b5b15747bf6dd5d0fb73b8fdf58d955c8773a55424a34c741406f6f904\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 909626a69c803e9acdca97c56781eb672d6fb31430a53b853f467ca26d4ae96c182d71c0212894b776c88e773acbe9602e3ca56584c39b5947724290def7dbf04c6853a108c1282def95dbd5bdc015b68daeea0ee959b35bc5af98a4ae4cc7486e627bc9432bd009b21ee9af3085f074a3ae1bca879e321018e991e7898f2897\nA = -14eb8e28dd04a159c576eb10578c24fad9eedd3d8b7560b681002a54a4bce2167de05cd061338f63c50b86327a79595a2dbfc1d3f4e76aabaf88cfedb69faf5148c61f8cfb2130511a3bf4a17d846ededd4c08f3b635182dff1854e8c4c48007af028e06f01235fc2becdb32adcb9e2058dcf8f8655624bed9915faa06be972282cfbf8530bc0cf2de5b2057df32e4a6cbc3c772feea0a511cfe3408a6dab0e2714fc4cf15602ba0da03bf0016f1f3f5ddfe1\nB = -388da160568aef9f82fc16f48a22e8d7aeac99121cfac9b748c815e5d3a823b673ddcd20c1168f98ba204df5e52535f61b224fc0374092f8c834321949fa0a812b5e65c492fd9fe8246b74143a943bcdbeba16024e311d673357a3dd3eaef9ae3a72bb06e03e34e091cbe5b6a9eb9fa3d7f36c03baa5c3e242f2c186b58db5dddbd73f6aa54aae027529b8f8f0a536b9b283ab08247b9977a2ac2d0d9f162ad03a2fe247d2c589b1a2d14b5f90d5b9c0a95918ea956e261b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 128e8844a2f04704a4a60cd33e85cb7ad373ff683abd167894a35a1d", - "af947f504c0abd7a614e293ce10797a5330147c88c4d5e1dad1bdbeaf74095e3f5a515f2af68b7bc11ee1f53b493133905b654318dcfe73118ef1931eac47deb6c4958406b704ce027d9b027803eb8e639b52d5983094b8ff4b54e86a7dc6ea169ff1af4\nA = 75e6b045aa44dd9b8f4b434dd4bb1346fcf558a5e96b00fef9b6cfaca72fe8b1672edc2a64beee8b959683b1861138b297629b44a0caec6bad2ac05665728379cffaf66a129f0ba40aab7c6b1c3fbdabaabc87ed3dd580ba80ec7ee765e9a8fbe845c0d207eee7a1a3a0c39650c75ccb6bcdae2e0d5149991dc3bf899ae9b7626a2baa17b168b260d82fba84a12f10e09234035e08b730cfc230f0d2651c03e34d4952fca6409b5c6ea5d8791c90466bdc4adf2\nB = 102fc193633b0e60a48dcc17aa76f3e52cbbd1012f179736a0ba7a102f8dfadaf434063b0ed1b1528a018b349eaf192fe62f868b538cddd7e8e6fd98b93147727d58561517b2836e4a373bb31fc8d5e42d16126ed80b880c1a37940c138fc1f7255ee0b7fd39b1b799c34e5178580cdc076ef3fbff65fdff7497398fb1cac75e5c09cc7df1168a20f88a16e7b3ac78091a90f1169bccd48c0d06b4707ab79b741a168deae5ced5d48bb5f5dd3f465e43c82b9db7edab24569b2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9aa9699d1e5d2c6acb21e31890c1899f30a925b834adb5b8bc8cce83a1718944a2c90faa71b34379a21340457478c0c43121dbd65d62e290eda2ba6230bce4e6f18555a1380c7c95c1700793157f7c1cbabeb09460ca28dc596bb17851ab2ba6dc6bf311ea69bdb7fa8eb78df74adf171d4677a154b8536f8104d919bdd58648\nA = 157fb9e1b38f288db78a1a0e22fdd9f48a59779487a9ada2774a094d34536b85993e7b9ab6e24f081c4cdfb64a82271100a054169e4f1c24e3957ae9aa8300e85eb2a45a6d5987eed4f0fba6fe8557cbf6128e018c5f9df028131bbba6c544b2c6312aeddc71405f0e4ce648fbab9e5d51685949408e4ccbe06fe501a36fc13ee65c31f062313135054b7679eef45964c77f5a1556ac09b11c496d0ba8c6057e283bdaebb4e6d9e5c557d975745f9f98a288d5bbe4\nB = -82cb6334479bd997c771e894cac1ead87dcbaf8f5006be5c70ad48ef94303137bdc45f261af91a201b276a17d884a56ff27af7dc06cc5b7b9c94f7c4d4a36f68f8d309c477b4969a6e7cd1b2afab9deec06555cb753d8a0eb00965359ef865a84bfa87b815a42b2050e1635d5ae5e3743c007bd79e820aa37a968702a960fafbddecebe63f022553cadd7a4d4fb27b4dcb981e8b490e80bbbf13af8c4412d158775db71f5fbc9986e7b8a8f9299574abf7bdf9ce7544e8c4e85bc\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 46e401989fbcde9d830dc6e3c42768999f153d44d270d4805c5beefb470bc1e82706aa7173b359763c5e15d146eca91a32a36f0a80802871933cc7f2ed15a5472988849a2d2f57543345b531538db57ab9bcbfbe787efb0a82e61baa505aad628df5f9e881dababb35bc2decff267eaed3d3671757ae1764ec5163b792b4db3a\nA = -590c16ea2cf7fa7f63b5cf74804333f22fd2d0e1da7d226da8425abad2b39a4672fcebcf5cc15d220b0ecfeec09665e682fff0140f16889f7a6ade9ec11aae3fa3a369b3fc133babe52e42b7a8bb9a24777521f4d9e0efe7d7977dced9e40784c24d2c6056b3b668ada7856da71af73d2dd33d2e481ddf40999d86a6e236d0d73f31a67c52cc8b38203bb2840c0b92c2612ffe5fdb6be87f9a787d70b3dd506f9a63d144db3417495f0a48523c812d14a89710d95bc6\nB = 5a2865cf2254710a1a51ee3056b0c1f6c5f77d22d7aa8f939e6f48ecec529a169e630c554bbe682a8c4de9ce4daca77a278d7e752cb678141ddefa75ba42e661885a82ab55d699414ffeb75802cb8f4e7583bec8a7ab58803b378bb60fd46f476ea490c9aaba568ec17f3a6afdd6f20ec54a512f7aaf62d2f941e35b4b72dea77095e863dcb38bcaf8777707c1dd437ef2ac6b6a8b2b832f80ad2a6d6f279c053d02058b1a657a1cf5b6b269e15d29087b0cfc0c2d4c3fbf32a167a3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1c9649f4540556ae82ffd71b2c71ea8588aeb845c50dab595db9f8faa01a26c809d30d8433b6c0add465e164cda2b6723c942ee87241eb7baf9944cae08babd8e22a0eaf35c09e9efdfb9f8bfa65d53ee6eb23fcbe1d12a66ae05e7592ed788b231b000f895d098a24febcfa4372d249575926a5faf966072f29a62a401ec51c\nA = -1bc9ae5fc2f6a3f1274584bac1e145f02c5e8c4779f4df15e98dd34344c988c1437ee4428485a09090d81b18606a6ea5c1b9136872ab5b37373fbffbb5b3fa8fbeca1e112b9f1643658c2f38b9548cd8f0f271779ce0acad403177057ea0a2af2e7435109879941fbf463488a2522b831b95c1cff21d2d816d70c25156369dbcf04a0e28e1d746afb8a77713703fefa512816fe73e203bb4c3428efe09b946b750199bd7a03d30feb90230c219a103ad4528cbe0de1e5f6\nB = -39cae179d955049f830867d4115d3bae25127c945b1fa0c16fa850e8fd77c1b3b9b7916b9983c1659b7cee77b7dc72abfff1c56681b7931c5e58cfe4f1bf0168ae32df0df8f652223885717a98f858a497b1a4be62a2215c39316c34451b0d957791f49139921d9ac8041899b8fdd5d3d443547a26ddf5748147e4c3e93f5043ede42f38a9baa628df65d3d6148ac2ce182056700f0f94029be05d3ea3a218b40f65a87b4baf097fce107c080de24880259f1046175db1297016af76d94\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9fcf6a47addfa336557749821a88ccd2573a5ce2c3094a17d9a29b33e043bea165499e89fd2c939f17a670694aff05e9af46836b62c96e597c83681092d63ab9d6e22751aa8fd4b9ea94a90a373876ef0f6514304a495edb5ca1795c9ade7965c70f9aa92f8ea460ccb670e9a62c81e9c\nA = 71b93fbad39b1c2755f2051ff7d532d59c985756410d58aed3947d6ae737ace5aadc35e7e0d29c684b9d4bec9c0fa277996bb30230f70431cb7b905\nB = 167be8381a3392dd4df62e150025e13b388bf366922ba8632614928922cc290772135857d1b5234d51c27862cb1a055c1b86260b6ec\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2ba940fc5165c6c5f7f4cb55cd89d1d5f59e90e78730bd66fb120a814514784879dc43ad4f355030ddb3486a59bc34b601474978a94ddbceafdc0ee23cb18708bdbd824d37cc32577802ac6057fef29a71f168e816309fc80cc46f251e7289c6a57fd222d5868263360af63dd73e7c8b1dd6b3f3b6939849580b9231940a4d\nA = 1220ac4bde4feca135268550ddc79d8b05ff72f483b39f77436f348c4f5360c22c598f7dfb76697bf6d2ae86c68e90748b8b729b25f932b2e5fd33f3b5\nB = -bfee56cd412318cd62e7b6cc49217345d3a94e7fbf6fa19053fa685efbc0f8b320b7e43883189396781c49371dffe7d126c032d1ae4b6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2ba940fc5165c6c5f7bcac0e449b64801e75134a390f120acc58cbee43888f50d07f7aa6dc2b33643c025cf745434d20eb1aeda8fcee5fa3fa5baf10d67c21390297857aa50bbcc4a29a6b10885f97fea60f1b88fc72512c111b938142ee8d67545efe386622162e8fd50418b09769b8c22efe54fdacd652580d609f0528bf\nA = -7bc53f6f2e78628678ebc8e35ae4905caeec61acca5c64fdf595689cf005bde2265cd43172802fc133dafd933d7b48def44256868d202727a4aa6c0cde66\nB = 74147c93e729707111d0d531b1c135453f3e59f63a7e082b43dceb8b16cc5debdb6d7c0ce0c00ec9b5ca51e7673e411c3cab34938124db6a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 43c47d7e319c32a758360dd726a1d91e2cf5c57f73cdf9ad2040e61a9c282a2962d96d300e04288461eb1ed37df19e6b88f104a250f9885898740f6487b081515314e0a217df2d4345d3cf81eabb2bfb346b634b9c251624748f6e9407cb677aff4c53fcf42cc027de267e6ec011e14bc7f3bc6666f693d21\nA = -1e6ce0b44105047d0da0eca7b936980267db41d41319dd5315889fe8fa2329023d7cf54f71ee179b5bfedf442cdad1920d311966f7175cbb953bb42ee105393\nB = -23a330c7e06cdef4b6b121d15a9c0bc774eb5e432e72d04c5f03a0c588e55e010b61f57c03c51edb1211685d8dfd2a35393091fd0e3ad2304fb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 768293c84c431b9c8dc6e538ca3f856c60ae5e1aaf42325865418b7bed16c7fc2589968319cf41cb370657c8edc7b969", - "de10e0566b64ec796470b630e22477e7aafb38e99b6012f100c9d23d5517d486e3cab1fc60c1568c0228c9b55d2d77d23b1351fe37ad4fbf9c07f29330a539de4a32709d043dfc9e21aa1a\nA = 6bbaeec78b6a41818b7eec42fa3be7d639dfd86fbace2bc14e0369dba6dd3f04ede8b808743d809f43f70f1146dfdb1d649546441919e27f1f7a9760da4a3b152\nB = 1199dc2f52868a0cf440f6666b576541c7aec1e9cee14c1d22010ab0f53fe8bbf3029c639ff78d89dce82de85fd8eda4e67395d435df60158623c5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2b90afbdafa02ce68d537ae807b4e7f3e05a66b20b84cff309941fc3150f99d083841ddaf6f19f5a76886ad5d853c73051a0457e95eeb0fe3776a084a027ee77d14f3825713a59622ea163a679cff904db33bf6ab23b06eb4b31f4e34fb122c8c170321164439db783e7bec1c265eed33f33bd9cb6d1611c00aa18a9b4b90d\nA = 1c4821515167f7073d4b7cfa318ead1da1131499c12497447846caa84176a9d4af576fe549fd8b0f77bf8dbebf6c395f84dffd40400101bf28b1dda0bbdcc5da255e\nB = -de60cd639044e863c6a49c73213dbc2ca84e4225aefa5f880e829f2d9cb48ae92e3f2680c462ac697dc34da38f65fcdc1b4d8c3c99e8cbe29660b539\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 33e8e8e193b4b99d8bb382c29c1fc5403190d7654f43cd77e28d1bf77bc3a728dde9de9a89c6522ebc7222d25f46833fd1753a44275b04485c77b675d816090280b3541ca61bfa33921a79f7286830131d6eba13acc46cc2c449b3a359f1cb49d67a4d0cc1245f3f8b59b1684aa0c3ff1c928b8e880a3375ed811dffc991fd1d\nA = -50ff3e00feeb2efc6df6387d6409a622b7a8297a717b8d94d0dc41c6ec6f29a8455c3580019349660b31dea1e4f66b74147de93535e671c853b604ba06a9b62d34646c\nB = 49ff858c7081392defc3ba12ea8869fd61188ff15d9339be72657b00530b851de53b1fcbe16034816e73251fe1ec97bcecd8bccc470373974287ca328af\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2c88dc40414969e8b614bf8db05fbc38fb2b7ce144d7e707f9f8eca40ae2309c1fc67e713a8da5fbb20e808ad20aeb369cb72a77fd285e38a7895ec0fc795ade4ef1f1680f3a3b3cee4569cc9d5e699984daab3385815d2e515ba5d67d21dd1defc12ca81bc8ea645f8f8d103b4a0a9cdc92eb50690c07a037df274bbd5217e4\nA = -167ee0fa8e5d8b569d7848b068df06f6baed80f6fa6a442f9d11d9712622b512249b92c7ccb821ac751fe4ec0a7a47e04ea5571c7cb45a7985749ecdd87f0c0faea01d232\nB = -2207fd8dbf2b8e9a5e3cc515479cde241dd3671803f9fbf7859459ac66705be055fa759c85631ed2a61139657eee7eb08fd963b49e33666e60b7e75dd26b5d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 674885ca3ef617a53eaedb9564cf96bcde131760ac541a81f4b25c174a6fe1444c2c206f7171e343e1bb43f81610162994c497419e75aaa25b664c122ed2b27640b45bf646fc5da1703fbf1cc66e10a3c306eb69ae5f937081a1a18dfc8db376ea18f4c1c499109b0cf8806eb32cb1f28985da790047bd7b32c1f67bffb9761\nA = 413cbcbbb5851a4ae12555801f7f80ccd888bb82ef1b5c31b99e1901d7e0ab91ee489c84044bc21fa2010f11aac21d0531fac09feb482fda579cb9f224c3149dd6249b0225a\nB = 1b6bfea70f1d80350eeb45f9a5cebda954d72cf5cd27a299ef5a42e1ed0b50a541d1657b70e50b0cab69b22e31d0944fd735957b1ff764865d9385af302bb802b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8d74ba5fdc67733ced4d468f6eb6ec4c1ebd79c97682c1d4daa06105788ed9c5144992e555d903804d7ed0dd9b29ef2648568ab7ff462a03e0bceb5482485afc3b91448fcfeba435dc587db6f3a022428d37fa0e85392d0e48e7d4ed6b21253084e653da8175587b3b709e28426cddfec8d9dc582d4ac2f3d540305c0fe17327\nA = 17c0b7f0e2cdf316e4d32f040e26d41dbde1e6689d98f0652da1c380daf5dfeb6a511b72d82f1b32d3852e9aa2f594be10776a8fc89a8a35c160e8e41b42a06a342fa1c309fd82\nB = -d7b7701340c5a358455ca5fa314ad83860d9f765978ff652d7f542de2e123bb976930b8fe84b9608648324450d8ed2bac4e44f2fc71711ae813cd8793af8d3796e8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 57e60f79b4e156ccec4c253e70df8d86e4aef326150d612a5ac4dc285761e88ede412d28d9dfa5a6f5c073d3c91a65ba9c86067d81f296935f0d0ebd2af82e7f6b5b336422429cc3b8427fd8d3f5a6fe936f4208362632093bdd3cec1aa8f4b176d260f605caf4a12cc011f3d1b76135ac2507346674e41673eb16c0f55d8010\nA = -4f1568c207a9ec970b5c26f068f3cc8019e8cb483525d251cd2919b368d072ac8f40017a19fc7437cf88e927c9e7d6f539ee84865f0af24be0d6d98fb33d74e3e0d28020c00bcd61\nB = 723db98a78f42aa45496f31cf78695583526d25e167da48ec310e447ad3540be2636813a2c2f7b8c622795ac451992e91bb8e43e5737f0dd95623282e729d815b08ed8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 237eb5726e2c628a515104bafd44348dbf099569815784eca5d6a415d3c12421c8c70fee23d6d82f7b5b136b70ffed3b6d9e98cb47854e79239d96c26f2ec955e4ea8dabc29a1b0765c9b7af6ef09ca673d1ee21c680e4b8cfebf47bbc74c993d017ead6cb6f3319ce4de9e9765cdb3ed8fcc57a1b153327e1a6a965e5dfa89\nA = -1fd1f634685eb1470dd9080529a891253a28a0b31e15c662733e20d43fc4cd71f4cfe83c3774adf8293a0fc3bd806d0b31b61c6ed0b4414ccdb91e2994e22797e5771c63defcc0887f1\nB = -3ec0478afdf54c949a097ca411be41f931acb750ef4f0ce97d0f0fc77cf15970cfbe24b170aa332de04836b7a0e6c5d456814182d27c8310d5fb662a818bc421587d95fc5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f1d500443fc4f4b86e7ec93e4d0dfd3faabda35a6dd31445021928373be14c37fec369ce80ebcb77aff2151b7ea94d21592da1823ebfa0af196f286d7a69ea54799573bdcd4d09ca4f33b8a3a93b35de5ff7f65099d59367914f1c79440b471ced6773b0802bd8ca99cf531b62892eb1e78d67f8210592208859b0aa1754b14\nA = 572de2984fe2ed0d5ebb5bc3f62b197fd592795d91cb16b48a0c898991ee3e884e5870b92405f248036ef9b3898c5ee6100a09ede5a48bf7edf3a067e4fc77e7e6bf6a6e3d4f538e3d66f\nB = 12c379402b18a34dc8b80c0dcd25be16c99d6f76d5d64b6050b90910cce594bc022794640735710c7ded857ebd44fe5b2e51574a2296f7d7a61b59c0123051bf2ba4a168cf8f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4001c734e1391a88640007893f167eb79ef61e4717d5eb14b8d80c25ed59c753be63fc8e54bdaded22c9c7d3e49753eb49efa010439807dba0d90ec4f9b498aa97f109af542bb41922936223213ddedac4d0fad8f1446498f4228b758aafdf1d9692f59029c76ca2832125ba50e811cb95f2b982a7a4d87b4726e6dd8b1963fe\nA = 16792909716b581a936287d0a8550a1f3e840935f0f3ddca75aa32e3489269b078fd19a16f8d6b2326eebaf46da76e90890c0ead3b35689bfda8c1ead17a4f672588f982cfd3da2c2b9bdad9\nB = -95ab2c47f85001aa852d6999f29644a6a55f9e4e12bf905f911f90d29cd1e4fa4fc9d1a2aa6c215bcb5c5643561499aab8f2678fdc5fa9c6ec138aeb2d62f635c45f239e46b0fa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1bfad44b58d3f8bc987116d4cc7ac98f89f838a8712d81d726189e9e1469cf46fe04675dc0b82e6e556b02c350ef4e30ec6203c7f1df937ea80f435af7c10f48538fe7755ba78993f304e64ca0d783b0f46f61bd14fd3fd30768f233c59018ce911a94b495f58e", - "b96438e416ca3c7eba5b1bca9dea5a770c1d2d9f2f62f821e5\nA = -78a6a6ef40e443c52036e75f0b35938d632bd45aebf45a1fff5c2e1b6f601a57382b9a82c3e8b2984e643eb1570cd83f3a6be6daac567ddf9f37bd96785662bc3cfee6f47503d239c77781a8df\nB = 4920f870cf9f371050e64a419ebe07ac92dd3525b41e8ecf6939a267e1ba853d54862dfc95dd21b3526eb0a0a7a7f8fb67df2e9472dbec81e15cb13266257177c5f2b92fced4cea5d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6b0b84505907a5ca37abeff9a5ba169975792c69b5751d9845c0f09dea833fb679c8dfbf3895bc470529e0cc736c9b4a0d08b75d709a1d04525ae583c5ba082d3bca1355055c7bb674aa1b92689cfdec4dbac84a96e81c855280e417f60e7e4931ef4f428420c0b85d2cd11c1030a47788d6ee6af0a76b5364fcf23b270e9d4f\nA = -143d843e3b12431fa0d873815a757a214cf731c298db61ab13cb87fe78b0a6184bd1fdcfec0c7661b10775b4ee2c815dede0ed497977c9ec5154f7b24a8a786501ddb8dd257bea51b9fd9401ff760\nB = -25d4da7b64f439987eacbde66abadf0da7c1653c1c1c6d9b2092351fbc714a20d2d7ad8093209da371150b69b3602480595533ecc1f3c5005a8ead10732272246d8cdfbab87c49e65223\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6bce40524278ce242b0b5292d27751a3dc414f962d9c1cacb45fa3ee693ac6890d2ff1647abe578c40ea8d4b326a2e0e2fa7cdec28fe2da089338b5fed91c4277cc5be37537eec2f17edbf48a45fbe38f15c58c3e733d408d001262dbd40c9d246c323e7978df4fb7207aa9270a12921743cee2a483e7e71b221b09a6b2c667a\nA = 402671b0cfe14655bc650bd35dd0c36ce7f65de274a0cc4b708c6f6c3e84c2125ab2430e702421904950b29aa8a03b049910305127890457cd0cc97a3e05df67f29d28b0452969986959df02f59d207\nB = 1648c29205f19fe4c646eb62e8ae9b65260c2cb8424a526423c6bc04ed55870cefef9b8ba808f8ed2e1ab170e2e411f68b934abb1a22776969f79f9420f8bcbef28417582942e26646af60a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 40db38dcdc201648da555f1062bbbb92c632c29b66902eabf90d98dec69ab3f3b28e60cad1571e7246f4c9e6aa62ad26a6d0bc08598c7a8571fa830cae4c2875c5c95a59f3295f998681edba7749b7e38cbece8887a7823b4752165e1a897e638836d408f439f009d0fb6c196e83e83ca3289d2bd0f0eb36b721331e4f9f80fd\nA = 14361ace8ec5223bf0165b78913b77ef921b7089bb5e28891d120bd3db6513ddc90404a4e6cd027f9b51fbc02e80d376d59e1f2b043954199ef8218bf26cacdc5e749f668ad3b4ab35cd796f94c06307e6\nB = -851a39d8b0101fdb22ea9e367286e572dd132b8a77a6a14dd0e995131467aee898230f37dc6224e35bed2eaf459aae579181a161450bd7ebe6b62ea7154a8a0ab590ca4a6c2f05531c4e24650\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4b085796665458b798f824d1c1a88c23ecca456fb88713b433228ca8735141a616633ccec4bc53ea4f6e0c74e4aab6fece2e4cc4c4efb479638cf54caf55d4addf75908076f5fb487ed00d540e5b984acb8f81cae3ef51db926a06382a288092b352793de721c23c371fd0ce7a789486b2e8b867d35f47b5daac2d339d22dbde\nA = -511565611538828ff7dbc45c273fe46f4f5105d41ccf5dd343b41e9dc579429e56a9cefc54657ef0422960d1375b72411a5cc93ffa323455e006e242580358d6cfb641f46b9c36fa777a613b17dd4a187454\nB = 4f22597947638b9a9e9b9b7c2a8d37f77259f1bb1c7db65003b6e1a1c807469c84c89a75b80bbe0324fc3aeefaedc6ad9c0d9e470dac9c30bc48f6abbbdce9547ad7624f0ce9ff3cb6be23e47bc7\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b90a57349ea94ea818207fe15c164f9d3530c7cdffcae178557274552f79c4ab56acd78033a570bd6c3e45789704ef0b0ef586594fe4cae3ccfbf9ceef46e769589b084adcee3ef8345375b7103232465b991273df724964248737d5eccbac558e35e4190112571d3e7c291baa7aa8b1800121bd573b8419f627c0091e1bba8\nA = -170cc62ad57094d307ce1b317ae5e825c2f2e317ad6060437afa105501caea00dc9a86af8729e2f3c3a854387dc3ba368c0a84aab1a527ab34fe27b0a69bc71c728cca87be728457c65eea7d7538ef3aa282615\nB = -3d9da1377a88f647de57ade46dc7caf71b4f42bbfaa5e77f16cfcc90f00b5d3e9e9d82355104c7cd0db4c1dac0496be3aa35706cfc0a30a1329755faa439694e8e9b41fba8f1ebb46140818c7008e27\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4cd4da762c7576d582572d3427abc4b4297f740705fc14a32b46347541b152d0d1e3a11f27213badcea1e2009e34a63350c7a59e4d43654b28298d2757d6b54c4d82f580e98de4230cd119ba350416452cd4b8adff29b9f35ae0c533f666cfed716838e2b91941dfbea8d6a978a369d5f27554ef411f15e5a89850655d7f3f5a\nA = 4f4a28af27b926d8ac347503d6ac0bfec388a6c0b38a577501c3ca4aa709c69601824ddeb5eba4d9e437a97f3e4477e1487d5ce7b4a35b90fb863657a5b2d901bb8c3c838db40b89b495ee9875e8eee607d7b8013\nB = 13ca192603bc8b2da29dae67159e4f8d32f351a503434ed9e4e24f74abb5908ef7da80781c71b1a5ce64fefd13a16cc1eab05a370bfba2a97e6cf90cfe98d3a487ba72dde0762c36c10e1da175f1c1b5fc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3812e9e835ae355fdf328b29ed8b86dc3f6895e379b8b5d65a5de41eab5fb20ad3e2290c8ca69f9500248ff883d9715f59d0db6257d13c5cd612211bb1fb99867161daffc77968bdffc1fe48bcde0fcce02ca93975b3cd9e93b56974ab4beb59582c3d0ef2a65957f701549f8bf858de0c5bc98af3e5722f1450de391876a2d9\nA = 14ca6101af00d67139b985ac9f149accc260336237dd2dee802b5cc6e506e217b74c1a007ec10c20012f071ddad34e7407012669109ec1f385566ff04cf1a1ab7562353c0af1ba1be0baaef920a188c60db27970f64d\nB = -94b683326e9de19e414f653aeb2cb4bd7b17e76a23de6a4d91c43d717a35e08f2155b444a9549dfd01a8aec4dc901ea9f629f16bafd2c84828b12d2f63dc154323eb2d54938895ec4c9efbcaaede274fd4ab\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5ad7411cef0581b2e675d03b0ecb9969102a283eba5e779bdcbb7646d94e843083a07269c932d18b973b57abe54eaaad0aa76cf7b61f30505a263bc95aa063efb264ae829eb1d1d5f7d380a0b4db59839de9ae6230ba51901e71b3e3d59e8c34a79678e751c8b7ab139123bdb2f04d90a18ed81d2046ae86da1a73c8dae4fc4f\nA = -469f61cbff01f0e4124ba69a860ec6dbc75cd758dd8ac7cbfed97645b16488a329adee62d1a66e90ee4212569d56d58b61676262f49dcb68296bbe5d8e23853e3fefe8a304710cea568ca65c183531a992ec5b4d82e226\nB = 4a0d48e31cb8c24a3b2c9c95fd19edbe46823032ef4c97fe65d0a30d5c2cad7a4fbbe89e0ebc9940ed9f9ccb8ab18bac269759a9740a7985809d0f38259e680f0703febe7fa012d1ded47f0cace4a133f59a721\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b2953981db406ebc544c39dfeb08a8b089064533221536c7fa2bf2a7a0d3a1192859b7dc0ea5036eeab5aa371e3e0070c3980433adb3e3a5202ff257bb546bcb9550423201a35501fd717ed4c0016eb3a675ed399340bac7f058a04e69c1774590fe747ffb9c27e78ba50fcee30ce533a1659fc49dc080a60f21357a6265d24\nA = -122621d97f42b65b060c84df3f0c0da097b5e240731b77a37bb9471e7e398b242db6f1b5e25062a9bed702860ccf6aaf386c1d6fcf60fc31b8c190d3486949c5772b9e621b863a7cbf29449ddd68b7e0c21e669492e58e94a\nB = -33978406dd30ec2b192c416e422428683deac210017cac9e4355e8446d6969295b0fbaa8cabc92c1fc0068da70efa047f938a419bac160ed6f794a9f69f53a88648c9725610d5f309b652f5462bd3011cf68ea859b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099", - "c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2104dfef151526e072c09a4a277eb981a035379de3b1a55a88cb060681706f26131c388f5572c5646826b119c85ed450207f32733487e3c4e1e9d701a65058c4b4ef0cd1db090495643038229ed177b54695ac32110619038f1c1cece14faa693d88476e3d70329b0084d0ba5d547bbaa5b59ba1ce1fad5aa2f1c11a75bc7c0\nA = 7b79e6f1330fefffaf8521089c3348593e40ab7e8d4da3d4346571b43b12740958336580afd13619be3dc2d42eefd9e30599405da3e32e7f3a5655ece8b77a367059668021aa092460de75e627526da08e6206b0f8f539ef40e\nB = 156e234931907c0c0970c1fe6bd4b24225ed94d5f5b1be4693c8e141e9a6032425b4a47b6eac6265afbeb9d796eb230efa707d5ac4a73808225181cf814b319142e9d175ac461c75e6d479bb6bea53954bb981062eb16\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2a392c5fc96c29df2f5ae9eaf76e7d981dc1e2f3b47b43a98eaf556a9465ae8727c622188123c64658053ec50c25e54ac5c6c8bc279b134d326e911f14c873357647866eccb4f9038ed0cef5082c2058ebd71e1619f7c8f8f2fb80871ebbca3fbfb7845bd855d307d2efd853f1bfd467fbe030862f165e53a9cfa633d0d3fa23\nA = 1e0430e7cf15173d00592037e83e717c90d7dab4f54a5b2f0f5772762fb5f56bc0b2a53ec1bc3b960afc35e7b043f9d85d0af6c29288486af3e186e52bae6300b58917647231b40a12648cc8c020a797683a9bd7ff34eb6d41b928\nB = -e08372fc766eba6e0ef55a9149d700b503e2e3f978c8a397912e2735d5bcff69c461561ac0822c44160c7c1bbf722df421b74beada57462ac54a9bdcdb42d6a27b86413036ed2282abf62800fb2518a32a4a135bc948053\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2608f68632ef14dc3979725c8cf1a0db10a1651f17d91247edfae9935b53f6364d233b030eb99871a87b7bd876ab2cfd5a643387a7af9d337e81770db04a14f4f8dbda2cff604838c9af9a31e8dccf9277d453176589ba33abf77855b9501e63370b2e6cd22831e1e70ff1815302c0a026c70042957d08e74dfaff940a91a7b9\nA = -5d3568858c05a15bc9777af949eb01d33dfdba58439fb3f7af2ba792efe8e78b16d7fbc2a303a4c4c4be7c9d43f57405e88be54d6ab55268a4739945ef582921d2877019659dadbc76e0939f4b2cfbc91e5356ba2ed531526ed5b9b3\nB = 47f81f65ea1af04f702757c02a175a299b23cd8ad551fdb67020c50cbb4110b5371dc5790b12484e9ce647eeb24c0220a5e62aaec3461a9dcdaf1a22814b6f22d66372cc5ee31944bef33469f905458c172ec7871d9dc9c301\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5735109bd21d31b5f54e9221bbed78c54cf387e39c13d31557e8173e173f786b2d2f1acf3966c3bf4552fe9bc802d0868a5a7632404cb91609a7a45fe0fb83fea8d83b0319666c1b0ac520169c15be708343359447f2fd37960c1e96d32799ac9394e839b391f59dd347acfb79bcc4e34e76490880d163ac97ee69e3a0a6e68f\nA = -175011349a0a1ceba11756bd528f2bd631c106e709aab223032d08d52d7d6724e8c5b055b6f97b48261f4860eae297badc1214cdae9b2500a7a47b4b777dd7b8f1006757754ff1143b637d2a3adc555f38eafbd5478cde0b04e5f46d3f0\nB = -2aa7f75d6801b04ea9f690aa0c5448906595fd28b53775059c01efe54b463f1d87c9fb4b39cb038e770f99bb995a2118b86ff8d004bd964e958c2af82becf362fb0b927c671cc3bd7185990419d26a827a2d81bbc0126e1029556\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3b4ad19b75e1301d19b57ba9b68e0666c28c7c5c99df1d5fbbe0685dc1d3489ff39c919222719c5d8b7ce2d7ff967730d776a02b36a86064ed66a02011bab82eb575390f85f0104715f6e4954a1bb28518450182a8ef58af35d00e2fe417f07ba25dd9c85e00c3451082becd22e3aa0c9bcedaa96e6423c7df6c375b4c799c65\nA = 58e1ce4a9b512eb0632b02cf1207936d6707b802140540fbcbbdd712e5ac1426b4f36e74a9a9ddc812e572855d4fe4fca8a0de6644226f5698fb46a5f2a479dfc8b588aa8e02ddb15acdc79ed3d17143e290f1317274f425b869df54a4807\nB = 14e341cbb5f5a7f3b4dd864172b82ceed2887fcf20aae7d0598b3d8afafd2f10c27bc7456c1488abb570be3df04f43d892dc6a8dbe7621f55bccb0ee3acb1ade989a510b4e0cbe29b6b93968f323f0016d87944c908824d249769f8b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7fe0bbbccad6032069b1a335b3f2dac16089051cd9321f903181fad23be6853e2d209958e8c48e008be94a62c6206b34b4e994ca08b8f24a2df0e6394ea65b3b7aadb3bc43d04dc9d35a77e673c4476dedefd4568b4ade5d16f9d89486f3d5ed0566b1eb428cb0b688f10fe3901037744f278385754fca481f937cb630f60308\nA = 1cc0e3ed58090db55063c9ba11401636f89262d6ec096d361f448496e05181c5f7f2604333f26d511c13534618e90637adc807d622097f7eabfc03266135cb626e1bad20997e72da71bf2b3f65a4973dc27d2a594b1fd96b7bf7ec14b9e4b983\nB = -87871b2058d33cb67d83b6a56ab27839c6a6c771bd94e55f200a1257f2c737e39c4a0403fa410ea64e8f442d300df1c19c2f03d07fb74d94f86d26814fca23d4cd2cd3718252cf0cd8a0e36726f6e68827a1dab6bbb1d23b884381c702\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 35d7ac5cbc7e6c262ffa41be168b02a3bde9e112c512d1f68421d705ea34461ce3e0dafde67f44d44cf31d91b38d4d5f2fbf8c6c6a44ec3ed0298dd58f3d45c04346c11e57229dc3d2cdfea02c802732d9a811d7be5e81094d72172cd04caaa3c9d55a951c09f454f42add6e89e2d8a98e124aac86379df377606e7af9bc6baa\nA = -4ee01518f6581c560a186fa05c6f4bc26809c4822cc74a0bb74d5a6b0a368aa9bd0108f26113443422b8c589084ad49f919a9e7821d99127bb210670e732b7cdf610e464e300a39d3dfa7c82f90cf00ce329bc6763d7b1d4224a020095112fefa7\nB = 72dc8973f7af7122a05c90df190bbf1e39abca908c197590dc7ac41fd0712f48f838ca62a72a177a293ee6b2afa7a10c21e7993347c3df4f161a5641ff62ba123999bf1eabef29ec0d33ed0919818f4b7c35b5f41e654759fc9abdc0f80e7\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5d83a9b34631dd6c63c05a0c012adf97b4d0f20f61907e1c2145330211e9a7e38128517b058e0a85e993c385068d1cec768deb814bea1323dbd333de091ad2cad72431f20c1e70ff7e1b119768ba44e14292c38b88dae7e55ac9e10ff98e9bcd5f0ac05af499196b4be0c6222d1a63227ee895fa6a8221a4a182a1323183cd7f\nA = -17b3e0c9288be15fda58c8fd228216bc466731d631218a7ddf1d2c9cc858c0219cb0757d3b680bca1b1964eb15031b5b9d761a8bcbd160db89be339067a2ea35e1ac3cfed701912a17ef9ea03999d92e3592e893183ddc05cbb98a656983b54590c72\nB = -269f96a4634eb37cf8a6608408128587ba45958405a29827d0d03d34816fcb1a2297f1319485439d3e8594532545086efbe4d21d31d30e2daf09b74fa8cb27df54e8f9f993630cd9a292c977eee70887158bd3fa3cfef321ef900a0598ac8cea\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7fc1c65eade94d9de7440eb8dfaecf1004905135efd4f98257c3295b1e76ccf1e2ab6808d158d360b7419c6210c50efe960610973d9ae855c72ec0e81d423e5863c80b542ad455700d2d0dee5fc403dc01eab460c24687401cf6a3179642e59f2a30268df95fa80dcdac230702352bbf6b60acb9ff5d45c5b09a3403b954d173\nA = 7906bd8d3bebb1303c1df1fea0b2503b0abe9c69b4f4f5bd01eec9e314788cb7d44b93428adbcef570477e8ecac2a64822e481bdf520fc381e1bb0b2cdae2fe94e484cef5236dd524e4dc364b72f4c06d57f29dd3c5079e532b1ab1e71dd6a65b3362df\nB = 1479ef2807b9c23c094d0416f513894cc92e023b134f44a5333360dbbe98b8161ab899302f4fa11b470b97dca0c4e8ab7ae47e5fd0962834e6cc1763618193f4ee027f667368da580c623080de137b5869c3081128e6081b9d5e2dbafd791773242\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde", - "2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 730c04094b1ce944588e8291f7e6cf763c70b79cf362dc8a1bc63bb8790cd4cfe4eb51cf15a45a8464d69ddc3e1b9383cfbfd643f317108cd9ca6a6eaaea177c5c8b6747bbf40108cbc0437eb8f11bd2a0939da59b70c0c6129e2c249823897f2ee536b0427bc45035f121d2cbe7441c175899b97c490e6c3ca01539bcd05848\nA = 102cf23cc3b81785c73ac3613c816de47fd585c7d5f175185818dbb4bf0bd47d0dda9702bce97b29d66e48bfaae0fd07b47b40be2b48ed702ef21c54b10bb927f9d6b43604bec4f4b2796b44aa6b4e83f8bcd00f2fa3871dd901570e1a32888d8691454c40\nB = -cc5349a9c5280a933e87ca38ce458a711c71ffebb40bb1f7612b42b4684afc495e99c4a5f32eef1c9564c2b7612ea4cda7a0f5df6b3ec9026447dc565ca08563d46aec7ced9fc4cc5645960210d44cdc3944149051d569c9295dc50862f8f6d1f6cd1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1cfe1842a53d00e4619265e2fce7cb566ffbd912c9213925d01408a956af304eacb85e29fb6edb812a95e90769bf1c3d62b0cf6cd5bb8f8992391d2ad70f38a14fb9d1d1eb522aa7b7fd9f1b52790beebfc887193882377b7ce567d317d8432e1d9a908d6ccfe8d2de7de497d77b023b3959cc042ae30aefcc0229617fd2a146\nA = -5c3d24fdb193ed83f5f6a825c1716f98e3cde6b32e09659f253ca3fd2a39402b5bc3a6497ed7bc908838e93422559a13cf59156254bd3fe1e3b8600b2a777943cdb39b9d42c58043f1d587424425d3ef5f5538ea157112970ce3e09a87fbb5f7c96f1b5e65fa\nB = 675d9d2a05288b438ddcb330acbd59e4639375f3f14ac2d0e9e8b72de6ffc1d217ce62f997577f7eaddbe4603541b132cd41f2f2740363d9c331ef22df92029d143fc8495ed0152b918aed7ff22f564c7cd94fd3fe4178c90365ace43def8fe30ab05c0e\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 83ed1948276d689bb7fde814e67fcea72c4e3509c48873c3e7349a8fa1c08ae11ea4d814d8deb1021eb8b8ceec342cba5002a2ca45d5f340ae1aa500af4c7db120d0402c6cc8a840404be7221bbc46ffa10236043e5ce4415d3ef1355bde26d2d26eb7127326d4b8d671bb96a08e38a2c1dcc281830ac77202903a5e4777ff02\nA = -1be86e7c87827922d2e8a06e3cd6b64ac9a280c525749bcdbfac4856916321a964c9346d17465378251e6eada42dadf38bc9d7d87367bec94ebdc21af6b1302e520db08a64ba6b39920683725ef02b011a3e4ba46ef0eefadb98582cb911d0cbeae9c231b5e432c\nB = -352059faf97b433089a688c702b97adefd0c91d51a0395647f822c6762fee3287693e302fc5a5584a12c048dea1a320cb96fa70b5daff7c2ea21d249467d14c6bbee15a1e94c030e908342a939fbe8ae0de58cb6d6eae7758485e392ff6d5d64465b701692c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 402525e19b6b68942253d1a51fd9b2ca36fc84cf938d80b3d52fd4302de142b9d93d1663e89340fff10c2b5efc8cd47fc3b5cc5ccd49a6ea3038ead6454bf190b7f88f52c56bcf00c6ad5b0f5dfb7615915ee8af137dd99cd3d21172ab772f36d291a6856a8e7912750139c09aa024b930a0a6b9eccc83c2c5c0ee2473ea32c\nA = 65e5db532ecae639bd56dd63045bca39b33b4d70b2db82ca3d0ee8ca436e671828cde80217b48eae7487fe110830589ab1be889f1e1463f3b0757d529b2f0cdd2ac92c35e8ec141885bbefb6040a3b5e00e64a541913a38fe05824a929f8c5a2c46568c61989c3ca7\nB = 1d9c73eef8373cbb1e8393feb26d55c33a245c33d7031c234abffb2f06a1601f7f3a79ef1e8664c51ce5dba5f5aaf3b9a9e42470d381219b4616ae93c7f6e64792d23bae523b6a224c1f714ebc82a11f9be42618922b8d2eb7b55e4d45572e68a19fb0ba72228b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7a9cdb5dcdfb6e04351057d731fddb9e85f41eb432f01c0d980673d294d05ba9b0180133a89930e74cfce78ed54991b494a19e7f80f310b85904784cebc5639bbc631e80751807868e7fe16719e8ffcd1f2cbd1b9f303c3ed488b647670be3080668b5fa0e53b6342c33c87f0ca1efe1ddb1c877bfe2556aeb61805b06f41343\nA = 1e412c3d66aea2c503f3aa5dbad368a61d969a2951c0094f9da32d2794e47f3bf4c481ae23636baabdebdcf0753d431426b1865e62de8eae7238a9245d62820ad7f17b5380d701f5db776cd4e1ddbdfd542901731ffcea5bcdc247fa9c83f7e08a9389e5a76d38be21bd\nB = -afd61df72361260484fade8b432713eb740df83a401d73492883a5139c918d5c911ff5dc00140637da1c6acfbab4b0bc8fc1f337243d90beeb1c2a083ad8069494c73a99372bd38712a5b5393c779ec1915e878600e0b48157bea44ca8e97c6099c4ab07fbda57d1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 712580a1ffde78c8cf98ba71843c8130e835fee3afbb45e372d04c04cc388e403c9efac742611d7974bbae982c3aadfd1893f5da280afe0c1db1d81a9ed73b6ed9b7f05a20ce828316103259112d7754560d66733041e9470ae0d4dc95fd0484bfd56d66739f38ead7efa4051187ea41f7bea8fe5d958a29af41328246e2bc35\nA = -47c5755ca61ca8b7ea927f6fbe347f1362915548ab38c40f0418f4c9ba4ad520c3b2469d9ba3976669dec0b278461bae80eda53e9d11447512963e797f45460f74678acdd69fb9efe3897913b6568f8e03a6d90b4cb5bfb06af132bf118574b70e6bd2f6d6cb4d0089379d\nB = 5bda68c0a64218d3609d75eb4832d5468298f19498507d7d515f4c410f04dee535947571a5e75f1af7f94a5b3b05fb742fde23e7cf3f8b3dbee0a569e5a36d7a3d31a26c4a48a299044fd72339d2cee1a68966c851e76b93ae34130b75f4abe4f2260207d2254d23f56\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4a1a514aa4d1ada84fa841d0b668930c904783fac521377a7d622201867d773ad23dbb667e0d4181616358f3cb088cd157c8e72bcd03db64647b37aa1813f870cbb0318ae0a3667f8e6c19f6e0706217646ce633f0cc8bf4e8f0f4d7329a8647252ca6d376416d545e73cb9a3cba40f8f9465d85d57c2481b84b6d95dd42d50a\nA = -1d68bddd8c3e6b78daa0acfc63a6f39e97f19527a43f6cdec47568d57b47f4e4b7ee88e4a28d683b569e406ecd2510351dba25f10b9f7c82d6da16d848bb970cedf7675e67937921bd334eec4bc8fde83d67aca57eec804ce22bb342167602fbff452d5f0f2a7f38b576e1e50\nB = -34d219765916a4c8ec843ebee9a7aa1162974d41cb4d6b60532513608452da9993749455d9701af6b7b6c7454d7f2fd5c344cc938baa5259301d4b56ae8d25b6f6510ae6bca114cae6791fa5a9551e8a405f5b1c0bbfc27138563b2d64f9a4d7a8f42a23bfacc3f1ec9393\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3fe24e66e381eca525b24cf767215837019f44ed4fac6ab118d02cdbd658066505ee5b0feb7af51859992ecb97d727121e38873f748a61d70201cc43228a7732156a80dbe399e05764be19e37dc1b93222bcdcbc45b1a4817460f7021dcf1d70e632bc6a306628790201222bb522f4cc80adcc907463a539b02f74004d42adff\nA = 773454a43f495959dd55b8a064d70b1b1ffe45c084f5f9553582e24fb402b564de68e5379a8d9d02af101594e717a6c6db2e7173e557a64d2f28fd45c4e06041deda040705d99acacf8086830af19c7ab5e27f91738ffbd937dc27e5b7869bb6caa12c2d7930366ff75eadc570a\nB = 13d884a2396268f1a8186748a15722156a172a56dd3d8c77b9cb7001b6ee06720653507eba9bb9918f2f699cb37f3b5ae514f5180108a704647f19b0fc075826153edda66dc1105c1008ea8ec6f8c10057f8e8e479e1a1274edfed9ef719b30827a30f26da78820c3696d01aa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 715bab8708e53f76d2ef2afbb845bdaaf978b54ce25f84dbbf9074f16d30a18733a02a4ba5d7b092fa6c25d3b9b0d8243c743910f1b7b785d9cb02343fc6d59eb0817bcff05646030ce4fbb2b9ff76781cb1af66b46553d365d02c61e677ae97defe92d057d4378dadf8cba9824b0022c086e0d78b5442bf3d3263ba22c643f7\nA = 168186208c734383d472374fbedc2d5d430e85690a48", - "81b740008623120a4f7f83b2cdf85dc28bfaae5870abcd7ff1bc782ef11c78a75c99d41f8aacb52fceeb5f10266dc65eb00b0868937340146d8850887686d54218badb97647a6d82c0c6650ca1f9078d73fc6222aab95c2967\nB = -9711e5b3965654bd9427f79c89a0b3f3cdec1c857f4451eec236c1f221bb6773e5dcc30e7381a18a813ac2b03ff4a4ba679aad41e0e5d7181d4627f682ca2dc8af9a8b4f878771446fb225a979ef9c7e641cac819c307c8dc50d9c1ebadf912ec7c844e416f95b546cf09391f9f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2714b99dcde70d6c3be8b671d78abc155793f13105fd4b7c5d760a4c68ae89987311dabf2a9238d18299f983b8aca69a9ce398fdf2c9775d90b11b3dba17bcd8edf661efb6e9c50b4e37553cbecb54eb214fed1d0847287732810e550a4c86b51d4e5da1cb7722ce4317e69644620ad806d6d1c94e1e3fb4d87de6178a997453\nA = -75231ed37f1dfa4487c9fc79a6f7b36929fdca086e42ed41f79430b2dff521919236fe415ccce590e1d3b986e16dda866f3f0d29ac1adcf55d87fa5cb67dbf4693293188516e360bac513303769c42181483fbef7abcbc4fea1310c916396d29f37d9058a62aead94511aded7c4b8de8\nB = 5aadfe65df0e5b877fe45d42d7ca02882cb6c686d486374da5ece6f87771675153c84d74b6f40df1db567b7e1e3c60c41d21816f958f5576fd2ce2f84a8c3be4749dfc7e5561266b7c9698c7581292d0d813cb77955458d63bf94ce87472924c4ca79504d1ae9d5f025c7a2504156f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6613b1c8ccac0cb8fe2f59e76fef4dd05acf1f1b2bfc20aa3f193622ce3e9d4c7824ad544477553bc68f05f0b546e7c1ee87301e111af7929d1f40525291b88e211db7175f4e5c0953141914fcb4fb951dbf77442e7cb28fde495704f1b5141de1e50fbd0e359d0d86ad709c8f564c84dac81c7602717c269219ab1cf12e809c\nA = -1bc03897b02d1edb633e2c019e40c20c1d89a210b0733412aab675563fae8bd75dd7e65988cd8df4d9b343586e27f548becdde274f62dd421679554ed9eb127e527a69d69fa8b17aac0424dfa2a7692d1e63617ea45564b55f01a70325bca050862d583cdad96c4a2e123d0ed827348a745\nB = -3d5239dbe7bb3dcfd8027204eccf5e9444e68d322a0b0c535a203a1d0c054e7dc1e588bacb891388241462a5d2b43e6cce34ce46a23e6ef29670603d31001374dfa347dfcc794988e58945d0d2d17da6565cfea559203dec119fc357d396f65b296deb07686b0ad2d25a13fd4fad88d2c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3a7fc5680aae875b9241200b9f4112a82cd624ffd9044138ae3cd65200631ee9d7b918fbffadcad7e598791a9f0bef3e23005d6bc0048ba92461283492df3bce74c66e417b082ee052fd8f808d71f3ab18f9ffc40f8fb51ebbb936d09c26a3514bf868141f7cf238c1abb3d88e5d50dfc188902254f07d63fb8cb611ef8e4149\nA = 4a30f32d467b29dc83b40bca2fc4ccee5f08a64069cb87f20e63387b2219b12aa312400c4ca59608f50a71d2535cde40a6d248290793fe01693ca40b93a5cded2dcfbc9aeb36e187c9d650782d12bea917daadbc6525f266e074037803e4b2f300778ca8dcb304658cdb502c93c94a16c6261\nB = 1ca5e5218dade077fecb81d579e1c9290431b34df5ec84aefaaf233d68f17dcf60ee010db26320685af13a821b6daa9d73d8f3a30826c3ae7b2bc5e219cadcff826283cd7dddd04cea7a5e0585d6e7c9f23b27f14ff815fe53bcd75fe700b1b91671bddaba737fb43bfecd2a77e5b752a206\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 768d312175ce7d2601f30bb38339f046e4c2ba5c19ae5f7ca5a562cc2462c579fce9985e9e8afe2578db542c8d9e7693e0c74ba161334b249ce720d568e9c18f09c87cd701e6f2080b752362f2fe6252a1d0caaaf1fa18199776e4c6078d89d520b9c63db159d5fba7e0838811e68794b1413c248f3f7173ef29eff28f15b656\nA = 149353e91bdb70cdca8f06648388508511a64d05221305cad7187ea40d9ccef91fe17ceb1e79667bf66e8e6b7a57faa90a83bad119c02984a8f860bc1f23ffd33d4ad84896610301cd2e8e80a5ca7e8d3ee63e7dfa459793c9dbaef3569eb4f8a021c6a3d032a9c94d3f6b8278274d0088a98228\nB = -a7cbbb6a434e4b022d312ecd4a45fc7fc4d3aaca038cca0fc56e529fe7119ccdddc8e76d51a2fb862ad3d27a16ec8a51e5f66b9c7fdfbddcd05a0ddea14172339cee340c8c651eb653c6aab6551c99ae94f26116e15dc62f2c2e63305bbf84590fba1327ee721150d46464d7e22d45d53ffd44\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 763912f4b16549e6ccd60eaf7a0a1f64d9c3bc83e4a9b87e209a3959ba3cf609cf47183bc543f08e346b6e12b8bdd5d1c07c603f74b286ad432d58d7001299ec7a4dcdb56ca875dfc7ee5c75bcfe2aaba14959bf3facaebf8df92bc12937cfd4a4865b3dd74b243ff62ba256d110b01b4089730cf48efdc66fe272f9241014e\nA = -4df3899b40d51c83dacb442fb143835bcdb550136921df78800f0515a6cee77fe3236dadd2a0800b79ebdaaf8cf4aba5ebb60cdff3e4b4531ecd0903c1674a4559339123e9f09158080fc53c4c6ae72c961c8da2f357b7c05368157b4956e592c41b25642457651abfecb4fed5d9fc1fc3825b772d\nB = 450eff382e73f2f38bc3a4abecd5f8de478f80a6b99fb6252173c90d7099629afe859442bb1f796855ee9a2940f21d1f9dc44f462edd74b479e1f2926ff6faefeb55adbc6152b5c97967b1dc8c44dfb85b5e02e870d2920b75422c8a427e99e35e2a4be92cb0ddc04cb7f4044f716be97b36f045a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 56ef57d56c6d1b94cf0fcdedd3611a8ee444c2e25522b9ad175587619598da341916b183be03b1e73be300f9969120d8f3a23750cd8c4ffdb87124a2139e8ff2c15d8dc944bc3c3a066aa16dbe6dba4a74925e16acdb2b2e83cd7fd5cedade6a7f7409a509c00dadc182b2860609cc9a375cb8bbdcc350bcb2c0df9b3bff882e\nA = -143caf995b7783b1316b5551978727f06512fe114b419c735b3381ec351275fb7fbd6ca88b848c3e8c9faedebd6d084cb8a231636f68f6803d14bafd90534609d4a4ac0fb953417be7fee4e4cfefa452c5ee5d1e1b97ee75f83cca8691a0efeaa8bcc1f1e0f18c0c5d6c7684c9da6c9495d31a32f40a5\nB = -3025fa05c55826c40089b12741b7d406f748cabf692bb0227519a124653160142633700e3c0676000943556f97551171d231c1a35f7b7d8f96b0366eb74942466ceb4660f09aecb2fb2ac050ef699eb05bd8834a2ba959ac71550b5c026b9093c8cbbb7c5fb9390a7818db682b7c11e58996c9d0add5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 42f363c34c20c443c1ea7a1c54f98c6977b6671164a80308000533b2404a7f280adb1f3b98101cba25249131288f7ac68b0ae2572c7777e7381c1f4d05fd82188c4b1ed5636652e0bfca4d096bbf4189a9358b79f6b6333b99e5c4b7a940c2f7d1413bf9f47a2ef66b620b5e220b2c3dd7267452eb1b9d8d9cfb17bbfcdb6abb\nA = 499d05de867bda3118a8cb82b80ac91fc505e0fbc6c7dac5fb61713cb6e715f56a31ae8af4b400461d7ad1687a2631faecd90d7829f67d1b9e36ed7d55704b3f2aea65eac061172d698384daea710ed92cf1140cd4da427174bebd173c2ff1675b2407a84649b0a318602f33105006fe4d5ed8d0e015b99\nB = 17a426a12a0175bb46bf7a7e727eb5238af383cee6f4d5e2bd82b0d29b9fed35f3d8ec95cfdfcac49bee47b25d3b5f375a3340fa83f8dd9330a593a974d208debb7e567e59dbb7251b54e42dab2cd50fc63aab050a41bd88282373f8195c94c35f61bb48aa921f574cb4ff0984ccedc070efea8c46e5cf8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f03374e9596cb56cbbd89794090ca7a4b437f4c05fa38a09db60e5ca900b208fb85b52f71c29fd35e62c9f9529d7ffe46fcc54607ccb07f6f8e13fdd4ff1185033ba4fcefb1ed4bfc42c3ea9f05276767d8dc9b7b4aea4c8bc0ce84951d1f590cec0751f73667db19060e2bff64da30fc048a1f5700fe3f489920675cc3540a\nA = 1073531f678877ba854fd1e7f857659614c526847ffbe8ed131dc9f2ccf69e1f1e917bb44a7b905f7ff758f61c06dd59ee09567d9f0df2550fcb98b776ed1381ce052988aa08fc5153e31c621c6a51ca61b386e3a9163a5cd69608b3e200476a8ada35d906c41d044bafe71ef5c6f732935f15b53bf36f7ef8\nB = -d", - "e3563925474e5408e245184b57f328e265b6cb62eedcaba809d8f257eccc0a457eeb82c451f93af93ce9f36dd1aab386e7c02b356f31c2d170169dbe15e70cf5bb9073b35fe0e7c7fd7faa91c5b2b0740734f12eb741a9d9ac6dcf7cff59f6e16324ea39e1e07dc5b9daea27ac674dfe5d0a5790abaebde9\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1aa22f9013bc1cdebbdfecedf710c1bcaa41c696a3d7dfc1c8c601fcfcc1c85c8cc24be7df2cf3c7311b3b17a4ef2dbce545dc467d2a92d371e02a196a9977cb9042b236acf99d8c0d34a1c4dd8792d3497cffbc87c397ccee5d01fc2c89ef051324a7061e423720d0a3821a36739797393bdf7a45b5fc600824a17043312bc\nA = -4fb2e3fde2a0c653104c077cc6459c9234f86cc2d7b317329b68289826d3e2b975f1a69bed1a53418a0dd86e1b2723f4c4c5a29d003161e667c2315ec24a36f8bb5f2eb0a94f261e791bb829db685cd0ec9e1e301dc140ea57cac1da228124ae029e2b8ab1fa3ab99c55a9ca94dc7b767162c0a24af851fbb984\nB = 63702537a07971e399aa9a1a0795db052d6c8185c79107216babe11d6d8d472b61e604cecf9eaa6d44a2fcdd1ef0b6b52226ea0c6902d929b09e16576e6d1a6921765b2134c5d23c69ed61f36ea9a5552e5819350366240693558fac7a9d09ecd3702076c8c758a4bf6843fa843dfd688bef3f73515db31bfc26\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6acb23ea695d4b60cce53079390da3cb3a4bc3a6486c238c421f3bf6c93c027a0475f656c3e5435f0211e90458ae81772aa956ef284093020f7b58ccd9373f3fdd39fdf4adb8dd64590f4a7fc05238ba20017bdad07f5f9a6f076b71554a7741bdd8c98ec68f8fee88396cb1f47c64d6da4c228caa3dfc7a9a1c032a9ba4fedc\nA = -1b2496ef929bc673042996ae80f27c6bbd33fa7c20580240ef8fba985d1a6117d6e746989924e34f281e7d2509175d0773dd999bde16662e88fcef52978d19cc45fbae3997fa580a66171d398f4f0e7605d9f4aa4f728902cb886e6b6dc9f0161e7cf1ebac05a09c5a1bd69a92273280758173fd2c14550ec221275\nB = -28399206ae2820d26a5aa0bddc4903776611d08fc4cb34a22a8bdc2a19e9f8cdab94217f346a8070a4145f989e1dfb49cfd100267635af0e062872cc879c534ff138fca603b5d45a6860ea85b6de37cfca000c81fcda3d14ffe81da919b2a25214209b085bab9cb511889665fc845acbcd038711533da171d8308aa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = c012c4d17ea4c95a360218adfc3363f6d89f5aa524aec70049ef94c2c05e59a66ce01e25588e164bf2412f9517b7740de53d037e71ec3a1d426f05b18b128c41a878da75421e8c8ef3ebd5effd40735c00818eeb1ec63182b44e817403c9f1f6c1a0155334be63a3a15109be6d45ac0d1b1ef5cc99e9b284b00c487d91e5472\nA = 796fba6276fb7129eef2d1572b305f63d7b8c49371cfb3b2c67b141071e66ccdb5e321fa2c1bcf624c77317e2aa135e1137dfa46a34c3ffefa2fa3e316be81f45614d422bf86fe4518c2fdb7e416bec199de033cb5fef7f193a80c0f0e6ee924a12c8f705f5ed3793ab770914924b45cf2578bdd09c701169f0a881e6\nB = 12cf934763127284e642ddc232b1c889cd86617307b6ad72a9fe0d48befd7c5c5370a0062dfbde2add256dc0af850813b22320ceeaeed347eb9319bf22320b2fcadeb51c4bb26a160f7459fc172c27a91d367d5a232d00cf7bb778fba83afb744177bf1ddf45446baa035fcd0065f9b493d92eda37e9138f4fecf3ec55\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3de123bbd50c35805b943e76e97b7e664eb9feb99860750bf97e275029e836217375cc1910c13269ffbd0bd72bb82ca445ccc4b693742a96d19d3dc23f78e5ccbba46d9ff5975f239551c36403ad5fe86997536456c4a5ce54807c24e3b5317b1c7b2a1661aad85b63859d427f0703b460cf72b9acd3f87e2e69d7f8f15e972d\nA = 1d0433d84f1de082d2058475e0168ceb369013a67aa9417f066c29c28272a0b3f8be5ac7190ab78591ae72a1dc8ce628c683281a9ad563e134387b9258b9c96d2df288fc118a8cff068ee49d635343772c2fcc252facdfc93112358414e1734d6948b909b53e46263e9a0cbffa141ef77bc98e7fae8ae2bd85bd875aa7c1\nB = -a31a574d105305e47f4fc00ccea0cdf854556886b524901c22e6f3b59a42915932ab209a8d5da29ab70d1472dd5378d9c79a7447d17665f9d1f1edc1e545e417cb65415cb8a368075c16264f42555d26e83adc704b5c126c6129318a8f394af8bdbb32c8114470d11b2acfe806acdc7b96e1e348a32ff96a988de76d4623\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 770f0c3104c0f3395fabeb75ddfa2c21a111d23438463941239f7c63e4b6e6832b84508ebf3cde1d90cff0a2801beee05cd5118f9a726a987eb58def6780be899b473ea71c697557ff63a4c6db894e9438595acdd98abfb529d75bdf3c1d619d6165a9edb6aaab8ada50b61a3a84de654706a9aedb7321b0523558e8f18116fd\nA = -5fafbd498d610e9f29c38a5c6c262b71672fe9e9c84f0f071b549390353e4fd0101a059b7c547007e27df97761767302458f1936395142ce5776b0959fc5ea039429d64ac5d50c2ae0ee45d60c0c50b7ceb4ff9853d57c6e883f588017ffcaddf5a1aa3e23ab068877a114d9a2cf742f01f5f5d611424c8ec0d082f5c165b1\nB = 552155ef110c126afcb87dd20251220c7a43bd0215ecd22249a21c93583e120ba6f046c6fe03086ef3c97311c4d520110a450470a473d8633e3560d2cb44c25559af07516aff50d6d176e8782c06cd9aadd3354cc695c4ea8dbf85e01dad479c8e8438154351fd5fcc6fc7e9d2162ce2f0179247f756f0b9b34b54be74821c5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2e9ed66317734668c4c354d720a011fc65bb67439b2ac9203dca65a8f567682be40cbad4f55a83e836f1fc135596b624e4327acb085a61b6398237fef5a6e6560b488d4a673b5ae7d734b896d9647d71087621cc81e94d58e01fc2cc2dc775f9ab1b6031840a672fb715b77bd636e3d87b4949ec7bd60721bec8f9907b7c072f\nA = -1a6b046d691830d33eecf2c53953676ed3f6fdd20c2252f6e915052ec28ad1fbf7a5f264acf87ef8ecd515ed921ce6b85017f3d8a8f1d14f269f31e3307c6f935ad468cf012a912b0650a15106fb949cbae7b36c9cd496538bb0646a7a28989dfadc719424519bfa43cd8833d3a748c758f813881d83c98f7cb2a63c2a4d06b8e\nB = -34f87db0f839af6e4c4bf146789db36b3d0bcebb9bad81db690ccc3a35070d8830c9745b2fe730a1f3a252612e7026bf9889169b57b8984a5479cc4cdd6844ee3e150a2e7bf7680eebbef30e0591c895cc8b2ca488d489554f2339e2f55598717ddd8ce444a060cc95cad9eb478491ee8d3b8358c3762a970224abdc1068af0bde\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6455ff7c12bf3bc37120fe3f1302a9916a6ffdae6ee6a37fc23ca2f3a7ad910dc0e1027d4dc304a8eb4eccbcf3c87cf52a13dde472c07e2df2420c1d36bdd5e88c3d76e774ccd2ecaf6a0ef55b8c60231b1348a738f812a4fd9d0c158fd5a9fb19cc7cf9f000860d4cb6509271c8e43ae4193843324db02a029beb58ec2955ad\nA = 54ec203e2ababdb0348135c0679eca2a8e778ed46e53f195331a48d3828e5e40da804ecf95eed819ecefaeb9c5377cc1afb1fb220175990d347981353e7d90637adf8cbb16812af8a3783dd312d967a490f8efe3f23746929cf2a5a8df58e0b878367f6c5e4d3c086f947fc2bf70bfc3a0008a8bb1d7d83f002930640b6ed94c334\nB = 1311b88a05224e15f1465c8da26784dbaeae84f818e029301ea39a982f714c64312f9f02d094c401abb6a89e8537d64c178637364bd261f4a27beeaaa901cc7b3d4e36ebcd9453cda33d47a53c6dd1d121dfb83a222cfd16158eac23482c8abbfaca59e765f6c1fe871d884d281793eb19f6409dd6bbe4083bf762ef24c24f0127613\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 64104f6c06e563ec66de4442d35d88117f2535edf9e012897f44daab5a1b8a8696f84db7a68d64ae24a394debb993bf6734c9df542c7e473b2e497396ce39a064789d5d7b339b65766b002a18096e7fb9f312ea5997c2a85463fbd6fc18f25769ac2a2123ccb0e72f14b0608c4c22add72bda138b83f986e78d5c9da31b15b9d\nA = 145f580c2ebc6c0354ebdfdbb1d3d7fa17f0b55493b0b9a11b71001c840a967dc77f0206c3dde161b5a773a6b5fd9471fa08b205cb6f728e3afba44", - "0b55268d6a9542e234ec313d53583c580a391d8da5943f4a900b279ec9d8933f2cfbb260b74ab714a8b9a1af3190d914b6e42212df84f933a237728a5fd5473ce2e272eb82bc83e\nB = -c67f9b9295dd5844307b8fe3cb9c1875257258e4be6229ab097e148c0175ecd0de4d84fe03c8da6e27153c709c2526092b1abc73b5fb40f1d4da9e0f3d8d2fd5f8a4e6f3c30befd80e189b73fbd77e8547b34010d2aa57072db0f00537cf3ced95eb517b23e0c854b4becce128a575a31037c3a9e106a476d8b0277d26dcee435cebedc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 11913c40d577f70a5346ff1cfdca492ff52b640eaf257510d311872c8df7ba9756973da5b9206c6e5254bcbbb4bcfdad5fc4594e41ee44e77f168e2d20a4b228480a9908b102dafddd039ba7f7619eed7057e8af3a72ee491a61dd049bd947e5b09a94ef94d5f336945f47104fddb8493ef22fb648ff5376b68e96c0555d74ca\nA = -5537630b7cfb8daf76d14e617f7b69f7b75b472801a9a818179d83ef2984d0abc8ea4214ed3d3d2bd785060e9c2819e861d0df760fc1daca8340e8a2c997c9ad201d6d2f12a82ae3883cf9f5c51ff1c25277c28175859a7b8e5b6cdec7cb3875071cbe415bb698b85cb19f617162587516f93c728ba8b2cfc19f238e2cfda115b8ec0431\nB = 597296cb27080f33a24241c1e98fdec32f7a4013a7340d367e4cf2a521cd462a2803109c27fcec353a30dd20053a1f744394fed75829e8396f8de434399bafd6cdb6e0ee81343f0cb99ef3087a7c69bd43bd722745a46cdff0c2c837fd87543c3c63df3896ac101a145b478dc224644996fc72460a89beb5741b91a42f2fbaf0d62c099b32\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 42f420adf5c6b32ce53fe23af4e392517e37013b8c3a7d035a93f6ff45142b0b0bd5525cde85f9b7bd9ce219bd3514617e89ef4d9279cb9a3e89e44f1994d72febd23ffbdb0a4f19cb76448199b31c5cc6d7ec1e46fdb67be1211c0ccd93c123d56ac0d9cd2ad11f0c58c713165003495b75b60665047ef80f6a393474cb727f\nA = -1c6ac9565d1950ae6c55025f76e0a040eed0462218e97aea87208ba879acedf413ffd5e63a92dd8658cf5f49d633ce7b126091a55701168ee4932db004dfe8c35c939887fae3a892b0b04d8eb74191bf8fdcf5566b4d3796a5d2596b1e750f64201057ae60aa705edd58aba4b48f6a2e511bf5007a6c44a27e3efd5bf2708f7046c1fff7864\nB = -244f2a90a57e5d066fe22f4d52f91b44882b8ef76d1dafc3387abcb224eda4a2100239e729bbc745237f8129d457e98eafb2ede2f3afb81e63520493da2a5730f1170b31fcac21259e90c894f8bc488c5e5dab2c2635bc7b1ff56c3685607f6fead73a09f83a7a168c4245729ce5b06e482d7d3d72eff33d14cfe2f32f72175484ffa292a9af6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2239459025b257fd0b6659f54b8874f93f07f4d6240f8ad761c9da288cf1537d8bd001eced284bddf78edd611c7f28f1393c6fb879aab6e7df8eefd347d63628b1ae086148f488b01272f67ca19db71a2b284eb17e17aaf1e3e8f23ea253595de474d5cf47c16aecfae360eab7855868b8af361491f6ad96f893f9d3eb66d07d\nA = 558613de283911aea1ee21d6b926f531f778c5226e978ce329860682b5375fe5e5328ae27b00f504f2a2d24470d16c1edcb8e76b4d1a740e55538e79ac7da4b45c5299993513ec3bba7e7395dc829a00d4e228618dd348fbf838eaf0bd50f6c70253fb1c1c734a07d0813915be25d3163df13511f3675022cb85af7646c14ba5d13f615ded8e5\nB = 1f3c3c468146c29408d9207e15b25186d3b06b3fbf9556eff7ed7ef7788032d87ae1a4d2a0983902d4c70936c615d8c9ee26c89af8b58d60231ede54e859763237d5ac59af686300a3e92f456484ce77700557ddc0f93bb40e5d2e5117f2356ac7ffca26dcafb3ce7a5573e07ee97515b6b082fe75fcc9dccd76b4fd416e69a247fab2b30965d9be\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7650985e7c6e5461268867dfa9782cd8154bd6a4bb5857d6555e9d9746ee79b37e44638940bf8d5e974911327f0e53bbcfda0739056bae2248015c35839f35e7e359e93d3a339e7af38c0cb43eac5b41e1406e34cdd4afd458a5d126f70b5d683415b490e0ad61269ffe7ea8972eda6addd447d97e60891e5099ee920e18f233\nA = 184845d3762ad1a9c925c51fabc7b9e15570a84a06ecef994910845d56869264273d75fbb84a31c97c27eb9779e8b39f6829638a78b266326b60546507f65128caaaf36d4e7f85939b75cfb3145e2b1bd8372531cda579f59efa0da9c95a8efc72faf326d35c660b4444627d328bedf50a919029dd164de051a4c0c924103e365cd640b9637d8244\nB = -977390f52af784b52c1d54e82131b072a1c308406e9b82587102e67c6f7145f0020952231a5f0ce9d130677bb5a7a37d5a06dc570a13a29673c8a9068f06242ac438806c37ec46136e7c1c1487ca2d330fc1f3c1f42ea51ba2805b74c44a61fb2fac109710dc3dae78a07057a753898d4e849b910f035bfd807178f0108812778345b256c7b59f8883\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 35d48c3e43070a10dac0e256afb83b219aacc0036f554bd998b9092ce3bf87bb5d3b00947f2c86fd4e7ab830502d15fb2d4e47ead087f5c779a9ba56e272ea86116e2c81345d379dda6b581e9c8f4df8ea56c78f04d4f7412d245e00ac645847af6ae97d5d2ab27e48cc878d8b510c2dc753f6ceb1b9e7bdd923e0e065a6c11e\nA = -76e575cc79d7f0c313a489b255e85d114f3933383cdfe75cfef649f639921eefb9b3b3184351fd0ad252c6e477e153ee586a0ff6da1e1b2bfd7e953e6dd778c849843fa5cc355b31f5529ca45aec81ba67a1e364d5a74a4656d266f7decdd47b2fc2d81d6c298afa2d1c39b5e8eed519a9997a14513537cdcddde0b5b41314476264d59b7d3f0e9a65\nB = 6b7faa437b4e8db8fba56c62eddb8a81e9090d1b6655a2185d656b2db0e85225992297381d653e707aa15f3017880b0f07abf3dc455cb09c4e551b3df3516c6db4ead79b88339fc33dda96bba76ff7c388363c36b67fd5dd0ee63f92f67549dd77e37e9902ae51cb58057579f03286fc48e3b7fba763fc5844c222e6a1eed9e1634d0bd034cff222bf147\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 445039f359b55eec647296fbff4f22beac09cad32cae79c13d591e314fafc2b77839816aa4f641250938865b0a2c30a10e23da71a6dff5985ebf3df4429fe64c327557b12d987ad9e9971f7c7b1e4ad01c94e1e5322dbcbc4707a959a401624619029558fd6f5b14564469b13146f9a2555916491e4d77caa70f51716b299135\nA = -18ddf976fec2090f7d1f4d41b8f875e56c813c04338f595d6e591b3eabf9e105be792f45354ee9beff997e6c0e8ec3fdc714c07b3466ad1a949b9d30da0115f5484c3b9e00c7cf0c117db57c3c6cd7434371c6d9ac7a5da1a0e2d705bacfc22f62785222d59bb5bcd3e3bf2df8e845953c6ddf1b546cb75b1698dc8e20bc611294ff288056723f1e46ec9\nB = -2cbaff39103570df7d85a5673b50fb8818434bbc19ab4e33bcc8289a4047d85de1b7029a5cda3976ab12e1d891b7efe3d5576bcb3713c597771f93532853290068761bea04200fcaf9b05d8553b960ef5e28064de89d9e5097d12b26af0b64beb40b33ff82a55af7c5838b44282917fd4342e2065942c724f3cca515d9142fb8e46652242e8f0ee5ae07b6cb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6727c0d0ecb4a375d0fd1bc52146da1242099d445ed9e87b1fad4daf8369fbeeec49027d88bd98efb425c1e3f73e412fb327680068ae57d4a53992f3759af0ac1b96a92f56c2cf552e6682d1fa90c3910bbc5c0b1754862ee13c5ebd62d5b98bfe8dbbf9bf53bf9ed0b967f3c9da24d4334b9f3f75314b429b05b8e27142623c\nA = 5cb6c49efc6767cf956885690ef740337aa71b90c1d4b9b0a9e4734de0c0c50f2358fd45aeedaca6e1dd0fb510bf097bf46513ee09f3343bbd1c11f507eb61d51ada40c5d6b730561756480063f60caf05141bec9a769c241d367cb92fa8e229ba2e471fc73f48812a25bfc7553c395ca77b80443ccaa82fbb7198f8c35c3b5a2fff977d8b2a29cf9358ee1\nB = 16ff229a0e67a410555dbd4b687f1470ec854ef67db73a902f2d19953c55071c4a26dc320baa8571586f1fd54fa490b0d87dc83e5bf20b78956084275518b307ce69aa4ca1079e3aa753d97fa1cff62e0b5f3b99d96a24e411fc3a3e375ea21b7b35a578a72df68d28286fd9a324c06930905f696424780083715f77961532bad061f3901ed276a9eb6e81ad4b4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323", - "cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6e9947beae4d934253e481d27e854a59c4047eeee4fdc7df7e174a8f045776109c148ba3721685195b8fb59263def88891c5953b5a0ae85fcdbf02abc76f4d3c0f5d9496327d063ce8b3ba875b4f119dcd8beefb3ac884c25955af61c35a69d0670c3c349564e5b84f7df4252d6d3b29d9a75f09e9ef79f0fa9f797bf75b8ccb\nA = 188785951a3befcab56128cb6fb9576bee2412e6cdd7dd1bf5643babae83c8011af99aada405e119c3be33653862440005be994bf37d3802cb6c73cc312824c56841004c8e871ffb560e93a1d222c93d63684e90a91394b9c8ba8cac27b414bf818ee0de7217bc2faf099783800485ce2e93612ce39fc7e2f1db708bf9bb032d92b66159073fecdb2e0257058f\nB = -8dddf094f30284c213577ceb7f1b2efb1e4213a548e6aa840f801cd6382fb6d4995908b7827078dc3f46fccdb9e071bb8531ea8971de0ddbb714d678bb71ba9d961e58cdd5f41b8472146ff9b814a5d1d6368bd94812f8d38f235f39aeb2421a57499fe7102c1ab167df7d33b32a6dc7c8eb8f4babdd6b6c929d1ebd9bf4774aa40cefbf136feda7b6e10ba4dbef1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3f4a8d90017dbe8e77205e65fa7a0875a1ace6f3f215c2974e47dbac779804143da3dbce92db391c2614c078997c7d1a15439ffb51a5787f5bbaf98a4dcef576a6317b9b92dd8141a8fadc05d3be7c150630668e620a4e07b4b00519f34e422610a160de112f1ab8adf09a9169ba95b60242c89196ac6e155021dd84b3054511\nA = -65ff4322f8e46e03aa6c1fd10a207a5e51db6991bdca232c0dbc9d73ba77fc485d881868be7b14c25b05bb59b7f5bb6c4b2a7d53f35d2d7af282a0423285c5de656429ab7d3af7d92837e41ca701f527845e98c2bfcb51647512e6abc6675cec2a7d34ce55ea4dcfe9e7a8397d45a7a3e73bdff06e303a8f04ab6285eeb1bb78b1455931cae203078eaae826a6e5\nB = 4d936b603eba3aeec3d3f1f9acff02a0ecc28a8ec64b6bfd9b153b1bbacf4f1e186d3deda8c1c81e759237921cec53251250e3e838f5063c4a1eb6cc93637f35aca10b965533d18b713617a312e74c446d63eccee93cc97e3723ab27357ae9b3cbfcb3e2bfc589a1bd582480e776198df047c3ad85f611ca6fa480c70aeb98af02f57d56dc9659b2a6bee222dc3e0566\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8a7f3cde3230af1f1fc25e0c0e9ebeb69161d3864fa5a03e5d7f8c82d9940ded285df35c008f61cc151b4578e2677b2f2cff3236935de5bb1d113597eee448496fe29bb18343687f6e9f1c783863e949a0954de2993d47a03607423b458bfd18c844ab57e9e2a43930df159ce8564edb5a2a37a06425626502e3ff9363b73c79\nA = -100f2984dc1451fd7b71e5d290e4b7de2d26175a47b9bed524fae02bd5abf96faba06e955107329559bff3805689633a4a57275732bc42183acdc792cbf7b6b24dbdc8921b73c0308d0c0ce5d8aad75f7eb16352e67116e859b323deccfe5d9ffdd1f0265297bc9eede073146a06acc3c330458b07b8fd0bb652c7325cafdcfa165f69cd0de8b145d49ddd576fdde15\nB = -21ac4953e54347a56800d75f6feb6ad660b0442174cf3c5dcbcf6528e2b5da95a614d3a8399da14507df4b8eacaddcddd627b10ec2dc5fb8c43d96a38e6dff37189ba275afb9484df800587f4953e327af71dbd58780bd5885b4cdab15ea0f2864f961bbfa9bba6b2d9448443af87c0cf178990254c1ae6e19003b1621f3240a6e5d0a3be2deb5dd253f5e1f88dbb60b522\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 76f8b44df8d8547f8b3d8537393d2805c699eb37d19bd115bd5539adb6b6a00d004def3b7793d5c71e0ccd2b7e9fb87103c1a5f56a8f18ede1bfe1607a346297166596aa78dc584c7c32832e11b72fb4f2d40ae1591f341919bc0157080ee8febb7fee5461a918d2178fa407c37a8243e24206ce2c19c3addcc2b7c3c1912b6e\nA = 56f4d397530f5c90203df1ec799f82a0096888fd370d543e33b5a2c8042108bb75a86265204c40fa5a9a44965ad2fb41896b134ea56c79699a230f38c0e3fa4e5d346cda70e0253b9993c9da5642f4e645a0d96cb732f8f04c99a83d1f1360a385c6e1a972b89915489245ce58830788ce23b9e62d6b48a7ff9a486614d6979033f7914a0735d201c6f29e512374088db\nB = 10fe818f6af7a95cfefb0ea0726f9a3e0e7c30dc9785b1fdf6e2b810515448386c7efc656479794d389e109ef3efe37fa6124c5a7db3164268da0d98538606c57bd2f7df9482860e81f272a27c727d7d81a66fc1a9bc8c385cf02b7ca6bc7ec2d8d6ba1dc992caa216d02c9bf0fba8ee754af77567c6e275ac1b6b1b36b065760761300d156e40da8445712b8fb206c0df346a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = f580f9d2438b22700c3ebb23d1dc296f3d33deae2d32dea51c7ed3a0ce7b06af11046bc1cc279bb744bc31e7f822c17ffcc5dcbbdabe213bf97bb85c7e19ee71a513bf59b25b3b5787e42e9f3ef6aa1acb8705d69924a107b4f88e0cf9276c2c7c47fa4bf56c4900b557aa5587418f0ddd899630ad3ff678b5b907c07247b2b\nA = 1017a4fdce8bf41ce804b7c9c836d85ff6ee899807e1736bf0357b015b701b9675297e5ebf588ac6c295feed3c6a367987e192be0d89523ac7d64b0b9576f311b5b2705c5398276a52f06085027480c2ca72884ad7be34967bcc6c8cb4ec4fb761e88c16866a2e284b40180eb14536810eeeb180ab701ec47ece62af65a0753f95ca657e7d04ebf3c3a7db02993da9089840\nB = -aeb03379fcd4e87cfd18957a72fce42e016951a72b673a9e81f666b3cb20d2bba81400ecc2b38601bc3270eac46a633a1a6b55c50f00e9d7fc8a20176b93e971cfaa4f41573b17b8ccc498f8a3230825afd0d7f102daee347a9d59cc0914ac8689c1d8b39ccef1f3def44054307a7cb7706535f0cf4007231ba21696424c3d5b42c8e85c278f7c2e8b7d1787effa601ad357eeff\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = be05efeee19cc91e30a9277a6551aaea63aa3861b63f6061efbb0b92296e09f4709529eb849d9f40406fc59c526a4697144cef9661b556040458940ffd6a87ed56cb073d2ee0e6d1f05936fddd1b9a8974a3088577847ddde6bbdfb3d69158d5b3899c13ec78fb5cb6aa7204efe308bbe0b52f18381fe838536707a8a27ba0d\nA = -669660e75eae9930dcbdb99c477c980869417ec9c0e8c4053f0bd8ae62d496daf7539f37af96fd1cfcf3149bc02b8182a46b413e3397b49d4b4d204491440eea65505cf5d33a8e797af08f3da41f5a0804214846bd95d730260c6545d51126278181719ddd396c55f119e84da71f0683eb6db8393b098b3a0c5999862644e073b4918b5c8aff17efe860744d85bc94b582d45c\nB = 6045f903a750b69b709cfd6a1c8ec9fc0d7da9c53a9d26fdb0ce9a17c6a0ed5ba633d6fc01f004f4a48cf247d61f7df609008ca5bdc8eafe06dcfa06bb67efa6a584b5a2f02768718a908978edd475a2d2926af2a6e523549a5cbecedc78323c5c295bc0b8d3e14053078492e82e339ea2c6301412a5dd7efc20da0aad0577a37d853eed820776e672bc6d23dc821b5855eabcceb18\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 705bf20b7d92e68a69019cfd721b27373c7ff22f911066907f556321371fba70dbcb9774d3a26ca43e44ab20c586a3c1546fc3152ce011be66e04a59c6631bc8bde18efb7bf1743b9ed75a7a6c5bf5a4117368b81b112a3cd4e1c44a621f534a11c426451ea5fde880939ee5bb28d9843730e284520a976cd9f60c94751050ec\nA = -17c1dbc1ad1d2d33dfe1af7b4cdc7b69fefec5a92656957e111aac292e44719c7c752ace33dc74a6568be38b576a5ba174bcba77a034af5fe101699c99ca39f8a3b0a20679e6d0180868a232fd8fc775089e185e5eb81585403f32619a2f4d857bb091a824a89de2e84529e5b0702b45771a5816c5a823d81ddc89f8a70cc3d3a0c6bd6d85e9d72b69d2713b61c46161f7f4700bf\nB = -2252b54c602456c5deb86a0f249f3982c3836b70a946f636b22fe00c6e3b91b94e19200a33087fe734ce9a3f92a6099ad03a95ca523b7edb9e1ed3464d38fb96c470464e1c54790cd48769677efc5e1d22f5be4c15288bc5ea1dc184a05fddd5e576b3b4962f37437b4f9709dcec374377db44c8ba1d8611c0c3ec35f9bba213eac59a047e78195ebbbeff941c7f862e8c80eafb72b1e8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7306e3172929c00c29ca1db360eb4ce82066f237e9cf6aae368d1f531620e9b61eb64f5b3e2b735a3b565587d7e955d052df94a20e4aaabe493dba2c18e85fcfb65df166cc48733632d165129b112598bf5e4c58dff662e558e5f71b25f36708d3ab6536b1cbdb5aa2ee56d9e019a9c3629185b188af909831629ffceab6", - "34fc\nA = 6b31ef80767a7693e7d0a9ecce54beaf5848120f036923d80b7a0245aa6a46135e32314f3b227268e0bfa1f45b4dce83bea890526c7ac3efdc8e485189ce2c51597c2864c2d3664584be23559c03670622a53edc2c17b3f1a92640078ec35189dd7953e55e4da0290ff1e2996d164d69f1bbe6f5285ae89209d611a7d760e413e23285066eab8e126c320bb6130a91d67ef26d4dabd\nB = 183f06828033287497322b05ac08f62dcc5fa67b7a10c6c5a319c9a1e642754230c6d9809dcfd2de4bb9e360d6e6e1180f6ec6e0d4c6185e34ed299b6171e653521d0f7b8975ed5e7d2c51d27f9784a4b6f9b5e97379fcdb42e4df981462cd5bb9d0501f93f217d954f6baf70343ec710065eacbd2b778430ddc36a7ef0515f29d5fe78d8708d8ffb6c3391c6f632cb1bacb4ec52972ce0a5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 361ce44d153f4d251952c0b90681a19b7d2d8df7a6c5d459691a80c06107b2e818f93f30f8dad352d2dd87b01530d51fd1c67cede9b1a6167697098e41bdc5dc5e7a3c310116aed0c7b5fd99dfcdb3517c13daaba6ad10879f600eab846cdc110d392d9bdc0e8ab34b317840a725a7a12ceb48c75e8dfeffe2947aa85b2a5158\nA = 1e1f2e44bc7c79a00afc3b2570d5cd27ad5ec9f45aa94f63f2ec3fa6b69077480212a1cbde25ded7ab1c6cb1ec26d5905948e5c1d6d109bd5047b1e038666054606b42e880b609f6f00a219dcfb504d481d6fe709f4362940f6c4b6f2e05d243722cb32bee5508ec94eeebb53b5befa551d3ab5dff9cba3daebdbc97179e56cb778aefdda6a0c24265728ff9e59ca3c2d615398d97e66d\nB = -e018708df037aa2918850fabcad82731487fb812213b1c067d0688462a4d518e5ec7c4c84f2cb2017aa6bc960e2faabbe361ad8f66355366cae869d366f06d7cc32ea08dc51631e7f36a4c775611095d8aed06a0086d0a471749246d7157947a1eb5d5503f207723a7062382b3e45bb84c6f555e48f6d63aaa1c04fe13c0108507c0ced669a5296bcc16debf18e03c32eefd177bbc1dd2f19cd\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3aeb3ff6e797d271fd2271499a740a91569f300d7392a7b5898084012a3c5ad379a57d5169e43089cd58fc7210314758d5368dabca2f0ec5cf6786801bc99b45cd60403c732d9f98936aed76da724bd3e7d4b622dc690778f11fb0310fd4cd980b220627f7a864e107f93a6259081c6581e5dddba4890508af8057c1af29a745\nA = -75e06b47f60edd23148c3736c9c125a617beea7c8fd47e662c9d9be883ae925b7801a0030df3f4bdd3c9fc386f18c4e002e5daf4a6f7fa27b2f71252c83d5f1695e50d62a10b99e1900987b342290decf681a064f789e11bc3fd75d64e2e78ace56e7491fbe0eddd6f9958a5f95775c920ad6c051ebe7750fa76891ab00f42c910550a42bbc1c1e5aea0ae13b7e6f916a5d228bd57e854f7\nB = 434c8e4767d0d7df2125def75a978bb1509a26bf8305cd03df748c6c12b6dc580a2c1ca9a4526eaf3936fbc4ec797d0733217a54ffc9e1d7c6ca04fb39679859d5bd3fa64cd0a09cf1a056094b9c20ddf1f00e134533ba9892c2ca7346ac8d0655250eb45df9f0b7983bbf71102c6f1a2d9497e7a45eea7b3095cac037b7aa755beeea8a6191da268780179a652d94a732a2a5c7b626c0de3145f4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 734a429c91f5b0f06fd47725ded06222c0193dd407e9daf136696f203e153c9bf6df59016849284cef93fbd35edef2cd31c9b956fbe562d2a22100f177254144718ac7d22c99783fd523b642984794bd7beb0d0b363e28d3f3469ee332ee364faaafef25c1d4a11b5e517e44a412ba717a113ea9e1e8f2d6db8fad6f10d06950\nA = -18dcd213e9938fe4b6a64abee3b9867f65e47e5b0365d45a8dee14ddf787f34072ce32f38d4d48ccad236005a23c5fcdc02b72cf27001495663fc56f428072d3f1bf5e33ab2c5f9dd9facf122f7225ea03c2f67321530a642803f65a2e9428f32d0d974e68a25f705e4f8140568f7e4b132942b49f9ff53f04f241feaa29aa353925fcade33a0cc192fee2628c2111da1e652cace9d304d0f1d\nB = -2e5397658a5e6db9d30f09e93e67a30dc84b1e17c25786e041fca48ab710e1d0497ce615264f1abcb23d5aae8412b58430bd801775acdce06cd362438898697940712062b611c92ae6ad10da31784207c5e7b9362b20d7254da0df8caafe0736002dd466d76b1a03e91a8dbe8a71107abd5f07b00fcdca2017391c7c3263881a3d02a89b0e16a2a765a32d24ae6584cf44a88975c539402db9a301dca\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 427609751f28edb62c717bd98ddf999cfcf65128b652be1b5aac0dfe1bc0f7687c580ec70c8290455a9448c69dcb550c0cfdd109af561ece2ec8707c1d02e8097e780f32ddd932e706f81f68711acda0e7610f4dd0fd55f6ac7ca3a3184f655b0b29d2d62974739b43ded96b413b9e3f0033ca1edace24b6bb610bf06b5d940a\nA = 6576c31d48daaf7d6bc3658952c4ba18095f1a0d73726f6fe59381af45a2a6b592adc79fbc3b597e1eea711ab295cd991441fb5fc4ce5f047e571a7d949c709e0d31156184be4b8a6a49691ef93d7d3b120193f6ee82246aeb896b8b7b4c74c27c02cb39fe0335883a3f088a71ab42b947a0cd59dd2155c65a0274ec0836bb8c2fe394500724ef84d869bee40291363389e7012d672b1eab6696b\nB = 1ba2888f30be283b588cddf00eb3ae3c641e35fc0bb3a9fc85d7fac1e81052129f499afd3e8458d4cf893d51fe4a2bcddf70f28c8edef16c7bbfb791daedf1a8248faebe36953560498af652d1f1c7aa0e9a5a667d9c94f7d9525cbd5a82147d58b738dfbba5aa162858c2c66d0dd7d8db38d41a2261e6efc7d0c8b2dd2d6962be0fc796705cec8e87a13092e4a3febdda3d4dbed9d11a1d5f92d7dafcd6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 533d6d8d7384e6e65569ba0daae0a8cffbec1d20e417a6edb42d401a59de0a91a7e6854db081ce33b76faa63f6d866993c245e69ddbe6c86d339f7107a4807856cbca23cee2bf5496388ae8fd8d7c78767d0775acd7bd6202dd75451b424034e2766185969b5663b638d539f718e50a9f752f406c224c000bf1ae1fdd60a2a82\nA = 111940235b144a42a13201a41a3f9e4ff02948f8e9127d9a3007906988a50b36d7622d1221155f2516812074a7888b1d8334a01c02ee33b3164d761d02b36729c299ce2455a462bf18471fca42e5b01615d53723c3fefa5aaf4a039a6caad35c348a0a4dd3f0204f084f35c0b93ab233c4066dc50c5fd3897a769a7c5bf309f7a9c30e905466c8394d509b79d62a69b58c73d8d3f1665ecd9a8a4dd5\nB = -e2633e43c38c0b4b8713c20bf4e2b8ccba680ecfc1139954fc42724277beadea438596942fea1094091671c2060dfccd0351b2fba8cbed35dc963cc18f8e8835052da884799d88ec1887712000a0726b17cbc4302421011d5be8d234440eecc363f09e2c04bc9cded3cbbac9a5bdf0b6d418822fdd90dead20e5bbbb3566ca94ab85f3a00d32842eee6521edd18b9aa6872340b2f47deb961f58bf231e01f9\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 33960d7ceac73f342d46275e04fed56563decf2fa4c0e9307c90288e911ac8782f8e1354fb051a9da8e2db83d7c710b5d2b611495e72ed42259ce783a7e7a8f601c07061ec749481d39a082f29dda1f9c7f444a33ae1c1055d37a677b848af371cd3bd41c851d31a07e144d7add66df39576b8200a8b918201630b3da8e664c3\nA = -402034484e499a8efd610200790d443c5d3be35d19d8808da85954d42dca3f24177de48f55fa2efd7e4f7f624d806a8d461c3bbe0b626fa1f3cad2145746464108b367b13f3537ff395262256bfccce5f0414e1f98b59ed29940171d46ebc4bfa1a27802cc30d9221cfbceeb92abdfa6e84ab4a54965568aa10ea631e82067ae358a1a93a3a3fe3a5ed5636a0c4cb373b4d49f46f8fbbaa665a19200b7\nB = 78ec7dbfa2b28e268619ba6db34a23adab25e7f8690aa9464a7d8fb7c6b87d5dd9d33d4c023bb665f2d96febf2638fc087ed30796fe7517fd58e4120c0d319688e67a32bbeaf62a987a9764be75384bd499b0e00a850f27e303f615031299c631844d10abc571f9f2a0f742cc0e8df2fe3c244bd825bf1d9134b2f1059e2a1b61985ae8daf9bfbd9eb24ba268ca58553891945ff1a314a78fdebb5444677ac081\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3a1ea3fccd6f336e6d444d68af1753b83145131954c20f1e3c433a89eeb7e267425a34d91f67fd65191dce85769ece2fc7ab12d032f3e30f8509095ecc05148e47a85391b21a18257c338a6a3ca9816987abc8143fe443342b34afd8a52fff00dda2e42b1b39322bd38c6a1f711051f791d6cad2a47ebd423a9b933485fd5861\nA = -1869c53f86755aa350115a9f49d6248cedd42a339506b8ff59cb878b7745956f142fc4387", - "322c41f369773ed375b72665026771d4ed1b9ece08f84e4782d4c3b0177853cf9ac3a55f7e52f39c1b82aa42b30628a4fa6a838754ec6ff9809308f675e455bca6f44e298394888d85fee29d8a0c8e9cdb9aa08d68cd70e13a243b5804a3ec199f52ccd462ba6594d856602cf1d5efa509047633923d31f78da3\nB = -2023c544b6cdd8d971bbb345300f7a101f6dd44dede6bfb5f4e6b4eafb7a40728a3063f6d4bdd0f606ddecf062828cf889b2f632d0c9254c28f36dd974aef116b73cabeb2bba98635841c2b4d2aea833e35eb1db9fa9a9d33bf7b51c49a14907dbc6036b027a039192b47406bcc56bccf375fbdf40b82ac4b3c660a43d5a6eb656868d383cebd099d2a73506f675cf29649617fe06097a46de93c13d1e590ef2cc71\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4331f18a94c169cf0253136bc4eb7480c9fa4401c18db1194371dd53e5f7b75f07ec2e1e1c4116a5d2a8b2cded4b22925b67a88af9b8479c6e821d58cec7ed9f780a4c41e729982cb33f69b87d01c11cb9a8f7952db1920b6eb2124fd5d820555a99327117d7e8e26d18e748fea3ebc17e1d07161fda57a21a70c7f4e251612c\nA = 5e7d4ef7d6ace6cb106e38d96085d3f3505983fd952498af3c1d9b2af61e4ba10e14961b339c6e64e11ac758d5fa18c3222138290866970d67d0a4f4e19f453503eb8dfb85b44d1050c86943e7c5d6faf7851bedf7d0cb6b13d2acee25372243591d37dd230907457fb440f83b62395f80f59a2d02b87134887406a78efd77614f3193e517f234434ab3be084f1484d3f2c1f68c67c0d6e863585a8a5ddd0be\nB = 114b6e6726433ea88a2ba965f0881beb3ff4d377526e4e099741f069abfaf29e129a1f5fd243c6599f725a389728f755f9cad767ca1d6ae5c8b3a32102e47af211e86d67574bddfa42b2cb466d968f38b47333b1b55211fd9a315acd5ef62cfd3e83c13ee9d3fa20a06b2292177961dddc7dc39abad9ea31ead1fedd3d699f651b656edceebb0bace11bebd0cfa581dad577b8b42f0a844bcd8c8227880876dd7b0aad1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2468cdb1a26eaee34db3d2724e37f023c8a1788526b3dca99321b574685cc8303c609c85401a58fe6da181daf4111fe8c6d4b7428b1cd301cdb9bf8cb6f33140756c8b490d3b2e538ff294fd6471c4d17b9d9e4adeae0df088cb9daee18e825a368be57af4a096056b9e76b94c8d3b911b6a074ed41082926773a585007752ce\nA = 1e6a59efe0b14fa017c32ffd0962700fa9752242b06ffd0b604b9bfd125114d4e0909534ede704cdf1c9e88a6567f4a2989df752510d087d7b7afb515ad594627ece54b8a8e539074386121c9a3e1c12eb2641ded8719e56d42ef50e2f3b5d7d59f8a6f897174cc00a7449d2b91f33e9df07902a95479731a44fc4ebe8048c449bd515ef6cffed70ae78c832cd43491203a247fcfe0a403862266777947fc2542a\nB = -8a9d3646831dcc852fecc8e2335549e8baa2e2d82fcb90846ee82bcc715c716d4a9f62be29d5e1531db73c2186a4d2f118266de33d966b78f989600d772ffc55b1364117d6750cef67f4bae851e7e3f8fbdae7b79de7eab54cc1fee56e25d0632b2929e352c882ce78fd64dd0a1473e80b6572f0d4eb67f6bd6e45c7617314219d6f7de5e505a9b395096cd36650d23e8d57d6abfa9faaf0ddbff90d32865bf5ddddcaf28\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2909d3aef7a21244efc9b5b16626e260907ac11f3d00647f2170ba37197e47b9767030195c2f6d5eda717a83a152141bffed2e26777417ecd8e27aed8666698c2e85a414dddd52b07b52b0da7e08b3217fa6a331f84820d21086a4424974e1e8cfed3501eb054242a9f8bf0803a94981b7b81776eca6d07cd50c050dddf81d68\nA = -73ecc8a6a1507fb5dad40677dc6ec75f0d130ea704d1e87b00d2bd56a6be21714bb30202739170b8dd3605f0553ff57439051efea2a97def70a6d2cc3fa2b9ec27a00c1338bbd588513f0f320272b8933fdf6635e585d1e79203efb5c95a454fcd7f33aa2aeac08902107e9bfb29587ce8610d50cdb7f2033c5b726742fa9f7f20b4780cf9244e6abf6b812171a64b870c3ca4c9e898d4c15e9f5b0194ae736c3783\nB = 4049ae926bb52e862606842bbcb4a5148bd1063b6a56f331cf10000c524b4aaa80b3bd914cd697ebc98d68bd3c2bd5c87fac4ec68606c264c56e25b19d118dc9f2eca19bebca07269714f2955e107b3fbf85530b1fe99c42d33031958280b8e8abea5a918a41cc7e6980149ad68fbf1c0041798d2046d7f88a395348b295858c61c2f33d8512b6fe75aa8fbad62e2f9b0b7876ef95af8a7b7338a2d6b25ec6355c276fc6ce23\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 22407e4fe280ff5a10eaf46d8e1f5a1e77a07410cba4106466d703b11764c60124fa355733b47327e952a12869476306926cabbd797fc80b4a6dedfbec0b7718ee754d447825cc405a98b85f1e09ebb9294c4a4636aebfc61af4545b921cbe759d3f389beece3f29c2c7c07691a4c46a1a72ce418a239fdec80df48732627866\nA = -1e165ca7e1eabd2ad1264d5ed9c3d2b687f2db5b507a0e4d21d9e042cd46e93c2444c6aea8491b5caba2d8146bac656b7754b7b1ae0f6216029c7167fd3b1c3ba2e20469d386d8566ebbc05cb51bf1f1eb2cad9dc4fa454b07cc1bcdb9b8f5a43e354c4e0f4e62d52798f667080a0e0a15414391269fe8c92f06da74f6209a3b215adafa1eb6866f8b3e419468e2e5b4db0d0ada80514249320cecf034477977bcceb91\nB = -3f314681eaa4cb41a3feae8467f7d76b8b05939731fdfc943235aa4d67bdca30e64de541d17a8971e829bc0159384643672bdffbc93b3eaded7844d824604f46aa58b1f1b9d788106aff53438954af015a0387268266a6ba262e2fe7a4c51b5af6ff7f918674b7407ce8282f66e84fd2582edd809b465e4401c67e5faaa9e5748c06e3bb8ddb23fa649ccaf9657dbf79b937eb8959aae8d5bd9513c1e601c0e536cf60c4fc3802d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 385ba217033463cd9cb882fe30373c2d8e8475dee54aba1ca9713a709f40844905c2544ad792784cc8eafbb412dd68de6f98522dfca1c3de8e3bf4cbd09bee4656c4341153b17c98f9ac09411d16ec9880835cae772bdd8eee51eaba7c02ca6a1034c2c5d2d48e7ae3eb0e22f59bf69537ab6f1e49e58a71c64b8934113eb069\nA = 5137226623f4ce4dc9b80a783777ef4e53ad3c2ec648264db472c517a96383ba1173e52c2659a97ce36341a11e832f4ad293b89696f91a051c35bb1db6182260d4a276d1a9b4be848c206899f87a361d318d38b4073a7470c5743b816cbbc3bc1b20dfd7971b11ad4e20d947e352d42760104a5a3cc590b985ee3b5e98c779e38d2581413a2208d31873f9644ec979602671c9da72fa6f66c603c1bb6d8e690dba8bf4933\nB = 13b45d4105e3f5e8e0ba36c812faeafccea2f1a30e2ce8ffad57ffe0dadeae3a23e813758f270423ecda3da083b42432eead7f04842db8865f9f1e2226a3d298ec1895ae69adc55d1d338c3fb787f0676664564eefe46ca95206e81678cf1a2f173c52d809b1e06641a9b467f191ea09fcdc597271eb43da1a9a856784972ce0eeedd49ad363dee882438f09863ba5af063925871c525c6c0ffdca428054e039e149a424c6d1b5b2b4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7865f718cb30026837ca006f5cd997c5b917726ac6d9bd8c3fb9eabda0854d528d6cfc10e4cd3f93f6848582690c6a83955072daefc6959d33192fcf42a111650e50776ba9ae43d3d26e0ef2c6b60c3871aec33eda8c56353903e7ae96592fbf350b88d2f56e03f7f327022a2aa9b7c484a000135b85bbaba6f8836cbfc81901\nA = 16978c06a03276fa2e0bea45740a98d55fccc9d27321fd0a5b8522298a2a90d391c06c5c59e7eca85efeb9b4c91d4a1e9178adf816d597311f004ef98d209b59a2d4b901fa14c57b7297861ee58b89c9b2e931e4ce5818dd4006f3c40168bb4d3dbbd059c1f1cc24ecdc64d37df16b8e8d0529247c06f905ca88a5d283ca1b9e6856fbe8115a326061905b369791772a47900974339722d19b3aac16a0bedd93e1e4e4289bb8\nB = -de6dad276dcc0a9e271ad523620ec570fe6e3b350b934932ebbe36dd571edcde968b6590be14326e0f6394c0a2172052ff8dbc3ff15d94fb6e36a098286333768a84fd0404dfa354173d01f98484fb20897c439c48952b7f1791209fed94e9e72bfb3df5f368d420d587ae8bf036db6700f77b130459e9de2a541ed885c69c5641defa9436a4f7a69d2848d0e5d1074f77fa688b6dcc4d4c7de25a3b1b040546ef7f418112127cff173b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2d3dfd14e7ec60f842d1db83e29a0f6b052990fe8900887dc44476ed3948870c57e72e91e1941c476baa6aa86f76dd8ab6e6ea41707242c46d39b54215bebdb1f28e59d719fde18bea9994610214ea6", - "8ad9f2da24e1ad8a06f8bc698f8e76379ff332a2745af472d52a4b8e57d60280e19f93d5be669e0832824321e9ad8e76b\nA = -5144d5ca834f7bbb35d3fb95818c1f89ebe08efdffd35993a7691c05aa1b67f6a28e219b27fdcb66e516097c9ef5f00e4257c561b1f94c52c577471cfcd7a55314d3b0fa308b59449a36adc884c48ef5f34753bea746bd6fab2f20b86814c9fe50e8abaab742916313a50e3c390c67fda8e3729ee3329dc5e4b7d3107083aa3a07daf7952ebbcfea15fae7338cd0b114e9ab2f81dc2e80f90abff7a7ac59e3aecf76fab87633ec\nB = 48b927a46dbc4e23d714b256084fdc7cb9d4c96a988a71c956e0bf98785ebc9bf22b9d5c6ba0c419e60afbef7b96cc0c4a13e397aa2d2dd7995875d2ccb127169423455d138131199a263151f28d232ff4ae24e316907ace1fedd02a02cb5ff9c831de33e6702010fee2232bbe3c1c193ce792eadcad0c81e7d7c17e49168377b68690bc61f22dfddb17d82a3b993804726037cfac8aabe8548befc52a3c6c6baaec89a392133cd9c45b1b5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3f66970f600a9d09d73fd1ff813e977f539d69fe1784b8a2f99506d868418e4b47338ee0cbceed555f88824f98ffed39befb69e8907a5822ef7cd2a9950a070aec8fe4db9d68e1c0620f9eab4ab529c7e69466e325fe1c6c011bf7ab62bfd1a136597d7d5c47e8eb161ea048477bedc88fa30e4f7ddab2cfeec3fd0bb3fb61a3\nA = -1343c391be3f2b72c4b79d8d6091389c9602e97774b18eabeaae81fc0539336cd8c899341cf75fa758421c7f32eba9df474c934642003408b32db66cfa92e6e414b42b1d49c7e655ffb4c80f5bbff8d2774ee4f7198839680175e1ffec0428939653c6697eb3681d0f92634cab1cabc63f423d5a71d65fc7150aaeea74f9e0153923a1c65dee4a165e6a01a88655fbecd2db7697f4d2b49fca2508e2b8f84129785d36d88bcf59f4e\nB = -225a0a4afdde6f6450f28736c3ef6e67d67ec6206a63b11763bc6e69b03f1494b275ac504868caa6d56d684a12dc1098ab0d030583e73a2f45a42b8607c0f19031b9c5f07fb71919868911806d210d43aaaced5894e844881e89bab85a203af9ec3adb105e50b4250343ca50c26df14c46d73a22c2e4804d26d44ff0bbcc13d0dc7e326c9e4eb441f493c9743ae0eea0de045e05d19ac32d2379196a165e63ba640ca42e4861caa24c29cbfabc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 54e95e86e87bc220c8f53f8485402327885be34e34063a1b81e52a23fc3056758cea1c039ac4e513f70ed9d394f5806fb771dca8e342368184e674e6296b9a705c6380bdaf11550cffc73f9f55b9385c85fb648f105f11138a3e1f9dc0a39a0f9755f8328701484d45784e3e4b2ebddb32c9d9132867c6513201116428b791cf\nA = 5f1239e0b5dbfefaba906bfd9003336489ffdf634333cec2484c582dbc19b66782ba40942d047c3749597ec4d89ef61b7803d33a9842f0c903461be37c679ca213aea894d36c1e12bbcaa1c679599d2adda9bd23e712dd0d0bd3f91d146e7a04f3e7ddec8b0db7e12377ab32ba241ed1e01da070c1f3ec85efd8387a7b9421453969ecba8cbdeeeaae6ddb098084bcd250601af780960c32f0a1ad7d7e61fb19f40dff1060c5f332830\nB = 1113f145de014bb6dd6ca05de159b97e9736c45bd3bbd8477f739daf79615fe329ce948cab9787838d7daf797218af5ba7925685ea341b802690bc9588ba3e916145cd3ae9d0c4a149637b890cf50fdfa8f89a62e508eec68f9332787733aacdd57ec1f359ff7fde76138d5b33d32e64cf7d252f2bcff14be3adb1afd8da9dc930f5261e6d715ac75752b29f083bb1de7b0b89ddba633b8137f3fd299a7f77abf79781a10d897e7bf2c958a097227\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6e0160eaac8e1c31cd3cb6c5fb91ba086d033b4b69e41dfffce7569e61770f6629f23e12f0074c47c46653bbba94701ca798e1a242f7c4e25708d3acb5af6ea307b95cfa220f8879cb4cfff96b843d6eeed2b15c8f1bb21bb2b511cefbad0618d49d9ba33cade6da6ab3b846a6a24e35fb36d41201d3b85be831522b9bf509e0\nA = 14f4e24627c773527ed2243c0d1947395aba5c9cf95ae62a48827ffc1477614ad9c7aaea4b4fdd97e3272d3e220601565aebf87928c301656e9edb08d6e680de845615bb3a81c61ed043adb9d708ec1447f057087211673fa6ad8977166a2b4a8079a4f29d48e7fdd6875ccad05d2c219922b814589996cd9642ea2b798197407acd274da30d3ca008fefb40a25b38cb6042a581393283d6448cc69df9a5dc2b0777052566a8608a1010d7\nB = -b4188ebc5bf3ba31cf7c5e100e79806e92ff6f863c3d68a66aeb3ae8385f596dabe6f627f3812d0f2baea319d93ae00de41ab65e42eae7d396cc8fd0a2dfd35f303117fde4db5e8438df0c2b3b680dca538b42a7c844a9bf0d3697fc89ad0a73594627578dabdc214e0f4aa06b40987aed473e7f42d318bebf7392d9c898b4b8d73a94726aef65807b2ff746d4a9aa76303ed7b4fefbab34f5c87c2df82d20457f68289f7b96dbeab581294974e322c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8dd91f390c1f85f153f332de17e5de82979755d835398cdf3dbda1ee73c68f8e7565a964ae33fd5b1f1060572bb3af67eec79c4c3e2eb4de118d471f74351b80a5dcafc682bc3cfde642e611ac1d5bc2c49b308c30985b1161c4d78cf7621b503e2dfaceed886befc004f3a729b4a9bcbb8f13791d973bf38fb8101d6b7a4d4d\nA = -70e99398673324ee83495aa0aadfffd7bb9c94ee5251fff365124fabc50175d794fa84509f034c2b86d83607789338b0eebdbbf709a129a0ed0afd21c130d94b279c56f1c7c1eacfc6cd13f724a9352b2b37412242a47b23ec61ef0040a8855371aaf238003c45ab9d18a66cc7dab9653b93c323815e5404762d3f964d4654a6995af507bb2db2149eea59acd72af4d034217eaec0be5ba1d23890081a6a234e125572e3bcf68a6ea52d9437\nB = 661d8832671a4974b493e5d71e547cd46b36730f4017e50c5d1a7520fbb75f0314cbc2ac948744dd494d566ba580a2108106b120a797cfeb1fbfdefdab6bd6b2e073f90c77e814cafd0b7f79afeecd59778b1dfee3446fb32139b2311011576674f96f151f896b477c631237995e11e61e715dd8dd38e802af93124c66eee735c472972000cb4788b26752a630ba63b45e8ebbd979f0a4da5b359abd2905f0b7f3a21b1d381cd02ac08e284218ce41c907\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b591d2c57f6a5484b43cd7ca247c48a1b38319e843257331c8807d499c7763de4eefed529e70d4c144e5e843ac00ee8d106d0d82163cfb7afe528a7daad8e7ed105942d1128a67e38d59325cffc0c3dab9185247e0082e3ccca82a900d917c9bd0f892d4b518a752f8e9d38eab2acaf3b3b59f15b0fe4cb9a3dabe6e0191493\nA = -1896f67485a740720e23e1642ef02742ce5f10a92e51af19e112cc99c0fbddb60d7190086c942d293d076b474d056e74ec9f0c42055d745a57ba370c51ab2b761d889b766cec909811e2b2fd11d6916b753ae00622f038a4bc55b813a5d06e6ac136e81689407de721ee852cd21ea989ea7c8cbd00b64614caf0974a62097b2eb865f46fdb0c1a2e4f2d839066b797e51392e5ebd14dd92630c070acb546dc7438631fef01594878643a4cf77f6\nB = -3a8e2f3b8378a2605f5affa21c4fadcc655f2f8357a3427d2cec0118e55fc2bbc25931259e294d91bde8dcbacd39e6cbc125683da7d0dcbbc67d7c5866f08e7c4732cd4384d9366868370ea40a75beb23b81306303da4a3e26ad357c5c743d0a4ae775a472afddf8f21cb4a1a3350bb6aa71037607c334a0c79468668d3e727cf1d0610e49f27780901c68aecf1d145953e45f5b090855be714cb39aba2efb0f7db2786b331dd9bb8843de8c73c95ab13b6b1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f53bdd643b5b22445e2af3667a93de52f8bc7bc151e196c0ab0bf3b4e4dc0e5dae9e507508711a9e3de52e2aeece6aff7fc8a1db65588de3272839390a35a847e29204d3b9b70e10352c88a10c86cd33e067fb530d20a3a5ffe67938c5a7a9218f1164f36a73324adef64da64d5fa5540d29a76a87ce010fb7d73a59b109280\nA = 75e31ab221c08b3bd73bed03f878bf7742f9b36a89bbfa7e90f9b05ec11edeb0140dcff6e9ad1d62cd7af34bb4284b3a52bf1b48a40f744b561d9ece056a9405ab15f508700b14914e4f427ea1df3093497410a0108066e9b259c1a26ea72082b3cf0e3a99ad054804da7bfa0200d93d65354b75e605b47a4e1e17ef851a37c59a95e1b5172801e6ecabf70f1e6e382740998fcfd8a297aaaba7d04b668e3d6eed40358247767323a8393ec359628\nB = 107aca18938a9cb244ad646a37a212859b3dda7518a5827aa2146b47bfb3bd08d772eb7a866e1f674aab7a1c74cfdc2bc6e9ad1a365686213655b2c7b1977855bcd42ccecb804bc01d92bd7d2667069d853f18a0f0661f028955e39f71ee82b9ce6a81dfb2951b33b123e71264e819bba4d0a8c53a1d99964ad9ffb58b7cb5cfcd3e30b1baf5aa5b3cbd20a0df7ec37563e2b32b4cba91bbf3bb6fd1cbfb2fe0f84d720efdf36e9645c7e9ec70442ea5174528", - "bb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 32d16f7ae2632b5cec2e90c34d191599acd9a1b5f97845595988c1d0d4ceb9acfafbc4aeee9924ce55e109ec88c57610fddc664316e0f9a5e3ed56ea447111c0383ecdf117ab42351b80e72720a4b1d98d4c73f5235507c5b4f7849d5e9b527d054858c0436ac3d2de2704c4bc25de4cc702f5880d5ae34094766938bee555c8\nA = 133a439cf006c753c132a8559ea13c64f598c5f8bd5043b89d04d7ecbf0ec58b225551c8df8dcb341198fb0b487774867e5b68f9058f58b3cc98168fbed0d0ffa86bf74b4fb0d4235976fa86d52b8dc7e82df176d70892954223cc484ae58b6a60459a9a0803ab856ff9699789172b163615e322e193bd758016f634c83cf50403e416ae241d9b1e44add17c2a663771ac88cf8b9dd94622d80d879ae41f0f4e7a1a32a1ab164f981900fc159aa85d82\nB = -fef33e21c07dc26a47d692c3094205bf4efae6af32f1c0f46ee579c1a22746a3663d66f2919f46f973fe558c61264157d531e66bb9ea10b4b49d9f6ad3ad8762a6ea8169a9cfe01d3dd65518c2e6e58e8c88d1b2f42d207399d7326752560cd45d0ff571309301683770793fe3765c1337d14021d39ea6980934c5fefadb93047ef07c807d0ea5625ae0cefd098988d6eb7af993c062ba313e23176e7abdebcc6e566304a5f9e03da05bc1cc58dfbbc898a67a5941\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 20877c7f53fca97f8e484ba31f23dcf51ac0f4fe4c5121eec576e043c6ec5492725f1b9f9ecfa64195f71909500a69fab2e591377cc2120bd5f60d3fb3812f9e80b2f6c787e0081c1439dbea76b819ab44bf6bffe87dffd771a870e4f5502609249c5260f91175fb217a9eece4166540be877d564049389306e0d6b313706297\nA = -534042b0811c9afca04d20d83898e7653f91a73de1e4b516f3228c6d6d9b963c7f8f4c36e05383da90f4edd072a7eda382c47b84b46b4dfa16f269c2d9ad0fc53ed2ce51cd31e4e32d0c1ee21604d3c7eed2deb35cf8df6fe1c0740a1515e4c702a2074ad6c0fcd403603b4a4e2195d19b265958ae854ccb0b41cf22480389a053f71544cf594f6833f3e4d91fd3d9091df0978d04d3922ed72a4fa3579c5fff50eee812dfb2a334148227a0f5739f8ac6\nB = 6935a3444434b0b03d27545721e253e4281884da027246e46ddefb01fa7cf7a9a030581dfe618431a68ef6d79b03b34f3ed598e7c8ac030e2b4cc887dd31664604fb8afe4e71fbc3135d6d3b4e596044d6b615de7184ebf8dae8fd58506286ae4d3b797aea911eb59ada39dac756d0e9eb6a6c767ab77b9348929a00f8e311f639d19ed88c86eb91f0d4cfddd34e98130eb520fcd2b77507c24b6804d3d65d1b21e6f6d55d1f6e92bba0544829687a096be79eaad7d88\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 24823628d4fe9540103ce5f611f8a6ccf18788120280179a40c2636f30a13e5076503e8a4b6b6ffca21da5b0f9f0d85feb2ce10b51292ed069f35289ebf5130972d720d20dfb8e6ee80c3ac598570d38e57ba33dbd75f1b03eab7847d865c3e8e471ccaf302461a6136dd13b8d31c9f163799a3c24c7284b8826608a9543816d\nA = -1d476cc98529efe5b926aba3160b261723b009e9b880bdea04e9b5b03f173040ffafd1627b38be8e00840e85d7acd3abbae2f7a60b305256b920c2b25a8a4373ebbf1a0c69f6e74792cb0d849872500519b6d1c190da30c572e26b44590b7ffdb464a900fc38db013feecf909b43bea549e05f1b7e70d6ad879c613293cf61f0cecdba1a6565eff1bfcdf740bf553ffd5bb7d74f7e9537897184c527b990dea20387bab0dec3e32727786bb14975b23ff09f8\nB = -2b6e12c87ad91a2fa878b9245875209cbfef400e637b557c868ccbd6e94dae65f1ef8caab61f292d739b139e384137a747210c09ee6f3b2ceb6dd212e14525852b8c54215191e116b7097f6729f6426a8bebdff86cdc16effa08d932ab512d7265cc0f57303aa5e6fd2afe0a45180557935c230558d02c3030b38ca88de5fc75c1240d25a22fe32c4e5096aad0078d50989812d7dd0cbb02c736fa563efd32d14109c44297cdb3d4fa3b93a2e15bbb6eb678e93e943979c2\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2c4bc23d0b4b1f79141be9149ee20cc9f1b58ee0a76d5f4205e0862492c18daa20171285d6ff0b600c358be487e78cb5450d151efcff8d53004eece94c5a37f49a15fb2b5f62a79568382cf0a4232407b139e1ec5a9595bee8435b4f138dd72fdc2946b03817e49864812b7b61f179bdd8389791178a95bb6311df0a5c60db2\nA = 5b0a181f07068af6e1e4b715d92c1b8391949a1e3cf0fe0aa49f3333c826f5582615d39ec28b1367804c1ef54f15fb83b3c578ef3ae957fc89ef22a343175df3ef2fd425f724ec1c3363aa000ef624d64c6d678a4cbd90b41cf7d69a7e03dd60c5d3470dbb75228b34d35469847772ff3d74b1a89a2c492c082d3ddb45ba4df6e3f228de6c64913b79679cbbbc36a2924e722c2c640d0c5a0e90ae86b5364dfbfae80df3d75823aa58ac6c1da78e988a11831bf\nB = 19567bbcf615b777b35fa7030db7da18126cd695ca7dda67f5146c97beeb20df24ba0fda4a4f03523a0d9b9f85d9acbdb5793ecf9c1f4ceac81299a1aa34417779175a4bddc0e95ac68309da51e4f115dad6fec33a75d0c5520692a38df64e8d684c9304f9e2e6ac6a66d2e16a03c19a30efcac712aed2b9ee774ea28af4f37c45609464289de3f9be379c733d711875216bc223f2f468a0c9b4a8277bfe49c590ebce2e027102537bddbf2856c3b6e9389c4d1f5390cb0f346\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 36e1e0b44e5afc35d1e19e88e75f030569eb99d326721ced9bd7416ea7367a98305354eeafd204f1f8a652a8442eb0823d2e6644e6320933ac481a3709777381dce8a7c165b23aebf31b2ea2745ce5b352acdf0707234c824da9e1af98bbedf80e940fba00c229539f310838bd625f1fc103f267265ac1243855622c5df72c17\nA = 1dba8bd9d1e6cdc117a5a01b5046353084946fdddf2696f831a942d9db4637a5ee76b84d4ba63156b8cbc72e40559a2fe9b8e2682d8ba1db0cea042bb86f8ed71f6609df52526c42e7494f6114bb62263d36784dd55d396018b8fa47fa49ca6e5c76ebb0b00e6c764e36cb3ec75e3af6a2c14dee01fab78070239638521743d04f184dae79d49a2bf209ddeb4cc72e0c94a93a47c107f5369070ad95ffce034c554fe2a8391e67f817c6cab5b88ae9748072da5c9c\nB = -849602ea3b79b33af2bd3ef9d1250c507d332e759d428902dbee054fdbcdcdc0a357a51d00aaafdacd696a15a64cbbdb7e1fdb347be5ddb1f609a4390a6f29f79ccdb51bd1f0547d0d9a2780517f8753a906428fd236f8ee1b433e57f2810d0ad51846304a5729f53a871d8b0e14355d24d3f092e50de4f044e2b8aa14cd8a51fbb2ff36b0b37defa7be768c56fbd4f5169d9d4698fb9072cbb0a037c219552728587d7c35f27456c02020f5f9374b6c53bcf8eeaa14be51899d3\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 77eb3cb5277ced02b72368e41f04a35796c2c6cc1273f109336fdfa745aba7c755b6ff3833e9b124d9c78584f6bfda1c94273522f020371107870c288592b7c23964320729d2308bac8813586e72078119852e1d7706d8e15c195486b8d94358736869b15d59c037ba4dc8032ceaa31eac3a9e3dc51ee17706a6956cff8537b8\nA = -6a0753edddef8b74f762bf802d7fe9b38638923ee2d81bfdda354d40df4422e6ac43724de1715c4088da2e68b63c10c90b236d7dcab39b9a0ecbce57628f4c2950c79cc88a89daa20d7a8679232c8ce5fa30525c56011570107697222e0eaee6871adced52ba01a3aea0ccc9901cb3a09eb4db2f93aba0083180bb41f3f9eaae00fb458381213dad01997e9b88f21b0a79ada1ec3837ac2b63611455fab6839363b796b105c3be6106ff284544bda2a32352bbce6ef8\nB = 542c5fde65111ec8a38d76d8c5735cee17329dc41cfd0f13bf47e6d0e0093a129f3449db380ee9a70ec1e44640839ff18b950c8fd89346cb4701ef753e6ef49dfd9bd27d9987e572bf8e68df399cf945813582fa1d33e07be938a7729efd9a5e7d730bf61c537770a0727f6bb9ea6add5aac9267bf910eac1b7d92ab4184734ef8b1d184c292b2b4295ec1bfd17b8a2a2e4d315a8b37b8ff9bf6a1e94a4772267195c5a7ea6f0a0c267337fb97a023f1b50ad697ea31451192cebcbb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 660a1f378a23fc3b47f693a347d90640fef43add9729d74546933f4b78a26968cc9a70ad6fe8d85bf28164881bf7a99e8b96683c6f4fb54162c144f99a27e3feb736f0d382d7e5b934cfa835c723191e5692b7672cf6918c4a7a93b24af00b1beaf1b80320b14cf2d1539e3376779872542406a5df961f765e59f3480e1cd40b\nA = -1cd74c052e62ee8156ba5d97f28aada75211979b1c5925ed015ea75f693a", - "04c4dd0a705f6a723ae7b79958884c96fc07f81fca064ce2affc70768923bfbca6049952eea3ae048425b7c6ad1611ed4b8b77f7605629b9d198a77a27f25eff2f82867845cc868edee4ae31afc5d022b2ffbf43c14fa01bef8d7cd9d0e58362a0ff9abbf250e43ea5065512cd707791ea4868e95d8fd2357b3b3aec1a06888ae940751ceab01cf9e49015d42371fac30d48ef5853b6894ca83\nB = -2ac904d3632e25a4d536097d80a157791a6aca6eb10246ea21f4cae07aafe907c6e4c726694e14ce12e376c02d326f4bfc02ed539a5b4615a3cf5c838ffa52124f9b843598a3821cf9f1fe94e7206d6a525fad1ef77e7e77162e8c6d3d860d4f568e8f81153dc47f167860cd52c1ca59b15f1eaac6b9023c8b375bb63b6adf6972af8ca62b39f044378b11c4a969f3939d9fed5cbe18c06749956c7acbf963f640a1e1ceab73fc4c77463ee8d1575d018f49bf0f08161ce4f88aaab5a70\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = cbbeda9c467ca801ec66fce801c6765a20148787dc6becb199a15c58fae8d20c1d391a1d9d57e1c74bb412e1b8f271dc2cc53c3355c83f3e2f00f15eaf0df735160a48e2273fd1bd75533cf94c5175ce67e79fa6c1422996fae36ba288a658a7a5422a59d39dd81ddea50979e933efc02\nA = 7ea551efeccda23622a1a5029e5525f46d5ccb83c28ec9adb7a3e97c2b7d936238c483a4a9bc92fe0e21208d5703611e2795b91fd5019272d255eeb\nB = 19bd92c534f56dc4235dfb7efff6d941112d66acf81b079382c86fb10dc5473bb8adebfa53ea3fe6e4df8412e7807aed029694ca786\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a9cd6a0a89578ea773fbfbf642e05935a995a38bbd54480ea3ecea1751370ef95ff5ad0e3203613f0ef6833237d549676a95b720848c5e9897cda82642a2f373951d5746b559bae2d98ac00fae26e5957c61ac1de95318b1b1aa6d5c64a6ceb6575f1b807060f9e2a241e378e6ebd72ade7d2df18d5353db7737caf52f888\nA = 13c68e450e9e091ae45863f6c1faed25906dcd90a43620b1a40e7a506e7a954256bab0225f3678e7ce6c4ba6e3a83c8f04a3491d9bf097adbd98fa6e78\nB = -ddef76382342178fa6636e62887fce6e19590065c766b047073329ea15fbba96f2cf088fa5a989f6ee3f6a513fbf66f621c6ea6ef2fe8\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a9cd6a0a89578ea772021f58ce74cbdd8c44a09b3937b198adbd8e95e8e35541eca26438351bfdcd8600b4f9b71616e1f16cee707c712d40da9a440681f8c8647bc90ba4c68b08ce4cbca458bebd5110222f06b2ca980a2e9419e71064324e8c36289eff9c67f6d5d011e6db8538a54aeff8c20800b0949fa42c38fbabfa1\nA = -6d7e88715e9854b435876fc9bb2d25218a1451efb73ad9cc5f52b2bee929530e6618a858000b3f24fa5f47b5f461c84eca971e38cda6e1f475f6612ec32f\nB = 49eb76e4614ac7b0ed3f534811a4ea6da5ea24be925ffeaa38bb228fa117ed56ae976b590d6c9d9a7a8546d8a6ebe4bba771d6587ac44f09\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 44f8596fc06afdb72a6e4f876b70b8d5d734589f41089c510b0da60ade642fd79cf8e705f09910912624fa1f646da596c137f124ec1a327beccba62a44f228f3c0977fda2af631e249b2a4de17d170df07bd812c233a96d17e1e93910267682d24c5c485f99aeeddceb658a7db258a2fdf73eb0266d26b92e\nA = -122231b14c249820f0dae625342415f0c6e7f93787b4206b79e9ecaeb09623636730810c7936e17a1eece68edc7c97218efb17c069bc59bdb9681a79c910c4a\nB = -3cdaed858523fd55553ef85d018c1097d7b88f6c30060d1e77b84821ca20b5625723c7d4331ccad1a70371eacc7f7aa11220f83f1bf3595650b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6de7efcfbc1e8d2cb14cbe4465c4ef71f0d1d7e80a1d80d9ac2d0b161d45fc9d915c54e33131591e8daeaa11ce02404c9b8494added1bd83e344ad4de7c04f626315caa56fcc5ca2ddd4e1ff064a2957afeb5d280477bf1f1195c7294d89049024fe821dceb53c7d270a8b4653e2fc0a4d8a3863a854bc3794753a\nA = 47423c4fec1eb6779fd23e3d4070d0a7bf9a946f5610eb469876797a39c58577242daef8c34926f6974089fc595508d9c573d0a275cbeaf37172f10b8c849a493\nB = 18ad789cf09e9ea182eaf43b28b4f2540e533f0fccad325430b73101c00e440bb64b70ce0f2680184aa8caea2f6f6517e9b80285fea8b61887a41e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a906994d3247bf8a00f20e4b349a500159d086aa863772e71a68f91af9d19e4c021843f8bb6eeed1df708d55047dc8faf219e00d559517632dbd1cbf4bda61651b9644481d052903be1970f04bb4ee8faab9adbbf858324e6cf5aa9384ceba655a1a107210a9497552ba8a56d5e0e70b0c757baa71d1613683707357827f0\nA = 122773509ee608cd9ab3ff6763629a18eae41be64bcfb05122e0b3e112db48c64d2a5a515d96a042850c1c848ae5fd5f0ccc57b273d25bd8d68568cb00bb17b1589c\nB = -af398208c01ec9700e332f3e694894c7cc412a73bde8a79e08764ded92f0d58db8056883972c79a0c9e0ce810786cdaa3629baeb9e5c370a5a59d3ba\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 64ef5e7063a1d95226569a27218e35e93d870a19a43fba9889a2ca98ca5c573fa56ebd77f1403b3bcad17c1351803a809c245a97bbe32b45e21768f28c5b11ad542f5e687a17f7811df6c8735e1778e94d9313c19fa32a6703af7ccbd88b489c96632d10eebb580cde3b905f6345a2a2b86a871b4fab36fa4b0dab9a6c1c5096\nA = -7dbdc37a51b601417efdda2516aba15827a40ffc304c523a47c544d5c0bba6c1367a20d8a6268a5c3f723b1b68de57eceabbb00d44185ec4ba7ecdce5d80456f8cfe7e\nB = 641cf85fcb5fbacd6214be4b7b06fda1b80f4683c21c1d08311f6e23a15434b42d30a51912898a1c46b46c00aef7ab7663ecba683897825a4b07d2b7dd7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 370f20360ac844bf4275f78b7fe71ba5db6f0bbabfbac3384c04b256eddaf04725d2d57b31afa48f047aade156c34441b4a41c0b2146790a2e15d13b584021ad55965588c6e55ed3b5cf5c36b780a27c5dfb72678d57528ab17ca2ac696aed3d9abb0ca448d9d5789fe37e632fa9709f3bb924c4ce34244d239a940dcddd9c77\nA = -1a0cc5b07271098a23f01b3c0d47cab8b294794b74a8b162ff3b313fcf85ea81fc99433cdf4450970311e1d5ff81e9ba27eb867073ed250aaa7795e44ba8d4000e879bf31\nB = -308f93984acb78c5dac2426d9bccc2e3ac361143807c7d34c24ef8f8db5e68a904ac8bfed1edf3cc90d21c87ae4d224b8c46fa42eea77797f94aa848160fef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4c8f466d1d9829aaca1a22fb6ca5bdba885606b9264933ac2b4c18e3afc0c406aa71ee7ff490fcaa804f457096e44576ff8096fb1d2b3c68450a8bc36d1a2797ab8b621ddc91d75e7d6ba01d86e959171fa428a5bb1f26766f94a553c94f6dcc2e0af90d7776ed3d9fb67e842e88f7d7342afd86e2f5d159db7304ae4d204a3f\nA = 57e894e37159cf3c161be9c97a946454e43bf09a7ae8e1437570a86c6b06f84005c1463d27d726afd2e25aebb1657eb78957a9a12c8749049d12007a81d766dbe008aad6d83\nB = 16dba5cf077403ff4af47438f5840f65fa4e058c5cab3cb730154ae0fcc982ea097c6d0e75bbd635e97314f33ec7e31f0e41cf285ecfafaf36382b33d5e83cd55\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 29d13ec304f26247a45ab6869720720fe019d6cf370b9e2df9a65828214aeb4f8b17969b8dd54339d08eb99bbc66720ed78ef79033fdce6da33501fa8588af86ec18be4c4ecfe01781f9d1379865100dbbc020b892e77027d1f04f8171ca51fb73129dd9a96568904eb4", - "4e19f56f842b223724a9ffe28826803185e4208f0ff0\nA = 135ebb133a0beb909101da896e3aad7e26ea72b23e60802e54cc6c58a07b1205e2ba1fef6eb86c420f011b70e3f725aaf9fd1873b6e1c1cc7005c7c09e55550414875cfe846357\nB = -e8cbf3feb7be7fd12b01d5bd024e47538f434b496613320ad71f48a8972f687992f97e4b69b5842d2d6a4176a5701327c40325e98b27e4c0f8fee5a457d92181e40\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4309b728306535bafa6787dd79e58324b3f86eb5409d772018cce2159f75832b87909a672b8b4b14342b352e76ec5a6dd66737cb0a20b81c5ce222133bfddfea878b132b6f9fd557133973a0b44aa41a01d54ab565d6b9c62da67378a4058255047a95923daf5f0f7adff2a3f06074ab1facd986d7d26cb475ee818199a390b6\nA = -7a63e108bc9790ab687e0fb8a1cbe1e9ff876e7b5eccfbc136ba05fed93412dbc2ffb1ec49518e9fb867429cea1d7f82e2b159b75bd40eb8370e8a54bf0e0ac0ff24aa3662774bae\nB = 51ee025b2ee8abf9dc5ebf1a4600131c00ae4b6bff966dae5c49ab5b9017e6b1abd6434736df6daabb2bde254022783764c94e66743dc752c9040563df7016a1581fe7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b9ddcb9ab858d2229cbfab87d87236e8206cf5e1a042eb5ddde201d56e2695a3d0b2a42bda6a284fbd2a5b2c2b80446ce88c024137780c277ec80bfa6e9d15397cc5bac98e58c9130756ed0fde58d475a033fd94b1fe0ecc6fd91a8b42177abf3f77e87c0847a4244b9fd4980f3b42c7c955836bc994f2babfdf9c5b43315ca\nA = -1f971ee9a7c966d1e82166503681afc280fab255665b850645321f67da8934baba1226e9efb59e0ac4483c8724f63556a213f2224b993e4e082eefff0056f7aa8a3cf5b655e0f72ddd6\nB = -39309313b04bda1103ca6f56514026538b4a29ae258a2a66424abe2c652b959f5c1dc4755ea37ebbfe404839505c2807ebe069c9abb9150205fe35bc286ca12b64ac46133\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 47555924c31f040619681d4a12064790e981db2c7853efa17e4d20f741f33c56d80862caf86bfe0730870b6c0afa9caf66e15047e60256fec29469d1760d5e9b77d79a84fcf7a1dcd0168a59f870f1635eb033e0ae0ac17bdb73da803206d48cfc1da48507cb812bea540daa2393321ccb0d88b57abdbf3a3bb765692a2c2ebe\nA = 754d78d5608fe8c7ed8e26a174fa27833a24c48d23f0e702454b7eb578cb107da537dda11027dd6b41daad329e036794de562d7623bed8d9b0e909cb3fa38d4d21a95c5f4246e0b030a32\nB = 1839baa8b8fb6575832136f1d4632f72f36cdbbdcbd00f197fff3cdb88b851cbd74910ef6d43cfae9d3248e9c85662d7fb596ae45a460feaf308823f06345bc5fae8823230af\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9b2f026b11d0674e9ec060fdb24b45fceade3070db4405b363d53df1219a02a664882819fe602f430636fc0bda935b14c55c8a0bbcc9b6683417e3ffe7f5d58fae229122ac6e42e76899254295dc5a08ed43c79120a5e5e4124b8fa6048ee90836bd2de51bbd2c6b9b53212e913cde871f11bf32f91b3a78575a006da36627f0\nA = 11402b3b1a45d67cde9730062e38aafe1d04fb1f8bb1975f25cd9098813efa2727cb229adf9490267bd437220d9ffa05bb993e45d2f889f140faed3ac3c7b53216455a830d6edceb02e8db92\nB = -d8e011f18bde068badedce8106f6602429fbcac4766334a0101b57fe94603203a4a8975fa499d8a68198aefd9e68f28e68914f920eea1083e37c67d59476bca9819a8bd628b89c\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3a74066e7eebd9b63a1dd28548be60573c95f29816f3b3ceef68a5f6bb797d7eb0b0f4ee612dca794ff82f5d7461d995b9dcc09649e2587639ea017865328bb5deef17b5283691724e8aa331d75c635d5e19ebfd268fe5471714aaca8b48aeb846f241c1675e18d35f029b132f81128f19028b0a471b3f75a530321135e35fbc\nA = -6c5dca3fb7b85573d1c8899868940794e428171e207b5f9f89fce4b7159236c0755e2959d870754e902e9c40dc1fddeeff6364f898ec0dd669283e6d26a612d9af3c3ab04468707bb8a7827756\nB = 5446269bbeb613e69286f1012ff62ea767965533624542f3b5c866cfb569d6193aa603061701992cb4873ea8b766606da1b57d7b37cf52f52bf85b58309387200b0ed36164f30d52e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2a4e727ac67451ca9dcba648050a085196460e4aa4836c5652de863c3e2a76213e0f590de3aee8639304c54a9dcd5f7d5d3592f647e3d07d322708e1e26329f4a31d66c7f2e9d482f22cd9823074dd57d14040a4f00ac2af9677a2c98d58ee1e094b1a8c40092e77eae454638bc3655e77441d4f218c637f95c147776f5bdac1\nA = -19fa688008a12cae228c6ac4982ecbc88da248d7ec785bf2289dc9103bfa3a91eb1e5fd6afe9e0cc035d3312e9ba64028fa6a229db6d0eaf8af43d8c410be7c689c3e557137ebd60d3fa04edb60cf\nB = -3e8c87fba4a41c3a84874c987acee9f560b9f027338b584a775c1fcabb766700f758c4d451077a9427257334a569037b0bd006375f71223add62eca19b1e26b86dde0cc251e48d3b60ef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 52e4a3f6892b425b935c6f9d1396d2034eb0331cbc5241e1d745a9619fa0cf0fc521585cb9d6b1034c5fbbbbecdc81c757f768c7a82f6ca291cf5afc98500c579f82ccf0be233066730f738c205c3c188f94b878c11268871ba42a5d950dc8a399887997cef2b6b68badec1ca641b88d1455e6d97a2841da49df7eeb766b7be6\nA = 67df01e34a26e8239c8edc7ddfccc3850f39864ed237d4dd67588efbeaaed1f884105508f69e20ff6a5cfae1516f6179ae6fb515a66ef0a7d633ba4218c30875287ecd0cfeb5bafafc492619942f97a\nB = 19f5076405b3c81519c0863d0c963d545b2834343e42bb3c779788cbb46d89be3f775b62f4114268a0ca0e6af6c0dd659607d40071dfe7f1ad0df9a5c53b741c04612158de396e9c96f7523\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8ac1d96abd2cbcaa8f7e3267b716f675aebd23694d24c112d202653979636d4d47e27cc36f850355cfc5ca16b78cd1848944f8759fbf6b03fbb7eb347536a9328a5cbb778a6bcd983081374a3f543b1380add14a9468358009ec2baa7ecdf13e7260968eea74083459406e8889936b2fb98c8b9a3597e5f9ca10b76e1dd0337f\nA = 1c9ab23ea37f324544280d176cc02762db7a39935f1ede9695b53a3ee2db49d0485c6a3742a3b5cfb51f3c21711bf89ed05afd0886bbf61cbd57b23439a8a165484ee8e4c0e1c0ca2b6478776aa2897d87\nB = -e30d28dd01655b7a419d939e3e7530258a667420fc759bad585802c63fe5efbb309cb502babdad0afb208aff5ce5830071c5a974604c69ee47f76fd87e2460a5b03a57ef0185881502625886f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5df0700adbd880a5730d8c0637a362a9d42c64503c3b9784046b946c2459a619b5bf804a41c92ed6370bba730c7d39fb2e01558f7ec38511b0449d6e9db8df2cece4ed348782ff1582396ca8b3196474e7e5817f8c197c44d771923b6e286e41e7e23c33fcd8765e06793169999544a310f2e080ffe13640b85f21a18fa11928\nA = -5c01fc52e86f3a344180bac284d2376d1bd693f20a46479c77fa57077df62f83b1e81c94e577d1d6733d276f9cf70555b20e3afcb97534e4e0108a6cce87e9292d78b2d7367ff15fb33d2c3289d2a2913b58\nB = 6bbc39283be06382ea91ad6b1630b38f32385ec90019d2ded7ca6fdaa39defbe22585be0df9c0cf613f6f146c71f901adf525336f6573f7f43e661c44b7097f110d4551e8c75449da8fd39201ca0\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2a01005f1f387c4d8d24a365708e2506b044f86dfc011262", - "d3577f7313a8f51ab943037361bed1858e021f8a46491a5c73284c666eb65cea1392a780219f13d7188721d7d4b975272293a5eef63480f30cc9618aa74bc51f4175246301a46fdbd34a6ec72d5974aa920be5f321a97b8f19c0ec56ba10eaf2e61f2b45f134b304\nA = -108bbd8824e8c16b81dfdd4dfee691e012e578cb9cc80cf050c0ec4cebf71a968732da36552979ffaccce6667e46c29144dab75132cb087681d5549dc5508f3719e129553fdc97f545d7ddb7d3a4fc575ea67c5\nB = -2ad4d4078c47a3c8f5f9b48e10d52d72349ecf0f54abc60bad63bbbf4d8efb185de90e5e1a686859e1c429e30977fca492aedbf084019e9ceb4490aa471776ed2e8a09151b37c5caed9ede66922b7ec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a1b1b2d33cb610f1b398e03f274ef39a583d13af14b79e6766859b9ca748237b481a3cfd5d490a073e82e3c53d3ff5cb6219b2b2f71927f27ab6f567547a22dd35fb5919e1ed2b6dfae4d536d6d44fa6216d94d26b33f52db06c4ecb29702588b73ebce87569639f786df4fcf569bb07d5379bf8b83743327248c2d71b5dec6a\nA = 5bc53b3895cff2bf7bf10e24fbdc43d17d277a982d5d92f17b9b5a2b9ed8b6104229292ef3997591e2e6a116fca21ad5d061ce438f33b7f7110293770f8313077152c7546cd522ef4054147edbe1878072b1043e6\nB = 1599b541c9809779df3ef40971e7a83f21564bd5d6596d51a3d96defa4dff41e83ca6247969a3dd9a746ab72ce21137f2d7ea015ac6b2ffa8a32997e8b821064d35afde3435b23e47cccafa74d5192535b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4fe8897417446c493725521c0ea5b2110f91a1b5ba236cbb6ff3f52b0036a49fc82274ca949ac2b592fa4bcc792114bf2f2a78a2cb44cb22c6fe7e4bee7981604de47f6da2ed1fc6a8eb32cd9b8aaca0f2feec76a2438126ae6f409645d897769a6d340308f82dbc6a98ac059fca6f903c5aecd668fa838b67300c654d4013e3\nA = 1717c6503d069103f10bb4b36427fbdd2371b30793e492e4161fe185b2e27469fef6a25566d6b46f6a7f97446315a22d1f1f662f912b17e71feb2c82411ed7eebb84d4f594deffee14934b75a845d83761f36141ecb7\nB = -8808f540521c20eefaa037fc5da782c891fdfc668b955eaa2e4edb592e027a964b4cfbc94c548d785d92992abe282d90dd137c4d76419926740ce138d567da7350d89f2e56772d8f5bcc9ca8d7076540fab3\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8b9311808bef497d8a5d14f7d851567a196a051610246964917a1f9d4f4449357d2411ba9fd93983f6edd76b8a8e1501146b08b6e1fcdd97b6a41cf637b6ff0cff7a2d6351aa1ded93f8fc1cedc81879eef751bebfbd1559d5d0320595c79e3eb1db0951d7c67c663bc57a672faed9e14c7da6be6b0c6bcab3d4d515e51a0b5d\nA = -511312fce1849c3d177d42088e55d534f9f7096282916e16b041f66ea90e2cccddab5cec0ba8ebf0b047ccce72da349f420cc28ab19bc156c1cccdcf5216f19ea922698127f090e97444751dd58fe7a2c90197a9ab3d35\nB = 6a5cab5e322d5f651f798aebf43a62af772fa2cc379905e72d253c49be8193a07ae6164f21cf08baff906ef800e361e1cdf1604f454483e10c8b2bfdcce77c12b0320dea63f9ac0afbb86115b656d0198aa883f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 665e16ba6cba87c646637a233ae04805a302ef4a10d79c5b65b146cbab8c9ccd491faa32937d0ee955dff7dd0ea3f79fa43c133021c8680490b91d9c1d8a8102ab709ada7508bd59042940b2bd3a4f8c195f781313e45fa8d3abda1f8e13b35811b638b2ab101d1caaa92188d2b75b2b10d596ab159583135b0d4d15fcd3d882\nA = -1375af024e9974cf8170801f4a709b4e5862ab7d18464077727bfc2581e557cada991e9484a1acf80182458158c44871e67e783f7573f214ee4ea1f1821a65068f2bbbed7575f03a4bba36b0fa8cb6dc58c73b100a6c4a6ce\nB = -2d64b6bd987d496a3c121e89f4b0c88b6ebc6e30fa9d47981b52862551f3b7251a3fc376db0f2d6daab6e6fc5ea8fa10b040d0dce334ee91d8cfa6db9648df907b199bb11b2b5c41c67d72b760c404b0451f70fccf\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 49e9709810d9f3fef159e5cb45211453e7a94878dfdece19af839b89c0e43b226d7cfd46859963c7ccc753350e74c2501131474e3b8e0edcda18583b0392ee15f1dedcb7144000fc7fa7eabcbc83d12983d2ade477b4687d75b723c1a98a951d21b2e8ed95735aaec77e00de288d16422fd259c665a08a34331cb99299ac11e2\nA = 4e550ba2fc2a44452f068860ce2a59230738a7a15f5de0aeb4d15bda8c61ee3003568dc5971e48343d402112d7a86860a7f08f5cdc0de21fb1aa064ee5df26fa23839b5ff6adaf64a4a18c07efb3582c2fc9612d2208fe99f8a\nB = 16f31365545772f276d8ac952506bf4033a884edf1ce583a63d8d9f6809e29d9cce3b3d227f839e6c09b459951465ab4570d2d36127c0f677fc0a63975801896f2fd17887ca16ff7f265e2e7adab1516ce56ee1ee9de1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 89ca20a3fa109a65b9449edcc729fe97ed45a9bd69eeb31d4a566ec1787b24cb7a2c25b3f89b36fef1cb3645b17c69ac8ae243cdba35e17f5738b35278478bcc391add0b5ec42db9ec1eeffa63a3ecd2ac0338db57cde9d2eb9ca4bb1df84f1a62245c4e585c4f20f26c98fa1957df34409a99a18bb442ac14f0bd309266a35a\nA = 1fd8a096be30e4435ce8cc604ded337a3d9d2fbc9666d1893c38546c4e155315b536d1bc323c1e7be162bb0fcd58440915b053ca0d0896e99265241f2afd46605a2a7486e1394a07b23f3382cd190e943e596c747b6529b04bdb13\nB = -a3960a51af5ecaaa70146ce55d639005e9b6b9b58592441d5876fa71470ade6d1e2cdde17bb80532551bee0dbbb71a0cb24dc8a129c1f6e28920055d87e9c66be27fc4b425737f36add7d72e39bc83aabee5534637e2e22\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 654d9c55d4a62976670a5ecac3a6165734a65f1edcc1ca81a8c444dbc98c3409ac8c4f6fbb92f122045fef8b7971a276c7dc4eaba21f7be7495394053d4f9bb14b63fc02c8a55ad8fa9bb9aa26aca5c47968ea1b7646ec606f53606d5529ded83639984683b8a020e8ded4b2d9f668ceadeaa8160245b36a819db14e58cf2bf1\nA = -67abdbc70db183b8c25b0664805ada269922556bf15aa80a47d31f215e216673b8d59edfa10a74f3f09d066055c3b9abd5434ce95eba91dd51576adcfbc7e2556df95fd6642a3b7e0486a635ed5699eb7fb285589c887c8659a2b7db\nB = 6ad3e854ea57aafb8980f1e99ab9cda24f183dbbc513e1fc92d4e239077816843f47927bac28e41d3f31c9ef134b72c09dcf14e2e9677a430d43002ae70c577d9958341243030fe58a800a068d6b01fd377e61844f0d434dfd\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 74bb23f7b0cde7924ee52e58bc0680f151e6898cc1bd4a2eaaa05faf218b419a19ebf85b0219f924a26002f9251b83506684af659e5b680e05138432ba227977f38a479ad9d1f3cf68a86ea214645fc4bd1a032f995307e9c9ee432e816fd852655ef20214e24522c17799ef41d1eebc6e097b9792757f7fc43124c609ef9696\nA = -19d3e6fd6de9092cbea55d65154208a0c93ae409c3ee35569cf774b8c8b7b1c9dfdd52e9f408e14ea3153073ed8d92746474e524a903a45a882fe46af92b033f2c41eacdd7e3c1ff661dcc5349ed6bd1aa845eb1762f27593708aa185c7\nB = -3d466d29e8c0008ee6f402551e3d62fe044787bc9f243db9252ea97da9bb75f5be416def97f13cbb008fee77f2eeda672bccce1f36fbcd26e1f1299619535da0a3fa3ffa0c6fee82a494efd7407cc770cf46ed1b8b143f42790a2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 197eaeb8221b431d5fed3d701a175abc146a9fedf8060e8e611a54f8da2fb27d2fee4539ddce1f3481e6a64435f09a2d5012540d6069900a332461471b22192fb87b63221c7822d3f2fcc35cc38feb6b3e49b5b0fceb52b0ccbdb4e1fd7b0f3eef3d582a6ae194c249ebc52f215b568712b3e50bb8e01c64b114955ebac2da48\nA = 7bd216d", - "0acd4ee392258a7341cd56bfb0968492fe75da0c9d935713a6ac883525a4a520b5b7940b05e3f5e0c40372cb11b7ca193e93f0d3883fe5840e66346aff0f38829322bbc1f0a0e63ce5e528ba5b13596ad7ca19d20b2a7c9bea4214\nB = 1ed4805e53630b886cd733e5281f6d2699b3c79da615f4056120165cc63858ed2ddfcfd0af0c5fc54662aad90f26c55dcf70a30d04ce05bdf61028730b900587716e690dc0c6e02419622ab8c115078b92315e7c7a5ffe38c4a404a2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 40f69f2d1660eeb6e1840164817621dc95eb930875333bc3f62a644ca5910c1080505de0d54fc9fb6404a61bb2c03b3981e558abf9e86f2047c3928599b529ef3d91c7ccd13c1d69431fb9ea3f02b001427cf519d9fd8182219ad904f47b3785fa05ed24cb0ceafd537311633a2e26c27e61be92eefb28a49d7f583cb6e072c2\nA = 155fb75044fc54a6ba6c46972e2f97531861b8d6afbc358db456bac33a44bb0545deea2fc83023c08b7be473eb68accf5b65b3c5d6af88bc6d8ce722c80d5d1527e475905226b01ab9d7b5a6557250cf8be935339db330df2dff92f2e88e80da\nB = -8c6016966a2cdea4b2d8625aa367e1d079638870f1b61e6b3c3a1e6281ece41018d2ce93684d1f0088d021107fb595390664c11435c6c0a7b93c2c6895217a89c469a37d3250dfa457b928ba6119b5c9ca5f2d47b36e60e4325bcb4383\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9b9e6e1727326fea099eeb008a36539f3d47e3882b77d6089032b99c6cd36ad79fa75b7c19d1509b3ff022ef781b6a8c16fa6881f9ee2c4e00a4dbc93a49829622f4ce6ba9c55639656102d81167ab8a5e1fcf14d71caa60be732f1fbc71250256520c7c5a4579c3fdafc39356a2bbf2c7ecc526dacc0293c7578424c939ab6e\nA = -54cc11ea9806ef27911ba721f19e2ccb111045711d301863792f0cfac798758f0a29111e3a0f84d294a79721067f50858767abf507cc10ec9ea3eb27a91f06e7f6b7b4be7001b548cb7fb734166bad6739935081bdf6d35d58ef56180d377e5fda\nB = 7263e8b9a6f5387f44c55af64b64160efe97ec8a8159e723ca8977bc17c861e22041ea227c9c9bb467faaacfe352b03cc620eceecabb6db2db108b49c69752bd0cc61a5e998ac2f404ad052a51286ccbcfaa214ea8ec14cd9a2a6db56c3d9\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a18a7498ac9194f600cea3d66615595c27a3efa7ea196ba12a80b5f608f85fa72afc366d23f5ca98452dd190b8f86031a9dc097f94a217b29fa676a6042a3aed2355cc8e767d464a8adb888491c8cb82dbec8f117f57c4a07b41e7e6f6cbd7dc25418603b1d1d865dd2140a649c9d52019ef39dbb6809d1b28b3c1ae64fc6813\nA = -1b663403c73e4a9003467ed12766f16354f79073ce89b66066857d19f3b42791eb360004d23e02874254bc6db54662717739eced153944c4776f334576746c5c4145b21a23caa2b2a137498554c7b749efcaf3393c5457b2bb87ee2ca3bef5f191107\nB = -21d12aad97a5c6e639a2ea0a82b1292aebd418567718014465a22b9ac5c8c927963a2a4530c41d5a7a6c14805e56a7092c8716e4767b54a393d8552c5d3c366b39fb3b8667c60e6075e9293bc938e407c53afdd1174843b76aed187f56bb4be5\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1983576ed73d4d87d8b94cd3f70c149c0273e966176b85fbbbb7b3202e2c843bf1f8f4546ad7a4916ea4c731a22bd337b6177fcd2da8bd301f3af9bdcad800449b57986e7cbcbc7eb313d6512b2894c0cbb6cd753a870860a49d6a682c20b5e883b8c4839b3321aede51bfc42bca163a924191feaf05e196d8dcb7fdd9941a60\nA = 576759af0f02406e8dafa330babe9473d9d970bf371ceab30d2f98f4470f669e042e1708e2677d52cb9f99deb9b53f30727d16c389bb63e71e923475314b615762c7612269b5ad7bcb5108068bb5159cb8dbb8d08de2bd4fa4d9db6cf6e3f5997b9b416\nB = 1a4e34794747cf4aa626e964b839ac497b1357090ff63088f9fd4399312df894e41b395d17b8ca1806baec6115b1476912ca9c4309f00a46d5f7a52c8f640075422af06d6d6d796359132f4955072ce90e61b40c992a155b2bc31c262e753aa7d00\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3448648ff9f7425937b6faa54551ce14dd15566e5d41b2bdb1a8db62037459235a5b9546d289cc2295b0ed584fab2e1a798bc25a0c114238f61ad3381a5b441cb67f92cbf66007c980db3351adb9cfd2cfc769b5b9b0bd1701425ce1ee8d4b9f438ce1207fa850aaa1d3d1f970aef874c2b2499a150d29c2ceb7bac375009b77\nA = 1fb54cec882c274b98913e76342a9b8e631bf1d381fd8a4f7e0eaef475642ab3f5da70ca2e38741bd0182a959e5e985f1e0e7d737beb8c725c9b5ea22f7ec25b6e564809601e8405a5b1362e7792791f55ab64a57c03a99a8518d7f65feb0e21be619a6a95\nB = -8180d172d3afe00e0423245f47591d5f750f20d2cedd8ba6ab6f9aa24f74498a96c9001a0124c4f98dbd402b63e71eaa3a7af8b0d2fa417fb1d45f64e10030232b9155169153496aa202745a432e547002954eedda7cc9c1ca76811bd902b192f1a1d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ae0fd585408a99643271eef575285a6261a4c4a92c1956b1ab436d3cacc8d4cffc07044e57b357ffa43bfa9aaea57824319579c5c3e2fe4dd48bc818178beb5fc1ed60afa08828657d00bb88894c975378b1dfb452a5b88fc3c1d81099644a998a47a497c8a2b12c444fd2a088f47576b7f4fa40f34a208fbc3348ce33e59150\nA = -7dc7dfb753c0bc3ab4d07d5aa78664a7f57d64be4d4780ea81e3efc967fbf1bd1390248bbe259da32108ad96bd8b39f2c9f118bfdc96bd06147f812af831288bb687e4e1742dcd1dbf2b7adc41afa28d07dfb8df8bb2da5359e66330f5c65964096a96b31dd8\nB = 756f3e407a3ae698f103fa37759e90554f38378a9b8eb38581e0970ec8f9c00f8392612c61aca5fd37d1063b78c19e3109f35c0684ce523c634190b3164ef06959cc42e2b77e1bb2fd50eb59c3dccdb6090beb809ecb0ca30457a5c5948328eb218e219d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a2aa4550e855623a8ed488bb63db8fa4ac374c1ae953781aac590f78a364fc33380ca2806445fca5bb9ca2fc7ec4db5819dcd5769e3b746286c49a7c80149e7fe276d095929e2cac6ae57e8102f7d4c96261ca44cb6f1601f429528495b6c3169e15f9babc5be696074d45559d5abdac42393094c450d6a4a45bbf60ed7847da\nA = -16d0aea9c752b2e6e4e13f7ab1f0a2c1776874967b0dfeeef7e00f8d9edd1e11d2aa702be45fffc284c47811c51dcee184a134b8f6d1874026eb51e2ec80c94837af4602cac3efde556ebfff578fcc56c00de99a43638ab68387ec087ee269ca64233eb5b1762ae\nB = -3c6b60b0ce4b13a5d6d9ccd67c76ec6b71b94ea7205e408eea099c7ced2f3a462954741d353d0af850b10ffede8ce0bf80b6893288413674504829793d7ae0cba53b163e3f26cd99beb0a9ad540f6d2cd5097beac604b1694a9a2f4c48b28338f9d6a63e75b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a1a8fcb68c53846b3edae33ec070ef5cdcc1346ab3a98a116344e6d2810e2e3f60f0fe435fe7ff257c7ef4c122b3c34c776f4912a9621b6949308e2cfe2e0827536c7464371ce804bd7cac1d76c5bf8b4a6fd4ed56b65434c3fcf0ac7be543fe2d09ac01c564d7b9b463740dcdfa9068d4d8e33f29297ab452e6ec55c263de\nA = 7c4878334ccd9e20cb11a643b206626ea5d0b20973f18535cd8f0fc2f0325a67d3558e4cc9cceed0d88c6d2215c220b8d0ce230fd701502b02081e3f6548e58e02bc2e79e4991f8ef188a84b0a367758b4e534b72cd87de7f82a26de14fafd162a50b359574812cda\nB = 117d8b1d2a3e2049e6edbb9494c68a97145ac3e658aeaa05e8ecec4b090d5f467cde34e05fa7f5fbfa32f1d9dad70955f22130c358468eb371555fdf57a40e1df398c166a22a9df2e1f4e18590b00856b4f880f6629f1a4296056dc66a29b6f0f25490c6a8209b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2cd3de06953acb87b773b8bb28172b24adb283d6adada676f5f4548990827635c5", - "1506c85670767828dc5b4b91b45a7ab89a700d70bdba4e0355da32b52c173305767721d18dd2cb6c55f890611e7abc854277a453c7500efc4cd4fb8e6c9bb7a73fe5c77045e715fd35d415b3496f7463ec902cbdc18f9f6f67c33fd78c3210\nA = 1a20ad042f46330df937b879c72ef00dcf39fb85b59186b8e7a9d40723288677ff6ab2b9bce95f34f2de37887c8a9cdcaf231254bd00c7e25b6042695d7dfc05a11765120d1dbce29dc74f35aa1492ba0c5ee65114d9a246b57dcc2eb2ea4a310be98383fb934121db20\nB = -f8ec67323cff9d53499ceb3afd44b28f0538c39dae8c965ea27d645b430c2f8a4965eadc8ed864f2549eb636ec558419be71f986f4c5783d0dd5253738b876d9034735bd13b18fc670438387f84848308d9357ec2aa4f6a453bdd36ff08d54a6800bb41df416b17d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1aebe2bc35eb2e449bda63513b1bfb55988cc8e6ec8b3c8fed5ce4dcf53b95f1b438c41e3b2348412b35e1f734edba30273935b03d16efaede429960442a01849c352349e23b4af88de4d01e9ddb53ae900418d49a84b7fadd2669261a574557c4fbd782f8e8f400895f6a6c9679b72983ce01bcfdb641f5067c94694e9eb80\nA = -5f97994c39265b5389526e3847876a10aa3699e3c3762a127d1a9f892180cce68ca6139a6f71b235da26c287bd3e1aaa1436746d983c23c3105c33ed2e06baa1e880f1744d81a80b98ee1f16220940d721a92118a9b949d4da7d1477db8f5b357b3ceb7df34eb5f62078cf\nB = 4bb4f8f4f4c8e63238e8774ed61a7eeafb3fe9a6e19cffa648defe82f4846e3378c892d223957564fcce79596151658a726031a6921cdca0adf0f5325d858c048a6b94312ebfd19b803eefcb93bbfaaddef120ec3b8c366b6d978524d5c74218da77e4c3b5ebbc66cf8\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5d64678a32c163874d1c81824d628a1051bce3b55c37055acc47a8630d3fee648df5d319e50b4c56f465bbf696433409b89c07e442425d3018a059ec757d77b3a40d516ca3148010036b003721ec9c999665915a3c442d95ec3c01c232feb201be08c88fa3c6b0769e3da30f1d73b66f98e31f4306bf4e23de78e74743b224ab\nA = -178d81e419f0473c426e24428caf25d61b648bbf963f7fb753ae15e5ea3706b53b00bfc8fe917ac9fd6c7096518584566ff71e6d35197f9aa25107a235678cf9ff8ae1501c1d5a15d2a27d39d066e169745e1e8c808209bcede0d732423d0c9cfbea322ba3201ebefc5315c0d\nB = -27ed464895b65d9518923fde5caaac0c72aad0d1b38fcb7827d6ad4e0c8dc09e119b8b98183f0ef8d5d1133f3f108e951caee035bed0d48bbeee6d1ddbff5864bc192b84eb8a500cefd223972ed51c7f720d1736646825f95f2f10ce6ad47a267bdd8c80f65d644df158d7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 52dfb6bcbbc5cff46942d76ba45301cbff76e9b894703a6a7fd1af29d615336372d147c3932589affe5c6533f28d3e6a57ce2d3cd7448bbd81e09a13266ea31630cf044f654b87ec3fa3294eb65873964110fd42d86e78d128bead5f117cac98145051552cc3a86c193d738b973f866d068a8994a49df3fc7c7314fbd9805e80\nA = 797c67ebdc083f3c8b3ddf9847b7f3c2a39e35ce2119f746ec87fd5d86671d8fcf2b4f6d440c43e93f45019032e629879799eb58adea729d43d2e40ede6485143bd35979609a12faae7e4393879c40c0511c886c66a24454e4f9912bea944eaa417c9942f09ddfb227feb14e4b4\nB = 1a599d1cd0ab3614f50b71b93c999942bd3d4cbfe7900122d5083151c71d9e0c299bd927095c5c3291418424a7c12947389bd4e0a3c2fdf67b3f512094ec0ce5b52695e527de2b3804dca2edaeb1ea4b487911053272ea926cf2fb3386dc4b1dc268b808bbcf4eaedd21168ca\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 99bb9082e4537426c61f3b813f8c97675c44ba9ca418960ca6e2464cf61ad4eabb01ba00798463567ed3d829d3f14201c740f19fca623b1e9b57b534a65df0f070a2130489afae89b91003cee432fab11426c4d13b7721e6f9db1bbaf0adc0064b33e4b9f4b795511a0744b52f93e3db7bc9c0a991e4e122c463ff344fe14cba\nA = 187a8144a0045a92dcad94f0bae7285309ec8fac7dc864b08914e5a4dc3b1a6bb9212161a18c22682ace16a4bf3c03dbaef088b09844902a3255fd6adc0b7c6397dda86d6ab67204d8061c36ca20fd4bb348202037b249f6c110c31580148db46dc5b1bfffa38a683a27054c35326b\nB = -e93ff16817b725016279a32dac247961ae9bb00af890fb49c4fd8cf5e815cf98b58cfa1e3735095e6034c9a2f2b5d8030ab30e2271abb45b347d755cd9ab5ab5ce37950380cb306bbec42b6b8056793a0955bcaeb23e2d6a9548684030566eca2d34c458f224c8e337cb8e3c252\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 631f53d02c031f592b3dfaeed106160488c08e0672083ff195b22a2c0b006f11165a245acad6f35dfb15a871a9a2b45c544111f71f86c920b42fdb6551e56c55199e6173c00e27c9f47256349a80236bcfd3acd1730f823031ff9ef594725cb9429ea183a7fb2e03124ebdd98d435313e43819d995c4fe81fdd4ba718aeade94\nA = -72e20f1aa2b5f2c4218fb9e11ced3f45a218f4c83a2017d97d0cfbbf227c9082cd43f939c8909e52c8795cfaa75d80392d3649dd85ddc35bf1cc54ba389bed9e9dcf867da1c05eda080274beb6b868b54fc85e12ae127dcbfffeb043f9d59333d0ab3374c24971e1bc7269450b418c8b\nB = 61cb021a3a957703d14061c21d3b0fc19598e19a17df9d6f2418c76d4d37b3f62bd4037aeeb1eda37f83df44c440f5e49924cc72ec5b153856c6b621350ec89d98859d9d1ec7ac4f0c418c6599674322e7d618c5ca588d5a873d5af356d4771c6cd375f5dbbbc69f50b982b8c4d1ec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4654a62d9491f28599a976288cd2068d8e3228da12f645413a92f482efc66d1737495cd4a4c733f147eb5414a2ef6266a116ce264491a3463c9df1b030d83b315f76f3bef8cbccb5c538478a65092547b91e991e6be91ce4549c3a6e34aa7b466e63eb3b88054f6714083695c616a078ed54e1ae46e00f3593af845fcd0ff51a\nA = -1a342c154aad619e567fd32e7053aef8d98335a4fa0e35bf06acd7998c43d821de1076dc1fb67dfa1156d7ff30203ec736384a9aa7f5f08cfb302eb3a2a7179b2664094c2cc0df73fa05bf2af24a62b8e394fc76014dd83b434df26f8a67a624884a0b9b4f08f33e9828ae64f5d0c8cdc2b\nB = -2c57e15889c3dc9c94361c17585d506933a72fa954ce44dda9f5e33408552ebf49cae87bd0be35197f887fc6c7deca1452a4345eb67d19bd2e7d3dcf651667a8900388e4d5ec71e9433e3b01d2b3d91bb94d0fc3c51c70793f978e4b5ef93a9c6356c0b2f7accb9e4eb457a2174b50dc6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6124d9ce4de2880ae3811836235d6d89a1a4b710f1d5a517153ed7729dfb5b56b0ac10a4bbc811db9b26465f03cda355701f9f28c5257fe288743cc0789cc54a8661f46e36eec357580b00a84f1d4c8e3d689bbc18242f1cac30a87cb7a47ea06f80d7c5633cde4c8cd8a1a7e27acdc3a2aacd608cce9e2efe7864d41a56ceb8\nA = 7b48a9663d914e0225d7275e965d866ee6649d7267474d5336d28d54027ffe8572f4aa26230dc7abe9957d211e6c2c8f3185cae962b878cfdfaaf6cfe32058c299247f372ae170a1f7cf71380787f6e90995da9ca5a4be8ab1ddfa8e6e5dc65b6f168b9b8e29e0257e0eec853a6e1911b1afa\nB = 1fc4dc77f4a18d4406a4ba536e500aff68d133c6e7725717ae6537b527c6f40f93202a2292522fe7d04e0ef804d1a7013b04cd3d88462fba31534770b56d2e5672e8a6ec7a723186024c40b4717defd1433b9967bd692ef81d5d4e39ba10a3223d250ab6e71d5d253dd0a732ed386ad57e54\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6443de73e1c826c90aa36fd7ec5d0c3324c42058b1c35d3adeda1685470d363732d23cceb08c3f973034c24fe65506bd33dc45d7d617a53048dcc103d3d1b4fd0534586c2fb7489ff5ffb98303bb068fc14b1bb6bb43f763dca2c891095e613bb7b6920163aa6cbce8cd93d9d39f4512b6e0b28d361ae11cf76037eab4cbc819\nA = 13f739846ed2c3aa0a1923168cbb46f4f0a2f3942ba57bfa5c426cb4d4b3d80d9530405a31bda329a1814c560d54defa3e03fc4f808606a598607783d539dbb1338d5bc0c2e272a7ff6ee6f93e1665d6f5a0ade30308fa047d", - "b086646c763106cb875e014e2c18ff8837e4d4d86861b85a5b7197\nB = -ba019333046f76325fa9f258006a7c10d27e89f6d482b95c79296c07a65b8e3bff4a9c9fa7e5d0038da129390ac851f8c0651dcf655a3d4164a731cd20a701895c12a906c732906038a8e459aaeb293fda21346964a6d53fa3e370ebf43c7ec8f66229405095c6a509d0fa15dcf45de8d0e901\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = d3a6fdf4a26993edd175de9a0f012e1eb15a5a1c4dd2741dfc6d0f9177cd5645508b8ab09c7fb34066ba893c38144c7f2ecadfc2b0d15728b407e5db4fcbbaf1871580426400433f14dceac43d28f03376e791b7ad01a112981f29ff4b66102305f0ecc4fd134c2cdc79a5e9d9f085bfcb7e6c187980e68b6c7639c12e8d200\nA = -464cb16fdd395e32fdc613c63ab4768f8cf72a5b74a0a5b0cc581ee4aad1972cd97db7966d3124e30c9a1c80d85c46da2d36eecd7c3bba5866f9eab4d0fa55b2d440a311654466432c681372a80a7896c9163c12314ac51f652aad68fd9012dc63fae6c7673c5da8faafcfa1b4ed5550f2baede5cc\nB = 40389ba4d2f5fc152308c9e8a8c36258c770fb2d03e6189b96c4f8dee97ccbe426cc14595c8482e9e22486b61fc570f0e7aeddad2f4e3a480d4b75d14294a3b912928da5692043bd98ab88ece87a9bbd973ec82f990c0ae6091245318c2810187d69c38fa80e835300ed06c0723fe475f3fb22de6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a0f9eff3a210912828fd7b5f2d72479cc9ccdcfd3e8d21739e301de02dd5c257c7ce4bee2def06c9d0c90d5a86bc45fa9f31e456d353775916b3d5684759e4500f99ca1f91f6767a5e2f4b735ae4b756d56c358a06447fa2c2ccf0ce667be4ed143e9e1dc627a561d92ae53a62477270a7944482cbf671138bd2a85fce92b08\nA = -1da555639228fc6ead68049d836d60a4927ee77472fa0ffd3c787d55b6067012560f5b1c2ef8bbf6119345dc6419444c675c1c9cd50602a93ba3718a5b3e1a30bc108d796998b24474cdad19bc2960b295fee97e03f2ca7589a3daf35bd28eb37a67b5d2cb35a30998d5f8622bd7e6b7d3fddd1ae9670\nB = -291fea1ae6dd1c66c62ae3a3d22904f4b4adb2a48cb795d50074095345d661a033f67b20c5d7231236dab871892deaa9458c235c342bc81457cca3f014a75f5124ff4da005dcc1108e75527528e5cc9c051a97fc6cd202bb9166f9e72e366bdd77c965a70592e5684fcaaf2e03421a2025ca190fe158\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 50f4d25875150bab63e4162265a632109d6b4743f9d6b55306858034732a4895ffb3720286acceff287c38320ee9945dcd0a1bbe5ae1456b7f36337cb7d22b679a6821a450765471257d52b6ab7d59a763e75e9e64581a93aa54761f6a760866d6baf186cdf4ad2b1a6af26a3e76cdc261d1f07b0a7122c8ffdef595812e7208\nA = 78a1609a7f08c93c9bf9090ca7c93459aef815719b5dde5f217567a9f68ceca05594f6ab17a4666ce1c0c4434e0f4f38ca1f33e501d6958a10da47211cc011da219d4373d2bec4b7c6477b1ab3b00b6c45279212db39bcc11d1e7ba49916c4271adca7eea531adad509ae119348f374ef1203c5af8bc019\nB = 152b46095d3f8db5e6e1a9e3f35c085da00e52764b261c3aa775ecfcd38572d2e86bab2f4bf29c2de4fd2fb6f35f66e8685714634e1be980773526bdbf9c43b1335c5d59f4dffe1a1fe2495ff9b7a3fae3e53e7c3208968e1ad1dd1dc8cf2e2415cc76dfe5df9e2e1eb63f7c7687d539706502d56247728\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5a3ad8d6f1b0763b77f5d40169ff0013de638b459e401f50f4cfb505565c8a4465e28ca1bf988071701dbf52ac456e01e170788ebd2b7cccb50dbfe1a65a89a8aee18b3c11986c9d6e6571f964f376f322e10a1ddd9310bbb40f14b0680385c40975aba43153970237c535c6b0e2cbf6bec918a8fa26cb2f69e98d77215c23a6\nA = 1d5c14b0b51cf31e9d97b7c49cd26097d40454978663f8a74095fcbf9c63e533708befb1a467f94cf599a41220ce13493a273fc30c49275412c5205db712d5e1832b39e65c150c3a4b251e2aab853e4ecb4f00ee5ce6982ef9215775a33565bde3ddbd932665aae506941d3ee31b3f9e4ffc0651f1fb4a5c6d\nB = -93cae5dd84584a2a3d88028d6d4cec4146cc5e350b4d92c52ba2393ab69fc1dba96e244f98e2f93f31230904169641aff30dfbdd3dc5fb1f3489d63aae1efd29335345a79ded546e42f2ee4a70ed932699fad17a771ba65fe6e689664bdd1135219aaa905c962d39531eba3e82c3425c24041e17858cbbcf2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 61211c706730a1b98c628b3c8cb070a42e2ccf9fc6302bb1c2960fb165087f210e9d93416ad9fa21634a05dd0723cc23b8d2a846ab7c3bc402999138433725e737102094db5792249b4b5b1514a416b80c804ecfb04653c5ab18b0a34d8777f6c2955ac66fef62c9ec2819f0e3c075920f951f86b32e02bc43239d9218580067\nA = -46c8c68f492d8f7ac7834f89bc76098146432c59b3301d4eb70d9861a6e24c7c9073f910108c7b35538a79de10640291b54e5755359baf47482b97af56475211573576e9412ee017dcf961a090a6ffb5cd995992ab68e3fe60b6186f7595bd9b8acf8695c4f7359cb2ac709f032fb993d16a74822b4935536453\nB = 46953f424d988fd20700ea08880e7e09ac22d60cfc294bd4aefe637408a3cacfcd0ea6822a679b68b665d6bebed3506d25edc83cc7154b83e22953f9d91157cebd219cd5177fede28c63a15710d0f92bd9e542a7586855bbe57a94c520408fc920b3f8d65b194af2b2a580c90db1cdb27ec26ba929de4573c6eb\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 50a063fff02f2cdc68edccc23976f4b3db99641073c85709626292b9475b9a988fb8509a6223f0a517dbae0cf7cd39dcf1e8ae75196d9f5008c661d8b5153cbdb9520c71068e4719820bffda4c393032edabacf99339e0cbafddb6042ef887b8c498e87e16b62417934015172e63e7457242b864a47aa10e203f47320f03c0e5\nA = -1740e8be7b4775725516d37ba643fc64203f3a61e6b0164d112af56666ad97afb0059c2c4981fa81d72264f8669db4e50e11865907655b1f669c88f5935cacf1b12c1db63cc84507af12cf0210f990994055d04d93f148f213e3d4fdcfe9dc42117c059897697914e3e3fa8fdbf0eebbbb9c3b9fdaa7efa0c9d5c93\nB = -226308f8fbb35b5f9d129c0f6a2bd3e5c272a408bf32020905acc6d02d7e506191e76a3a2ac47cf7a63e6306b256f489ca5cdf76c7c3eede175ee4a7acedf922955e92599647b69d463cc14f2b178b88cd471b8a1c1512caa66b6d5fd8840b98b8d070e6593136e98cce9643e006b714388768920a79944be36624f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 747cba0d1cde75dfcc0b2af9072c5027986b3e3917845870c73c452858ba21d6d1615eb71ae1b5a03ca44e22845d5432b368541b52a4bb02498668e8b99dfa2eb90ec1948d90564e6ebc388ee9816e329e1d8da0d3e2b12d901d47e22e8a1fabc37408be0f89e7a4ab0f30a03f7e2ed817006809e69c21104d0efe548165f64c\nA = 5fa76e37aaf0eb3d34d4f4c590e02b6c63fc62b1d4c9e172cb0dd82409df87ecb43a1680a2764f62d13a5e919db2db08feaf98d5cb92a859dd42bca1047ff57b8fe5974fb3ac11ba2c0d8e2203750f30650db4b2cbd31d07fe18c4df84a0dfdb30f9e528932c097e89d8f8be6ff029dd970a7d2c2551529455b9131e7\nB = 111199f91b3749f8cecfe90e9b9b6951472cb701beb39d63068c064cbb2a1e1d30736026f781836a52ad0d828be6c20303c6c0bd03ad664dbf6044a5bfb67fc20a049fd37c62ab0795d836487b883768ef7c8f427eb98e5ab6621fece77b4955822f8efd190c417ced398c221215b50e9532a869eceeb605fa1c936554\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 646cdb3ed472a7b4599f02329054846a8da173000eee7533240ade4dba82ee3d7a6a92baa3783c19dbd3f76fce6b5bdd83f1f229b1c71a6faa18602e368f1b0b9f8c62bd8c854844af85c2081924c9a153e27853b2a48147950fb614028e090e2198e613631c95e565c2b9b64a43237fd4052089f9d1dd2c00525dd35fa946ca\nA = 1c8438247c0ca376f508ccef7933724df512f9e0877596f7f4ea73dcd824809bbc472749833b537eec01ab23656e9758da22ab8a4aaca1aab3fe8d2cffa6672ca0c44ac029c2ca6c3e71780c28c31b5f154c8dee782f6ba009a69d83b1a3a03a2d6275bb8bc3932a1170470", - "fb7e405ae081f4770b535edf49f73a12ba589\nB = -e365c8edbca8dcc4cc11986a5a901e4ed0adbe89b0ab70a53aaf5821862432a1320cf1850b515177b630e12692cb025e3aa43e9acee0d8ad5e48bb15e9a3f34cbfd39d285127b52dde58751f572ae68ad98692899ab12d35e33652c4426ec60c5029e51f7e32ec3d2031032aa7b6b2b63f84fb0023c81d031773f3652cd6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7a3e22f4a3f7ae7512ed73a07abb5ce291bc90bad507a5ccc0c17185804b9d231b0ae2e72bf270dbd60170f34b240f716529a449abea0b3d98ea2890a4ce3d9e2214819aefd070e00201e9f271de925c4ba59651e55174c97a13a30197e46997c6c2b152548111aa98df120a617c54b71f8eb8b0c8b4dbd5251f5509fdb8a1a8\nA = -78a99d206b4f095847e9a21de273aa6c47034c9afd4c081a8e93c2d75f4ae5b090921ff5108c863785c413e2f7b4a361506fb66b7561b8b1c5cd537e90274bddaa4e91ce74ad81c6dfbfe1a34a631dbe455d74ed9d041a9183da3bc469bdb214d2ffe893f89c3ae30f8ab99c3aac4d2fe864b891fbf4f537745fddcc60504e\nB = 5c41274e9590c1ea44c113ce505931758f2cef80ba3b10440941ec9aa2ac984b29868bece2922eaa225555dde84a8334f1caede99091165151a39538e5b7390e81df757f521236314239c213e9b874e396a022f04629c09bfaf929a0e9fe0b0c7386b0541446f6a2570491067f64e662d8611c4fd6d1c78a9f3ae69f34d14fc\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7fd27b6549494c9bc860146a3e8ceee785ca03faa94b0ce0a964844e7871e813414cf3f111da49fed1ede5e71e5539f34173d41f9a17ed129016bb9b04c86487f5def9fe350fd4dffc67b6e181e3cb26378ea15ff9b9ebdf1fc86c072c82ecd8bcdc241301daf1b774af5f90f37e45e6126c5da7dd3753a1e5b366038af6ae31\nA = -1930548d105661dc25a5ee303b61b559c4bc1f2e28b2c40cf3e25f98dfe01a7dcca0f3dead6463b55a5b2e0440a651cc9e08e125535e081c742bb3b2f8955ae897909cfca683a4822896d8a4a7073c29a80571445c6a0d53d2efe4a30a79d2fb5d08c0f95b735a1cab17ba40d71b054c9270ba6bc870e58591fb1bf9dc9b7ee8f\nB = -3e2a4c1509494f94406e3843c9446edaf0a6060144637234c6d9ce84d70fac54ed163d77d210bf557bbea0404922c8aebec67a0475a3c7b74bfa2f226403ce987c705c712bb8eb0934c2b390a173c3836378fe71a6939e48d187b27cc7236ac115309fbeabd9ffd0396fb7fcd6d46a1dc683606c757ddc3212f5d2ff3f2e450fc7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2078bb5c82a394c30a287aedcfdc5271eb3246be05954181ae4f86ad2880ce674640ecd55c2ee3f4e89e2762139586516a28558481303e3071cc9ccb9a538f887553bf5726f3849fc41ab027fb1c680ce7dee3982587ec71b3760e5da6956d6894ad8c4526d8de953c0e681ecd44883a21f0abef1544fe601743efd3e5eadb8e\nA = 40b4ba1e977825b7accb941fe0c0a49936a8a47429dfff53502fc0680d705b9fa0efe003eea3ff0b649998fdbae8d0831bea7f34159aa4c7add6bc7cd56fea97d25fb9a6a10f4572c26d792b76c18ada19b0ba06b6142c420dbb40d66be669b7c51d8cd2a5022fe1a8aef7b60965c0176eee69c32ca5023782c5410adc1b15dbdc7\nB = 1bb2f18d7c8d306bf80ae1901115c8dc3d286baf537b812ce06d6872b61e5bd44f3c53d7f31ca8461b3628b255f85338cc325856fda5a6248b7c476532c1bcdf9713dff9932a50e52a9441aff96092d3fb0fd76046a8d88288d0cd55741083a1bdb20fc6e9c20e82490273354bd826bfe001322dde9a15763f2c0e6ffd2cf60019aea\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ef21dcee9eadceaeab13287d6e3c9741811f6ea9d5bd111799ae05260b1de2ffbc192818fa45dd7befc3baf6840e3b9d24cecbcb2cb1c3d653c4aec6531b941d926fb6692f548cf81526acd0b6b0289d70dd11ba50ca8de6e174f502eddf47e57440142c7f74f594a9abcb48ce1873df057b132ccce8b364de3edf411089d28\nA = 19d0109e0c47ad45f57b8bb8519265a4390534d2ea07f969d84ad33556518b6234d40d1631be3c3cce6d59b7be14750aed114008458f50a6a84ff75b4ee7e4b826ddcb2d2293842ed29e4e484260a92199c5c66367c402bdff0f1a8057127c6ffe452498bb352802e0005e6cb084663bcfa82783a3d72f3a2a341b8075983892e86756\nB = -81fce71491eda139ed996f6a289dde8635a3a257ad6756e844c768e66746011fd797658184fb44b0e3f3c5600c56238ac7687b5be42529d5c9b97c3ce10f3219e1e451bb2dfbbb44cae0828ef894eff3b52b8dba4c115c3b471984441045f2c2db426cf5f86949d5bb7662cd40bb3b3172a19ca3fb6858315d688f13c17550e700cd5dc\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a5f90344071790373044193cc4fd92116248aacf05ce639b6aac4461ec3ccb0805ff9876ef44fa71088c295db14fc820f7ae2c0aeeffca055f8f7238c6c90db706d02f2cc43b4960abe3ca4b6dec8bba55327b958e75c60c5d1f43fcf9136f12481c267481a725eecc403a16aa6221346df680560ff316a63ec8b51dc37aad6\nA = -7a54e7ca04b9a22e2b986e72e634317ffa20f6f4ee90353d559db3f3c1bc6b3b92ac6b364f6c5929090373962b49b59cb5d87554387761164982955470cb45dd00c4a8982dbaae3a1ffe700e8903a4a8e4a21eff9d00fa496d475e0e1a205be267499dacecd31551f8a9d437f37dacfdf5a2754f0876a3e02509b78674e7ea2169c43f29\nB = 652001f073d63ddd526abc957bbb48ca74154c8f9698b988178b3313dcde9acbb19ea11a935184fcbcc31e0117d8d2ec695ac56b5a71614a12cf90f21c8882187428755b6a5f11c314ac8b952ced0f65db0987f0f87e20b82a811599f4160e65c7418af7f33604e7b8952b70581e3e02dafa025cecda970d04383ee552abc620dfb9c5df9a\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 67f903e0e5623258826b681506f3e94cc0b086e262bafaa1395294aefc9f6b6323410a44427010d5e8d8288993973ad9939199b85cf02ae0a09dfb69801536a3fa6af5ac373add7efd25ba5fee6d8f040e97056f9f6fbb45795c0bac94c51ffeaf496710b00bc9ddd8e445261d976168771060c9bd9d83838a84ee9428f59d6f\nA = -19c695ee3a4ada840a7e3626e61047c5081867b15843ee9a6506ce45540d23ad25ff23b72f988bf26ab8b98363d9a2997773604f43fa732f59a4b16ddf3a45acdbc7976a1fce01b3dd55559c20acfbb7501730f794bc45fc09b1f035d60413bbcf32a83fd3c41599049a674f165ac5283c42aef213d777ae47eea960f7727f5758146efe5bf\nB = -210697d47beb73f45207340a183a729a1e78d84bdde1c7d8f80bc84559c4aa4572ab0e6927ea175acc7a268d05616201cb235e610d1012500c8ba9351a37bd68b4ec42227bea55cef5ba7d12ffb180873ab9d33d09e6e969df99fca728dc12dda6903169acbad38388fa9b001edb09056a2ee2aecfab0468822bca14a4bcdd3a4122290ec5ce1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5fbaff0ffcfb2330283fe59611ef51cf045bc2690e31f2ad3265046fedaa990b5d5060b3c38f17bbe8b2696e527fd77ead8650d329c2e0c1f3b2f5bec4dd85641022f3e0ae6f66ce98cde1a785bb52eca796ae45c33142e8264621ab447cafe988de926544e1a7036710128c42fe8b574f7ad69d830894237d95a55d1bc7f5ec\nA = 482db04e35f9fc1d87b42bc5efe25a049ed924f816e1b0f9c8ebe34bc771e67e26d6057563fd5d5320681e1207c0b0f4b7df547cd6d5be6a2e0f2bfb088f990b0303d0ef263cf45681e0e9a1147c29f2ca5251faa633ca53f6e0b109ba69bbe20c58a76a22789243d1acf128dcc936602e832a20a2bfbfedf963bc1027650f483814d7f5e6905\nB = 105aaf563d4c1d436c6a4552770a527776f40bbb844b7701313c5ada95180160e7cd4b7175ddb943e5a22c910585dfc184b52935f06b12c84b6431395f28af2eb9ccfa66b2ee8f40fd44d753c6a83d67a6f3fe3658fecc7fb2f4a8f357c5d244422e48a33d0e2971059695a59d0d39b235d5194e919facbae7623ffc92d771532b6b0cf771912c24\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a9d204c1a497f350fa1300cbaf682c947eaeba8b3aa0450c1db9120852a2edd2a0249dedef3b3746298ee42834d869e9f765ce987a2aa4712a1f35ed10d0f7ba9cdef938b073c3a526e5bf45f3510c94ff1fb84bc77b08e2a", - "a50f5cc75e2f4da37a8a711f8aed5e92f7e486877229cb4ff2a4d0755029972323c0b51a14fd1e5\nA = 13fd3d7cc9d6d6821d2f2b1c40c8e070bfa85b994ee8f3e0baab544dc71328a1a57b7ee57392ab6d24bd85f9ea0f2a312148fc4f4b22c589e9a265d97e73c7a5b420bee180409ec179c438a67abf37eba61ac76197f3c9ea5edf2d4b8aab91e9bb1a432ef1f214c043664a51ceed1f2854880dd458ca253f09d6f6acafafec310774a672d07147b1\nB = -8c90ecd56d6c7cb129d1c9c26e94cf919c5747450542cab52281d11d8fbfcf9ea797b29588340d146cc40e77dce007b68c0c24356d4b75513b75eccbef6e22a5b88417cb6c516578d17d871e7d0957c09795f9a0f19b811db75d61c27e1827fa2773846857fec020f98444e307d3e52af501114b962ea705cb0cdf815109054abd00810dcc270d7bd3\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 57aef35a3f5388c810f576dbc30d4e4e5a39248b319b7766311157179d8bc1d7ef019cdd8c2c0175a8424abe7b33565afc0128724fa38f0900140b6f96bda2e78d7c803124cec8c2f2d6649afde4030c76cd33394fb386342d1ce97a4ecd180872134fd4e22667a687915bb4fda21f7e0bc9100ed8cd3a6668ed3a235d7b15a8\nA = -673bb11795d9d20a1e4ce8ae71d041705990463964505befce5949f895fa31c92d53f91fbc110df4e789b3f3f01f184c55df92927b8b680cc92864466ce5590ed2e98901cfb78b32ea79bf68b57a14cddb53209e08a7f430fee23f4a1475fd2640a515f8b609e98c760b4301747ecb61f1e6209b07455f1c8a7bb4e20c269e17937f39c6a2fb7b2990\nB = 46beea6005cf96a2acb16f37e357bc8975f4dad502fc3aefb4666344dde456c0ee7ea43ec493b6aecbc7aecc7d4cd107aa09e874ff564f5d59d7e12047b048c1da1faea36a7e2d02d0567bc4db41b54a75110626d13597db698fffd577a5810286ea8bf50625296ee8070419345fa269a354ca2eb47fa3108387f6a4b2c0ea3e779908a14469106eefc14\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5cdb7c451b2950c9d87638857407276959142958b06241b2010a9f93625f9106f065798f79ce5c534b9e5a31fbcbfc63cd200fc1cf10217096aa0194acb9043ccf7ced30d9f0bf66e0dfe27ee2ecc40bcd8de66fe2ed6f8cb0d874ff7b5fe71951412731fe4e19c34bee64c9312577b9e7b2ac08ed15aea753a6cd3e286192ec\nA = -1eee9d5d3854db52f9b43698e05d6a0f1d1f8df5f32884a775b25110309c46ec5c7e112eb64b2d7f948868bb9670068779b0a78bfc7e17860ee02692ec6790222b4384b9bd7db5abf29c46261c10d95f503b821a4694c45553e0dbaaa977892b916cb8990ac9ec29ab5c3d63ed77138fa1e95f395b3b233d039ab5daecb0296203166e9386d1071c61cb1\nB = -34587c2bf3473a2c5d7f3399d5ba2bb09be8105a0b9f3d8737d67b03d8b91b1c869f4e223d6246abd36d99d84052ae5894e58288a614a0da8d69f1aa57428632c2b059ba99315ea2f68ee210e65a741e94125ee4a723a7828bcc410aa2dae06ea8ed6cd23f66ccca7e85d2e071055787f230ee405e50d1519377cfe0cab4e5f97b6cb893b01134813a7c2c6c\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 95d0b209654de56bd7d6f74afaabed2cbb3247f449d80511d2d3c689f84c9b79587d78abdf0eb37f1b89f1f8dc8a83f7f9fac2c8cda1fd3fd64e16f5597b7f0a1df6da6db9e828ce7be0e876012bd52f5a74ca73ff8ca4611dd9f342bf77b485305ac28a1f8ac7538169f2bf3e4ff4dc5fdb9dedb97fa743fd8ac8791b8e288a\nA = 7821d4b65d529c30b8747e184e450cefb11b5ac5dc77905e6fcd3df64336661c82ea68d588ba616d23df485ff0658fb3376d5276027a40b392f47219edc5ecbf510cf0c5b431b02c65e5f432092f941d32ac5f71ce3496e403c7637f63a23b91e3326d01d2d32e99e0ab265108dc5e7919d3983839b3c7541848dbcd420a594e850e587f1846951852ed76d\nB = 1adf5c428f2a95c27a943637758d5dcd7ca36592fcb9d52ac0b7d27adddad5804e3edef257aa51c716801ad0c731e13c5dd000f11b5ff1b69c198f236695c1b2f99c0afffb5d084f80fdc534de3b0df4597404b50c7e784c3c55dfc9753c414d145eb0ca4d07e2f65b63f3eef8d391250a5500ef64d9bf963d7250d6906694e7670f92e3d5a7930f0f85964a21a\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 46914b197b84fa99addeaf55dd803182083a7ae34d6d4d3a55d6272af40a600563cc8d9f6b48110d0521b8b99751235bd5a340b1743497ef1cc459dccf5d6da970c4c3103c978ad2d513298f1fb3e68b24a9c7b0795f47d8f7f6ca9caaab9a9d80f15982599d764f8738217f9158517806fded5f3552fef8b7dcd2e725ee04d5\nA = 1c9f5f2a0d72806dcca92dac1450a50cba05b5dd571c2b3b988d33528d90ecc83444e3ea8df80802c30fbd5a6ec2ad9969be73aba6dd27e0dd2c842b95371d7547768916c0cb036964d041284cd323c8073095b2a8cb8797add5cd80f03595de9d18af8df7dee0d250ea7048faa47ae0131ba3f350d82864dc95e5829b88eeaf2681433dd4d58b2c6f70426af3\nB = -aa1e1b3cfd5ca0facc75e46d872584d55144620f849ab05931210b4e1526f12679bbd9cf00efdbd8863970e2abe8fc9fa7bbd21afa9e364e3c9e32f51fe66844fea4bab7f3b1bd278fd803f6bdbd0d296321e67751a0b894da338ab431871adf1514269ba05e0cea5558cd5691920fbc18237914f3dbe4b253f774e5dc1dc57023c080a3b90a004b809d237658ca1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ada55d93c533716ebd8c16e23603071950aa714deb942ebbf77206753d2676a7aaf61673c03a4db69d67faf6273828594d85e3c8cbf38460fa2af603fe9c1b6ce104854e7281757b26589f079da80685aec153fc5fd1a223004cdf30247f8398b8e92899857dd199d5d5c32412bedbf9d55f20e52895fc1dbd04c84cabfe1264\nA = -7d22392a8da1966e6cc5ef50d7409c614f8c8f8e5791778f68a00b4a056d0002707933043d05e48347bbd4d0dc1b6ca32a1aa4bab9992e7e620263283eb68d97af13b90a29c1b7dce39ec0b8a63878e8d65aebfb3bff4e67129e3b3725f999f1ec9ae92007911f2cdf738499661c5b6c9bf27712d0f29e871b17318e95c3d14b2e472cf9e466bea91fb71a493b2d\nB = 40279eefe59f954aa8c51c9c214fa07707b1d095f697ca40edb820401a45c472d1d7bb413eeddb64c14ce6144b4863fe9337ae4ae8698db92facacd6a56f3b33129c5b608eafa29e9d92dea620113051b926b80b75f320d7ca3d2ab597168c68774e68c47670458f5ef2ffd4604f20bffcc7817eb09c9057fd9989a6786a7e067ebe6724a89e7d1580f94ee4ed502cd4\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4dcae9def5467526b0ff071003e56f5537852cc0bde9d86eaed2c15e36e6429c68c061e12d321bad12e29626b5013c28f118ee59624ae2f35d2c53bfd89e6afdb6db79f0321ad5c55cab03e6a1a97ff7bd58c760d0e9fd7507de987ed2f94f9c79569fe7f03652cd53c67ebc6bd3c9e6c5672891a9d2ee11b300ed3b19753c0f\nA = -127f5ca6924851faa2340c4c8f425b1dcf41b313c5c2910e5eff8ef2faaeaa43305de2b3a65a75fe54c00fb30c0ce3e8007db1ea222521190ff1de6d0cf2e777ed61ce8211dc167bf115a77890d0bd1ca786e967a04f077c89939ce484bbb1c560f669aacf7756a4338d97cbd7f09a376d2dfd4d632bb451f52c03c05762f050ebbf112f8dc5acdd9b631292fd7073b\nB = -3bc5e9c352c46449a9155b7ce5478c771293599cd2dda58a962010f1f21d094aa6bee03f9311545e8dc6213f6aa73c08b55bcdf4d1d84fecb9eda35c83eae5fedee75b2d15a003f8a82b2b788ea19f7460fdd8f447d973c950b3b250a3022c19ff312ccdc86b6ab50c4ba627b15968c8a66d306bbdae8e88fe28c1853fdfb3fde92353f46b5bc448ae42306a4c91202f03d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 62a812e35f46e04b3afa7d26c8fd4eb168b6b64cdc839ebd0a46bf2a3a712af8e97380cdf0bfa8a274f7b73e887bb4cc73c6104a176d425aaf5352f14ee51ba549a6926bd8d059b8e3826b174385d4635b0c36df75a4e7da44c34e51eb82322b34ae00e8c712eb75b3882822bce5a2f2f5fd74355319ebe1973284c690bed2af\nA = 71c57b08127a956f0c17fd3c639bd1923ba19bfdb83c0cb9dd78e62b8fe4b7e0019cd0a6b73a334c622118f96fd6d91c1e06d4dcef8a3d0d6bf8f5beb6389226c50d14d3947ce9f24f7e0e6a7befad2e4e92dc9ed8fbb9811d908c03ac074b2a5c67b67831a350c4d548ac70810bb5617d261a045e53cdc48117b9fe86d35950d0a181b73c8cfd35edd31af031178523b\nB = 1cda2a51a707f8c4d2cbff6337c3f63519705614c26a489b545b1faf366b705af1d953701b568a684856fd3186c035f878788f7e5db", - "ea16b5e7b6e767cf611452a4272abf2a9c5e72b7251a1ebea5098c60cc5bf649cb70980b97d48580967ffe2913309b6b78cc12d91025ae403928851902dcdaaa60f5b323a1302a5ce114cbe174e3eb3c2fb5eafc44076396c23d53b028d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a9213cd809d41b6bbfc2123bb84860788ce22d5b91f8e24fb616efc286a218ae9652b42912a58bf8ce596a1b48e4c72f27e52c36be1940f7d2138eb895ee36bbb917a59f73e0b6c3266bf4759ffe2ffaee3f6179492658e0778bb43c4df4bfa1a46300c9da496033142ae2c1e33333fd7e82c5a14686b255e224c51aecc2a590\nA = 1cf4e2d5924510a5fd06ff4eeb94a740e430613277149993004b8de1a2b96ada54b05365f305e896df5fdffd3d7bcb54f9a9dba9689e5ad498012f7a684d083c31d7017aaaee720bbd42382e526a35d2add21d9369f7faa41dbcfe3dae426948a402635771a977e19d5c353ec7c1abd279975f2effc0b7bc19990154b723f2f8c29e606581ab9d3966702f68d8bb8065e9d8\nB = -cdab60f9b8e1add4c54427b638ec5f76b30654d3649b500f833b2943bf6cd5d8647549657a8ff999eaffe413ed87e06267b97bfc1b77637b57f29039235548a7569fe6d4bb16ae9c6cfd38c0b8c73aa60797d0d69b03d5a98314f7f7ee25df8b896ecdfc782cf8057f038b6c3e79c99df52f839fd4eff302ddd1256e51eb31cee24585782a0439da3db2eee79a58f889d8847fe2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4dde3d63aeeee47441a7e733bcccbd4f2e495ca3c746468e9855177f7672d5d82e51da8e268ac24e8971d802e25d842a16a6b8d76b8e46a7724108c02d38a4830453408ca5ced7093676a1db4bf4c94b9b7a9531ab7c26f8de520bafe4431a55a5f5d8c7576427a0f5bf2081b998b82da2e8e959f2ec4d5141b55e40bf6ddeef\nA = -5770ea0a75ff451fc2c86d428f2569884b2c88cb6d9d407cc22b191849d389f57a5765b83adcea21c350b37bc6d750d4859f547da22ea8a3698a5cb6154b946331ae2ca18e7eaace951dcd49405bf8d8a716f7762eb242b8bf5e4c53a662c906c3be89e53ddf7a706ee2406c7d0ac17b54ff259c1bd5a092325938832763ac4caf0232e80a016cd1994441808d8db7e546de3f\nB = 7e4246ad4af268695a51912053ab6628969af4fcaf7f1e97dd977984a1604e8c9fe6b920f39a764c27d89f75986a4bbc122f92ccd1860f24677cf346474fd9441f572f769daf834e6a00cbc027e15d6aa7ec2030becad41e1068740cde82abed768de7e2cfd325848f6063e2186faa76982b9ca73ef22434a28bd2e3a5ac477af50f258140bff938d3fa02fb904a8ee0ef3c1f6fed7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3d8bde8d0625fc46dec46fc657c49c8ab12a988cec4ec1c24e6f4d8ff94514c8d8fee4a08399c6bd23fb6464a38bb5f249591456c283325e343cc289c85df0ff2c1707a6e407ff7a24383b66ab603b75e2dc3835ffe9274eafea148f20764b8ca30cbe483c1cefd51f82dfb93d7793b3ec19a57f2ba03d884f345bcc3188fe28\nA = -1680dd51d8be6069c86ae157922d55df3b58ee6f53738677bcf7332d6e7ef304ecc7ff7c5a5e1f525459d77202f3e815c68f17f9a6bf358654a92f9f9acb252ed8e9e6a849da7491f26d0e33900541ab67ce966d042607258b4382b8108729a703b429babc34496528f198a7e0f814db80fad4900fbccdfb64908febf5e09805d3a3049c0f164f0bcdaaa9bbb06df8f05309be83c\nB = -2c6c6b3c89f6e1d1cdd9abd1a9706e4f642a25738aebbc97cbd60e1f4ad79b419dd54bd14f2bd147b1d8e9bfcf92faccee61a43dbd1a2c084bf06a2ca476b3d169fa2c99794fc827b7f4dd010c0534e7cdd03d00456033ae0203b78a7ed229afcec2d1cb96892eb18898bf53584dde56b4316b3bc5186d97e3a9edcd059d7fe14561eefe4881beb8519c1cb7c3ba22cd2e13d874aab77e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5b4fbf0445807c8feec7efa3c2bf8dd86b1070638f3c87f1e173ee980412a28847b263a266506e70381aae919ae05d306d3a67a6c1e72c8ccf1c27d6296526e87f0f436c98fd1391f83440b58fadd4fb1905a484bfe8f516661e7176a268660387fe6a7266ef02e5fad91ffa69247bb11cfc1b5c3a88c76b7923a26f8a31ece4\nA = 65fe4d55bfcbba2bbfbdae831aef3dc8c8746e1d04cea174c1d336974d81d026f562225b4a297b1c3b044ccc5dc9c830a805a399bf26c0369b52ab0dd2c0ad19e723fcf9f5de2990ebe5a1266653195a2aefd9a392fd3da8c22c523a362f195babbbf5329018e3b454221b3e77cd0dee79f612f86332b1d104aeae7d8d84ad06b107715bb76bce20220d1340ecfc666b2bfce812814\nB = 12f775dbabf1c112523feab443f6e95d773e8220d66fd87bb7fc702588136a048e17ab6845a9c784dca275cfa445d007e8d8383740b156df7048650f89c5ef1a84148488fc405898f9e326cb8052f626c8881abeb70f3a0f52dd83e3ae0cb82d178cbfe8c393449caa2a87e7c8e2901a87e276b49b6d012f3cbb65641add3694fed3e3177777e78fe375f3a3b378091bb8d2998286562faef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4f0af7cb0c4e82d0e6589b24b55528818bf2164d41f58505a2b302a8f677df146f8077945dad3790c323e19b37e3379eb95de8abdadfbe4417f8bf8da643768a622ad4898513fdbc72d3b1d2791ec9ff40634678faf0e17d6e0851f08c39405907db85b74937ac403a9a3a1004013c7bd95a585728010689fcaf63b2031bc8c0\nA = 156dcadeca94985ea8bc0d1378daf1e85ecc4c7f8b6d6c7a5cb9f9ac368a97c07e381004023bc575691c082b5e9e13a02fe813a55e76196e4ad4b0f9b1e089bb71a0d5c94254b66e3e645fea25d69bbc5af266e730482a60105306d664f0ddecbd76d54e7235979aa2d806b809b3468078b5d90aa22cbd2c441198d4a52f6259972cf3d02003dc39dafdf3581638e56d08c5181d36e9e4\nB = -9a54586072d093939ad86df11fcd3337ad7e9e478dcbefb2b89d7555883fe8565abcd5b0a9c88ab135ce5327b2a326db645bc7c0e3ce24f902544675ff9d946abf30302f123aeed0f4e28edc72758ffa760277caaf4817a3ae8615784c81896d2404e2cf47c06b09085cd0ad1ec46cfc1f04d0272eac29e774b30f19939d08c036b185983c93ba15d1d27aebe4a357b9f6a298acca3940d2730\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7c3ac09486a6fb518b98a9bc8a8b382bf2293e2c1154470ff7961212430fe2dd28697e49256b1ad8add082ee27b6ecc016b120e971665be801b720069d30c0a8c6ea4795613017e8883e5c0d0e68f982c328379d7a0afb7825c553e087b33e9d78f90e0b95a6597076b8ec2c1d375e2143bb778c318ca0680a64072cf9a4fc08\nA = -71d8e7ef13d63b4f417c01ec1241020a8ff4c9b2db531500984fd3e45d22b2bd581894c8a248ed7cc345e70a5698407df8f0e4ac71ed2c0d42122a4f92279346f463aed899253206786928a0eb7c37f2e51e1cde7f97cf9288d85c3ed7f49e62af0bf9abf062d2c6544d83b9d3438b3881e0d07b1fa0f2a4446fd43ab3b4f81fa2cdaff199c87965e298943c68cc15f2f3f3225efad68b73\nB = 64d52de221f102af62ab1e9526935b005c81658f8fefa019bc58e641023fa785798ed0dff8f7f999dbcc2ecfa47d5314ac6676c82170d6f2b18122c17c1e1ec1b9b54e333a184a46ad35b2150c8165f0de19a24b98327715e5a641c1b6d3ff9d247c89c8749e775e6fcf5f967c6eb5e73523d4f1ec12db7321b14398f26201a364e1371f0ac922781ee252c6d2b3c657ef259ab73cb7992a370598\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = cd08b388ffd41d0aa29a3dbde74106c57b18d325be8f446a2d9ae95fa4144037dbd41eccd50fa34096984cb11bce555c117c5568d76a8f79d308ce11043fe2413d37d6aa60c366af6c1da93d525e4b2d79fc82c0a53ed62fbf72c919db8a3ae11f5ff8057d7501f5f6dfc9ae461c308d21919d0de9e31b759d1d8e3526fee58\nA = -12e58708c30c93383cfe6e99ee3c5caf1900a7e610605706e77d8f428fd59db2884f5021d7a382cb18b75ed22528961cf43be1c700c581ceac3877e83eabd860583e6e94f3f2989c179ee5047c82b53d37054c9cb7ae08be60a91b10d49510e9f0b90ddf89f93790c3e18cccad5a9d223c605a6c567550e2b4950e184fd97dd68bf30681d3f9c585365de2cadf36a43f5a5305dae555396dd50\nB = -26ea5079ba7ed137a14d00d413d6f818e911cc183c88764de4d91d7a9b4cc7af3fad703142dc7905992eb8bf489f6d8231bdb25603ddf3c31fda8bd9bc4d78835f9ddc1e6445037f05125cb1ccd92eea2e927297e5eb915d5d965a25e5d58feb8d79a890e6036c80ee91e7469d9eb672d7a8db68905d06f5981fc40bf486575a067d35cf14ceee3ccb79b72871bf8f52b92e4910ab17e5e59ab3ae6f9\nM = ", - "b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 34714506322dccb91308c403c267f1ec75f80faf3cc4272dff4a84c13eb1e6133af6681387006c61e7e087046b64e7ae74eea8a3c0564a7c1f381e1c940d92b2c766fffdaa7318d07dbeb877943a73b50517b49e5117778b8a60212284fb92f29a9f5304f8f537e88acf8afaf01fdf64773f988cfa9551d6884baa70587ab76a\nA = 638b7c549ed14256956bad532945ef9e11a50313172965386635a2fc7db79deb0cb5c157e9854117c17f1509d505d01a0e138d2e510dfcca45b4f7ec968b5214a6699b61b8ac68adf64d5394f50d577a154c013612090e2045462160d1f552592197d7da78e03491ae284dc9faf643805f2674af8652bae93ff230fc3eaa833dc62781e5f74d0f0b90290d51d481b0a94ae6e972197c6e84ad7ae\nB = 141f62297ee88ad527fd1e0e09d9ab5dd80e17b32f34a674a27b00d719839701664ccca1b00da2613396cf633b0bdc4482ad3a0c3e209eaea7c22f33706ae44155f527c9ca4e341e651760d1c39f65d5e99e649d013730d2502b6b65adb8a73e6bc734b7d879b430798dcd53fa6c0badd57896cb566d9f1e0a7b3a9161e9808e762ca819330ce9319dbe7f49bd663a9f57ac53d65c6851dc7bc4ee66e08f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7adf54c77eaea2a1743bc5011ace45b7651846e77f90402297f117d8b1c0377f93f49e92a2457f3d3debec3022a96c74c166d01b2279553ef518ec0e612bd7b382529184640c55b89255b2679da9cf370913351592de39f804f1724de36db90c045fa644e8ff20627f67d6afd4546f00d7af093f668629f9a06c07fab5654ac8\nA = 19c491d5b55aa25f2e18cfb7fda18ed4b020e3f63244eb9f6c4dfa86eb8a70875cc898e305a7acdd3eee081300edb3e4c837940bbc1927f5ed9f651e46581639e133515457464e9c451390828e5e7e00a688daaea74620363706cb69e02717489ba9ad05774c424c18e295278caf4df4ced80b4cbd20cd631df43f2e16ec0334564d9dc03dfbc7111e4252504fb449d5a25cb13630b7c0c565a82ea9\nB = -c3f765349639beb80f888d9c8b7b335ab46b55064ce2a88180c80ad280c6b7314df52b7e73095dfd82896e24604854a48121353aa1de663eff07882771803010005905896357cd5a56a59f0db0045f1aa2c0b5626e132c169abc64b9893f95932f54c1d8cc25f215a9ef6e4cfdd6dba85f6faefeca81793b2258ae1d1427e81e458482aab87f6563abf435be69a05b195d1eda90146a8cc92748ca6f798b10\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 32ba5fc81a7747c3d812cf036bc0edc49f08824d53b91a65a6d41edfb1651d99c11ccb4c074d7f04e652276ae3fdc8d6eedb72c6e46cbb1f7f4070dc9d179ce3e21a3826f7dd2c27943a8d26b192d7f5c4aee9ba0647e406133e3e89c262d37cf468aa3ab8c5dd1b8900dd06cd600abc6d372d9408497d9e20c86a9a6a4ad9d1\nA = -73958019a5a52357b9c1d954c9b14f51ddaced32a4d7b7c95730697cf90029564118ea168d23a54381f7bbd6718a6b662e4c87410e48ac53b7767148582b0bd6a3d35f488e7fcf2b128e0a58b5d468dedabde4d624f4a82e808dd7b175af0d3658c6df1ac0da6495bc9a8dc012f8de55c2003da9b2d478e1a089fab776d99026684026968fc309dae46a6ef2412039a8207c3084f96b4e38e4fa01d131\nB = 4330fdf00bc6d13ffc267073b68aea7419ebef257d63f8f244accb9ee46edd04fe5481292de69d377ba6b6304804ba7ec0a063b42339e6e37867261b9945ec705d3a0029c6f499420e02a773476546993b3c5e1efc2417f51afcec7145a9c2625496865c11636e285d4c8b053ffe66887333c51a712fe9c8ea57606103fd689dc88f1fe37dbc33ae4e92067c5bf51b53e2f8205164c800e5abd677c73949b00ef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 69b850a99b471003a56931f7856da357a2254ac50ed81dfae019c9b722b95af16047a0d5422cf7ab66ccd898e85caf0e03e74cc8a5a413661e5da483b3f0363e63a7031bb30626c8f73d6e99e290071094b7fe5bbaf4d303192e59acb5e53fc7cdee78576b51595d9f7a25ccf3c7f8889de68b9deec167778ca27ac9d4c71c3e\nA = -1976b3bbbf92acbfddbc05b5d9e7b62a7666b239c1e6270db7ec6dc2929bad1024e745b897840853d14cd815aabb01aed580e1cc66ce37f9d1cc4c9bef8ddd35d28285faa29f2003d2a4623ead7d73302ea9f380f16b3fc06b7c2b8bb4ce4c8b03bfb6056a61c620e4decc6048cdda5e2d3ed8a13b779b8829e2bbab91e9f6b0304b1c08bf8fd85e0f3cd7ee72255e5342e077ababdbb545d7f809bdf8145\nB = -2cab554f7a5d21c499a1025f61e6c81ab0fc68a874bf60470cfac57425a451365be62c380ddd31f6e202f29769e2b6106868da7c81522e03fa6f0704522a5f8bfadbd007bac65595e149f6c585d7fc022db016bab32819049e7547bf85d4232a7fe19084907c528e7eb0434f2e5a375ad9b7d463821bef2f6a721a635252576c176ba42519bfa5d97d0e47facb4426aea0d755507dac81ccf1537b1003ddbb0727f6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2ce33adf34f2249f8a2d2e073976cb4c78b71414e027657fcefd56fceb022a06c1969dfafd519eb9e2542662c7647102f5c528734dd005fca666be57b46234123bc3db286cfce07bcbb399eb6764daf2b9aafbc2898a5ff43ddfae849c7549289640edc4ab7c4b9fcf5e159623e5497f509ad6f0270a41fd864c9437302ce380\nA = 509f5d5b160e923b4fdd72f4d522a713d780daa4bfd10ddbd62b26497a2e7925c495afc2abf0ecfcb7980e588f96c4078bde51c7b2c19d86d15bbdad5de72fec2e0a284dd693ce0902b40e54af87ac5a5df38ae6d1d882ea6299fbe6910121ebfebd06b454ec5f855bf3e7cd544a4b0d9a764428662e824e2a6185723534f5e6ad829734347d240c48c2c0f8bd6be6ae8a495a9e383fbc7402a4096b8c2c214\nB = 1a3b7f55307031609afc974857a6cc75821e73a1a9535bd6b8e141437c3fd4a6871c904e22c5d9289df7525ac69a0341d3620bcfc5f04b38ae540e26beadbce0002a8a8bfd0f6a270007e4c52aec2fab11fb2a831b9886997256e4b7e7ad3b0ec64c0f31fb0d637869143712291f5073a5756466d7c82c31e08e09683478229bccdedc2cabb7e426af9025185d8dd5124e08afa4e981236180e0a390004adb7918de6ba\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a81fcf9a18ce476a839c896cc5d9b639fb1d74610e2f618c25310147b57cd77806c2aab90be7be4ed10f0122baf9b862b141ee8e4be5e0c23ea776267f14c31e50b119bdd33f2b41f6a4c43d35bf6f095864593e0d8c0f1fd4656d8371af844d197308bbff14e5a28b7181eb6e6a2b31ead7361e287f3b4550ab0484bf7baaac\nA = 19f1ce60ca50bfdf8e02313f1c9a45496720a2ce467f1e8bdedbb32525d762878b61476989c7f6ae8dd29c983ea596e521bd4cbf74dba4d505dd9ea5df423474fa9725d5b65f1575d26ead95725e2a59a6c8a5397ebd6b54123e42bca44781b84c014b8e5d2c1a86cf34d764b242baaad5be285cec72ba8ace808058a0226c04f95eb2b53a828d0ac41e6b40e5a4c4092788d9f7e988752f175f075d545f421205\nB = -b115a1101d97664759538d22154de4b000c008e551e2ab10ad05f12274b10a4cbfee762d232df5188fa1161f37ba61d146e8b95fa715d98e016da8beb0600de65216cecf8b8816f6e7e73e2a2bfa7d0bac74b517b906bbc43357fca69de9cb5507bd95205515b97b3a4d6842f3d7b09606cce1c7436c462f49dd05e915d04ab6fe2748ccaf025bd5d19749cc468d228ba43452ccc479c146ac6d781717bb9966bf3835dec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1473f092540ae30de595666beb33e430cbec42d7a28d4f7982e62f58025cdb617cfc33f1e5ab93d2ebefd7345561b81271bdc50bfbb0db6381dc0ea023ff7c72605da26dc7da2b5664d2ad7967426ca97b3745f82528964bb68e70087e14dcf2d71d30fa0d1f7b3f10b19b357e7053fdf22bccc5188c6919eff1e5c402b750a4\nA = -68f280cecc512d51ae534f30aa198cf7b170c346c1159fa9cf158d0127d43e50a8d4704ec54b8b4295dd7f51c6771cb5767fe0c975414cbe6d2bb58ae66a095e8832d5f443498b1ade1f5bf249da58595ebd878677b34e3b4c99ba6124e2b71d86a8d99727a16746469de51b0a61d9d981459a6cebe206cd36a09f00ffce7f532e2c31999847ba000b9e01a4b84f454544b6362a5c093b9abe9d583716f4534f2de4\nB = 5b79684387f18d7de6eec3a63d737490dc2a46c0616ec16388dca2be60adcda11ae13063ede3fec177171a51dbef430f8c4b3f6d297b9d6c020fc44e3ffab891d0d751d033fda813861bc067c181118dc613335ce89c5960f952e5fd28bc72c41b7b6e374ec29b837f1e00271cab646c794579d315260921dbc3b984b86d98b8f8816aca4f16de506", - "57e4102f34d9e29ec3a03e0da06e70f69952339bf2ec4a7e74daca82239\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5e4b3f4aea7115d592bde9bf7c6594fee77372ffb19f7745b4de878a4024f81e8290c77d2915424df20004a7abb64c214104a3123e7c8f230c159ccb99bd937521b433dcfb065b186a685fc40f9166bad9380a02e297ffd6a307ce8d2c8f2f1330447a9c06c327b74f3cfc2e98f3351a8b385bae855941228969d1c29e9da3e4\nA = -11c1d396693139df5bd91825c119d1241c3f57b7ce95b46472dd82081738cdeb0868d18eb7c8ee7808016b3311f982adebd5a2e5f4e201ec4a34f3037d260fe580e771222de5a1a67947a4552cc03c5c59f9e60e25063a702ad3c3aa43f061a22567f938a91f1dd697c3e3978fa11ab1d65030bf327f8049bda745658bdd4ba8f3e34b060c6a2c6c5a8be54c7cb5f6b106f54a37d2be9f674f7747744d4350b3acdf373\nB = -25a65b6acda692ba3330d70dbc3ea4dfe208c0df358c50b7872245a909c5ac19ec568b1a1340e1a094f5b8e7d1e3b7e04bb4df002558aefd4540135d62d75bd5ce959128c1300b9d98429d7369610866d98b22c345e531f2beb80b042b6ad48da077043401a82e223e9e529e7407bfa466dd2680973006d047d837c26a60cabc36a7ef538f603ba19f8e923f168ebfc3834df8f77a559c9e0342e33df245f551bb242e5a66e5904\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 47872b544fa0425981ae17bb450ea346135e6ed7a9de0572ae14a6e85e8319f27cfab778cdd8cb5f93b417d9c66ae0fb7bcc6652620f7f3f74acc2bc9f2c090129fa8315aeec9ca7adc5356484474ee803883ba4695d7bc47c87eec508d16a15150cf3f757c4713de71366e958d6af045b2d282b6ce96976692c80b1e0b6f846\nA = 7e8f55c040862f12d8cc6e506608eeca65ce38e9e8ab18ef7007e3cf0f1c9a0696795bd10f8e1e1f55bb4f4f3a35c2e0ad18289e250571ccc26a961f730346efb1e29fb143ed97cf72deaab19834fa2e98e9c12ae4cd23b9c5ecef4a04c439f7d42e110b30caedc4334372ca24cfe4171ef1430528f7b57bbc823fd606fbd30915c5817e6c57c967c4c404a0847b1455da17effeebbec3f9357358e00001239aae209228f\nB = 1cc00b95f6bd3abfa697400c98110725a7e109aa9b8cbbe9ae16327c4fc8e5bc93afc7a94da32e98e85e4fd5eb545192c73007d97a4e84ba64fe187ef61d17f0941e165c9fe64c7b8054e24dad30f92b50d1f526b4bb031e6b1b9058be24884b170a145212273c51692b71bc57ee53176d8702b975bb6ba96284b462da2ce38e12d86b342c7f4d3cd489fbce88a309c7df1121d7bbbaab6814cd1e54953e5cc46813ead98f02360372\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5d193b085e57c3f1e825cf3b36c8bdc936c603136bb782a244b04a79fa713dc7b08436b85ca3b483d2e100a012d6430679b30c8e4101c8f08ca0f9010dc0f27fb37be842054dfdd99362e03a7f55ae58db7b47f694bd35d91a58975ae1f255c41617e773f91c2640f768bc702a213f073682dc761e056b34c57edd85585fe04\nA = 1bb1c759ea94b61a1721ef5680f42af30fa31444b27591a03b7c9bf5b90845ab965339f463a78bddedcd62fa21197c32d6850c61bae195f86e1c7a23e7a20dc618c59ce3a1c6ea6306c0b01b11a36d0fadf8214c36a133d689438021ce7c78b20c85256ec607360cce14f139513d9f3ea6eab067b1ffd0935d7c43419b93ecfadf2c5a902b7c39a69bdc023173bdad574adc77706c1a666d66f69578a5bffdc7cd6eee28ad8a\nB = -e8072c49cea603d48f20276df188fd2fb28f8721d578220cef7db1e56379c04a6b372e56a047cbe59ea84ad026adc5d0aa930011db63bf4959f15781e060e0240dfac0e2a2c26be12a21e5650d12140bb49a2a8e0f6a86e4b1eb79d9b8aab3202bfd339096529170cfe3e0c18263128686bd9305e92a3c43e1523f97d8a6a2707773e3d441da162a79089c9ea1e094cd5a23474121188013c8c287965a5e77599f6a7d64174b06cc165e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = aa79c899c2b9518857c9e4f96523a44607c3f6a97d1f40d6474ec79deb2feadd955fe92d789df4d362c828084559fab56b5e33a971abc5449208d31671c7e220c5945886e33ed1d804c059a8e439a92524a785076f9730732bc5a152aeffb5b9ecf3a7e4b55983016355c4c29827496fd4d7e6532c270cb9ef263573e4c63074\nA = -41b326c2b86e7ac14a2050bff67bb5bf9697f02594789c4a2b3e8455df4522546278d0620f28a680f6a88ab545de5829305485422f4e70a5ebf0ad15508dfe3f16ac556436d8fe8a8cde83ead549d88e0bb24dee52ebbb49159ae71589d918d3fac8011cfc3afad613ea09173856b7b79b55a2e43e0f7cd21eb9122d5f6a1fc5408414f5aafcff863b870c67b740256d317a0c58af9a81d8025a086a1f3d79f7408d4bfa06b9dc\nB = 4730f03c389f9bdd92fd864177e06140c9dcc02d01fe7d37b51d44de140696f116d11bb67adf7db797edeb7c304386a7f5e37bfac46a5462a6d4c49b1bc034c2e0dfa56f14bbd2a4bfaf86bbad4f6d0dfa13c782fe680847d4b43373d7137f5c2ebe4ad58c695a7d4c407bfd888ce04abaaec60a3fd33db10eaba6b6acf0e16cb61d1beb9212c2b07921bfb5595ef1eb389200b356eafe8b5288d8f0e2cf252b38301de65190d56bfadf57f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 23f9850dccd2af799f18268c3a2918a69019513c55268faf2477c50677fce277d8ce58a0cc06dfe389170faf5f0ae13ffc4954c746eebae66efc14eaef2c2ac9001f3c7ef7e32fdc31dd725b6a8093e33daa6d19808908e0c2d3e7c1c58e0fe9ed92f4d7cf3cc222393ca4f95feab5d34fe29116410a1882dff7cd92acb87590\nA = -10a75953e5fb9903411869a2949f8f04144d6e2d61f95704ff55a02f40c4f283add405353a68bf7d6acc1b8cce738f0c6f9271a538b4c688dbeface58eef0a0a1d491a9e66958750db97bd01466edfd245cef03bb6a3acb81acc63c38538e7f15deefd15afc422a8641c357c31a069258dc0ebb63f06094ed8fe7d4d420246b40302361967c81f0a9ca542fd1de01967514ff2565de7ae3b4a200d63feaa22fb99a251cad66624df4\nB = -351242b6e6d0122f7120deb8357c3bcf25d221a15f83579883bfb4dc2e6099e6b7b95fd08f6e573d93354b0676f7bc9fad563d6eb0f3567ef43efe3d874b9c7733e4fe1ef491043e1f80aab6094cc9b9c236570972233ea74e8779a6eecda23a65d08d878850cab6005159265893dc0f66920a12c26dfb421ec326a1ac09e9ab8085825c31aba488af02cd51f96b205c50e692dbf2d844ff0a989c3ba9f1c2bc7f2e7dd9458a72d310eb28d490\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 69c7fa326630d7de69249807cd8bc55c9315acac26fed3caa3c8a9c6b51ee96a7dd0b3bacd5cc13c15f199e268c5eb91d1ec36c085f83b437b9906caa6e39ed7bf09778610b621426cc8d36d96f541d0bfcc7693525d33e0c2ecd77ccfe80289a11155b37c7ea7791b5c2be3f9b954e230c19d746575afe9a1a3a9677d23c5bb\nA = 7cb78ca8e5d903096630744c85975719c16333e2e44931956d8c45b001d35ed4e184dec88c9e2167d2f338fe6f25540a144cc419590a4ac7caedea3bbbc565365d3357baa62fdccef2c5ea616614e0bff60e81916eb4abde0c9725b1bf6869e8b1e11f6d0d08fd712bc68003e55ed462ad4946f7f982e663f65d45c07c659d9620d5139d2b3332a68d33aec36e21716a3b75f44272a19f860e6ab3864f06def9a5ddeed340ac0733353\nB = 16d5b074e008fdd30e73ea95cb5fb87de806319388b3a44f33c94d38be0e6f1a92103dbdfb3d23b6e1d19bdb29ac14833003e9482cb7524d0d7b4c377f4911e3372f2cea6f84c938d84e3994e80f0d68e7e385ca29e02f70294c921dce7cd3829c5854ce51d1f4fcf7dba910b51b48a3f53cb1f187182435f21f6981cf8440f9c8287a9749c92c0304cc2bc91eef32d8e6526be802de8aa16684e8854cb0b67d9f7ea00f6f0145d14e3c251f70881\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 72192604b2f3f68b9ed3e261120ea52b06a05869f6abd21828ce8abadb3a71c360a14947bc738e5d1d530b9636d796f785bb44508477eefa80c4b77d4e8e35463e15ea2a48c682d3288c5abeb66181e4bed7d5b4e0db20fdf5ed68513aa5ae7e0978ec1c4646368f206636ec90e808817bd1d03acf9adb9ba57dc153873fec11\nA = 1112d291463b28ef45e879412e6607a3e20d50dba5044e71883bb3cdfe9bc694a577fd7d896dfb836a171f3a4d8fd025d3a979b43e41baafaf7b535d9050e47f4880828640e952435648960bbb74a3c25dd90bccb3fedd254dfc0f031d0e8a468e93bb69f771ed35f1653cffea1a763491fdf6efa21aefc287cb611f5ea0085f64cc3705c784f87ce00846901833d01a3c45ce047d822ba390b538f0a24720", - "155409f60ca0d90e13991aa1\nB = -d553fa2dff0265cd9d083ad097af87a99af3d8d93a9f4c07440a28a427082004ae5c81d22bda1dd2429f540de8df175c1b4d0d50f0227489ba570b28baa35055df951d05b584ae6b051a135d7eb2a501b2441f82c135a8ec0eb81d379b96ef8f2fd526ee62293bcb934c76ef8083727a4b28bbfc9f515ebcc2bb7ed9594a106e137ce94e9105b2e2f4776aa9c6abdf426a181181fece3251c3ef4f8eecb634e6bd47c5878663fd51c74a66b92713fb7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 459e19faf105ab17ff794927aff86196b3cc3461e69cada53ab8c8c81e2b1820408421ea1af6ae10257e8cd9dc16386906410761fed62cf9ddcf0da2a92800d99563fbb9cb1ab0ba46a17cb9dee3f2b68992c2b832a5932e4533fbd5c4487d870f3fb5d7a1c358f4aef02993360915a9e9cfde234df5f51c761d84568400b618\nA = -7a964c62e38e4124cd2bad727138dd12a086a2bf01c095b078ce2f81288d3c8435ccce0c8e00229184091130989434bcd107a3a0787a2f5f4b0e8c23b1cee9a8f39ea279fb6081efb6c3df1704fae9e87d63ac6eac4c6687b3551ab7ddac5ca0541e12047d04c2fc760fda0916cd2b585a90d25880fcc1bde8f0a1a413969938d42e8b3b5f73118798e85b901c2e15860e29e2ee8b1c95336b97dc10a21f5300e0352adb60b40a8a99333380\nB = 743ff4d91ea3e0f9c4f72e5daecb4fb00b15b86e30bacebbe4384324523d14e22abe29b00573733f594d652a88d98c987f8db08b27b4dc68577784fde02dd410ebdbfaad9e9afc6a22a8cbb13a780222bd212fc61e38faf409e940fba35ed909e6938e83b0fdf5b5e3ce138604823e788efc3aa0df924554fb70fd2faf8249e17a827c5d85942005b328bed97e5ea1f1810219d77f2fe121ce66518e37c84d64aebda3c397684212384deebd520a776b95\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 183950095d9424b0ed09985aafbbd2e5d64bf541a56b68b42ea8cf9b2c051615ee7bb6c0687ca6fb0036888fbc927cb7aeb303750871442ff2c0087a95f4efad568f48b03bd2b9a9ac26af8c259a3fa97cd2af7e3d8f36148c26785489cda6c00a21e7eca219d1f41b2e82ba8e2c1cd752eb08a2fd50c6f9077f3096e2eba05e\nA = -1d2fc778cf44c6992d1f3a056860eeb12f969358cadb087dcaebf5f96bec42bc0aa98672260adf1732da057e9e0d22081e33f5fa71f248cf89dd361036ad58692637cdfff584a191279f178242ec0ad397efc52e99462f496caa0f3133c4238aaa877fa7094662f080eb284c4cbeb992a368c2d157ac5c8c9160c167716406190fa39ce0abcdac52c8020969b87a4f84bc09a51f7b2ca288c93b1aac64e19623a7d9e69976a31074f637e4c82aa\nB = -2f188f1245b75cd21d052ec76edeb5881944a143fee31c67370fab0420a748f3f1957bb8332ffefdeabd0ca806169629f130c86c99bab490a9668fd8200f4a9b1704c589e75b5c8c855f133d50b2ce06191875e2872b36c78438d6032d53004c047f49e4cb81e19fa84da16d053e6cbc7c8eec0b9129a8831eba690e0542ca3fefd204258624e92844c8b7bcdccab986475a47c8b22e89079ea6580ef8f496099cc24dc2911dcb1921d1451e2163b55bbb7db\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a02c38d5df9ff7055ff84122342ccdf6ed7f7d54fe8227af091371f5ae62844645586adaae99c11f4ccd828103a81471bac72dc20625962e41d603e760591bb3569a21f45bf062b86b5fd1c617a4769a4d767a0ee14d104084c12ae875316a8f2be7adec0104381dc02c20b5851efdf7d4bef0d68076975e0ada3e58e101e8b4\nA = 5daf37d616da184acb278a75fda4e4fa49e544eadcf373c054b203a309ba198233f2285a1b55dc92e05d0213b26c82e261d8383a845813077b2e1b5f4553400f09410987c8dd21d4383e0f05747d0482d1a89f160a5220b22c78393873564fc5b1e4d5627ef3d4a05612709f301381df35606e99560fba07a917d7ea7413110fb5a8290e114d5200cfecb00b6c53b2ee29911bcb2fb2930eadba0ab9dfaf46443370307d9c3b61a329f0b8b8cbe7d\nB = 1d9539fdb1afabeb9be6e774dc7c7cc4bb4fd63af7abb557a5fc80a3fd23a4600de3c7fae89b91f3d441b61d3e24b2fd3d7803cd71620e7313917b4afb89ef5171a3d8a68c3c74aa3dfc8058d555eac429dfb6db40a9e0c25aacd2050418d6f32bf21cbb76981269dcd5883178d4b69a931a0338b93022a2ed0f78f3d8877989cc406f19d6d082ea344309318c56be7946412ea0867c78418ec32b9fa3a61017c10939c9345021133116933a3d1eb86a3ef16424\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5fca287abf1f487e0ec18c230860eed4a2e550228b1500b1e33bcd6675646b5afe505b55073129f22352dc2b113c584ea1b98808214b6916933e90e036b129b61657cdea9026e1fa087ee300e055ae8f94ffca933a2d70453ed220468a5a3cf1a65d81eca11cf570d7d038722397f487af60531f24a5f069671354882c8bd2c1\nA = 1d9fe15171dce97475f4ad329fc8fb5469fb2b8086e4b01eddb6ceffe5324cfbd28d791705848569739b6758ca7e7d7d49adf0c11d891b0a5879ca870d1ca5ff475513322ff218cd26024f97623bb8a53084594e1fd64154e1db702522883fcf4c0d677a7fe90096fc76dc3800816996308d8f0be2dbf3b879f8a000c0ac534511437e2ce2d7ebcf42fd1698a829eb846b3afa581c24d5bf97abc6e247f110f4e872a2474e3acca6c8c0d518104c3375\nB = -dc0da8f7adb8e9f7b0e3f293cf623528dc8e9668317910417e52301c50c62e7d30e77ec7e38d6817d1f5a93e851f8560f642f23a0b9f836812d27b1b41c0867088a3108332b8711047560052ea30c8840f03a25c65b227a175d8f340095823788adb5bdf2b7ebb801e20f6b6435e154f78d17b8fc4373aecee56ec7b8f5686a7d22c8571797fde85cec884d45ddc4b1f2cc47ebf56a879bf286f349a0edfb531168b733d43de3b86b49eacb10b06a432c96c63440b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6222c1a14c6390d73944cead58eae5e7a6c19d19e4563c36cf624f5b61d99991bed7dbf6a0723abc56469eedfb1f7982987c2c7af6191178cf0933ed5f191b8117c9d726cdfa8b82a2fb25ca5436023f5860aff5fd482c611f134569ae87395dd99e5e9d400b5ab1e3064210ded096411654518110ea45899f4be2516e35a229\nA = -7f6766be6c6ca9bd1fd7ea1f80bfe68693f7ee4b5ba2946846839060d6028eabbb9079a165c1a07eb6a01239f3f14095225b8617753a1cc3d9c1e69b516d8705cfda396f4f0d05b0944a0f08b478d261e968c06918914ba87c8e7b7adef5cc2a875917d00585571542af219bd726e502b7f3f0bdf0cb1dfc6796be2e22e8ffb5b8bfac7e15e991022974e75d3a5eba214ab8a1aab2fcfcdbc6ded2abf834d1899d2e3ff94bad9c696aece045212531773f\nB = 49c6f869745983cae44d33cb7ba141234905441ca53172abd1a2dd8bfeeac4b236605cd2dc5b04ff9aa13de84872145b935b85479136065d2d57fd15fbd97480c25c6354636c17ffbca33c9319d65e82523e39fab49321380a130fc160857a451a69b1d0509d5718a9cff8b49c2d677c1f66bf77333d2511f58d3eb2fb47b3c162cc9be8b012d8df70278f0e21123a69724a1f126369a236d54da026ebe222c513f24b577707b5ab4b90ab0e22b4e38ceb4181d4ca101\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9e9cc8c5342dc6d6daf55fc9aa9f79ec18592e8b9724a66881c379245c91f06a7df50a6ba0964603a6dac97e77a55d06efff17c93d5faf107fe65788d0f56483915f6ea0f1ccbda7656eb58fc032b5771600beafdc12c2076110a9b9670bd0754ff6a72c5d6e1a9e4e42c688e1cc96d7aecd815bdf5dcb16fcd1be1275ce7282\nA = -11635fe16dafce21efb1c599305e9a16eb5651187cbf054cd9d911c13e8eafbb738013e212f9c2b3662ea15ac9bd82b5751d43a38e4475d2310945a812262309094ae9cf59e0e9f3d02c92d8ab01f5733a20f051054a240bcbe3a7b6bb3f7c434229f631c4af239d33bd3ce30a372a480fdb49b2716091d26071aef372b8bd8ee8eb7f2965a372a836000b3737d2a833a39230e721e4844e16031ad69cd45ced60a64510c1248fd776611934d8d2a913d965e\nB = -3bb2cde9d3fda96fd7e6b24645f8e00b43affb223f2b5c3f4b7cfee905ddd6703a9d6c01f1f099ad1174da215a645ca4707d8156e762e2a253d7cfddd05ca19823ada9d33924013f677cfe4d86bde025391e0aaf91c6b776a9cf8a09dcad7cea59ee7aea1cf5f5bfe67c9d4456332d1f98e5310db9a0230381e1867a8f75b8757283f911f1a5e0d4afe5d544afa8d86637f9c9d87428fdcf8b4eb8f477e617960948253b24565b2f23081c47e211cd3c788a92732a49077f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 30dc89bad4b449d1df9ea9b8f9d40b323c7", - "1d7e1133bc44d33bdb87c38cddedf83bb849e83436e4c92a06546fcf3e24ce6cc89d2e97a48aff2c7e3703da1b167a112f662a89742355e11e131e41052f1b379753cfa32cb0efa3a07465a258c585cd68c86bc9a473f5262c86c50992aeccbb9725b69ea8b3a7ebd2b6a24db52dc\nA = 60463fae1e9354559160d55a453c12d75775a53d1606d1fd16bef7e4ad1c78f9568954112f9280c46781180951534c5372dd5aaff3f33ac9c2e0ce4934d7009aad2ab5d6a5e5a141a36846e8925c7a28d116c68fb78aa9a687ec9bef173c1b69e0d7261f96eacacf237e1fe5874e5d553985b0fe7692ce8f2a5feab9ad9a2ad9c4bbf050b73b8030ebc36b94af8c6ecb67f8c94607d80cf600efd4ce4aa006f9b1832da8a1fdf8a564be0b4369149e8639e1714\nB = 15bfc50290b771ad147695a4c6701c47f2e8aec0657a4ef999eb45685200981b0ab5f8abc143d64878b85e9548651a1afd0913e3b14d11d3a26ab9793596801662a67b0062fdc8888feb029266f71d170518b6a4a040f59996bd4f257f221e830d0faaa9688aaa6afbc1f9b40d25097eab9d71d80aabc085f3a07e48bcfb37119aa00de60be55fd07d5b1281adf7b98bb589cdf2026252edf2f075ee176e23afa6b1f924c9fcf3c34c76752e833278a2e6b62017b88b77eece5\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8b506c9bfb75ab7ab420ae6c9b371ef035fab512188d9df76f0b31831573b44cb08266186a04d20cc761d61b6df3e33ecb86c269205c2c79ae6aa4d3ebacac8ec71d9bce1d7ab146530b131c9038041c6ce8152a6f1c09b9bec8eea4462dda0f08d75edf296eacbcefd62a0c197ed30f799343268bf6edfee4995958db7e0420\nA = 11c16713fbf8bc9696782cb5a88174cddbe68a04e8fe93dd074aab33dcd85f92baa178b2f3b8817be0cecb802cfd3ebb06734c9d399a1f090e3a8a2110aebbba0e920427bcda74bf11700b945985bd532286d44a1a615cf7c501412e454edd647f8371cb8149474557a0d47cbb782f460de7a3cc28991491ea0fc510286711b882987b09341c079565414f2c930e7c3c3a3e3e0f1d786260a7f45c70e0fa20dfc63849906af61707cfdf5a9b7a4291a1c1586d16b8\nB = -cf5638af39c6da3757a09a92e0bd54f852742682dc91c71dcdc6e72f7825a0979a1ead2e158479ce5565d22472dc3853e6bf7ba43296a5e0e0a355f0703cecc02ec79da83e3e9de10a6eccb858dedf7d4c400c27486a5b8cb34d787cde6a5fd271e83a6cf66057838fe30db1f30663cdfc22ef5d002b0b5a05831228ea200f95382a58d0d8aba36523d9b5cb7506f193131916f3ab66ac9552c26cd0c2ab1c449eaeb8fde752f4f3c3f9b060cc1f8a1e37c4fe5ec306674b66158\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 347706abeb168205cef9b0b8c6b9d6449ac501af7dfbdfbd41a20a6a47872cbd7d4cd32f7b0805ecf1573d534418b7cce98181e079d5061b02639fdf0161cea5314dbbb2ef39ec841f695281f3c7de45f33664e0dd1658f645adc1dd225f781a3fb1634517c556403587b2aecd56dceca9ec19b930cead2b1d303aa056d28bc7\nA = -5e1c869e5dbcc684c245d5c69093bfeaadf388cbf928d33a8ae2148a2b5145937e4f654c5f6a36de1124bad1de8bcc9067fe1f9a44fc6ffe55ce7ed5cd0dbb6337b0e1e96bac1eb2a3606dd97b0bdb975ea59448be50191cc7ea36481ca9fc85c1c3e1c97378dbcd6b355622046888df2ab3d18d805f4d31d464f62a8e630e955beeeb5e00c70242b8f8df708705abbeb95dea3561756298b5f3f7fe16e965294eeeea4546f5e8bacf9d6b4f2136d2e206a87dad1f47\nB = 70225f0cadd328be36ece2172c836405db3fe80ef99ec74fca25406b73a537adf5073f2b550abfc4c0fcc2c2850dace0da9a266768cb4d5ff7fc6c1c248ad74f47592101b61ef96c1302924381abbd96cf49f50c44bf7e0551721a8ae85abdf9925548d13b8c5d1a27be8a40d0f43eec3136bc3035057b75aea779b4262cc66e6bc68da93c218f1920979291105d4b02117d66deb92c3e511aa588b27130202acc9f69521957f79c7e731bbd5461552b9b6b24240dd71ac449be9777\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a2cb238f326d47f95869e2dcb295eba819a443dcc7c2785461389b58327742702f4c86e47af129f1fd4611cda93631f9333c358a29121d58286333083d13e66f30a9533b77ba3e26089e7eff7baf19bef8054af4e24735525908864ea9c4756b42a69c897003cab7b63cfd9a5927ed562e29845308eb2a55e7f8f03c87a5b7ce\nA = -1aa7ae6f56c38b654b281525b9da953ef366c2b9cffd3042105ed428dc7e5f2f2d53ef90b468bb471753606cc7a3775d86bcd2f4d5119cdde3c487cd39bf31752c5ba297e529c1b8121487e0e1de702156d0166ccaf51888a24fe7b48624eefaec855e2200929c21858676ec9bf4ceed0a832b69efd5065af544e49a3d209b85a77b0953652cbf0aa897527c52c9a98de9ae4c827f762e251478c88d410123625ea52b3478b52f6b9987d42009ae427763357ab53195772\nB = -226630b6fcdb5e274a25066ae2ca2c803549dbb935a97c0d7f6ab2c971d74cf6acd265c9d6815a6b2dd23dcb3c23b390fe8b1bed92b8c64c76c0ce62d5e7ddd7ce445bab0ca905dcfd0f128e5f4ffe966f3903d7ff1c61fe174e373cfe35a6d83249ec40b4a354d46fa1c90682efe468e895ea3da710838c262e8a47752dc6e7a79fe20051f51180173b58e0aa37b22eb8efee5b6dc264459ce4d135f430cb15afbf8c53f0de894bd2aca1f7ea32b4209a22a075f7b3b18e86f778a9e47\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9ea62ef634\nA = 55cc58c9d8\nB = 6b49179821\nM = f753311ac9\n\nModMul = e9ab3a2aa60edd30108\nA = 5134a36c2bad180dd5bf\nB = 2ba6485656d041690666\nM = 9b9cc4409e86c8b0fbbf\n\nModMul = 621f9b797e866028b7bd1ff828bf29\nA = a202338dffe171c99434d84f3\nB = fb71eee7045b3e3ab5dd809dd\nM = b3e6e8d53b7249df670e3c59c55d33\n\nModMul = 808d463d06b7b7f98e3cb2783e2196c349d62672\nA = c669426a92d3cb5b316e2b5b9\nB = ccaea3874008dcc92450d8b2f\nM = b04dd2bb325baed1940cd000e8cb2d786009ccd5\n\nModMul = 872164b92b9426b237858c4cdafe1694f96b0e0e4c19e894a0\nA = c3255cb24a813e27c3dc410f0\nB = b144f39e7c2d33605ba7bee16\nM = f3639f4dfb782f3107eb402fabb5fc878903acb5e02e129077\n\nModMul = 6124d7d171\nA = 235b938139\nB = 3a56a22a28\nM = 83eb4af4e5\n\nModMul = 9c006f56095d442ba98c\nA = 207e14237c42e3764e5e\nB = 8a495a26872432fa8e33\nM = d0cf2b8ae5c67d6736b9\n\nModMul = 97387cfaef652932a230c82de59cac\nA = 82ae0fc5e943af5bb8c4adebb\nB = db1279be12d59ba3a9c036a61\nM = aa36dc1d13390169cd54d711eb511b\n\nModMul = 32ee73c98da657464c6fed4274df20b099689e00\nA = 9baf08248ee24bcb17714e420\nB = a7f0428147bfe098666180749\nM = ce0bc198331c9ed1d21f0d498326e8185d3d602d\n\nModMul = a8b3fc0b53df3b92753edecd6fbcc5f4840dad3a44da704e34\nA = b36249e259b303e453757721c\nB = f0c1db50670d92abd93bdc84b\nM = b05cf978bf2dc7e093d7d164e46d547219c480382df32b33d9\n\nModMul = 2663b741ff\nA = 58c8e7f7f6\nB = c84681fc87\nM = e0a50dcb45\n\nModMul = 21af3c0b42328f41b81e\nA = 1f79f5b5bf78c9700d\nB = 5bd1734ba0f0e59c2a25\nM = 9ff3fdfb5c089244f327\n\nModMul = cbc280b5106c2c36cb31ad7e7c986c\nA = cadf6482b769e83ce7f7277dd\nB = f9862a06da1a9c89547b76c61\nM = cc36144c88139ce921d2fd1740bc4b\n\nModMul = 3813f2fabe016e19fd8e70687ff473651a5fbb4b\nA = 9c51a5bacb5d9f055a9ac2962\nB = bfed5625b21b4e82d1f105a0b\nM = a47977acad7c5deeb683ccd265cb30cb193f22a9\n\nModMul = 76ff291a02715fc87ebfb3e99153c04e53358dbd7beae43478\nA = 997c4a7b537d9500d73a205a4\nB = c679ce666af284a459ae5a26e\nM = d0d0fd4922953941acad8beb65c00603b19eb44fb8ca51e3c9\n\nModMul = 1a90c92fdb\nA = 94fa7bb475\nB = 564b0a3339\nM = a1501bdc75\n\nModMul = 5e7ae5470686bad7996a\nA = c725797912c6c5f30d94\nB = 3a7f4c99ee3f5fa9582c\nM = cc50c8b7408f09a74973\n\nModMul = 72a15b13bcd1b63747342a6be8f0f2\nA = c33357af48a2df569e3c11ce6\nB = a4b4c5c14d7796adab54b6cae\nM = e22a0fdca62a37f4c8a61c96a429b9\n\nModMul = 31e179bfbf65b0695dde36a4fb72d131830dcdd6\nA = ce8d3adab8cbf15c332c0b289\nB = 9333f94eeb7d7a86b82becc51\nM = a532a76bd5cff409b580d54d12ef75ad8179b381\n\nModMul = 8f4b8a585415adff3a7bc35fa88891ba31e4a82672c664fb14\nA = 9a2b56a54bd0727ab4be57ff2\nB = edf1781b4296567990773005a\nM = c5a7c3b97ba00d6f174a019c6d37eda52036c528f351bef0f1\n\nModMul = 917bcdb402\nA = 55c7dbd314\nB = 997b29ef79\nM = af5b4cbd0f\n\nModMul = 660c4bb2b771f523a4fd\nA = 43fe52461d5139620a11\nB = 1f8ec4b67de1db54ddda\nM = d0458e215b7e6903d96f\n\nModMul = 7aeff02c143e4426fcbcf32bd1277b\nA = a2671586369a990dde7829f36\nB = c7ff67937c900daccc0ab1d8c\nM = 8ad9c1d4d3cce681d1ae27c27982df\n\nModMul = 4b153d57433f0f7276674d3484e9bd0d25227d07\nA = aea36cf51dd2ce06c66b7a407\nB = 80c9fe5bb0afd2bf8b3644f96\nM = 8cc22a67ed7e5a7a2322aaa09ec2be94998494f9\n\nModMul = 7f8447dd983b113f04c6288f9539e53a2e9cddbca8b2fefcc0\nA = f67636b03821c8f13f21217a5\nB = 8473a29f4ae33f", - "36a0d2c6dc0\nM = b829af37b557c3ddbb5257c8b19144b90708a45a274d6655f5\n\nModMul = 17fe4644a2\nA = 912611576f\nB = 7a10d36b80\nM = c5fa605133\n\nModMul = 8159b23d4fd697b4fd35\nA = be2d646e76494439e60\nB = 60fa770d05ebc69772b2\nM = a6e7c940cd749925a85b\n\nModMul = 7c412dad5c9fff91357bf181caf2bf\nA = 80f476ed5acae75b34ed54c52\nB = fb818e2bdab3b5f4bd84db3d0\nM = d0339f7ee41337d8462d1a9c207d1d\n\nModMul = 70432c749da4ade2c38237545ebfe6c4c6a92f6b\nA = ee9c92de52210e61adaa6eb4a\nB = 8ab55a85b1abab62d33e75fe3\nM = cd3faa6de4cb62fece4c3f94492d457834a6a041\n\nModMul = 9fef1c18778a8691c5e71c0b5208e82778e9bfb632da0b7e28\nA = bd162c90bed25e84dd5b6b77c\nB = d887ee03020c5df356f091db6\nM = a2c2d45fe9decd93a0ca3edab8fee46d27ba23fad9b5294d5f\n\nModMul = 958951bd0f\nA = 12bd0d3375\nB = 668bb65b4e\nM = 9c617dfaad\n\nModMul = 8a109ebc9cbf86613e43\nA = a3e7019f1bbc35689a77\nB = 3189ecd3fd4ffd0229ef\nM = ddadc50600dff2abc1af\n\nModMul = 2b4d9f85a398c852b3a0cc82524619\nA = c244fd157267f707319ba6c6d\nB = 8a07018a748992429bbdbf326\nM = bf3813fb54f749ea5627f59ce30e07\n\nModMul = 28cab7d574e6dc56a6a622f8a7523cbb8dcc5e0f\nA = c9909dcfd3a59a3cfa538b267\nB = 8bbf89cd5a4e24adc2d8c646b\nM = c8f02682b9d480ea98faaca53b747ced33ed0419\n\nModMul = 69b2dfb3f1d8dbb13e9e479f38edcc427d5968acb7751a226a\nA = 8019266c548982a520ab48eff\nB = d33c3e3b13576dcdb3ffaa796\nM = e6255103732475604df7c6f7ef7e6b49a8ef9e2b0c717925a1\n\nModMul = 3eaa4c99fd\nA = 6fc42faa85\nB = dd0b4e318e\nM = fd7f22301b\n\nModMul = 56b6b811ced3433755cb\nA = 145573d17cb0c996c69\nB = 9d3297d5ccc184896822\nM = dcfb3b383506239e83e1\n\nModMul = 34315b6bc6d3690c28060485ae331f\nA = b963a26973894cfb42fcb2d22\nB = e8523304bbcdff1a0ed4141bb\nM = d7a379aeac7d8cf94f19e7924d35d1\n\nModMul = 2ec9466e8b3357496f07e37ba24d36a237883846\nA = a75f3904e564997695b6707eb\nB = f9f47bd779834dc1f5fba0654\nM = b3ae5abed45d09c4dc5abcadc3ac9abebe1949ed\n\nModMul = 88b4d86b2c1e1bd780e8d2499c2221e05fab4f9b7047c2a044\nA = a38eceb9c551f0e69a544072c\nB = d5f8e7c2d534b2b8985bfd213\nM = ff81809b84fb8eed3508ad891d3d8208249d8a902a12d6acf7\n\nModMul = 172f2e2e22\nA = 1584ff1055\nB = 2e0aee014d\nM = b904cb0bc9\n\nModMul = 122c10d3200270b9eaa1\nA = 86fd189e62a6dc1e4ba0\nB = 5235635f7b0336f5f235\nM = c93da97d0e95fb63dc4d\n\nModMul = 3e461e10ac4eb749512097fbf76616\nA = cf4ce10cbca07164f3812f89c\nB = b7e4639c233fbb0f923fb5104\nM = 949647857e1406871593fad5c30101\n\nModMul = 88117b59d9fed79dd6aaf083ee938215a995a221\nA = 94c888795567d434123d441a7\nB = c60ca79e61a352e34e0f78bee\nM = d2553a7c5dccd639a3927697a2e1af03845f2f25\n\nModMul = bc5f0076a8c2f6cc8f4e61540d2d6f6d6b13b775b363dcd71c\nA = c170eaddca5295d6ec6272dc2\nB = f94a5685ced7661df2efbd34e\nM = fa6bc46aa05033af72aa42793e9174af2e3ba38992f33572fd\n\nModMul = 1110cdbe5b\nA = 5db02b38f3\nB = 3369537903\nM = a8863f7979\n\nModMul = 90fcc5f3a346d3d4ea4c\nA = b93373680ea0feeb31d8\nB = 37f9dfaf0e180be64bd5\nM = d595cc29237d1c19e2db\n\nModMul = 8623a9997e514cf3c1d06c33c14053\nA = b396f5ede6212f1fdfc7e7b77\nB = 81a1ddc18306f2d2e84030148\nM = a6be32a91b34857842255ef8b1aafd\n\nModMul = 63f8f0254df06356f5cab8941b77619ad58025ed\nA = 806b2627b08d987438f920bae\nB = 83297039f4aa8efc1a185fea3\nM = bb8a7e7c19be02c25cf5682a0eee655fcd5b69a5\n\nModMul = 697238dbe3d395e81f20c9fcc8db30c234a1f75f3b2bc27438\nA = 930b04224bc097ac1d8bae8be\nB = b79496a80e45212c4663e5b64\nM = 8ff7e19d967d317c255380411898d73e3786269f09079f19f1\n\nModMul = cd93b5b8b1\nA = 47a51b2d5a\nB = 86d6ba5155\nM = efb0ad3643\n\nModMul = 2037821ea789118bde0a\nA = a92215dcae19be637ff\nB = 93b9a3664a406737958f\nM = 9df360b69ed26f610253\n\nModMul = 3bf11785d28ceb668dc55b870faf7b\nA = bc8758854dc48e057cb6210de\nB = f03ca689620a77ecd8a6f0de3\nM = f3ff0747d6e5f34a0ba4200f579259\n\nModMul = 7b30b44f75ed12f54136858ce4fe77d00e0952cf\nA = 993cd09f3e46423a8ba2053df\nB = feabee384158032dd013dc08d\nM = cd0b21388cb2033b1e792ec4078334df70b6c8f9\n\nModMul = 8ce1e17972f1a9d2e9437d0c5219354728a5f31337808d7650\nA = 90e5d18b017118177ffb080da\nB = f8e7e09032574f6c66e623ec8\nM = da795e6ef63ff7dc4baef5c327022ccf65d44e3c4e24823f11\n\nModMul = 8fcd412054\nA = 2e7f9b1a\nB = 6283de2c9a\nM = 9bff560ae7\n\nModMul = 57d0d3b79f1e2f3632fc\nA = 2f8cc403de5af54cfa39\nB = 3b798c3ead52878dfb2f\nM = 805e6cbde400d4b4bc9b\n\nModMul = 23331614e88633af879201f568c359\nA = f21f19da4b20980979a645dac\nB = ea752050b79883dcd69222536\nM = aed3faf4c88f7c4afe257c5ed90599\n\nModMul = 56dcf9ae1c787e773774df3c8762babb4675a212\nA = 9accf901fa599da05fa6ab5ff\nB = f7f6b9b1d7bae06237532e39f\nM = b5bcd776bb2eb0805ade3c8b47e883962d3cbdf5\n\nModMul = 61d0ee0786963906a028a1df01f836841ab6d39d88ca2717c0\nA = 8e57680f213d088ff1a1e7db3\nB = afebecc9943b0093f87022940\nM = b6201f68a45265d7e9183c3255feb4c110c05dadbcb13881bb\n\nModMul = 143ae78a29\nA = 334abb952a\nB = 74203e7a50\nM = c9535a9505\n\nModMul = 897a2b57e69f5a1469ea\nA = 1ec8ca0ea4fed52bdbbf\nB = 3a6273cab05e478a57b8\nM = dcb33163a8ea42c1ae6d\n\nModMul = 4a2c10e90e2d37111db79a44d3e31b\nA = a90e7bbd63fc4af6de83029ee\nB = cf09c3dd50b41afc7045e057b\nM = 8ab85d47e4270116a64f97dc4f0f15\n\nModMul = 70f94276c9d85fd3f71edfaad6051456f754da85\nA = fa3e9ff6e1aa1fb78e51711cb\nB = b115ed197c50b7ec4040ca255\nM = ad63f69ef1346e7549ba71c13b24b279f53bc9bd\n\nModMul = 861e7ef401866f815b983ba18a612913ecc20a67016d79cfac\nA = fc41a9ce06e882942f751be7a\nB = 881c05a51d1ba8134d126a48e\nM = b12200b39526c33b70e8aa23ebc400dea0d4d8fe42be103d5f\n\nModMul = 4e0051898a\nA = 2a06523f70\nB = 651b5044f0\nM = 9da4eb09b5\n\nModMul = cc8274c88d6affc3742f\nA = 9ccf0133f9628532f4f6\nB = c1d80907057be7a67b01\nM = d6e76e362da831f32685\n\nModMul = 568f15bed5c4405be9dd04673a9c46\nA = dd6029c3196feb6da7f0f4a48\nB = a5f6745f2cb64913d1d3236d8\nM = f62f02c9b9ca8993e3be9a02b444bf\n\nModMul = a629452d5ed19df040eca26eaca37d82c0fb1d8f\nA = 963c51a9415b03e85ccb09f25\nB = b1cffe333afe44311cb968ffe\nM = ab2128698d498e8d75455033cfbbf4487535773f\n\nModMul = 814030123025d287aaa8b826792999d72f2d589e0c7f7f3dbf\nA = c3b33f391e78bee97ceddf313\nB = a9136f3af450fdeb245eff425\nM = b6aa9c517eaecb70781e597b907583bbb569e970d229235a35\n\nModMul = 8735bd486d\nA = 563e15c52a\nB = 31293264e1\nM = 92f4b193df\n\nModMul = a541f69ca163b288dd0e\nA = a608b48c1dcaa18424b2\nB = 891b0b296e911068b00c\nM = d4140921f4b2c84f1eb1\n\nModMul = adc1b7cf65967b013d046866b4ed9d\nA = e97941448f65060cf63ecd486\nB = ca68936f76cb87a8fbdd37311\nM = ebbca2482fb82eeca2866057cf1179\n\nModMul = 44aa9f0dd58d4510a7364e130698b34eda23a632\nA = c11f83f01bb964ffac93a2e30\nB = e05ee40eea39f4538d735193d\nM = b5e8b511738979dc740a6a1f7291cf4561787be7\n\nModMul = 8b16b82f064f471983c7154abc9f9ba355111bacb90400372a\nA = acff8da571e1c96810bf95707\nB = cdd23e5504cc26d0c34a62b06\nM = f38902a99190ae0b5ef26849a6e943d651925666fea271fee7\n\nModMul = 193f453197\nA = 8cb3078675\nB = a8fb003a87\nM = b60ff22f4b\n\nModMul = 849c26c8cf5cae426a80\nA = 5d1e3d2b4d038a0a34be\nB = 34f70325565bf0523314\nM = cbc189f9a732cad8f425\n\nModMul = 9a4e64ff530c53a4c6c5b6b5021920\nA = f53b81723cf74f520a61e614e\nB = 9d8ac2e6b839143fdd079a2ff\nM = a115375435151798f3644bede9d863\n\nModMul = aac303a4623e80158af1cb3331965cc8e3184edd\nA = cce0a88606ff962fdc37e72c9\nB = 9840a500a2051625c517104db\nM = b99dafdbd91ec3c05791031df5e193c03d6a441d\n\nModMul = a31401dfa761bbe82b66b5f094151865b18a4ba75bb9b3dedf\nA = e6f48c027284856aaf3b96425\nB = b4c326f72a6a22fd4b93ba5b3\nM = e57d9608ac6e5b129b2c014958bfc59137f63838b1ba88a4ab\n\nModMul = 8b0929adbf\nA = 61fdf77ac0\nB = 8892f05400\nM = f12b3766eb\n\nModMul = 91b57f353307b173679d\nA = 33f8e73752072b4b5cfa\nB = b4c730f79f4f2c07945d\nM = d41be1d8d2e5753e3ae9\n\nModMul = af04c564adfeb120bc4770bc8c650c\nA = af151333b3d4cd1d29fd801db\nB = 9ccaac44ff91be11b30bdcdd0\nM = e0bd6e70d5f5ce08fbbfd48d43101f\n\nModMul = 1b8d623796a5065d9e993a53a9587a0fdbea1bbd\nA = a2fd08df2d4eab0cd6d29e213\nB = 92c9d26ae7c215b52199ee28b\nM = cd529f4cfa46f3bd3e7fadf167fdc02f6f881da3\n\nModMul = 4a8573dd8dc50a4fa39f3579d3869745eb8c1153ca508deefd\nA = 855f941d085305725da617f5d\nB = 8f09b7d2c36e0340523da5421\nM = fd8caa05edeaa81beefa01957eed97a981ab34bdeb6d8c704b\n\nModMul = 2d278e089\nA = 59d20a1716\nB = 8e2a58bc75\nM = b3d61ef699\n\nModMul = 2f937ce359d0f6cedd1\nA = 1019d11d26040ffd5b1d\nB = 7cdb6252087423d43e08\nM = e8f537323004447e669f\n\nModMul = 6567332e25af83089f7458786ab0ca\nA = bf9565e9f8a098894447b58fb\nB = fc867626f268c24cc0ab7bf8b\nM = 930f39183353363dcd822933a438ef\n\nModMul = 3692e73ad1d91ddc19cad3808eba2c5fc88e2bf9\nA = d0a42ce512629f0ffd233a9aa\nB = 97f6d3c4c655c7353a62d6ac4\nM = eac2ea84851f880214b8f40f881a2", - "e56a6ba6f2d\n\nModMul = 81df390c9e51b30bd639db15adb464c7cb1d011cb5e260be58\nA = c237eb242c40960861c938c08\nB = ab2f481f0d768eebd90d2574b\nM = 8697d7a28a5f42c9a7b31949b8b568f861142f44fe66c6cd3f\n\nModMul = c952f9aef\nA = 81973bbcb3\nB = 28ddee3bf7\nM = c4a40993c9\n\nModMul = 241dd53d93f7bdbbb2ee\nA = 2136eda4495c45c9f96c\nB = e74c4baa8ca3f6b7cd5b\nM = fff4594e7a5f0a1d3e15\n\nModMul = 5f861ed8b0aa835761613e6c869cfd\nA = bfc5c1572086079f5f5d18d1b\nB = 95902e14923c8010b7e905178\nM = a819c6c109d623f9b845aa23712c9b\n\nModMul = 5b8ab089c4e4c6804e48a2bc1d218718b3a32598\nA = fbe65d3852224a812c432672a\nB = d57a3f38da966d2471d70a048\nM = b9e6a626d3ad026d14248fc90c882bedd64a1f13\n\nModMul = 761438baf5b02dc095b7040e082da7b167c2b9ace956284ed\nA = fd91701ed2151f8e994bf4ee1\nB = 88b66e735b76972bccd9db182\nM = 8008b2d1274456aa68dc627b1ec3e1762c6ed2d660c64a1a55\n\nModMul = cb743c97a1\nA = 9c69ca9b60\nB = 7488f48f5\nM = d67040ed0d\n\nModMul = 931b2bee1bc30725a31\nA = 650f567b544ce02303d4\nB = 5858da30dd1fae88a675\nM = 91ce30234bb29fb9e833\n\nModMul = 5b4f262cec958a20390b5e568ccdaf\nA = f7e240e8a077e8e87506db2f1\nB = f8653fe64e3bd414782f51634\nM = fdb8225eefc1620648737d31dfe1f7\n\nModMul = 4c011d1ddfa30c901793cc6ce74db47584cebbd1\nA = eda8e9a9ea3cdae17bd50b1b4\nB = 992e8ef4a45593e4ceff67876\nM = 95e2f120cfcefbada1058af6c8853cbebedd5763\n\nModMul = 6e99aa5b8107399848cf24fbd88ed6350efb68d737e505b466\nA = ca6c51ba2f410d09bf71d60fe\nB = 8bdfa8fe5ef3b2ad02bc63c4d\nM = 84daecf412b8c50ad6dfdb546c3eb783dcc6f32003eda914bb\n\nModMul = 536175913582e73c1002083cb0cfce5471d4193a5b717881e60abfe927c829d\nA = 9b474b6f7d7f11dfbeb7a0724694f2daf9ccbaf2ec13269b5ae3329e8df95f7833baa68324509dcddfb5afa1d14f2dafc55e2c225475f16fb396beecc7a66dee\nB = d74a5081f00af2361c3537642c06cd47aae7e366741c9b4785e185af8b328acf3e2ed71e3b9a4b6fd49d956eef76740b3c6ec5850a90e7e444dfeaa7214c5eca\nM = 5efaeebe212752b28b5441a5d0b2600190504467c6359e9ab26320ee72cffcb\n\nModMul = 6161cceee2b74e7965a926fdf5344ddf8cc41994d72154a8b6014c18cf71634\nA = e7d6b74a1af0834aaf93e09a6488340b661449ba2bbc73d775e7d828163813ddbcd82719351879a6d67ab6b518011e1db43a3d620d1f24403917691d15ed6f90\nB = 3ecc8fd3103fe52a7e73ec4be4e60b69584bd886a030f017b482bde9d4b0b964ba8471cb32b3e9bd49864d9028a22d6b6b46be0451bb4222c3987b74a509f8fc\nM = 7c3e3b8b1a6110da82674aaf88c288cef4cfddf22e7c9b75640fd67fa5fad59\n\nModMul = 2acd55bdcccd55882eff0bb262bb62f78bff8e932aefc9d32f54d5d4e9b8bd76\nA = c221d1f0d1b7efe7e078dd01bed773f8876fa324b3fe91985d47d343e7f3878b457dae2f9ae68971245278a1d23cb541c56b94dd9ac43a9fbe28a46efc627651\nB = 49f94c19ff7ce990637c3d2019ed66f7e6dbb1442b04a4593cc480521b991cb1b878f8c31903240f89e34336d9e6785433617e729b71adcbef622a683357e035\nM = 43760c71742e9cf22cae6fc262c008b7f1b97a78c8063957b74aa4cd370c1eeb\n\nModMul = 504c11e38284a30e3647c1ddfaed94503d833bcecdff05e749422ad1d9442540\nA = 3fbabe2d65f443e7db0a6f332330ecc4d1d40e14fcb510499552020405cafcf10a50a5ee47cf60fd8c22a22b3f753b4167c213851f32109babe4b5c298d6c4cf\nB = 62e5b0f887dcb1f1794bae7dad46a066f810cf5f82a1eea99207b5f0fb0ae9084c5e62cc97b2672b1cf4cc1400a19bdcb093c97404876b584a6482931e7ba9b7\nM = d79fab3eb31189268b2a0689cafdaa0826f07d432591e8aa8bd3c7cdce1470a7\n\nModMul = 13a6431c57ddf0ed3979412ba8454a0dd9a2694a0dd76453aae63366c46e41db\nA = 7e1fd0bd9ab0aa75b264475604aea09f24239f94847ce2549d43b71890c0549938d167adebc7890d3c492b5874da7bf18d895ccaf1803b9776820598928b407c\nB = 5e54e5185bc86f16177f1354a57d36ac2980def141b389e4bfda134fae7c158009ccc61ef66281905128b6297f876662104ead2315024f129c56eaa387f80b4d\nM = 182572149b860615dd853f37f7d51a35e85f5e4a4249a60fde58dc68e0dd7401\n\nModMul = 145a44566bd75103083b7556a822ea6008ed3a6a1bf135b68fcf87a294c09b4\nA = a195e4315caa8cc0707063c7359c28139d4dfffb57eb726156336e13227ad9766ea1fc99152893ebb194fecfc153d47cb927a633217328f05e4d8782aeb89d04\nB = a97ae97dc7e9a224cab94ecedc08d0cbf7a012dc5209b1e1e8b5b843fcf61e65db3457d6085545a633be47b742e8237cc716357ff5bce9b00e23671ec1d049a8\nM = 29b060ee2aef7e43e02163d279ce49259127198adf462d13aa195c7dccf573a1\n\nModMul = b00740cef7791692d45f5a7110f3eeb260638f19f87c9245436fc0422de90658\nA = e6b97c11ad44fd451d168d65d1691d2220db8c3b6c8436d59f4c1366aac52558d0d6b61f5d6966460a4a31085fac711e5a09af5563d938963555d4730982eb0\nB = 6805eab5a4da534f07def6d2c320a6cbdfe4831fc2163dfcef740607b3181d8647bfae8f8c16237c1c1c5d14b9e3417132f81b3a7db4b7fc11927aab30dca590\nM = f975a94fa62b4c0e68df5c3ac5917d18927c0a6d9cf39c26f6ed97a81cedf227\n\nModMul = dc04b6ba2eb1e34ea8942a50d1d0c5479dd22109895796ffdc9cd32b53d4764\nA = 7fd3310af09a67e0684dcd8e3b4b651c7c13c2f6a0a47b59a7f5cd8bd80854d1d4fe02eaa61843d6bb2b87f99d8ec4842864681eaf056538ffff610c231e1d\nB = 15f1661c59ee9f93400073e18a91503a93d47537d2da5cf5e4bc69ccc87b07bed171a95f1c5eaa9c7d7ab207ab3f1f7634c5d16e706969e869364207f61d84bf\nM = 22e2856f4c2b6c01448d4aef74aaaee3a14e9660b5b277200f2e67464ecadfab\n\nModMul = 19299c9e960ce15087e9fbd66f95cafe82546431b92d70db1de87c3425c1bef2\nA = 8e3abb1f24e1f91496db99be9409f57f67cfb6e0e33d603a2a31e1309f1d0bbdc413c3e4fbb5e3d923f683afa9942b9b9fad6a6e558b2297889fff47ccef7d23\nB = dbdf5940dcd68127d476badbd5a2f3018aa4d8db79f81337ddfcb108637110b934e946d3284ec09d5255605ad72424f1894238ee4f7964dffc27fad838532321\nM = ab6b4e3d3909512f5d1d62a30c1ab8dd5e584cadbce9dffd12fe203f8936ee93\n\nModMul = 4f88ad4e30e6e8e38cba0452d98d4a3547c680f16308692e33e5577772658764\nA = 5137697bf48982edd869e4a42f3cb858bf65ad5b25d1c0e8b75d054460d0944ecb5a6924721c5728964d84231c7ae808f556837aefb23fe3ad36aec9f5f60f20\nB = c79554304620f8116b9a8bb56f6a23620e9fd504f7163f732e1e6367d25c6ff98cb01d16faf3e018dec6a067d1204a6aa95470598ce757bcfbc3ab4f5d8ec88\nM = 9ba20dd78923d8ef82897ac46a509cf22c9b7986a4facf42e5416bfe3576a735\n\nModMul = 985a4d2a7431e09fcad03e6a3f926582dbc0aedc588f17aa5db40c2d3566233\nA = 908bff40440aaeee6c90b6312dc017c3bdae884a9074e02b26f01be1f018390e01f0d111f99a06c16e20538df8000d4066cd4bb3628da88a3a5cc240cfac719f\nB = 6ebfe9fe53909876784f9d6e5dcca4cfa9463fbd8426c5bb8890ae84c2fad119615fe1e1f2ee5fa544a5ac713ed1da8c1e04f282f1f1b9fba4b4c4bd9db20538\nM = c66842e0a11ed6ad1e8f192ea97f5f244536cfc5234c7fdae1ff905123c72793\n\nModMul = 133d7b31537b627da2c042217cd28625437c28c3e06258427d9a4384046a1f4\nA = afb695e3e40347f60a500e01fba4df1c1f2fd4ed79e3f65913d82369f79d80db6b3978e6351c70c148f572b9c0c2b1efeefa605251b3156d9b66d240467e550f\nB = 8855046dcf50f80f278227d5260b9be53ca2e4a1cfe1afce4d35b11d0fa17a36a8bee8126e13bbb318d476becad5a935e9d160fa481e1437b292bdc169dc7d45\nM = 3eae4f0d6c7e1fb9de1a4c160404a8767783c7f839fe27a543f5c389c679d47\n\nModMul = 7f4576a315bad5c7fbb1616e8b26c5b34ca6f701b9b1adf0485fec181c41dee9\nA = bc2baf0153a4598f6b5f488c43b2546cadfaca2c1931b919f98ba71835a8fe78886da1fea25b194e60ed6f9e0ad23c988b64af9278155c1722dcf4983a1566c2\nB = d8374d91fd3c523ecdd6bdd265c9a8958dd222f9f0e25454fd683bd86d7900a273b56f1f47e033c46527e32c721094ce6bc927d25fac05d7fa6db4d7a6773c94\nM = 9975d8e7f2a4d9d1ff8d442b93ff269a83fee43a18bbfa8c2ccd7ca5fac3a8d3\n\nModMul = 57ebfb39605d4fa6ef5fd03bd8e4fd685664297c29b7ad75a40b133e15fc5ae9\nA = efed8e442154b1eb6c75775cc23e01fa65c9c361e222da123d07daad3039f305e7102edff23b65c333f0caae4f7929857c3169f4ae47c9f0fd920c38eb42bf2f\nB = db05415ea90269a74b0919ff772c148c0eeb2ff9dea76a6e73e82eb86bc76fb42308b55ef83a769a91d23b7840d5d2f5129f15279dfab7cd8d63778acf202f26\nM = 7704390c4b1da86d51ff817003e5451d601a5352296e339e5da219ec5a330479\n\nModMul = 40b6b0d44cf8a5ca7f4fd03dd6e1e2a11f74f3911dcd8727e57db8d65cd490d\nA = 6500f3cf686eec4e1f243616ac0ea8e8d11ddbade490b86baf231e7b2fd55968ee14b6bb7badf8c898874099831976af46bcbfbfaea10d49aa803c6e51238e2\nB = 1fac744fa1e26e789639e049679d0e2eb57336279f09555e10210e7143199a3df5fbf5294edc386ac762fa3a3b0b4bc28945adf21a8af747a29018bf76d3710a\nM = 5c0781a87b84ecb4362b09c623d511de53c085671dd4f08e9a551685b55ddfd1\n\nModMul = 6b778ae9822221e6a8376379e0032d7edb14d7b5e32a7310897b54d1d5626113\nA = c4a5737a9496129a136753f8c2e52bbd2660f2d3fafe4ed702900b01c14e506d13e3bbeab19b357e5ba9fce8a4fc3dcc469406a16248d6fb53862781fd9d55e4\nB = 444e5a673eeb37fd3b4f6b6f5133b0f46c2ea532e1953da4a0e144407a8e2534c5ff40cc9af7756e5aff9df57d938fcedaffb868dcf4e458b36f506ed7fe0ce5\nM = 7f5978c0c066132a9bdcb00727bb802b72777b9e8e4265f76b80cfdc3a788817\n\nModMul = 5c717e5dd25abe60f761d6f9326ed056416add4c1384682d87b7ff12e112f855\nA = 4351965a421c75c5b4c251861e53316a300ed7983e27e17f9308420f0d2cb11e9c476294fcd9042a525bc1a044bb442d1d9f853c", - "9e07245170e0e2711010cd1c\nB = 4e1046647c362c8f9c414be54075b4e9d151c6fa0c3da40d90e6042625947ca2c9f20cfbcfdab8666dac5a15f6cda9d47b09f654131fc5addc07e382c9639323\nM = a6c789884c66c7f028099e0367b3ed86871277bf070c541ee12fc02fcb6181d7\n\nModMul = 4452688244f542125168853f1d444f96ab0f82903bb12a97e59f0db633edfd6\nA = 9fd1cc81981bff977244c044146918057ad06d3cc26edfb8fb4118ee02b959d45555f9507ffeb23c3688e29ccdfe5f583fa3761f6727573542bee8ab5f5b600d\nB = 856e6a03b5c93fc19deea51b3bfe42c810c5bcf9ffbd08e2625eb209baf6a4e24943a3c090d89c1f70aea9f0128e511fe92e03715d917168c1e1ca77a3a8731f\nM = 2c245d407a78903ef2b279ddbe32106e6333b6f44cabf87b8641b047c79ea06b\n\nModMul = 375f8474ee47df6b9a038512002e56cddd374d69c69719d8d369232c64a839e2\nA = add40f1dd6d4a2414b17f0c628eed9a8f082f3ad1f34ec41935fa86b34d4505b22ea80c062386a9ed63f95c67e55c686f837bddf8f4da791f98b08c02f32d4b2\nB = dab1caaa11d5a208b7a6b7a1d6482a4859daaba5e3a77b1b1020e8ae62a664953dfddd0b47d40526e7a3c6a5363c6d41dd9f529fd8b58d5d31bb67e745cb71b3\nM = 4f506313a4f49873a405f2e5a6e9cfae9cd5e9f67b5ef900153366570e28a955\n\nModMul = 36fb0733a26902f0f8f11625305a3c94fcdfffe294eb6ccba110aa628a314df\nA = 52ee1498bd6a1677db801ae2eab4951345a1fcf8fe7d38e3f28dbc27fae508d87c9958e02a375ff4891b88ee916b96331e7cc082615faa028f6d541b5ce37876\nB = 9343cfa074f50c20e8472f8f7c4a7d330aa30ee417ed8027a4c956e84cc5cb31d5411c14796d9325fceef79a51b5d8a4c89182ca273ab633e6a7b22a27352300\nM = 9d7c334aa33634f9f313b71b42476a3b627a6c5bb8ac1d07a8d732d5c087bd9\n\nModMul = 4a377267508eb045e00cea66a417112dac07545304bbeac6315625275b7cbfad\nA = 19616a82b75b08499d4b1f869df2db8f71398672f3f97ffc6177a4a5aa913605ce8a6ab5f778cac508f0b3f2aa680b01ccdc57c0fdd6cd678a2ff2dcd7f01f3c\nB = a5643a9a9fe3be4134082daae4ee7dfd85d9452beee856fd939d3be9788b6bebcf3571c67ec481ff9b20f70d23e82e2171b1d0ddf0a9435b40115d32aedb6811\nM = ea0477e7f1a02cb6c21171066f3dab69d4e24429043b0f049de660fc80e51937\n\nModMul = 7952dfdb91252658430e365adeefd9093740de92cfc9dd3d92294f2dab6ca0b6\nA = 8e6cd7639b7c134b53e6ae6ac5f51268da83ed09e8e96d65e4bb130dcdbbab9e48226ddba6efe93faa510bde8ee92f2a641774c4272b5a2f88024b77a2cfa110\nB = fe4e8109a49b16b96871e384564cc096277dad4e1bbca8e5feb33f140a4fb800c8f3096b1bc7042bccf249aede88e6055c0db609f94e214b1251eda494be724b\nM = aa46853682af960824140c35d145a6dcff6283b2c59994b30ecf9b8def41a025\n\nModMul = 1aacec7f7e66b0cf4eb2dfda9d8d3fbf4eb8e928cbbc967d13e3e38612f0346d\nA = b0fd7a936b0908ba6fa797e4b855d673ff85d665ef3a345e560e2c0049becf5c25b6c0068dd617ab47a8fd151939ea0631f86806ddd40e557933c0e880fcdd0b\nB = 105c87fe2b1bf0be5405ca0d530beda1780f0045e892d7810f8a8abbe890f0a19de66497cba55bf38e190c52992467c22a320c38a4bd167f774ed812f1271d5a\nM = ac4f0a2b22df691331ded955a5d0e7d1910d7920a59d4a87636b2635397b7335\n\nModMul = 2c25d180156fa7d2fc20c9bd6d9ff0b111c9ad76ada0784e2f8fa0bd06413f66\nA = 2aa4a0a73df11f4e60956619d0b35eaef45730d619f9b920298e6d369b9861f6411de28a34af038f288d7a3d6a35b10c8082b8ad0fb275a8f67c6832ac46ba9\nB = fae1d50b72feb25da2581829409391bf289cd9f730c99d265b5b2d63889381cde4adbf85c3998c2478f2866526b8f64605d75765edd09b78ea45337207d173\nM = 65c9d79a09a820adbc9beb152bef387c1439147ed50cef872d36a69f1c7d5fe1\n\nModMul = 56ec8624fc199e7b4e68358f88f1a99f1d4d02577b8c6f7e28e4ccfdd981f995\nA = b0a0f9d05d144d2ef257c1e63a7127a3b8e0d8b64ff8f6447618560593574b5c5da6258b274efc28da0defd988bef1efca0f481f809665a78954b36741d668bd\nB = 10901b9dbf0016cbcc671da75a75b7a6ec6a66dd17b53a97344864b08f037098537380bfb0137b6becfc36a75206686d16bc4eb8fd54299494374e3f383d9b10\nM = 73882376ca850c125ce9f20c291e550ee48f0eb0d571109ab08c22d6719496e9\n\nModMul = acceebe131aa34ff21b3235f045bccc8a8f762dca20c1dd1ef6eb461ea971c6c\nA = a7714b249eb0f0cbe3e6fa0b04e895fcf14c404876197defafc6b57026ae7e5e993fc47c1819581adc03860ce07f2b7877a3f6d0912c0cbc659f5f6170a1cb2b\nB = b7278ecd154ef5243ad973ead291ea186acb63e09977e644a6a9fde195d1a33993fc47c1819581adc03860ce07f2b7877a3f6d0912c0cbc659f5f6170a1cb2b\nM = c52ae49e1a4b21ec392b76844ad559653b7b9f67a58b3bba6c2ce250017eab09\n\nModMul = 62b5b04dc84bb4ee04934c03ef361bc6e59b42144dc117b9f7771525c67c3688\nA = 2b65f491caf0b5cd9c66c859fbcadaec7213e6b848884638791b1620d6e4bc9dde087af0e7329d3b15a45df2d43ebde61b053ad7f63917aa922d58b4f3222620\nB = c1bfcdb34b0766be980540dc3256b9ee4158310fad2c43cf24bfafca08ee185647043f5842a9d9eda224449259341b7c50998086434528d47661bf5762a7ab5f\nM = f73398c32191b436d14a0b76c6069b1d61395568753c832dd0c707780a232dc9\n\nModMul = 5613c8fb0721bd3f605089def48fb2c38a4862bb387886c1edc1bc37d10f0e15\nA = a3d8b12a2c8f4021ca045a4e4903687dea63ee7e88893b1911aea77efbff00f8f5c7884cbafc71f59fa2636195c2ebee61edbf642923f34d87ba5eb49b06a7ee\nB = 3231829c81b26dcac432b502ce22e126ab564922b1e9818cd3da46edc5ce7df026d0e515809c97bcfdb9666581efbfd364437ba9959dfad099f90472f97c69ec\nM = df8344fa848d1066afe4f8d985cff65441751677dcf3a4e99b40365fc3c978e9\n\nModMul = 30325f7ccbc2c69e11d739ad7132a947c53377aa902ec70b152f3a75e050c244\nA = e4ba620125f58a63fe12fbd3eccdea477d56b120c76d5d1421bebd74e8686b4093f8169070453ccc04b63b173568385313a1d9c841a4aa82a61cb84d4286a941\nB = e87aaa990307855f8e5f2e5509d2ce31dd4b13bb7199cf5fa0593e350326e222efc33a26c69245565d6ebb5a484cfef7d2558f22dea8054d07831d536803d0dd\nM = 43d57108eb0ab9bebaa8ce137628ea825951c6accb9acb7f1e991c93b8563897\n\nModMul = 1975db7b72434ad32c9aee412645f6670b7f4af1f8a424a5031c559d3e18dce6\nA = bd64b1db27fa7da4c92a4ee092f58a2a53ed0f12d009fe13b36d5fd585defe778fafea4a60e8fe567d03e9ba3b72b189e22504ae8ca6aad7c2ac0f44abca2f6\nB = b487d8116198560d6c5b08c7ce63b0acc0c98e6f2a8d709cf4e3a409edd55f64d72fc27a70dc341e280ff5a1b09fe131773d466cb31991d2db23a2a86d225c80\nM = 39d57af763eabe569dac1a103e169e6e3b4375168e41e5c3b961b6e743915923\n\nModMul = 3bbb5bde9e3e240694326571360090e1fc0a4ea7b2311c1e0bd3961f6c159385\nA = 4181ee3bf9a98bcd49eaea243a179cddbf160981efc720685c7be1dfeb5aa552685a2cd46f340e1e1da893b3b460692fa2eaf6c100f24a14f239e45123242d53\nB = 77cd04d86dd5da322af78be54246dd6b7af490d903db1db03cbccde535570b81c6053a84110c07f097540ffe7510320024b7bafb77e9e239761def76092e1d59\nM = f3b9833a303eb540cf8b6cbc3cf16394b1634ef517be57684e42d364d8bec3e5\n\nModMul = 2d8174211f0367233b3a8df7c5bf0066d6aa792be7cdc5e850a477454d5c829f\nA = 1c08cec52d96136fbd9078b7b8db36ab63b86e19dd3dba7b2e3190ff566180e89dfee9423fa4e99be2187eda6aedfa86b9a45eb1e4655257315ae6a280f0a6ee\nB = a8b4bc9647d8df9b7c76cc6d0f2248cdbc41f5da9c061f9864aa8415c9557582cada456cf23cc32d47d1fc1caf19d36b398019aac4734e10f55ce3cad419e5e7\nM = 7eacffe21f88413af94155a2a8e37f70a431a59653738afda04a1bec72d0d9ed\n\n# Regression tests for CVE-2016-7055.\n\nModMul = ccd6f75b5f24b7c5ce2ce755fa89c2450c6a7d96ce8c8791e659eab84577a7695e3b2caa7c980fb23f60634233e9798499c28b0338c1f1a326d0ca89fd41f2fd88b759f317889832966b551a950043ec7a4b6152d3e2cbfb40e88458e70ab783b96f12d271f828d5b39e198ccaf8665411d85026282dbead5d24cd01b6c8a8e9\nA = 7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878\nB = 095d72c08c097ba488c5e439c655a192eafb6380073d8c2664668eddb4060744e16e57fb4edb9ae10a0cefcdc28a894f689a128379db279d48a2e20849d685939b7803bcf46cebf5c533fb0dd35b080593de5472e3fe5db951b8bff9b4cb8f039cc638a5ee8cdd703719f8000e6a9f63beed5f2fcd52ff293ea05a251bb4ab81\nM = d78af684e71db0c39cff4e64fb9db567132cb9c50cc98009feb820b26f2ded9b91b9b5e2b83ae0ae4eb4e0523ca726bfbe969b89fd754f674ce99118c3f2d1c5d81fdc7c54e02b60262b241d53c040e99e45826eca37a804668e690e1afc1ca42c9a15d84d4954425f0b7642fc0bd9d7b24e2618d2dcc9b729d944badacfddaf\n\nModMul = ccd6f75b5f24b7c5ce2ce755fa89c2450c6a7d96ce8c8791e659eab84577a7695e3b2caa7c980fb23f60634233e9798499c28b0338c1f1a326d0ca89fd41f2fd88b759f317889832966b551a950043ec7a4b6152d3e2cbfb40e88458e70ab783b96f12d271f828d5b39e198ccaf8665411d85026282dbead5d24cd01b6c8a8e9\nA = 095d72c08c097ba488c5e439c655a192eafb6380073d8c2664668eddb4060744e16e57fb4edb9ae10a0cefcdc28a894f689a128379db279d48a2e20849d685939b7803bcf46cebf5c533fb0dd35b080593de5472e3fe5db951b8bff9b4cb8f039cc638a5ee8cdd703719f8000e6a9f63beed5f2fcd52ff293ea05a251bb4ab81\nB = 7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878", - "787878787878787878787878787878787878787878787878787878\nM = d78af684e71db0c39cff4e64fb9db567132cb9c50cc98009feb820b26f2ded9b91b9b5e2b83ae0ae4eb4e0523ca726bfbe969b89fd754f674ce99118c3f2d1c5d81fdc7c54e02b60262b241d53c040e99e45826eca37a804668e690e1afc1ca42c9a15d84d4954425f0b7642fc0bd9d7b24e2618d2dcc9b729d944badacfddaf\n\n\n# ModSquare tests.\n#\n# These test vectors satisfy A * A = ModSquare (mod M) and 0 <= ModSquare < M.\n\n# Regression test for CVE-2017-3732.\nModSquare = fffffffdfffffd01000009000002f6fffdf403000312000402f3fff5f602fe080a0005fdfafffa00010001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000002000002fefffff7fffffd07000109fdfffef3fffdfd06000405ff00fdfbfffe00010001\nA = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff00000000\nM = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff\n\n\n# ModExp tests.\n#\n# These test vectors satisfy A ^ E = ModExp (mod M) and 0 <= ModExp < M.\n\nModExp = 00\nA = -01\nE = 01\nM = 01\n\nModExp = 01\nA = -02\nE = 01\nM = 03\n\nModExp = 01\nA = -01\nE = 02\nM = 03\n\nModExp = 01\nA = -02\nE = 02\nM = 03\n\nModExp = 00\nA = -03\nE = 02\nM = 03\n\nModExp = 02\nA = -04\nE = 01\nM = 03\n\nModExp = 01\nA = -04\nE = 02\nM = 03\n\n# Regression test for carry propagation bug in sqr8x_reduction.\nModExp = 19324b647d967d644b3219\nA = 050505050505\nE = 02\nM = 414141414141414141414127414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\n\nModExp = 208f8aa0\nA = 86b49\nE = 2\nM = 30d26ecb\n\nModExp = 27308229\nA = 17591bb\nE = 6\nM = 30d26ecb\n\nModExp = 2bdf498f\nA = 21292626\nE = d\nM = 30d26ecb\n\nModExp = 11317167\nA = 4a655df24\nE = 10\nM = 30d26ecb\n\nModExp = 2e1b88e\nA = da6b761a86\nE = 35\nM = 30d26ecb\n\nModExp = 20a12ec3\nA = ea811\nE = 2\nM = 23bc042f\n\nModExp = c42ced\nA = 1011a6a\nE = 4\nM = 23bc042f\n\nModExp = 4637d79\nA = 28d9a601\nE = 8\nM = 23bc042f\n\nModExp = 20e5669b\nA = 72fe6bc20\nE = 11\nM = 23bc042f\n\nModExp = 142ab9e3\nA = 9a07b9363c\nE = 29\nM = 23bc042f\n\nModExp = 14c64646\nA = 822df\nE = 3\nM = 30915765\n\nModExp = 160e35a2\nA = 15ea542\nE = 5\nM = 30915765\n\nModExp = 2f23a488\nA = 34d2e02e\nE = e\nM = 30915765\n\nModExp = 28e67f93\nA = 636a32703\nE = 14\nM = 30915765\n\nModExp = 29bfeaa5\nA = c8646998e6\nE = 2c\nM = 30915765\n\nModExp = 30959e22\nA = 81dad\nE = 3\nM = 326dd68d\n\nModExp = 1a1da4fa\nA = 116adb9\nE = 5\nM = 326dd68d\n\nModExp = 272bf0d8\nA = 2d21ef08\nE = 8\nM = 326dd68d\n\nModExp = 29f5054b\nA = 76989850a\nE = 16\nM = 326dd68d\n\nModExp = e6c7b77\nA = b88ee70d2a\nE = 3e\nM = 326dd68d\n\nModExp = 369605e1\nA = cf26f\nE = 2\nM = 3ce082eb\n\nModExp = 168a3c5d\nA = 1f82caf\nE = 5\nM = 3ce082eb\n\nModExp = 125c4bb8\nA = 2e9c4c07\nE = 9\nM = 3ce082eb\n\nModExp = 1c5fe761\nA = 523ab37f1\nE = 14\nM = 3ce082eb\n\nModExp = 21703009\nA = dc832165e8\nE = 20\nM = 3ce082eb\n\nModExp = 1228d1e\nA = a5555\nE = 3\nM = 24665b27\n\nModExp = 5226af4\nA = 1077bd6\nE = 4\nM = 24665b27\n\nModExp = 1b14eac1\nA = 2db3a834\nE = f\nM = 24665b27\n\nModExp = 161727bc\nA = 6bd962cb6\nE = 19\nM = 24665b27\n\nModExp = 10d61d0d\nA = c10caed407\nE = 28\nM = 24665b27\n\nModExp = 233da406\nA = b125f\nE = 3\nM = 33509981\n\nModExp = 24032799\nA = 1656b7c\nE = 6\nM = 33509981\n\nModExp = 129ecebe\nA = 2e671504\nE = a\nM = 33509981\n\nModExp = 20c20bac\nA = 4d7a2de44\nE = 1f\nM = 33509981\n\nModExp = 2e3ce9d3\nA = c53b3def4d\nE = 31\nM = 33509981\n\nModExp = 12fadfd6\nA = b4cf8\nE = 2\nM = 36e9d4ae\n\nModExp = 457ac85\nA = 1b1c7e9\nE = 7\nM = 36e9d4ae\n\nModExp = 31debef4\nA = 3a973028\nE = d\nM = 36e9d4ae\n\nModExp = 2333ad93\nA = 552b97c45\nE = 11\nM = 36e9d4ae\n\nModExp = 99ba1fb\nA = 8bfb949cbb\nE = 28\nM = 36e9d4ae\n\nModExp = 27b691de\nA = 93492\nE = 3\nM = 298fdb16\n\nModExp = 3c2b70f\nA = 14e7b0d\nE = 4\nM = 298fdb16\n\nModExp = 1486cda7\nA = 29acff81\nE = c\nM = 298fdb16\n\nModExp = 11725275\nA = 507489205\nE = 13\nM = 298fdb16\n\nModExp = 24d14627\nA = e71c55606d\nE = 35\nM = 298fdb16\n\nModExp = 222b8d14\nA = 9b1a0\nE = 3\nM = 3db59d12\n\nModExp = 3b8bd47d\nA = 13f4e8d\nE = 7\nM = 3db59d12\n\nModExp = 17e72356\nA = 334774ce\nE = a\nM = 3db59d12\n\nModExp = 306447ca\nA = 47079ddd2\nE = 12\nM = 3db59d12\n\nModExp = 90bef3b\nA = a75d62616d\nE = 37\nM = 3db59d12\n\nModExp = 1\nA = cddd44f47e84b3276cc36a5c0d742cc703e61c4756168601fbb1b6eb598c161019562344dd56ab6f603d920a12c360b285e6496a3605a2f8d691c3598233ee9366b5f2692554893bdeb67b7bdaf35ab7273ac593145e26bed82c70ba5793bf4bc5cac4c80b01785d1496beede493806e4f4aa89fd8d41de80dd6d0a3e2742678\nE = 0\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 0\nA = 0\nE = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 5150fb769d5c5d341aaf56639a7bcc77c415fe46439938a2190283409692f29cd080bfe3433005d98d24718a03a3553c8560c5e9c8ed0f53b8945eb18290e1c1a83d919302510f66dd89b58acc2de79ad54b8a30d3e1019d4d222556beefca0821b094ecf104b5e4cfce69d2d520d2abf54f3e393d25ed3d27e8c2e3ca2e5ff9\nA = ead8c5a451541c50cab74de530c89376d9a55c723e0cac3c84b25f0093c08a2961e49ab48966361c42c9f99111587252d98395b76788400d75c66ef208ea2767a28d6f8dc3a859f39c95765d57f139e7fc14f47c908c62df051e7216d379f52028843b4d82ef49133cce8fe671ae179423ac8da5be43b01caaf425cd969300cd\nE = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf", - "0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 1\nA = 935561297d1d90255aef891e2e30aa09935409de3d4a5abc340ac9a9b7dce33e9f5ce407f3a67ec30e0dc30481070823f8542463e46828d9cafb672a506d6753688cbad3d2761079f770c726c0b957071a30876c4d448e884b647833befbcd6b582787bf769d63cf55e68c7b869a0b86374f8920516cf5d528f348b6057450a1\nE = 0\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = 0\nA = 0\nE = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = bbad67352704a6321809f742826bf3d1c31c0ad057bf81432abeb30dc9913c896c03e69eb1cde6b78ffcb320c4625bd38ef23a08d6c64dc86aec951b72d74b097e209ce63092959894614e3865a6153ec0ff6fda639e44071a33763f6b18edc1c22094c3f844f04a86d414c4cb618e9812991c61289360c7ba60f190f75038d0\nA = 855144760f2be2f2038d8ff628f03a902ae2e07736f2695ec980f84a1781665ab65e2b4e53d31856f431a32fd58d8a7727acee54cc54a62161b035c0293714ca294e2161ea4a48660bf084b885f504ad23ea338030460310bd19186be9030ab5136f09fe6a9223962bce385aaaf9c39fe6ed6d005fa96163fe15cdfa08fc914d\nE = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = 1\nA = 9d92629c1ab181c50c31619e8acd0d235a1f5fc7a0bef4d4fd54b4f1968d45921f8522efe88e69c6c14c576c564592b9feb00d1554b88b038934eaf4a8ce81a2582732387490181ef158360c8b2d9ccb326ffe043f776a50cb8202837f08ca743b562eefa007150ab7012c341b16248478d4775c02ad71ea13d5e82b71e2d600\nE = 0\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 0\nA = 0\nE = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 24eaead5b57883c2f454928f8edd470a344bfe07a953194f7d635d705ef13ddfc64140c8ad6f363d4c828e7c7891a6b6d4df37335de4552c319dafd1c06d1f743240082a3535df4da1475d3eea3fead20e40815fd5a0876c881c162ab65a1eda494280c258901ca953d1d039a998bf0e9aa09273bbef4865f3054663b72d75ff\nA = a31618b4532f53729ba22efb2221432fab1dbb70853d6a1159b42fd19fc949965c709b209de106a652aa422d88922ce51dae47f7f6deaf0055202e13db79ee84fc3d3c6f4c003ef96597c49d6895fa53c22ac9e4819f7048146b5272f6279424fdb389819a0b251c823c76f4bebf4f1246de455aafe82a0d34454f5039e90839\nE = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 1\nA = a8558e7f455b27c0c46d7d0862eb409cdefbeca945e0284b5bf425b7ac0f3d316bc365594cc1639decffc621214d61479bc75135120d4ac09ea8b742ad7ec1822091b62b1c6f564fe5e2f4f5b7def92cbaaa9a898549207ab01b91c2324fbd306a87f7d6379b6fb6493c5fca76729767f136120da9c90bdc7d364f7d242d5acc\nE = 0\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 0\nA = 0\nE = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 292f0b39ca0f1c850b1a00cffd2d54924fcd5fc7e7504c9d593e6c0ff74760b1f4bdd81679fe06c50248336f3108c593fa111072ee87d0fcc89a63243a1dc89044503663eee9bc18f51c3e0193d9108303e12ac90ff78f6ec752a4386af09c42db524a7cbe9a3d4fcccd56c34d283bcc9debc17158b5fe8df0c1888a9841bf8f\nA = b4fde2908745ff92cc5826a27dcfdda09e8fffee681844fa4c7f1354d946d5d84e0e0c7a4a4cb20943d9c73dd707ca47d796945d6f6b55933b615e2c522f5dfc33e0652917b4809bab86f4fa56b32b746c177764895492d0a6a699812b2827fe701d40ef7effd78ea8efe1cac15ff74a295a09614bf04cae1a5017872ba22efe\nE = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 1\nA = e2845c572b46496ac158a731f612fd40ef626fa7134755c25b1b7614f4d7b29164e6142ddb7985e4c7ebc575855ff901e95927fe98a5aea2ad3a4720c75782323bea1518b2c57790f44efd9411be4e95b3896bad1e73c59658290b309e5a7eb5ef8be08125063e57336b80f17eacee88966d12bbaaa15a25929c82e027cf696f\nE = 0\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = 0\nA = 0\nE = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = c90e4c69df92e26549b016950b59080947f5403430698e128477782480dd70be96bed2b9042dd8c708eb432e02710555b97af11ce6fa9b53395022851c32d1f53f04237fb0763563b440ca6e81a50d909d907d9c26b7d3c420dbf88f7dadd488666848135f8cdc608dcfb0691989289fb54379c2e84c262f9765f68c012ca1b9", - "\nA = 882ea1b9b6c79a3b1bdfd284658cb6227ad825e0178cab713c7413c2ec34f03cfaec470c4f5c521f5e9899a2123878ff0f5b36a4196c08ad1b04d03746c4bfb5d126f5eefbfe172627d6732710a8ac8890cedbd4fdef69a19f2b3253a5aa0e5dd5484f72d59b17bdd1dad3db209a3ab839368ed3975069685911d7b35e41a9e6\nE = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = 1\nA = d7a99e65b8af86b1c51d851f0447e43cd4f343cb0ada7236283e69aa7ebd383826acc9809e5dbc4002d0f2430022cb026458189db3805ce2de1142a31ba71a6c064ab51f0059eb4b931b8bcbaef023c38d57aa5f3e14f5df77e547fc028702071b58bd57338be1e1e4f98d3553484e4de359cefa29c5f58d3fa5d823f389dbef\nE = 0\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 0\nA = 0\nE = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 186c50ae259aa0fd31859cbcfea534e626a254de33956d5d719334bb32e7cf37cf199a21f079a5b90497228994d05efe19ccd8c769cd81f896286e8ae557cacd1630a928c629ecdfece29ab3697794aa707734e007318fa7029b050bb09ebbe6986187c6ca843f55266d275620b3f0fec0ad5f847ce8b314d929d128b33a249e\nA = 9d5e345793faddca9867f23eeddf6816c1e837f7a2cf96fa077212514acb6be87ac01a237d8f2f1d07d27a8ddd1b0ae0d97e1bda4f205a89435017284cdedea3e407b1b940d6f52112b6359b3e86e4c83074b17c210ae2c8856b42b169b4a7a6dfa65b368a7959496cf9bb1ee93d019dbd79101830e3f5ed08604ab90890b914\nE = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 1\nA = e6a079bdf7b0638d50b183475e9ddfd5cbdebfb29f5fae8e9be402a0bd36085737b556492ea7fb4b1000ae9ce59db66098129b757cfb29224275fdaa46b8b7eb18a93ca7d3e446dc38c734b683d7ba7927b008d993aab01f44239d3c76be76d1503908e9b5e73b36c43ae0771368b01f39c042693bd92c4fc50810f059e1b332\nE = 0\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 0\nA = 0\nE = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 60719701a2dc0bcde281a93ce0b8421d1a718adee43c1b5d9fe9e697a48ab3db4f9f33c73cff305ab6b6c300c149b05c6b289dce4580860dc56bc59de81ac074ecebdc65aa3ca040b44e5b3c80ddba1658d78b9abbc4c77e5f171f5582e70ab4438a8e1e2f062d618c4ad09c70c73b5b5fbc9f8f0bbdf1d530a933b705f85af8\nA = e1b400cd3b1f2f1c6b437adfdb970d2c8108f1b39bdbb13582179552011c6c97cba6bff2c463212b7f62776aa3e3aff9f175990e79395e819c144350b0a23d61638d500ecc97726b098e1af334aece23a851c718612442c04eb7b3805a24cc8f5b90042145eb5e5d6a408092832b6bbeb8a621419a9282fb5c075f41c7f1fdc1\nE = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 1\nA = 9dd1e6f2d3ff24096b54e0ebf0f10e283e484a1cbafc0431adda1296ed97692f3ba99440fd4f67c96dd8bab850e1123361c99362df9ea205ff8e90d1b329459f54730992d5a360e46fcc5f5a909e691abb9a06613d6991bd7c2aa609f0d7b441d7ded0c07b8c394327672d38a905efb2d76aa3be5bb14d0c002aa37e287aee79\nE = 0\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 0\nA = 0\nE = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 86fb0b8dc161c41de2adb0f3ddcc8ad49c1efd729a52793a3ac987d4011c9c1dadb18657dca718df75c8ddcc49d60f152c46ab85ae9076ee7bfd405679a7da3a5195a1bbfd7d2b998c7b135ea91f8c445cbafe1276fa502c2a85477716829a2e0d24ba02623405a3654bed8f355bc7ccdb67c3f9a01e249e358b60d7699498a9\nA = 816610e6018ca47074d55750dd16a281019dbf95dc752605794cbb8ea8d75775317ce685737859728320b529fb3b4414b40bf3a93d08d8994a21ae54682cc1c357eb529837a7b0129a0843eebd9341c9bee3a8ae30475bdbff517e885a0c9f2b6a680643bd981efb53bf9dd49f3dc3cb757e117895fb34b1b4336d9bf8384558\nE = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 1\nA = 9edfce4691f46eadaa2043c7b1092b831ed50f3429f0bca02f985c0b77c686d951be84d772ae4b55f08935bed6e3206c8441574f215736b5c1c1b7595b3b789b55cf56db83741b10144d6767ba2b97b23a5e83504c60e06ab22834b0145655aa0463108317a379cbfc8a93de8a66925a999b8b02bf88dd85fb9898cefe9c95c8\nE = 0\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 0\nA = 0\nE = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1", - "892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 442866609915aa6f1bae9dfb59e721e1b63f42c0f75fbf0a88344120fbbd7aacf15208fb7c9d8bb8477d553cbd826d7e685ad764a8423e81c2131c040ee83a03cab8d5ce50866a941b48c78e9f1330794d908562d4141cfbf26e8c80c69551339eec41e37e2b37b54330f7bd75748f8d26d56ab9eb3b0c127540484c6445a7fa\nA = 8ff65e2cbcbcd8697cc3ce9a26855d6422ac7eb4e66500648c08be697e005cc3c854a54cfab91d43489cd60be8b516a9b3c9688e5e009a1689c6b164a133859a5464ef422c86344fef42cc477c9df27768377c126a066d1b62f593b7f6d6e906feaee16addb7cfbfc043d741b7dc81a87c17f167b7b8ef1b1fb3dfd1eb14102d\nE = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 1\nA = fe9f77f7d0475e00ec964c0effb9b8e079c32e376ce77a9c40ce4018c3df44a77b4f294d9565502b2b79accb30cb58dda6d15e1543b6d4a53296543ed11c7f51baab60283ef03fae37dfeacb431392487ec2839551a933895c4dbf18844f7b375d3e6f558d3c39993cea1bbf7fb743a6a07bd3753c03eb7298811476d7f3ff1d\nE = 0\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\nModExp = 0\nA = 0\nE = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\nModExp = 91fd879d02f95a9f40fcd1037726f73892caf84e9b43b4aa4126d9062a0d22c464e7af2fbd91aa849612d99d9519b724a7fb1cb018fffdcff321d883ab2519953c9f174f09dd8f13ac87339887385966eb4a94842276637b2c36c0a5036b1d3bbea438bc6efd4b4851c7ec06879d60694df894717569bcd31c4b13d80df6cbca\nA = cdec5edc1cb3ea974342b85aabc0f9385cf877ca328747d40dd4d297623ad69ab6582653faeed5aef225208305135cfbee32e066cb43e18afacea3a32acc8aabbc49617ac33e741651924ae56dd6aa044a12a1ea50fef573b5befb2f4b21b9cf83ab2aaa6fd153580a0761666ade8fb94f202a3c3dc4f33297eabb4564374168\nE = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\n# Craft inputs whose Montgomery representation is 1, i.e., shorter than M, in\n# order to test the const time precomputation scattering/gathering.\n\nModExp = 9442d2eca2905ad796383947b14ddfcc341f5be8fec079135c36f6f0d9b8b2212f43e08bf29c46167ff0fe16b247cd365df4417d96cc31c94db1cf44b73b0ee3ebcc4920d9b0d003b68e49c1df91e61bc7758a8a1d2d6192ff4e1590b1a792f8be3a1b83db3ad9667d14398d873faf5d885ec3a2bef955026fae6dbf64daea2b\nA = 3a4b4c57e62c5e9d1a9065191f8268fed9d5f6f424d071acef66f0662b8210f4c029ed991512e40c9c912043c816d2c4c5b53fa0e5c253e16808aad4225130dafbbb89fd4f30cdfc1c2f2179b636a7ddc4be579795820b4b9377637bd8a21a0ef5a90d0e0f865321eee23d9be2a3b7320b4012d02941b892df2c40bdc85c1898\nE = a2c56ea1362511cac0301918e15a9afe7d37edd438a5c3538d258ea01f0a6df758de07111e868b3ad8fc89b629b4955d78a1b3af902be1806410ddde25ccc6a196ba5949395c1ad5d8725b18815dc1cd5ac1c7dd17773f571e3f2e628255af14476e0494be23a4a4dfd18e23142f33d7a59c236fec61660e360d9676a747c69f\nM = ede35a3a7afac817d413373a2032abbc067b1493f709ae6e1282ee5469743391d891b904938857168802b7872d3cd7ac18ab249a9e540a86f970b1d0f310a4cc29df1cc9d4063d98c554f1a32f4ca5eba3523cdfb142e0fc609907c7a92bb0187009d97ec471db3545f42dd5fd29c07b7816085d09477ba31fcf90084660116d\n\nModExp = a7f5844fa9e7202d4b70ee252c9846e63d3d091b0387768ded872cec53458e19df0d9b4960226e269b8ca5dd4c4eda423a67b6dbb48235c08c12c6c7c78db47287756d3ed9cecb9232f7d18d5d80b9676cb68ba4a290c97e220beb1a069976b5e6022a4c1e5ddbeec86b62dda24ffea1deda37695c9f61a8817218e6370c0679\nA = 7d6d0cc947ceb949cdc4e9e1044f5deca5bb05a491041e0d85bc4b92a0944a57c72845fad91e59010c61ad1712bd2f612d53a846a044632262a9f2e3373b062fde2484e0c165ff947f2469f743ab6e2e5e13c640fc4029b1c9213eb8473c674e7f9e95a4a5c5636d4656c1e696962340d77b322daba47d6fc894f2a2cd9e0afc\nE = b78012afe806e2344d004c739c97324256850980ac97d88c4ed9a838517639ca112e235978d21a176c33f5a68703aba0f2a05501bbe3fc8d49a000fbf530cdb431581dfaf8683cb15a2aee5e239cbc542827100da3b47babf4a16ca7c588aff9912e674abb449e0b767a15e415f4e7f2bbd6380d7131da3df8d49b13bfd35ce3\nM = b72d5c55bd2998472f1965e75a51be6155c1ba04656da8f66bcb34db36a7b1db66a89d1d05b1bde10206acf85be7b474ab689220faf1bb52ab39d8dc00512dd4e26df1179c11b973e1274db85a88c7cc2a17113abdffe58cb930ddc5f3ccc4d68b4e65c913730509f7ce5656e8bbaba9b1be177ab9f766678f018fea05da9cdf\n\nModExp = 465ff295786a88496828fdc763e9292d557957544e9322b7996807b87fdbfa7a11614bffeec557ca831c4824c8e4ca3b1a1c7f3f4f95ec3fd6a86b73bb13d78b73af2b3c7e76954d0cc03bcb0cd606867ebb3765a8b3d0108cbe4f343a14016be9c33f6d200f0dc547e7d6b02bfab1e79dcdf9c9835a814cc6c855a12ebeb66d\nA = 89ad02bea3e9ab839a6e23f20122409daba52c68e1e893034b30d321c0305434a6af940015e3fa5ca9c35230da34beeb1ed4fbce6c1da3a8bfe3f3ae172276c1d1723b47ee61e6f8fcfdafad102d6f7ee2a79f510c7edb93096205a40a6c9e665b88b18f39a979e2e61286d939952a6f02fe8148b7515bb25f4252337cb6e60d\nE = cbd6ac628cc7afa3c61bee9c22a06a395087ec1811fe9681b55216700c435996c815e7cec8aaa90016dd2382d0306a5414630124e14f3d396a4ba02ee17851bf720f1607ff813e4bbddf01338983db12f59bd6371a738eee3eeb716f21051d6174d2d6c77602942b9edaac18d4b3a723096c0d00dd23a8a605c585022f311560\nM = fa7a3e40364c8a8d0f14f0213a3f3e035222ca0ea19d46d10ba41580e5dd2805c8a133f3856d7d5d97f922ea540e5eb0d10ad04dfdbb74f518f58da0099a6fc2b3f3def92985176e07fc78aff2faebccca10a429794e5f15ff92f75fe90f527c60ddea8093a9078c703c372ca09f7aeb27ade02f3595308c61dd9c44e62fd101\n\nModExp = cf08bf00261402102e9fe03f3074471dcf0e9b3c96d4d1503f099f24ec85e1901b023e9e048c1ad042244f5f70b38b25a99f4c0a7b57d5844bb0d0137367f45f4ce2cc7746105b77414768cb97648dc5721149aed2d4c682408cc0d50d26dd0bd77e848911f8625c727cac5f32e63bcb548f41a57d718d772f23983a42f603bd\nA = a419646a6631c2c69b18f7aa65011825eb31692eecaee9d74f92d92203811b68e9764bda31a1585bdf69b6273fc6f9f508c395ac081336506525dad88473512f08a205621ac8b16e9864c7a7c5a4f17435de00d0b32badec6ce4897e3e1076c562b6d9523f63d0b2079eaa416cb090471657763f24931d955d1fa2720c80a9c9\nE = d5a6f4a1842aaee39805356dc8d0d678ee03b2c81277345beccb2742f899132feb43271f95968a01ae68aa8277201851992dc0aa7a71c90aae71b124d873ee264ea400fb131be0fc6c4ce8c04c45f6bdaca89ac743635caf6158983d257e21cef6800d7f990e912ba21bbfb8fb779afa4abd19e07e7e07eee9908493d1ca502c\nM = e739689b6cc6def1d45fb1a2ab551643beeb303f4aaa4da47ee5e4948510f8445b4c40e99ae8354dede60b2ba6694e93bc4d573b7e8adf871b7a9a9636eb7d70f2e49328e2d7978143b177cee8374ef01bd1ee2d95862765883f5e7971668b53ef0ff41b6539faf63c397522b0bdce916388e72e26c8d3d2e58dadeb9eb5d479\n\nModExp = 827e6312ec3b14600203bb83f5b277ded197b2967363630ef673240df05edd3ba8ab2b11c86251a612206569c6c33952b31e264f129909bfe723bd0ee1624b36cfcfaa893a6ec8b5a1f7de79f83e79b459a3350f89f412ad1cfd6bc4c2a7a29272c783d6ecceeb1398fa17041835643f4debef9b5e87b098d104bb8912dddf7c\nA = b8e49c637829021d32db3a39a0c1e58cdd4c6e4eda7e8e9293be379e9c2e2d184f929d278598a81ae231cfedcf69cce4a6e31cda3c8ac14d753a7311f2436e29795f0dfb60259a0f61a997918ff984aa2284b43a9d64c974059e9682adfffd018305835f74eda8c75fe4877d811c1620f654ec9f7f32d1af5ce59115e2f41785\nE = 80e0febf369d234bf1aaad4f82df2e2ff02882c3184781f6ccdf4f7cd93b6887af86830", - "077c84dfb02109ada05b40970b1c65228b0c19030bd6361c3537fee22a8155c03b4e7007ca006c6daa3659518d05bb81ea0079456d0ef6116df248dffdb0c935f321f5a1034deefd5a9414a0652aa6548de33325b474b9e5a8507a082\nM = d5eb1d14af842a9973274f7463d90cf0ccff19c47d710edbae184478d4f29b02693ed7958bd487054327b9e6d8879e24c9af7730b92f323eeac05558da6c1b952e5dbf13de236050a77628bb5325fe0d14cc5773bf73338759d5ab43c212b414581280f1cee250007e53791b800b61c90de0328acd7bc43fbdda48158939392d\n\nModExp = 4a1efd29c7e78549f5cd4deed1454b37462c7810ee6a8a2493b764dfa479be13b314cf9ff98259517d61865567ef499a511630c0038c97914625df181c6fe07892f329f98b344a78d751e9471483eebaa7977371bf97bb25187ae7e93a9227d6c124ccb4644423c961a11ae59c4354f89d5a95164c23d9aa256e289e9cc0858e\nA = bd86c9211fa6a47a06e5016c46cb8a99e34a043a29e22f8c3196fa7197c26b38927b8d9bc0ddc11a5fa4bcc44deb69dbf37cbe7ebc9a2fad6c74e09ab5a9dd929fa04ab4319b6caad1035739be78ba631fb0748d9e53944836d37ccda6e6a62823c696d8f31139ccd7f2f86b22fa026ecf433cfb1271a3539ac4f1c83aaac059\nE = c40b9972006d28a84c2769a86e526a2b274f73afc7c5c6a2742166757f61b5f5fdbb228afa157af62af989ffe966f232bba9e6beef5403d1690ade31a6410f7f349a35bc4267a129afd647993df7d45cc0e1a1ba4678d7f1b6e8a344d8ff7037679e1f4db25a454e4246f6b55c416567fcfa188e8a3865115851d9edf0aa8902\nM = cf424d7af75ce7eef90cad75ae55ca8810cc7b4703fdb5bce701e7bac07e0c371cae06df2aa8facb55a0faa6793e4d2bd9d7969703743b9be170be82792aeea55e2bc0f7ab7617b276486bf474dee2f4556aab595ff3ef115139cfe5e21ccd4ee05c0e1cf901bd85df86cc17195a783b0be836d00bee82ce064077f9191188f9\n\nModExp = 3137a3049fd4ad2e26d870f5c998cf11bfe82101884a82e85e43facd0928cd7434a2e346ca124619769fa141bbe92ad6f36b99231032ddaec3b349a410f82b5ca36f45e56e5fb85dc63d32053dc90805d3f1854ab385281a71a57726bf97158494e7476057214ca7379ab8b70f5bdc15f70bdad3adf33c3a1f9cd1b6bbbad556\nA = 39a1dc6a4c3f14d9c350ee968d5ce139ef725952c967a2d1bedf48ace22091283525be03807e2e263d2640be77f0525247bcd07149bba50568cec5a082c87d72962cf9e43bcb5cdb1e7e9a650fb53e0ec2fad37f09a9f036c0d7dfa528fef846769f80a9a60854910ca1b4ee05dba82ed2ee018348d6b3e52a764b8ffae61e0\nE = deaee3a3f80c9f684ed7110c0653847ccc7be5ff6d982fd4b49f59b5dd35f7210b1077babbcedbc127df35cd469dc6e569a0f84e58149b5605c94b09fd7f0b098d02b4a04631328b3fae39e6c2fce25334225cab71829abdb9507cb903701559660f2c08c3b743336119d1260a0db27054cad3f28bc1b04b2289baa58fb33965\nM = 938388927d06ed3bb1286c0f06d3054cb0ee16dc7a0bbbf13a45293c09a5f40f1d611b2e1a1b0ec2ef109b508e27af4274954905cae52034f8740a744153b4d22059f0dd262ea51785522098ecacced6da07709ee6b5acc8c4e99331379a7c3de7f4e2d1431e43b19570140955b7bcba118dfbaa552cbfa2be531e8f781166ed\n\nModExp = c15ae334455d9f4d1030cd33e734726a27c63624c2afc576238cce5e0498298a4a0c93090a0d19568b41290303c4b558f3d9dd74f9cde8798710f68569ea0d6fd971ce67ec5b54495031de3d8842b8b49288725bee5c9f72b99054d64986ccd4e18d70d5f33943f08cd694eff538f84438ea993ebaba0910c95b3a694f213510\nA = def633b955a917569df3ba8517455eef0655e7a35985edda27097a063e0d82c7c3a76dc36c5d8a71ba9d540790ddd0ea514aaed98925f9a1808eb288d387aaf9605a9ef8a333ebee7ad7057bca012efd619d5867f02266f65976ef4b16da17468426ac4f99b3e8921707e01b4de20f6f9a068e6a19d872079a27f3a44449db83\nE = a465c47b0d15d48e01bb8b1d8e3b3253e11515f6874dbed6c25818adf1a8fd927124d5593beb367f685c11e46f18415be73ccdf16fa2e93a600b728163d21d232849e5278c3749d903edad3f1c4535a2f55a2ab65e7ebc64888bd2a0527e876ecf38cec3ab1980d08138709fad8eb88ae65d960adc3f0f8e92f784fe96fcb693\nM = e43cb9ac1446154356cdc31ec771c79b0e461e22d95185bbe1a279c0945e3af07903a0cb54d553380716fcdcafb4b7cf5dc6da481dc74a8c583d75ff6c1f8e429182d200246ebc473bb56e173787987c1b7fb2dd23f5b2e438a97bc4a1df628bc044fdd1e80c0cf37030adb7b04784dab827d0dcd64f0dbf37c980612570ce11\n\nModExp = 75c3f79ab7c991b98e65505342a8a563cfb08b5d3ccf8664c7db1de50256b1d17ebf7096dc98c7bb5d7f027a894ae5cbb14dee04d5d445e775ad7e239acc82673b0ac2d819a69c83864f34e73d9a636f05de8279619a067b4c90ad038db5910447e03841d2034635018f08cbcd21efa00994247763a249082594128112f95232\nA = 34def7d76f6f158a359fd12759fb889cdf6af0a24830dc3e84283a1ab4e9b2647a6a36b86482f829b2cdf3e3d6028f9a884b1f64f7262315446bea8b0231828e2f3d990fb103c17f820b39e4b8427c85643ceeca8f5dc8f191d1255768300e859bd7d88c770319ef38269660d221cb3bc061389b6fc0783485ef042b1c7d6fef\nE = c6c46453dd5aac6b37277a446b1d0c69cbe476eeff55b3ac35edb89ba97116b0e7783660f2c7b31b2a2d6c4709d0ab45d01a838100694b0777c9c9c14c959b07c437c73a5eabb7402f1001e802d797a2e7707285834fb6440a1c2f727f7bb84ddb2a49312d32fa0ce620c43872655cb5c394749c9e75d7fa25be00efe50d47d6\nM = fbbab6698a9142095c46b38a732592e4366c1838b84bf40f8c8fc7b630f73380a0d09765562365798f8c8030ed1b6728329d8bb06e882c35a1d59bfe84146a9db2afe42a414014e247390281c782fce806d62adb54778d2bcb49555459429d6ed446af5359657667f6aa19e8e3e0e24ab2bc312b2d90b5cb1ce6f2f15af15d9d\n\nModExp = ba16d7f3f6e162ce248490d164a13c00e7720d8a667e2d3ebeb13f1663e15ef5408d5b56cbc7bc793a8ca787cc50f8e15e0e9d4ee764531d04a9114eea556bb3e206ed7d85267151a056b6e68fbf35e03f2cf829708ffe1de13e95ecfe365aff1eea36340ffcd3892dee659fb1ecbe50f5080e54737c10f9c1ba638b14ef537e\nA = 9025e6183706105e948b1b0edf922f9011b9e11887d70adb00b26f272b9e76a38f3099084d9cccf12d04b1a99c0f654f8b9ed90c6dff9478c60bf05d58d734ab60eaefa14a22230ec60c90dc1f0704b61eef0bef345785ae0e6a9af7db069cf6bd2b4e0fe58a0ade83c7e46a04b9fe1d24cb9b65c6f80de713e61d70eae5b286\nE = d7e6df5d755284929b986cd9b61c9c2c8843f24c711fbdbae1a468edcae159400943725570726cdc92b3ea94f9f206729516fdda83e31d815b0c7720e7598a91d992273e3bd8ac413b441d8f1dfe5aa7c3bf3ef573adc38292676217467731e6cf440a59611b8110af88d3e62f60209b513b01fbb69a097458ad02096b5e38f0\nM = e4e784aa1fa88625a43ba0185a153a929663920be7fe674a4d33c943d3b898cff051482e7050a070cede53be5e89f31515772c7aea637576f99f82708f89d9e244f6ad3a24a02cbe5c0ff7bcf2dad5491f53db7c3f2698a7c41b44f086652f17bb05fe4c5c0a92433c34086b49d7e1825b28bab6c5a9bd0bc95b53d659afa0d7\n\n\n# RSAZ 512-bit.\n#\n# These are regression tests for code which historically reached the RSAZ-512\n# code. That has since been removed, but the test vectors remain. Note that the\n# lengths of the inputs, especially the *bit* length of |M|, matter a lot.\n\n# Control: No relationship between A and M except that A < M and they're the same number of limbs.\nModExp = 7f34c1cd63377bc3abf2bb5b2d1bf5f06454e1e8040fe19a72245ce9731cbee1bf9e84532300776c8021ed4f3a8de508d85b4cf320bd82065a013754857b50c4\nA = 8e4e67da6ff890643d0599387955996ef6f0c2045eb9944576ddb965ca64cdb6247727ce128ef178d4a84e5a56d2e67eb0fe389ecbf691f9244ae80f4c11b364\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# Same as above except A is negative.\nModExp = 71fa6a4c8ae75368eda8cc6282c26afa69e2af12a97fb9444f16b7dd6c99e0a5d6034cab4248cae4357346b211039f4a2bc4c5a20a297372094162417af703cd\nA = -8e4e67da6ff890643d0599387955996ef6f0c2045eb9944576ddb965ca64cdb6247727ce128ef178d4a84e5a56d2e67eb0fe389ecbf691f9244ae80f4c11b364\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A == M - 1 == -1 (mod M) and the exponent is odd so A ^ E (mod M) == A.\nModExp = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nA = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# Same inputs as above except A is negative. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 1\nA = -f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ec", - "f2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A == M, so A == 0 (mod M) so A ^ E (mod M) == 0. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 0\nA = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A is negative, and A (mod M) is the right length for RSAZ.\nModExp = 8d76eb0f8c7bc3160cc8bb0e0c3590fbed26c5932f5f525b48045c0bd46dda287ba5483f97c851fb7c12c2e858ee7a4a4d1af745cbfb3eb311fa54bea12cde25\nA = -80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n\n# RSAZ 1024-bit.\n# Note that the lengths of the inputs, especially the *bit* length of |M|, matter a lot.\n\n# Control: No relationship between A and M except that A < M and they're the same number of limbs.\nModExp = 8984f8c16044f9c0ad7bd72347af90f58e6e003acda92b76e3c7c4a56ea8e918409d8e9b34884d4c89d0b17cb40fe898f2627c084a0f1698e46beccbf6f48eecc281e11ea9e5135adba460ddae157f2c655b5f589ce29b254d43a960a71cede8a08dbb86be4dac22458da232fb1ec2470856827302ed772c9ddafa408c931aa7\nA = 21158da5fe20356825e72b3f5384ec57720d22f727b27ce2f945c8ee311db781add73bf8fae96b775c909bd22fca75c44c2b0584284a5bb1c07f8eefcd6b0a44047a02b185df34f897f11d4fb9a86c9eb841b4cb8d0383441fdc5af3ef385b5e8380f605d73ed41bb42eb2c2a5704d6034b3ad058dafffce83dbbfb6295daaf8\nE = ecdebd112b3b5788669449dcddbd479a203ee9ab72a9bb9c406b97623513bf0ab9a22f1f23634d269e16bfd6d3b64202b71fc355057411967b6ac70f8d9cef0a4e06819a9a18cc06bbe438243fa9759303d98be8a65dc1cb13595ee9b99f138554425d50f6fbc025d8ffa3eaea828d6f3b82a3584146bafde34da257995f0575\nM = ff3a3e023db3bba929ca4ededbace13d0d1264387b5ef62734e177eaf47a78af56b58aacc8ac5d46f5b066bafb95d93d4442bb948653613eec76837b4ffb7991cb080b6c8b403fb09bc817d026e283ee47ab2fc9af274b12f626eda2fe02004a8e27b9ed7d3b614e8955c7e7c2c0700edd079455237c4475fbd41857e206e4b7\n\n# Same as above except A is negative.\nModExp = 75b54540dd6ec1e87c4e77bb93fd50477ea463fdadb5cab05119b34585d18f971617fc1194240ffa6bdfb53e4785f0a451e03f8c3c444aa6080a96af5906eaa508862a4de15b2c55c023b6f278cd04c1e24fd0711244afeda8e3444256e51261ed99fe66beedb52c43c825b4c7a1adc7d4b111e2208ecd495df91e175573ca10\nA = -21158da5fe20356825e72b3f5384ec57720d22f727b27ce2f945c8ee311db781add73bf8fae96b775c909bd22fca75c44c2b0584284a5bb1c07f8eefcd6b0a44047a02b185df34f897f11d4fb9a86c9eb841b4cb8d0383441fdc5af3ef385b5e8380f605d73ed41bb42eb2c2a5704d6034b3ad058dafffce83dbbfb6295daaf8\nE = ecdebd112b3b5788669449dcddbd479a203ee9ab72a9bb9c406b97623513bf0ab9a22f1f23634d269e16bfd6d3b64202b71fc355057411967b6ac70f8d9cef0a4e06819a9a18cc06bbe438243fa9759303d98be8a65dc1cb13595ee9b99f138554425d50f6fbc025d8ffa3eaea828d6f3b82a3584146bafde34da257995f0575\nM = ff3a3e023db3bba929ca4ededbace13d0d1264387b5ef62734e177eaf47a78af56b58aacc8ac5d46f5b066bafb95d93d4442bb948653613eec76837b4ffb7991cb080b6c8b403fb09bc817d026e283ee47ab2fc9af274b12f626eda2fe02004a8e27b9ed7d3b614e8955c7e7c2c0700edd079455237c4475fbd41857e206e4b7\n\n# A == M - 1 == -1 (mod M) and the exponent is odd so A ^ E (mod M) == A.\nModExp = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nA = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# Same inputs as above except A is negative. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 1\nA = -b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# A == M, so A == 0 (mod M) so A ^ E (mod M) == 0. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 0\nA = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# A is negative, and A (mod M) is the right length for RSAZ.\nModExp = 9cf810b9e89d5cbc4b79ae64e123ea06d92965e2bab077df97a1b906dc2e1ddcf96a9c4ed14e2cd96309b829ea9cc2a74a7d4b43c5f34d792a7c583201427754b8f78b783608070a84b61f18913e3ced7f7f530972de7764667c54e29d756eea38a93cd1703c676a4587231b0ebfeadddf908e2877a7a84b5bfc370ecf0d158d\nA = -8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# Regression test for CVE-2017-3738.\nModExp = d360792bd8210786607817c3dda64cc38c8d0f25569597cb1f363c7919a0c3587baff01a2283edaeb04fc288ac0ab3f279b2a89ffcb452d8bdf72422a9f9780f4aa702dc964cf033149d3a339883062cab8564aebdbfac0bf68985e522c6fe545b346044690c525ca85d3f4eb3e3c25cdf541545afc84a309e9b1d7807003461\nA = fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "fffffffffffffffffffffffffffffffff2020202020df\nE = 2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020FF2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020\nM = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2020202020ff\n\n\n# Exp tests.\n#\n# These test vectors satisfy A ^ E = Exp.\n\nExp = aa6d7ac431\nA = d0e07\nE = 2\n\nExp = 12d416b110dbb4e467ff0c89a22122f4da8240\nA = 1a18cf6\nE = 6\n\nExp = 49a3b33e23d84f1ce0d5d83f5dcb651d50cf3920f0143da2310d0512a90a06cd8f38977df8a756c30883de38df092000\nA = 2a3acbd2\nE = d\n\nExp = 5b4a0d5a956f885f275712b194459980f24708bfb6393d71bd37dce852ce455724f5ee5030775fb86b4295edc98afaafc097e4d82a97c0078ec0eac763db16549c5145c4cf2d3124f88cf9a5c71da0625afb99b26801786fe49a778415dc025954021753d08691947a208b613f0be5c1\nA = 54b3ae461\nE = 1a\n\nExp = a0ea5f6a4de49beb8fb7f0dab280d6a32c5a3814c9a5153a7944cec0a9028497846a8a89044348721a0bb5f0c3ded3e980574ea321b0cdb0ead4f4e93841ea7478a7f15d9729b646a8165813a0750e8124f5465dda9b105e1bbeff18fd09c09a2e26610d9176d253b877c3a8908a6be521cbe1e472a7a1b7820e4e890f8f28aacd34609c686e76e15b01bd9324a71290812724ea564d11c874a6765b262c3e57d479da0287a76026a1e8fe53da0b02405da1d379eaa30fc65f\nA = fccec0f6df\nE = 25\n\n\n# ModSqrt tests.\n#\n# These test vectors satisfy ModSqrt * ModSqrt = A (mod P) with P a prime.\n# ModSqrt is in [0, (P-1)/2].\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = -1\nP = 2\n\nModSqrt = 1\nA = -1\nP = 2\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = -3\nP = 3\n\nModSqrt = 0\nA = -3\nP = 3\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = 0\nP = 5\n\nModSqrt = 1\nA = -4\nP = 5\n\nModSqrt = 0\nA = -5\nP = 5\n\nModSqrt = 2\nA = 4\nP = 5\n\nModSqrt = 0\nA = -5\nP = 5\n\nModSqrt = 3\nA = -5\nP = 7\n\nModSqrt = 0\nA = 0\nP = 7\n\nModSqrt = 0\nA = 0\nP = 7\n\nModSqrt = 2\nA = 4\nP = 7\n\nModSqrt = 3\nA = -5\nP = 7\n\nModSqrt = 4\nA = 10\nP = b\n\nModSqrt = 0\nA = 0\nP = b\n\nModSqrt = 3\nA = -2\nP = b\n\nModSqrt = 3\nA = -2\nP = b\n\nModSqrt = 2\nA = 4\nP = b\n\nModSqrt = 2\nA = 1e\nP = d\n\nModSqrt = 2\nA = 1e\nP = d\n\nModSqrt = 0\nA = -d\nP = d\n\nModSqrt = 0\nA = -d\nP = d\n\nModSqrt = 3\nA = 9\nP = d\n\nModSqrt = 8\nA = d\nP = 11\n\nModSqrt = 6\nA = df\nP = 11\n\nModSqrt = 4\nA = 10\nP = 11\n\nModSqrt = 5\nA = 90\nP = 11\n\nModSqrt = 3\nA = 80\nP = 11\n\nModSqrt = 9\nA = -e\nP = 13\n\nModSqrt = 7\nA = 7d\nP = 13\n\nModSqrt = 6\nA = 37\nP = 13\n\nModSqrt = 1\nA = 1\nP = 13\n\nModSqrt = 8\nA = 1a\nP = 13\n\nModSqrt = 54d4cf0fafe265056a29016778cea6b712bc66a132fb5e6b6865e9b49e4c97ec\nA = 599c10484b22d0b5a115268c7538ca99b3253a311a4ab1ca11c3665b0bec393a1167d1ad94fb84cb2c7ad7e2c933e8f613bdd08fe1f1aa4a9b0b9de0c8a7c9d4\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 38a7365a15365e911286c1be2a7afe76ef390234d76269e04dee17313f6ea54d\nA = 1c4aabb4d8369710131c664ecf2849e963c1bc31d66e0b939bacf99a870c71f24ed71bdddcf566f3908271fee43fc1ebb51eac7e3153efae641b49d2e796a12a\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 35ab18a560dece04725667f640ca61d1d59f14d191f94c79f58531acd097d444\nA = 685168ae855d60eba220d803f5296459b30a289580668db9ed51bca51cc2d453a937e13819ae34f7a9a143ac96d17420c53919167e46279b562b550be1cd9abc\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 288370029e87024175e5bec0eab0929179f42e16995e7f6194eefc61061e54f4\nA = 2a14ab77c045bdc48220ba9c463e1a4b4049cb01edb53be0937767eb2ec19b7d719855052281250a36a0b76d9a5d967d0756e1ded7a052f7056191ad66bcfc9\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 32255cf01dc943577ec2bcb221b98491d7a1130d046d6c68e95fedff643ce3a4\nA = e26f6dd46a513a1dd3fb14b71be1d4c9e9d79eda1cde10ea4d1eb8abfd4d5857572205e247184dd0cbefa37b5c0bf680ba2bd28c5741f725cfe2aae37419baf\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 5172345e801ada63fbc4782e32583cc3b4fea88b9e6dfd542f3542f8538ade66\nA = 40dafa8342b302bb04b1f3ddb3b9015a8fc1b597857c115b40631c7be9e22de89358fca23b331596ee5ff304dad7811e6d8e8822f7aa533c9e7c882634ea550\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 4dcf63c423bf0e39aca2293d57f6792d023db649d6719fe936446904b9f7e60d\nA = 5bcdb514bbe84261e169203e8017909b60c9bb330400c766ee01b0189378e70e61867a164a12643ddc9e94b61e09e5b158cbe85be228a3cc48f95a552958b8f2\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = cf77c5c2d12a500b75cbfb1f3e66ee75d886b9365cf4f8b4d1bd18a6be0f387\nA = 4652ddc2ea7b460d8ec3c9059b8f9b5dae6cac55b51f2ad86fcb336b25235737965cc515e2ff0b54835015b7ebeeda6fadd986471d8cb424d309fc353d1e269\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 1e0549e4c5a26023e9d24fd8c67419960746f82b1ecd113bdac66f570a475d87\nA = 5f4a6d450ab1390d96ab1deaa0ba18f897cb63daf0c9e1ef6c08e804c26b5e842f6c08f13db5d4a6e88f07af2a3cb04fa06fc3e59c410b9356f025ed81acc74\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 144481a781d831c1ca046ca9e322d79ad4d2c6dd9f780bea9d1ced9cd20b7b23\nA = 4c254fabca441017132b9eacd4ca40a336db3e5c09715773fa07af095989a91cc968ff07a9ff56ed06b0ce0c5269f7b2ab68564ecab9f4467a7e96b6cc6b21b7\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 216fecc7667f488a3d2d102a38b46b4860ab858300b8638af4f34e1103fd73ba\nA = 17878f8048227573a9d70f53c0e76ff13fe9f56e9c984c92514d3d13dec23c816661f0618d21371b80dfd885cb59551bdf80046f65f22ea9b89c78645a6e455a\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 458e5e789ccd2417174f7e30bb31914b9656bd8cf2b9f5a9752a8737a67707bc\nA = 5c7d39a4bb04e69201aa519f80ee7e62ea14ca55e13656d1da3f45367e2fb2d061aa2940708d02ac67d35cd2ccf54a1bf95bcbc759779e692cfdcbb3aa1a05b\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 543125a16c2bb8b8f8a2c39c497e5224ec77533602d7dbe24002e32dcbd2ef1a\nA = 3413afae333b2ad9ff45c7f3c7e5934b3127e8b1a55225958ee6ccf42423e81559bf070ad3f3353b78c0ffd41475af49f59d268ef78bdae879f5155e8d1cc07\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 10e16859c67bdb2eaab52a7c847dbf37162eda258a9f6262ebacfe4cbbbc1080\nA = 21ce7905894faf220bdf4a82a2d855994ca2dc9feaecaa53c7f146e1f49934215695e9bb46ba370b7005a90c399674caa8969eb442e7914d90f749774d7fd194\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 32a00586adc6f6cc2b1a04e1be0ab569fde235e1436c38b6af92bc5ebd60bc1c\nA = 350da4fd8cf03c12f7dd6ac6d3ab801a3413964083e374662aaf878d6838b97d4feb9e52cd307a25b113e101661a865463ee2480c626aa4e2ec437d72e7bae4c\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 971f75bc7afa8b4b50f1d4b05e52deac7d4836a08d30546f29649bf1ca6a247\nA = 655ed4c5d8d0afb4f9360372ee1ef1303898d2423e585108a3303faedb55064d2ef25666ed4c4d71fe6063fea1f3142b435714b0e30b339dd791d347c884654\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 48fa882b7cb6a29de9e3769f72eb67f1efd4d2af56f0c7e410c610efcbce2065\nA = 14f3503f33b243800eac1defaab33e04c01e80163fb3efd03860970cc016832431ca4fc6d1b760f4f40166b0b8b3c40dbebc81460cc10890172243770338f090\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 236fd7e397ea7f8bc2a288eb7236ca41936fa702b7dccca56c8852e147511f7d\nA = 1bbd0980feac854782813bcde4da85e8a054549a1b515e065da4236528035e756882e29e762cf60453e375cca9dc6ff637f9558bf86646e3b928f68f82af7efe\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 693f0cbe8c81b0afde0cd2f83e53795dcae6b0cc4ba930ab5c752400d787f14\nA = 7b20f9664b23907e152ab8c9a907f72e8670c1c38ab4cd1411ea7c2159c09aa131afe068929b8e6ad1409b74c04975180d1cd0a9fa74e923c3fd451e8da2c34\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 4a086c50b0bad576501ddb6280743b2c9d247841eb7f14d90561432ff7dca6f0\nA = 4367431ec0cd0d7626538b93a090c30fe0c97c18ca03b97ddae304b619112b5b4d02bf0f041fa3fd673f9ef2ceb07eb2079d11c56dd903b1a87e8252a97b8079\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801a", - "d4da7539fdbaa6f7\n\nModSqrt = 18f8433fa468d8065157708f1f1e53b8e31d39c6011fbc2bad93de1b5548e19c\nA = 739c032bb4139c199c40f548d37234298772e4ccb9d3ba28412b60ad23b4c465b0787e2382f1c5a4a87af2d20eb978b7dcbe73f2112249477d15c8a85e54a79\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 49e3c8eef5e067cabd51a7c01384ce05ab8f4342f655559d8a689eb7b20e0106\nA = 18400c2cc3e06b99b4e39c77b9af5ff0e9c683f1708321afa4cd5b6988d13b36b1d9eb4379b7902d9ceb40c03f814b2b6a01b90509bbb4532f13ab1571c4d04a\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 35548c530745f440329325cc8a5fbd90c16a7f0788879a4869bc4d4f73acda0e\nA = 181a3c5ab02566e7166c4d6d2f2bd4a8ecc25991a98d270bde80cf4332766a7068b14240bf5f5dcd45e90ef252596da3eb05b11d68b2063f7b3a825742593ca9\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 1ab7046e6af061ade5f9719008fa4d989007e2a579a134a5b9f19ec410984096\nA = 1008a03e211fab0d45856377079bc96b0776c2d4c0175661f3493246cea2ab0a02a706c85314fb707ad9906bedb2cfd577d62092ae08ff21d7b949373ea954c7\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 2be9e3e7515960d90f115b89f60dedc173a73ce163b4036e85b7b6a76fd90852\nA = 392053a9f0100540a8e1a0c353e922068a84dad3a4a8e8962fbc0bee2b6a06e20d08ade16eb1409a16acfcac3db5c43c421505e07035ca308b15c4a6db0864c0\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 5b301bb93bdcf050183107e36258b53b4805918114ea1c2227b0911d5b4dc077\nA = 55e55e5f94dc3d7aabc921f6469d85fa2e1e92a87347c57afad5872306ae69f9fb99297d1e3e793dd9e8632244208154de5da7114fd876383bf1422f7ece024\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 2df9609e2f5a5156c3260461b2ee52eacdef00bd8b091479813143a6c5283f71\nA = 2099325b7f12fe77353ddf3f2b2c5ef77b49671b150af954cf84e9675e3ecde3e057084641a633d19533b4712ab49924c8b5c31d591abcc88291f51253fa2a7\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = dfab751710e9008e25e422d1199d6fbec4dc7fba35b4da9d225a746eb4126a0\nA = c006af53d4737fb293584df6ffe2e4cb3fd8dc77fb7c1f13b97bb9c249e3ee5fb9feff7488265b3093906c08a4946f142ac7b491937d24bfba6413366ce371d\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 26bc030008d6c60a09fb0e16093a649fcb40c6c21a8e2da2353ba4b07c4f85d5\nA = 1eaabcfad2ed349ac9356e6f4da0b301266ddde811cb0f817aba8f5c10fb8b8ba9d0ef2dd386b668f16eac296118fdb8cb7afe1b865648c81c2fa3cf21f2711b\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 35051b1482ec2578f3dc0000a422cb5111e43c37f1ac20b1844d3de2128c4556\nA = 315ff9de178681116f2a5fa78eebf4818e1d680435eacdfaf9d0e5c4fc01fc034b352c82fd52c81ca30d68864952dacc99d08269c9dd7ca99ccf22da98c3840\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = a5474252885cacf004c460a7793ff0b0a2187bb1a9ed700ae3470199faef71f\nA = 19856fc1351c4b02abf573bb2fc6ff92355fa369d62bb8f2260fa772fb1693f509a56cad661930abcac049dd70f4b16bed4a4c172e73e772504c9990ce7f92f\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 12daf4722387ecf47de1b0b6b110a062dc5ea2685bc9dbde66b8d15622985029\nA = fb8479787069116abc42abfd7dc0c24d2ad04fe0c04b42a6dff714af715d17e0fd77855f950f264542b06d48e8818de813ddb7975798b7debefcdaa5ff86beb\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 397996ed5c0ac6ad32e43c337e9de421b87774cc162bf7ac7bbedf4a9029255e\nA = 5aa04353321bd2de92481be740357f979da464b53aa39111fdbb734cf7af6b3857d1baa08d3a126a3dd34a2fbae2bf2b84e900686c1d31505b390185acef5fe5\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 2cf4b844a54ba359dc592ef1b49f43fcfeae84d1087edfefdd0b9174b43c0a3c\nA = 365a8650510bcfd8fa87432f167cf487234c215857403b9270b5eebeafa48cd6da47fd60dc311b94d1d72baad0447c31f0b212d755f46c256e16e5e015e6546e\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 9277c73043ff767c3fa606f0cd66b9d854a600c8c18287f191ce277758c3f31\nA = 62cec3901626d03e8df66299a87c54b1f7a55cafc99f0b6bba1b5d51a3d2b7d2171c9135a9d8a5346d436e0136b12e515e703e3cd84ecfe154eb94c6772a6d72\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 4189e5a90c1b1abdc1c7c05b3587e6f362e06f927b6cf5f0d271aab3d6f90765\nA = 336b8d0f9dac842c696bc020f49c6aa023842c16f2052eb02f17959006554ca0012042c80c72590f21c6bf5a3714c9cb552aa69730e33db93a56a909b273f39\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 36ccd38cb5a6bd8a73bca55936a2227c503664422c2296faf7e2b1c6a375a43a\nA = fecfd60a376befbe48d2c4f6d070d716d2f403cd5daefbce62b720df44deb605162c8f20f49fd7ec30d4f8e70d803d45b3a44b5d912baa3410d991165d7c507\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 198fc8569be172dc9b71023ed3d42d2ba94bae4099643f6517ab03f540527fdb\nA = 65bebdb00a96fc814ec44b81f98b59fba3c30203928fa5214c51e0a97091645280c947b005847f239758482b9bfc45b066fde340d1fe32fc9c1bf02e1b2d0ec\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 21b7f74c30ded681d6138cf8e6fd798f32a049e94138e982f1845df3dc9e686f\nA = 9a30b791c1ba4f394b4e3dcd5837e474237f4fe8987b255c098a47b2c14c598ec69d2beae444dd4fe9c4ede8173d2b187677cc706a3c28f3b81627d8a5fb6fd\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = a1d52989f12f204d3d2167d9b1e6c8a6174c0c786a979a5952383b7b8bd186\nA = 2eee37cf06228a387788188e650bc6d8a2ff402931443f69156a29155eca07dcb45f3aac238d92943c0c25c896098716baa433f25bd696a142f5a69d5d937e81\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\n\n# NotModSquare tests.\n#\n# These test vectors are such that NotModSquare is not a square modulo P.\n\nNotModSquare = 03\nP = 07\n\nNotModSquare = 05\nP = 07\n\nNotModSquare = 06\nP = 07\n\nNotModSquare = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951e\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\n\n# ModInv tests.\n#\n# These test vectors satisfy ModInv * A = 1 (mod M) and 0 <= ModInv < M.\n\nModInv = 00\nA = 00\nM = 01\n\nModInv = 00\nA = 01\nM = 01\n\nModInv = 00\nA = 02\nM = 01\n\nModInv = 00\nA = 03\nM = 01\n", + "def8105ac28f2ecc8598ec0c4bfc9c1c80222fffc776722eb0621cdd8a0d55f08767fc2922282a76e529d81e4d6e21a2542b8c9a403709ed1132e3b52786b81e684591438fdddb5df2f0b72e6b39cd2db6c0cc55c759c2dc1b6ccc20a5cfd10c6fd345fc766035c7478570d4ac534db3fdb718e2bdad3d096b137bfc09a562043800957e2afe4fdcfe292881f6189edfce52370c0438c2822ce3b14d73b3eff32f7e5ca97e989326b4e3a8fa35544193f8590bbb0ddb1f914894ab87998090771a0be1fd23917cd792be86ea0b98e6eb24\nA = -ec953f1b7ba7d561edaaa23076987daf86f50e9a66c36f0993290549a9006dd9d424885c0fa77295cfe34fc81c5edce9e2371b3039ea18d8f998d1956196284e6d81eb1c62ecaa8cf3fcaca28ca7e64342803c8dc3c139080bdd4a1ff30d7288b085a579d9e90903bd363b48f2072bb6fbfbd9ba2cab30a8a63784d246\n\nSquare = b33f4f3ae453058f4e865ec78f0844bab7af66a97dc2f265ca73ae2232777474bfdda39e10652d7386c16f145272192af728893c3d8a8e92c60d77722b924c30269ff5a399a2449ce15e50320c528c22655ad06227ac4efe5a993179ec61c2fc9115f89d75b53961fd16f7797657f6fbf55662b019608a1d30f64a2c0838e0018b7526921fdd34fd462bfcb2462b7065e2bc7abd57d71371e45dfd8fcfcc00a71f7e45430820747c9a060b72e4f6d2919cbffd00beb0c31a2bdc32afe2cc540b38dd04a2b73ae5ba481a6e535f37a757bbd6aaa972986213afadfa47cb7a15a6f1d443f93cb0ed824a10b4b7d82cae524a096b65ccb39be3c37c07f59\nA = 358da59ef65f62f633675764e292e5a68879df24a4727eca1fc4d232b3a6d936976c92eeb11456b5e8c11319838c145c6529d2f3acc828e55b8274bfe9afb5db241b102715f8e8164e454ef39f13ff1b37cf367a5a66c4f743c750896b7c3c29026e448bb36c6c06b0d9a3d048086ef0c3cd922a02e794223f388b5d646db\n\nSquare = cd4246489f6f221f920acbd8bdcdd17f47d2b77268f72254de4190685c123e8c5eab8517fded1852e8316c9e549d3fa355142d91b2921a3c94aafd8862cd2235429340da38a2af131b8d002f17662354f5805f6a7af7afb6dbd2f641036600614cea42bd8b24d86a5109eed29c0865a5f30c5291b1d1ef3223f9b9826dee773d98ce972da92daa19e843f84ca5f1cd77925a3c1117242ab0fb509b94a83f8de4fc8d21f856f37a4d025b3024bd0dbb6d8acfda4ab2993fd6eb7a7448d4f66ec725d37f0eb14eb242c0ff3f0c4572ba6b98a4ce905fe1b7ca3daca56c225171428c56af938fb66b37e99e54139157bbf41f536989ef813af738837afcd62290\nA = -e53ad05c88568f09f616797f0b7f2756fb543d691ec2a5b645c1e5892a247302826419a35b1348cfd2c1c569c23c31b4c46d6c57d4a488c29ab5beb77904d4adfcd0a01ea0a26bb0cc8790441cc2c8c900f030d7315b4319f1a3cf5685a140e03abe6b94730ad79e8de1f4a0cded86a3d6cfe2db267fa7dc9b2bb32872a90cc\n\nSquare = eea8028b26e0df090504d54da714a6f5f2695202e53cff479c78aedd47a8dc676243ec586740fde53b3eca9ca02b91031ce766242184109503fbe25b1b6d318e3cd5970fabd16dfa22984dd2e9f1e0f14c189170fc69c031d66663703e6235a942d51a4545bd7b0769d01d302ce2b00b83f01568a1e378f61fd0ca6201b0490330580cd9de85719e174a71915d7efbf65cd73d8f4e66f27e0dd3144d58ec09ed0f7ed7d1238ee596922807100fb7a11127944ddcdec6a9ca3bbf6df7301e354f3f049bfb7c275b43c3d8cda5907a932fba507c9145ea3166081c1b48fcc710ee32cd931f936c796b14f8a78a592e67753a7c9e428a01719c8ba82652f3a89fae110\nA = -3dcb44be1e54c5a5d7db48055ca9afa1ebe2ae648aa6e16ac497502a7deee09ffa124720fad0ab163ce8b3ea6a90f110ea52b67dbc424d0cf1e8c9726dfd9e45bebcefaa5cd5706edeed27896525f31c6bbea3d67ee97badefabf3e2532470b66e3ae3100f66ddf50cf02fc3a8e3f44c304251d3b6a7ca3a6e4bd5d16a41bd97a4\n\nSquare = 0\nA = 0\n\nSquare = 1\nA = 1\n\n\n# Product tests.\n#\n# These test vectors satisfy A * B = Product.\n\nProduct = 5befab3320f8f90542f3120235abd926aac3805a19e343f690\nA = b057af553afb120db6b7764f8\nB = 857734c4c27a1d17f7cf59dee\n\nProduct = -ab1ce167f4b2945c55ae3f87df50ad07d4be87cf9f8aa07b0c\nA = ae7a6a87ea8981a567d0b3ecc\nB = -fb0fed5f8c737bcacef4d6cb1\n\nProduct = -c2606cd48e6b075c8da79eb4668e7157f1f175c2860fd4c475\nA = -c28dc31984d4583e9d45424c3\nB = ffc4581a5c3f885cf42767e67\n\nProduct = aa6805b5408aff7f914472756da07830dcad902834dbdd6944\nA = -ffa07ff9f503511954e5dd3f9\nB = -aaa7af472ad8957763f5a7c64\n\nProduct = 58ca2569173389df29b5ce4b784086055dee821a7243db7210\nA = af417d936f4690008811a1ae8\nB = 81b26b80b43aa65aa55ded52a\n\nProduct = -a043d31dfce8bd01724d31c863d0a64f1bf013509d77737c42\nA = fb5fae5edefb6997d44a1ecd6\nB = -a336e50c6f7845a1686cc88a3\n\nProduct = -b5d6a45ffce851b201239d938ba551bab7dcb59fc11fc35fce\nA = -f918faa58bb57a2ffb8b01f05\nB = bae08c3006fade695029a1df6\n\nProduct = 6f2fde7d1a18625d727c6345ed85e597d546d9228bf7f0564a\nA = -8d108d7a16f0696d4ceb24445\nB = -c9c764cae465207097ef8d2c2\n\nProduct = 93808b1140841dc9735cd61c6f855ddbbb83066689b0d7e1a0\nA = b386d08daf3fa2154e9c768d6\nB = d2557dceb2d02d04d9c578670\n\nProduct = -ad04212ca8cadb1f7861c5130ba3a747046a2a7e4a0c72b69a\nA = e4e5f7d1311e0c5f2e404d55b\nB = -c18057a328d8c7375afdfd4ee\n\nProduct = -685e75c232f2b4a0e455fe5ee8aea52f292ad8b8178320e692\nA = -a683312f132b2320632e74ef6\nB = a0758f12791453b4af354730b\n\nProduct = 6f588c53185c503dc5b0dc3002d3817ca2e7eb2370b3e9a647\nA = -d70c9b93170261091f0c53f27\nB = -848c86c51a186ac4c9080d3e1\n\nProduct = 5e3bc5a04e054a9a244bf7c86cae215072fdb70e9199989427\nA = 898b64ef09d7cf63966e1a3b5\nB = af638b12f26aa5d12e97439eb\n\nProduct = -8d8372b235b16108285203c03a8aef6fdd3c0e1a9fd31d4f68\nA = f6003dc83818c14fbe36c9998\nB = -9343f6cbcc81fa4c9399dce5f\n\nProduct = -5ee6509abeeb7af7fc5caef40d1822ad3150c8d74f522dc7c8\nA = -875ff6f56ca72cbdf614bb9ca\nB = b375a68a21dfb1f159c22fa14\n\nProduct = ada25be404a17385af5a330da799e5909da81bfa0715baa6f4\nA = -c9b8df392e76abc3eb7d5ce04\nB = -dc5ab818c70594dd917b4243d\n\nProduct = bb24422ee4656ddfcd50ec38201b15baf679d3b75e5cb878ca\nA = f8e12cf4defe388b78510f687\nB = c07ee817b4ae95c2915b88966\n\nProduct = -93da296ba164c7220a17330647aef0980c94eddd2cfa2a3b2d\nA = bc5dc74ddf7a1363d1c2b1f25\nB = -c8f069bad7f93cbfe6df51169\n\nProduct = -6b2e1d132c4e0b0dc9b7e7de7d424fda5180480cb5ff47c755\nA = -a8048acb66a8bb88df39266e7\nB = a34e0b265d71435ae8c92a463\n\nProduct = 6ccb2cd93783576a8602ae43f41c786008b6623a4cca0a010a\nA = -b071f1f54790c951c1dd2a1cf\nB = -9dd89bb4d9b546207e282e2d6\n\nProduct = 5c742ba47d0d64bd97509927ce957deedb855766cc24c60016\nA = b44f3f252c368096fa62747f2\nB = 83439b97dbac579fa4f7b7d23\n\nProduct = -7347ba65691c913286c2fb55e45b177f031c1d86ae0e9f654f\nA = 937cf0643ffa53cdea24d642f\nB = -c81881f78243dd5737a7d28e1\n\nProduct = -9bc0649a703674e59f83ff9b8a560e5cbf51f65ca310f80f95\nA = -b536f8d9769be6f62da941ae5\nB = dc0746fb101881ae0cacde6f1\n\nProduct = bf4992fc3a124de350f9fb90ea825cf663b1fa051282ef22e2\nA = -ff7eacc7de1bb01d668c693aa\nB = -bfaa6627f9fc7ba68ae41bb2d\n\nProduct = 7c8992d34cc0b63f1c953f68d4e12a99d3f3a34d16bd76caa9\nA = 9e0d5a850d078890a983c0ec9\nB = c9b72c118b3e1f1023a696ce1\n\nProduct = -a75840c95082b9a0ae0d6e0a4eb5e09288e4e2a66e9697d9cd\nA = b2b042a21045a74ef1a5091d9\nB = -efbf8b120b384e869692a1b15\n\nProduct = -a510b333bdb4ed7479c142e8fbe2b12f7671a42acbe16c0998\nA = -e7fd5e0bb5496b9d876c27f65\nB = b6262653b2be44501af1d85b8\n\nProduct = a1c1e90afc4684754155526e307fc6ed798746f347bae2c880\nA = -b84674832b26ded0a690a8ff0\nB = -e0b7bdf2fd05a038ed3640b78\n\nProduct = 5588e0c33bffbefcc5695ca0615abd383343f21a8a0d22b222\nA = 80cad81ad9a66ab6a1c2e5669\nB = aa0453a77c8af1584f54750d2\n\nProduct = -6460c2fcd6cf3304ab163ea883ac48e2031cd10f2e9014c0ab\nA = c49ad3d7c8848d4fbf913b10b\nB = -82b3dedbe3cc7cd532ad632e1\n\nProduct = -a18717330b711669e85abde8c4dce426529aa621ba3da2a477\nA = -cab4a9c0a331a5a5e826dda1f\nB = cbfee5041c13075dfe3399aa9\n\nProduct = 8ab6282ee892b53c083d319a9dcab48af97a1ac8493c0bfcad\nA = -f7d13e47f9aaac8c25f9bf75b\nB = -8f4aa95231c1e2336aa092297\n\nProduct = 8f2d1c23c78777ed371f13155445ca3c88cbc0a9b299bdf9d3\nA = 9d8248d00defce1ad081337c3\nB = e8b479295ecd9cef7301f24b1\n\nProduct = -86d5e0c5b581fe59819730b4b71e33d1f85f9ab504c7dbe2d6\nA = b21b45e88acff48562a19729a\nB = -c1cdfebccc763beeac394b997\n\nProduct = -484ca05aefa113bdfcb1bc623f730c9f9555b462a8ab4c9606\nA = -8c12b406c02c4417163c0956b\nB = 8422b15c80c1c087b17eedd92\n\nProduct = 614c3c91f60050c785fd229a3ad74674577a90cacb654e0a5c\nA = -93d45bce155a23a397506d96a\nB = -a87e339c3fd5aebede5fb1b36\n\nProduct = 9683285f194a7e4feeab196a36bdfc4f828035fd184b9cc692\nA = f196d8fe760fdcae7eb60e2f7\nB = 9f7d88a2163ad818bf3a6377e\n\nProduct = -988a64599c19cc64f3cadc1a83fea6550185f6cc3ab82af822\nA = d0584b2a306671e4d2c9d0c7b\nB = -bb6e7559df199c68d6df3a3c6\n\nProduct = -68456814cb0edd951196d04c853172afdd5787a5bd69a57876\nA = -cefce1b0a1fb22862418bb597\nB = 80f614139947aea5e76cd55fa\n\nProduct = b4b1cbf5d6566e7a57aee0cc5c9c8ec4ad885e8766aa7662a4\nA = -d68ed1bea046c6cad057e21db\nB = -d7988b9be54f6e332d019032c\n\nProduct = 6b09212675ff5257a1384371e17b37dcc268bbb141577902e4\nA = a8208053adc20a609d5d01404\nB = a2fa927c5458c4fe662d7a3b9\n\nProdu", + "ct = -8361bc26f9bcf55f677e047d822d3004027da0d0455b244d10\nA = e82b6410b29020c2d6810a977\nB = -90ddfe0e7f0d6b9cdc0815f70\n\nProduct = -f1b6da00923fd513a83e32040a515649fbd362f69ebc016d9f\nA = -f9b697d9ec774a8d1ee5ea905\nB = f7ccb46a8869cb028492bed53\n\nProduct = d06206963f2e150bacdb32c823c3a47f013d5a267c3c0d0c88\nA = -ea8e63afa99c719897ad7f2ab\nB = -e36f11f55b6148d1b4f46e598\n\nProduct = af774a5eae6084df5ca499ef005642730adabf6a4f9533e2fd\nA = e4c7af7eea3ec9cc2443b7319\nB = c457bc264c8461789931baf85\n\nProduct = -76350f428bfbb95e6c253ec0f457aa84cebe8c7cb1af2a2120\nA = 8fd1ff97465775d44dee58ae0\nB = -d268a7d328f44baf80e35119f\n\nProduct = -787ae3f114f9a8dd4d249d5d3f3b0897b02564b9469416cefe\nA = -bc0b398bd0ec045b0cf147b7e\nB = a4050955c234e473257d0c641\n\nProduct = 9d6320b3d4aabac097a079b9bd2aca7f1898bcab0f23409fd0\nA = -9d7a4ebac630cc0662b816fb5\nB = -ffda517d3eb3214986b04e290\n\nProduct = 80bab8bd800ac8c9dc3bb57dca306f10af6fd88c5d8314833c\nA = 834bc50140d6c6ab938dc58b6\nB = fafee47793cbc533b3c66af3a\n\nProduct = -b08920f5922226b1dec87151ae087d8a7e5c1aea8c9be148b6\nA = bfd5b1ad323c79428cb2db36a\nB = -eb956a10edebdd658e6810fcf\n\nProduct = -6d428e08e8350bb4b0fae3b662c82df2aef7beadaa17430dbb\nA = -a57da276998c548101f514e9f\nB = a9040c1909712e1149d295765\n\nProduct = a57da276998c548101f514e9f\nA = -a57da276998c548101f514e9f\nB = -1\n\nProduct = 14afb44ed3318a90203ea29d3e\nA = a57da276998c548101f514e9f\nB = 2\n\nProduct = -295f689da6631520407d453a7c\nA = a57da276998c548101f514e9f\nB = -4\n\nProduct = -867614005cc204a8d19720fe13\nA = -a57da276998c548101f514e9f\nB = d\n\nProduct = 12bf3b676f64e5929d38c35e803\nA = -a57da276998c548101f514e9f\nB = -1d\n\nProduct = 24d8f92c68303ed0b96f91a8167\nA = a57da276998c548101f514e9f\nB = 39\n\nProduct = -49b1f258d0607da172df23502ce\nA = a57da276998c548101f514e9f\nB = -72\n\nProduct = -6fd5e6ca25c3d51b2e529f22173\nA = -a57da276998c548101f514e9f\nB = ad\n\nProduct = 1276d4705b81b82da4c7e82559d7\nA = -a57da276998c548101f514e9f\nB = -1c9\n\nProduct = 1ddb9abfc5d4017f068a67b5f4fd\nA = a57da276998c548101f514e9f\nB = 2e3\n\nProduct = -3a8b41c914b1b4a4e341433601f7\nA = a57da276998c548101f514e9f\nB = -5a9\n\nProduct = -97c0f4ba414d6e7d4c8b7ced84d4\nA = -a57da276998c548101f514e9f\nB = eac\n\nProduct = 1198739e0c23639c176d46d13f7c8\nA = -a57da276998c548101f514e9f\nB = -1b38\n\nProduct = 159150954ee0dedf541e4dbac0ec3\nA = a57da276998c548101f514e9f\nB = 215d\n\nProduct = -441d4bc44c86f02ff12c3d91a1562\nA = a57da276998c548101f514e9f\nB = -695e\n\nProduct = -64726b76005ebee27592237ba5dde\nA = -a57da276998c548101f514e9f\nB = 9b62\n\nProduct = bbe4ec7cf7c5bbd198e0ea86bb658\nA = -a57da276998c548101f514e9f\nB = -122a8\n\nProduct = 21f717d05681fd2eb1796776a69ef7\nA = a57da276998c548101f514e9f\nB = 348a9\n\nProduct = -396ac788a1748bc6955f99be4d2c64\nA = a57da276998c548101f514e9f\nB = -58d1c\n\nProduct = -54a213eb083aed1a04f3d1b2da62e7\nA = -a57da276998c548101f514e9f\nB = 82eb9\n\nProduct = 1366fb9c20fb14b8b9a9be4b3e3dde1\nA = -a57da276998c548101f514e9f\nB = -1e037f\n\nProduct = 238d65fd26da4733e5d93ab2485d40b\nA = a57da276998c548101f514e9f\nB = 36ff15\n\nProduct = -38272a99be154d531e922be405aee9a\nA = a57da276998c548101f514e9f\nB = -56dd26\n\nProduct = -64651b62b6a454c08951632c7f2c398\nA = -a57da276998c548101f514e9f\nB = 9b4d68\n\nProduct = fb272e3597b816144f8b945ae6130e0\nA = -a57da276998c548101f514e9f\nB = -1848320\n\nProduct = 280d9f5ed7243712ecb9a7c6358bcb8b\nA = a57da276998c548101f514e9f\nB = 3df5795\n\nProduct = -2fbb6bb8e1ba78cefc47fbbc20e188ee\nA = a57da276998c548101f514e9f\nB = -49d6652\n\nProduct = -57f29c13691ffa1642d2860dab9d288e\nA = -a57da276998c548101f514e9f\nB = 880c2b2\n\nProduct = 139c19d7668e6aabf2d7206cb0723ed34\nA = -a57da276998c548101f514e9f\nB = -1e55aa4c\n\nProduct = 2950ce04bf0cf836d4fe94b88fb757d0a\nA = a57da276998c548101f514e9f\nB = 3fe968b6\n\nProduct = -5175239488dad05a58414251496d2a06c\nA = a57da276998c548101f514e9f\nB = -7e020414\n\nProduct = -945ff0ed38bc6020cf679cbd3e0758c6d\nA = -a57da276998c548101f514e9f\nB = e585e573\n\nProduct = 11c69ae98f6b27e95477986f796bc67c8c\nA = -a57da276998c548101f514e9f\nB = -1b7f653f4\n\nProduct = 209afe75e8fb5ac76d13c06b545f5d4d73\nA = a57da276998c548101f514e9f\nB = 3270154ad\n\nProduct = -386d64b215e41506514f4988ed237e4da2\nA = a57da276998c548101f514e9f\nB = -5749c891e\n\nProduct = -6c13cccdb1d140d0babd52707ea72fa278\nA = -a57da276998c548101f514e9f\nB = a72fb6288\n\nProduct = 136228a8a45540372b9b3cd7f82021f6546\nA = -a57da276998c548101f514e9f\nB = -1dfc08a2fa\n\nProduct = 1f0ad3babf9d132eaa08cf5cdb8f19dbf01\nA = a57da276998c548101f514e9f\nB = 30050f2e5f\n\nProduct = -50d615ce183258e95af77319b766fac81e2\nA = a57da276998c548101f514e9f\nB = -7d0bf92cde\n\nProduct = -817d358293b86a56a4e881e50257c549471\nA = -a57da276998c548101f514e9f\nB = c84efb12ef\n\nProduct = f09b9e80be251de474d726b16e25a6865fc\nA = -a57da276998c548101f514e9f\nB = -1743322a484\n\nProduct = 22996cb0f9c60e35dce49f3825f8a479db26\nA = a57da276998c548101f514e9f\nB = 3585acec11a\n\nProduct = -2b307a37c91791a61c0691858f5f783e4678\nA = a57da276998c548101f514e9f\nB = -42cf6be3e88\n\nProduct = -8826698fcba6c30d755fc523de1cc25301ae\nA = -a57da276998c548101f514e9f\nB = d29cc8af592\n\nProduct = ae37fc99fd419809310782714530d7428d77\nA = -a57da276998c548101f514e9f\nB = -10d8059d4a29\n\nProduct = 1d544a20f9bc7d95ab67d1f65743979f23bba\nA = a57da276998c548101f514e9f\nB = 2d5eadef1c06\n\nProduct = -367897184e9929a0294d320f10278889fbeb7\nA = a57da276998c548101f514e9f\nB = -54431582d0e9\n\nProduct = -943a509076a00060a2e7fa1cddb7468d734a1\nA = -a57da276998c548101f514e9f\nB = e54bb102f4bf\n\nProduct = fcce6e42879af5ad13545c0bcaab85b690cea\nA = -a57da276998c548101f514e9f\nB = -18711db522cd6\n\nProduct = 258c49f86d0cbb14ae9edbd3456be8cede2022\nA = a57da276998c548101f514e9f\nB = 3a1562c7c269e\n\nProduct = -4a8bbce59ad7daa51136d557f7fa16e9a2faad\nA = a57da276998c548101f514e9f\nB = -7350e780b0f33\n\nProduct = -82f53ec9333275d5cc271876a7db936db49280\nA = -a57da276998c548101f514e9f\nB = ca94ad312dd80\n\nProduct = 11daee4fcc713db5b2806e47fa5dff3b5b770eb\nA = -a57da276998c548101f514e9f\nB = -1b9ed6758f9635\n\nProduct = 17038cac4f0c94dc24985ea108ae6682e175752\nA = a57da276998c548101f514e9f\nB = 2399b8a9b1116e\n\nProduct = -37e5f14394bf347a3ed061769fe8e6424af4348\nA = a57da276998c548101f514e9f\nB = -567840a7569fb8\n\nProduct = -9253d4a32a88d8f725984514d969012ead7cc9a\nA = -a57da276998c548101f514e9f\nB = e25b246f733f26\n\nProduct = ace3648371c16a931d29004e79f5b9678391da5\nA = -a57da276998c548101f514e9f\nB = -10b717b27b6a13b\n\nProduct = 1faa5b45d04c143c339b09d3aad94d39b94ef960\nA = a57da276998c548101f514e9f\nB = 30fbd672e106aa0\n\nProduct = -3fdfe246d27aae0d08d63b2bc501461d2bff3b8d\nA = a57da276998c548101f514e9f\nB = -62cef5f078a8253\n\nProduct = -5b792bfaeff04ee3d948cb343a249d49eb344f57\nA = -a57da276998c548101f514e9f\nB = 8d805ac65649c49\n\nProduct = c5f824406161eec321da5a58e3e00d393b55abe9\nA = -a57da276998c548101f514e9f\nB = -1323dd41d2e1e077\n\nProduct = 2226dec8a57be8e84e42559007e2d101ccbe67f8d\nA = a57da276998c548101f514e9f\nB = 34d47842b5d0be53\n\nProduct = -340f50f812c7420b502000940788a700f6769788a\nA = a57da276998c548101f514e9f\nB = -508836d8e1193d36\n\nProduct = -a00f1d96e19c590479625c5329a87774b5964cc78\nA = -a57da276998c548101f514e9f\nB = f798fc858657f888\n\nProduct = cb94f830cba8997331912a6a31c34f1bef826d121\nA = -a57da276998c548101f514e9f\nB = -13aec7a5c52a0883f\n\nProduct = 16b45140b048d6dc0b9fc811df7ce7dd88357fff04\nA = a57da276998c548101f514e9f\nB = 231f27f3e347bd67c\n\nProduct = -2aa94179351b4e87de5849ab619d94f47450640199\nA = a57da276998c548101f514e9f\nB = -41fe3ec2189599cc7\n\nProduct = -5489401d3da93158d4284e557d74016c0a7cfd935a\nA = -a57da276998c548101f514e9f\nB = 82c5281df41bfc066\n\nProduct = ae04d5b212ecfc9a6d7df07794d565df52991fb70e\nA = -a57da276998c548101f514e9f\nB = -10d3139229f5d02432\n\nProduct = 27821bc811f45d63089790b41d307be978d4b19564c\nA = a57da276998c548101f514e9f\nB = 3d1da85cc012b3e234\n\nProduct = -3de3c9e9d7fa3020a578706339314890dccf63096c2\nA = a57da276998c548101f514e9f\nB = -5fbcfb28bfc9044bfe\n\nProduct = -627dcb299a6720044abcf11469bdfd3f951edbb5bf7\nA = -a57da276998c548101f514e9f\nB = 985b930517b78e6ba9\n\nProduct = cc0622441497a37fddf1856d5e2c99df52b99ea4573\nA = -a57da276998c548101f514e9f\nB = -13b9b88948fb7e95cad\n\nProduct = 1a5168e1a492210591ad1ed660adde9110390e4caf32\nA = a57da276998c548101f514e9f\nB = 28b631c6e04b6ab0d8e\n\nProduct = -4d8ec27b7460ce616421b9f5cae708c2ac241daa59b4\nA = a57da276998c548101f514e9f\nB = ", + "-77f99bdf1eb09da6dcc\n\nProduct = -55afd796db7bce822a00073fc8926d3bd0c79772f036\nA = -a57da276998c548101f514e9f\nB = 848cdd6212b9bb3620a\n\nProduct = dc494b0d73e8ec07cd2bb6dd8191d2b4d48e7700cc34\nA = -a57da276998c548101f514e9f\nB = -154c39567bd8be5f6b4c\n\nProduct = 240e9301b4345b914ecd91a49a0e651524dcecb6fdc6c\nA = a57da276998c548101f514e9f\nB = 37c6e7ee89cf87674814\n\nProduct = -39002ecfd6d96661b336157ccef6536756ad2e9219be3\nA = a57da276998c548101f514e9f\nB = -582cdab09915a652203d\n\nProduct = -695f49fc891d53f396f0593efae3973082b76d4f9e944\nA = -a57da276998c548101f514e9f\nB = a30074dbce2246af043c\n\nProduct = bba2b7b45b97cb0d7fb30fed95089870742ad69e7aed7\nA = -a57da276998c548101f514e9f\nB = -1224195afc7b394ae8cc9\n\nProduct = 1910edc278515ab7d4cc09b496dc3c06c32c75bc7368af\nA = a57da276998c548101f514e9f\nB = 26c6701c39334169e7bf1\n\nProduct = -3670b7f9b661aba35ce50984d83173c84c8fa60e04d100\nA = a57da276998c548101f514e9f\nB = -5436e84b4a29858a68f00\n\nProduct = -7fa0d3e0082b37475342b7e22e5dbad7b8d4cb5d64f871\nA = -a57da276998c548101f514e9f\nB = c56e0f44fc63bca242eef\n\nProduct = da7fe3367ce640fa5941c033ac1874312f10ba5950da75\nA = -a57da276998c548101f514e9f\nB = -15200043166ff309f0426b\n\nProduct = 1871d72481f66b1d413100edd6b339cbbaa67b3b2b3cd57\nA = a57da276998c548101f514e9f\nB = 25d057879db26fa29a5e49\n\nProduct = -3cf1dd1e2df3456757d72f35353c3c7a659b2ef844ad857\nA = a57da276998c548101f514e9f\nB = -5e46be70de21949df67349\n\nProduct = -5e861cbe47aefab2a7ea59292aab1258932b9a322f66e63\nA = -a57da276998c548101f514e9f\nB = 9238670897685a6c9cbdbd\n\nProduct = f623344788efb857db55c924e95a437effa4dc8bb2bcd24\nA = -a57da276998c548101f514e9f\nB = -17cc0ec84c228225a7cf45c\n\nProduct = 15514c916b0ae7cde6add16c629d3e19ba52a101d75dff72\nA = a57da276998c548101f514e9f\nB = 20f9f925b3ed307edbb154e\n\nProduct = -460cf5b14f9d0b547c3084bf44207bf881745c409b08d07f\nA = a57da276998c548101f514e9f\nB = -6c5cbfd29f3dae1dce99221\n\nProduct = -5ddf7fb91d765af97dfda5333d8779e80837c2b51cfb4f43\nA = -a57da276998c548101f514e9f\nB = 9136aa79080defd1bcf90dd\n\nProduct = 12c1a0edfb6ab6a0caae2553fb3743827e1470a8954e0a3fd\nA = -a57da276998c548101f514e9f\nB = -1d03b512470dc3052779f3e3\n\nProduct = 28388a244214abf046488a8d95308d95f021eae4b994a5a52\nA = a57da276998c548101f514e9f\nB = 3e37dce784274962ff862e6e\n\nProduct = -4da476e76119deef291c0f56934a912a0877278a19a561ee0\nA = a57da276998c548101f514e9f\nB = -781b2f2dc40094a7f8fed520\n\nProduct = -5792496d33dd45e225f9dfca17419a04e075ffc0c90b37b82\nA = -a57da276998c548101f514e9f\nB = 87772a4fb582acafd3e4ef3e\n\nProduct = dd3a3506a7d748de16fb43d666928a87de0354d8e8a1bcaaa\nA = -a57da276998c548101f514e9f\nB = -1563841bf7851ff158a395716\n\nProduct = 24e8fb09a9ab0808ff643122479dea5ed41060c6c5b74e8752\nA = a57da276998c548101f514e9f\nB = 3918c30b5568318a58e9be16e\n\nProduct = -366c125f96b38b58d01c939c27c4100af3377eabb792b5491a\nA = a57da276998c548101f514e9f\nB = -542fb814f45924aa09a16f2a6\n\nProduct = 0\nA = 0\nB = 542fb814f45924aa09a16f2a6\n\nProduct = 0\nA = 542fb814f45924aa09a16f2a6\nB = 0\n\nProduct = 542fb814f45924aa09a16f2a6\nA = 1\nB = 542fb814f45924aa09a16f2a6\n\nProduct = 542fb814f45924aa09a16f2a6\nA = 542fb814f45924aa09a16f2a6\nB = 1\n\n\n# Quotient tests.\n#\n# These test vectors satisfy Quotient = A / B, rounded towards zero, and\n# Remainder = A - B * Quotient.\n\nQuotient = 1\nRemainder = 0\nA = 8cdaaa7c422f3c2bb0ace2da7d7ff151e5bdefb23e6426cf3e6b21491e6e80e977bfa6c65931a8dee31fc7992c0c801d5d7c\nB = 8cdaaa7c422f3c2bb0ace2da7d7ff151e5bdefb23e6426cf3e6b21491e6e80e977bfa6c65931a8dee31fc7992c0c801d5d7c\n\nQuotient = -2\nRemainder = 1\nA = 107f0e6cebfe22ac11294a06fed2b994d01c9b3610d50bdd254adafd08c93be8ebdd1e85e1286fe9c9e682a90cbbd6351681b\nB = -83f873675ff11560894a5037f695cca680e4d9b086a85ee92a56d7e84649df475ee8f42f09437f4e4f34154865deb1a8b40d\n\nQuotient = -4\nRemainder = -2\nA = -3d8746ae2123c2d3f1d35910b42af1f86f5e81f8e98986cea20b2a1bdb8af6cf111f1258f112c837accdf4868463fe9eba536\nB = f61d1ab8848f0b4fc74d6442d0abc7e1bd7a07e3a6261b3a882ca86f6e2bdb3c447c4963c44b20deb337d21a118ffa7ae94d\n\nQuotient = 8\nRemainder = -3\nA = -5645d65662eaac73050de06f8f982a9b2ae680467712284be3e2b0e58ef4bf4d72b5be5e12ee1fd803b47f161759662ff5c4b\nB = -ac8bacacc5d558e60a1bc0df1f30553655cd008cee245097c7c561cb1de97e9ae56b7cbc25dc3fb00768fe2c2eb2cc5feb89\n\nQuotient = 10\nRemainder = 4\nA = 813bc46ee19ffeab364073a89f96913f340d43ee72129ea9edac1beb4ebe1336450d2eabc7b26e51c400cec60d6ee459033b4\nB = 813bc46ee19ffeab364073a89f96913f340d43ee72129ea9edac1beb4ebe1336450d2eabc7b26e51c400cec60d6ee459033b\n\nQuotient = -20\nRemainder = 5\nA = 12805392c55ffa0e27e85e15f2b339872793664e9ed3074cd2600aa52459a57197130d1ea46775ef43115c9413248cc7b34805\nB = -94029c962affd0713f42f0af9599cc393c9b3274f6983a669300552922cd2b8cb89868f5233baf7a188ae4a09924663d9a40\n\nQuotient = -40\nRemainder = -6\nA = -3579fc4d6083394c691b060cf9e20318fe17da0487337f76710bd11512578830ba94ac7b587a2d5ab7cb4afe611e349cdcfb86\nB = d5e7f135820ce531a46c1833e7880c63f85f68121ccdfdd9c42f4454495e20c2ea52b1ed61e8b56adf2d2bf98478d27373ee\n\nQuotient = 80\nRemainder = -7\nA = -74ebad4b39ebaaff82cd91082408c979527907c363d8f0f75db410523f8477c074c45ff85851b6275b1ebc5279029818e78d87\nB = -e9d75a9673d755ff059b2210481192f2a4f20f86c7b1e1eebb6820a47f08ef80e988bff0b0a36c4eb63d78a4f2053031cf1b\n\nQuotient = 100\nRemainder = 8\nA = d2d8a4419fb3b1c22bfca04ca08c2ee066ccbc9fce2f41861b5eef91efd3c13eeb7eae5abea0ef1849662cfdfef7bbff892c08\nB = d2d8a4419fb3b1c22bfca04ca08c2ee066ccbc9fce2f41861b5eef91efd3c13eeb7eae5abea0ef1849662cfdfef7bbff892c\n\nQuotient = -200\nRemainder = 9\nA = 1bf534da2f4365c96fc5dd4928e73ac24b157b5136ead90cf6596033ec387a2c14bca828000ae1725f3a5ace8ad67a8c07a0a09\nB = -dfa9a6d17a1b2e4b7e2eea494739d61258abda89b756c867b2cb019f61c3d160a5e5414000570b92f9d2d67456b3d4603d05\n\nQuotient = -400\nRemainder = -a\nA = -3a172cc9483774544311a1366659d9e61cc9fac7dc11c68e36aa991ef4d5e96becf5bac3e0967c904d926617ea11bb9551b980a\nB = e85cb32520ddd1510c4684d9996767987327eb1f70471a38daaa647bd357a5afb3d6eb0f8259f2413649985fa846ee5546e6\n\nQuotient = 800\nRemainder = -b\nA = -5ecff3a3e47fa615b6e3ce2dedfdeefbfe1d437c394631820968a9650b59dc3a2dd1c9a0b06537e4e5c408a59e580921503580b\nB = -bd9fe747c8ff4c2b6dc79c5bdbfbddf7fc3a86f8728c630412d152ca16b3b8745ba3934160ca6fc9cb88114b3cb01242a06b\n\nQuotient = 1000\nRemainder = c\nA = d3ef80fca0ab3ac3432b22e2b485131d816810c39d02a9c82dcc05ec5e6406bc216026de3abe53ab103ea3b2ddbc2ea377ae00c\nB = d3ef80fca0ab3ac3432b22e2b485131d816810c39d02a9c82dcc05ec5e6406bc216026de3abe53ab103ea3b2ddbc2ea377ae\n\nQuotient = -2000\nRemainder = d\nA = 163956bc32325f28f48d41d32bb08d2a9c4ccbb0d818368fb13941e82b27da21d04094f7e897ce79c2d0ff8470505f1ef63fc00d\nB = -b1cab5e19192f947a46a0e995d846954e2665d86c0c1b47d89ca0f41593ed10e8204a7bf44be73ce1687fc238282f8f7b1fe\n\nQuotient = -4000\nRemainder = -e\nA = -3763f8e43bd05e6ffeec6d509bbe6ff9a9022ced8cb191c9abaf5fd0e0b75a53e2ad581455e3af09e702a77b164ed3fb54ae000e\nB = dd8fe390ef4179bffbb1b5426ef9bfe6a408b3b632c64726aebd7f4382dd694f8ab56051578ebc279c0a9dec593b4fed52b8\n\nQuotient = 8000\nRemainder = -f\nA = -531dd44dfa9e79a5aec8fa7c84bd3b753c146770d22d2c14a6d2125f7ab95e9b320e84c31cf3e0d883e1295a220f2a546550800f\nB = -a63ba89bf53cf34b5d91f4f9097a76ea7828cee1a45a58294da424bef572bd36641d098639e7c1b107c252b4441e54a8caa1\n\nQuotient = 10000\nRemainder = 10\nA = 900996b61f58713f0755e68bbdfa4e0bb47f034bb0304f77829847923d14715def1771f43b526c41b9667438b434d2b966c20010\nB = 900996b61f58713f0755e68bbdfa4e0bb47f034bb0304f77829847923d14715def1771f43b526c41b9667438b434d2b966c2\n\nQuotient = -20000\nRemainder = 11\nA = 179d7ede3db0c105525286551331d5b9e1f97a7883f0c13cf250afe9765bb5aaa527af7945c19cdd4596565cbc8532a3cfa5c0011\nB = -bcebf6f1ed86082a929432a8998eadcf0fcbd3c41f8609e792857f4bb2ddad55293d7bca2e0ce6ea2cb2b2e5e429951e7d2e\n\nQuotient = -40000\nRemainder = -12\nA = -293dc443c294c6a6c53dd49e84f58305d59a432afb6c7ea2039cd02a513231239571ae07f29b5427e869b9faa485511ca45980012\nB = a4f7110f0a531a9b14f7527a13d60c1756690cabedb1fa880e7340a944c8c48e55c6b81fca6d509fa1a6e7ea921544729166\n\nQuotient = 80000\nRemainder = -13\nA = -5b637eb8aa51ef15a18d9b144031c9756527fc0fb96c84b6df03700e5079ae1b3e96940a2c1e07f3b47ad8a9b2b8ca99171a00013\nB = -b6c6fd7154a3de2b431b3628806392eaca4ff81f72d9096dbe06e01ca0f35c367d2d2814583c0fe768f5b153657195322e34\n\nQuotient = 100000\nRemainder = 14\nA = 87c846f5469d4c5819aed0c7e77797209b2c", + "1b83a7a0e2be70280b9f30946b5db9bd0f25a06cf4bdba1c7183a1b9eb75c19400014\nB = 87c846f5469d4c5819aed0c7e77797209b2c1b83a7a0e2be70280b9f30946b5db9bd0f25a06cf4bdba1c7183a1b9eb75c194\n\nQuotient = -200000\nRemainder = 15\nA = 11c2a4509f419aa977c3d37fa446fcf21b4b3b9f983fbaddeba4f51c285ac4032200711a54cc6edf24297b1f3d46ad020131a00015\nB = -8e152284fa0cd54bbe1e9bfd2237e790da59dcfcc1fdd6ef5d27a8e142d62019100388d2a66376f9214bd8f9ea356810098d\n\nQuotient = -400000\nRemainder = -16\nA = -39e37ae0edd92b957e84682358039f5e432c42492a44f3de01cdf74d643760260f2837946608663e12291e9b0695449c1153800016\nB = e78deb83b764ae55fa11a08d600e7d790cb10924a913cf780737dd3590dd80983ca0de51982198f848a47a6c1a551270454e\n\nQuotient = 800000\nRemainder = -17\nA = -72f725edd5a3dd6f20b5e9ca7da08a99f8ec9214c80588182c0d42e03bcff34b488b28c03cdf41813a6193c10672a8ee68f6000017\nB = -e5ee4bdbab47bade416bd394fb411533f1d92429900b1030581a85c0779fe6969116518079be830274c327820ce551dcd1ec\n\nQuotient = 1000000\nRemainder = 18\nA = 966df62c26acab2d3d1dbe729e48d0181c68e9f5eba45f6caefa38d60e34057d09fe620abb8640cec8cac755957aaad7c6fd000018\nB = 966df62c26acab2d3d1dbe729e48d0181c68e9f5eba45f6caefa38d60e34057d09fe620abb8640cec8cac755957aaad7c6fd\n\nQuotient = -2000000\nRemainder = 19\nA = 190790727c1514b4ef83a1c6aa07493c0af7087fbc8a675bfd9a1e97b8ef80ef684219d6c6f1a5fb5b919f105fd7717cdd5aa000019\nB = -c83c8393e0a8a5a77c1d0e35503a49e057b843fde4533adfecd0f4bdc77c077b4210ceb6378d2fdadc8cf882febb8be6ead5\n\nQuotient = -4000000\nRemainder = -1a\nA = -22d115ab02f8663d8c009960086a0275d301d358cd3b250bb9e7c16cc6ebed4a8fbe43bbced856d93be64a17377d95f5f9c8800001a\nB = 8b4456ac0be198f63002658021a809d74c074d6334ec942ee79f05b31bafb52a3ef90eef3b615b64ef99285cddf657d7e722\n\nQuotient = 8000000\nRemainder = -1b\nA = -41f2e708ba47494a13607223b08e6d99c0b4247436632961d873804e83446dc97139ffaef3e25969950bd4b5bb4ff73b1a25000001b\nB = -83e5ce11748e929426c0e447611cdb33816848e86cc652c3b0e7009d0688db92e273ff5de7c4b2d32a17a96b769fee76344a\n\nQuotient = 10000000\nRemainder = 1c\nA = e4b52f78179039499c2f6b500840f41103fbd60eac0d7082297236f25189c18a8301a92f533945047fbb83427dcade334336000001c\nB = e4b52f78179039499c2f6b500840f41103fbd60eac0d7082297236f25189c18a8301a92f533945047fbb83427dcade334336\n\nQuotient = -20000000\nRemainder = 1d\nA = 10888959278661bc36089519a215bda60f9ce24ff7c0ac1f543b6e652f94dbff1f32aa40cad2b4b4d676f16948551501c29f2000001d\nB = -84444ac93c330de1b044a8cd10aded307ce7127fbe0560faa1db73297ca6dff8f99552065695a5a6b3b78b4a42a8a80e14f9\n\nQuotient = -40000000\nRemainder = -1e\nA = -3ada453530a180fda58533ab8c62beb4f693a134f512e4d23e487dac3b575e5390c0a90992400e402bb47aac93d46ded55f54000001e\nB = eb6914d4c28603f69614ceae318afad3da4e84d3d44b9348f921f6b0ed5d794e4302a42649003900aed1eab24f51b7b557d5\n\nQuotient = 80000000\nRemainder = -1f\nA = -57879eb5d92d565daac3ac5173639bfe44b6ecc69ff770af57bd79c9b93841c5677042cb362b794f3d8b24b0d3b73ed1cba58000001f\nB = -af0f3d6bb25aacbb558758a2e6c737fc896dd98d3feee15eaf7af3937270838acee085966c56f29e7b164961a76e7da3974b\n\nQuotient = 100000000\nRemainder = 20\nA = 89a2f1792afc54467955839eddc9ef2e37d391ce7a1a4a205291220c1f49f59ee31fc7a7a7f7706c199bf5c8c951a0d0743d00000020\nB = 89a2f1792afc54467955839eddc9ef2e37d391ce7a1a4a205291220c1f49f59ee31fc7a7a7f7706c199bf5c8c951a0d0743d\n\nQuotient = -200000000\nRemainder = 21\nA = 1c267719338a4562e934bc57fabe6da86ca534a34244bd38c15032f01f47c2fd498c83f644b345c5c661ada0e586a096bb63000000021\nB = -e133b8c99c522b1749a5e2bfd5f36d436529a51a1225e9c60a819780fa3e17ea4c641fb2259a2e2e330d6d072c3504b5db18\n\nQuotient = -400000000\nRemainder = -22\nA = -250249f2185d4b428fa9534f03ef3cbed535bd31c56c0b273e6c3d35e0266f7777a6e59a99da5738b8e3af8ac60061d6716ac00000022\nB = 940927c861752d0a3ea54d3c0fbcf2fb54d6f4c715b02c9cf9b0f4d78099bdddde9b966a67695ce2e38ebe2b18018759c5ab\n\nQuotient = 800000000\nRemainder = -23\nA = -710b30c23c3c4e646ba90da33d2ce35af2ff181c40b02e3ffa607966730c6b6e274dd4c3c78e578e0b10f431f2d832274bf6800000023\nB = -e216618478789cc8d7521b467a59c6b5e5fe303881605c7ff4c0f2cce618d6dc4e9ba9878f1caf1c1621e863e5b0644e97ed\n\nQuotient = 1000000000\nRemainder = 24\nA = 877f1caf75e7166ef18484d0718947893fd1ec016984387debc55c19e378a487a5ddbb03a80a88316f6fca16ae148933e719000000024\nB = 877f1caf75e7166ef18484d0718947893fd1ec016984387debc55c19e378a487a5ddbb03a80a88316f6fca16ae148933e719\n\nQuotient = -2000000000\nRemainder = 25\nA = 1ed1b7d9e4cf3d44ee98ef69850e61a39f54cc407c6795c07c887374441fd9ec258c21193f8a8c55802fb8f8c579cf94cb0ce000000025\nB = -f68dbecf2679ea2774c77b4c28730d1cfaa66203e33cae03e4439ba220fecf612c6108c9fc5462ac017dc7c62bce7ca65867\n\nQuotient = -4000000000\nRemainder = -26\nA = -35d324ba37d2000f960ca1c9e1ab96e341a2ae6a5ea5cef014c73a39dde000d8ad9606b817ad67e4e4593cc5894d354854898000000026\nB = d74c92e8df48003e5832872786ae5b8d068ab9a97a973bc0531ce8e777800362b6581ae05eb59f939164f3162534d5215226\n\nQuotient = 8000000000\nRemainder = -27\nA = -7039477c3e0a6f415e25e9f9b1dab1edcd8a23f984e7e3bc149c206a3b756b1be001450af4049cd4535e4243d7032afcf6790000000027\nB = -e0728ef87c14de82bc4bd3f363b563db9b1447f309cfc778293840d476ead637c0028a15e80939a8a6bc8487ae0655f9ecf2\n\nQuotient = 10000000000\nRemainder = 28\nA = d6c59dd07409da98f7bbc7ee471b6e06c4d9e832e9f4d04ed9da63564d37d3072a950564cf549bb5d6e7dc85565d3cc8ba340000000028\nB = d6c59dd07409da98f7bbc7ee471b6e06c4d9e832e9f4d04ed9da63564d37d3072a950564cf549bb5d6e7dc85565d3cc8ba34\n\nQuotient = -20000000000\nRemainder = 29\nA = 14d27a16a9cf2fdbc85b88a604dd8f0e57b5b34a27089d75d805e05fbb367dfa61c085aa98b896e3e53b85ef774a3fa52417a0000000029\nB = -a693d0b54e797ede42dc453026ec7872bdad9a513844ebaec02f02fdd9b3efd30e042d54c5c4b71f29dc2f7bba51fd2920bd\n\nQuotient = -40000000000\nRemainder = -2a\nA = -3bd0119619fbb5b260c44050d61e6b1925a49713d754ceb06bafb1d730a93f199df654b153c40e75096ebbaf5a6ce3c801820000000002a\nB = ef40465867eed6c9831101435879ac6496925c4f5d533ac1aebec75cc2a4fc6677d952c54f1039d425baeebd69b38f200608\n\nQuotient = 80000000000\nRemainder = -2b\nA = -61a283fe41d965ee770704bb453f689cb82a81089422d6d904a91776a06d32857220286e6ef6327807b724062dda143b46890000000002b\nB = -c34507fc83b2cbdcee0e09768a7ed139705502112845adb209522eed40da650ae44050dcddec64f00f6e480c5bb428768d12\n\nQuotient = 100000000000\nRemainder = 2c\nA = 87bd03a64d9c56fe340137065ba36bd07b556119546dd1fc3ae087ead32bc79ca7efb5c7230ea7bfb00ad419096d9279fbe10000000002c\nB = 87bd03a64d9c56fe340137065ba36bd07b556119546dd1fc3ae087ead32bc79ca7efb5c7230ea7bfb00ad419096d9279fbe1\n\nQuotient = -200000000000\nRemainder = 2d\nA = 1eb7cfb197d19f56ad994eca52d1af6466fd09da07d68d63067602046b2d42d3063ef5eda6b58afd69fd92b0b727a0ecde1420000000002d\nB = -f5be7d8cbe8cfab56cca7652968d7b2337e84ed03eb46b1833b01023596a169831f7af6d35ac57eb4fec9585b93d0766f0a1\n\nQuotient = -400000000000\nRemainder = -2e\nA = -3ab858b3329e5bd0469118be52a867b2febbe2894d962cedeb3a5be1738db1cea106cd0710c9f6937348c2c63b109ae623d500000000002e\nB = eae162ccca796f411a4462f94aa19ecbfaef8a253658b3b7ace96f85ce36c73a841b341c4327da4dcd230b18ec426b988f54\n\nQuotient = 800000000000\nRemainder = -2f\nA = -6137bae6cf7573afcbb6fd5c066ba37648cba8db0ecafe9dbc66959b19deabf42f3083719a2268b7602bafa2140a1ee8ce7d80000000002f\nB = -c26f75cd9eeae75f976dfab80cd746ec919751b61d95fd3b78cd2b3633bd57e85e6106e33444d16ec0575f4428143dd19cfb\n\nQuotient = 1000000000000\nRemainder = 30\nA = d00fec043edadc093673e5f5abef0c6bacdf1f3faa49a831a645bf80db7539d657f69403b122a5c6f879eb8e63be54d35ed7000000000030\nB = d00fec043edadc093673e5f5abef0c6bacdf1f3faa49a831a645bf80db7539d657f69403b122a5c6f879eb8e63be54d35ed7\n\nQuotient = -2000000000000\nRemainder = 31\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -940693131e2ba7b2af531803794983337dd526f0d84d08d58723edf002a388d55c8502d88c2a2a6e78233a2a1b1c8d339a13\n\nQuotient = -611b743a0e2acb1043bb33de50a59eaa0405b37bf6b622075dd69291fe5b53305dbfcc377d1f3082319c153d0c1ffb3b3346\nRemainder = -16e346b6a4297\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 30c77f3380ccf\n\nQuotient = b9e34073d5e6e5b9e5d2d7250150f8ad86870faeb88d5aed5029fb25c176de216e2388e0f5d33f7c3b56102873eb40b06f2\nRemainder = -16ebc86eb88339\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a67", + "3426000000000031\nB = -197b6f6ad5b75c\n\nQuotient = 141bc8752e846cd63743e6fce4a22efc3eb5f0ce46ba81b8f578c94c516288ec3610fc9923f45d4af2b94c0b0a20b48ed0a\nRemainder = 9bab19f12d81c3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = eb90162ecae18b\n\nQuotient = -381bd85c951e1dd775b0d7fab344aadf06b1b592c643b5852fa44aa55159eedf3b3e47fe0d9f399ad92da85ab2bfd18240\nRemainder = 1e4f817a2f52b71\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -546c109fa8a9d7b\n\nQuotient = -5e385a83b56830626cf8306acc232f955178080e86384bbcf92eec3a8961360223c4cfc1d8d118022972e61866cbfc46b\nRemainder = -292e149300fdd1ad\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3246242094394c8c\n\nQuotient = 9af0246f4b49316df43f61ae3795a764fe9b1d071ce227982ebda7988a7a7a98129c94a76635c6913cb15e4f75ea1608\nRemainder = -dd3b3e32ddc79cb9\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1e928618913898b2f\n\nQuotient = 1fe40099811c648aa4e84e4fbb8cbc19706774a11391fc03a9667d8dc72dd0b26c4a46d0bae56ba90fe4bfac1517d241\nRemainder = 16e021603d30dde2\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 948887c1634f4b08f\n\nQuotient = -3f4fa4c179dab02ad461bbea8f890292c934496db560f72878323a4463d77ae261363f4dc8f53eab145fcc3815d3253\nRemainder = 407ccb4f0b814dc5c5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4ad17434071e1ce664\n\nQuotient = -4d17d19f7f6861189a520776339a1e425876808111c303e391118714370111151ef4ad2e6e84250f59b0fe09ab3293\nRemainder = -36f745b0f421d16db7\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3d71635bcc25183cdde\n\nQuotient = b976d544af44e711351c6618106d3a002c42ebbe22fe939a2457d24e8dcc35c95dde5c7c77af6b4545344a198be82\nRemainder = -107334ab98e5099fec5f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -198a54e35fa0cfa328a9\n\nQuotient = 1307bb8e89aaff7466bc238d32672fbbde7be19d15423bcfa14f9a23fe85af9739b72807fd4bc420ad0b0fac37a42\nRemainder = 170ebe9b83d4c43b79ab\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = f8e923a8bbc0242eafe3\n\nQuotient = -3925a167c1c4d2fae265f277302b989466e309a7211e0b7173031cbbb91ab7fac8dfe43c9d832764e222e9d8581d\nRemainder = 4d404e93edb435dbd60af\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -52e36cee22274556059ea\n\nQuotient = -4d5a6ef346a872142b999ff9a5429198b3c2a97e968f55aa2c01583efe30e9687c57e2bca2372db4d3d443052b6\nRemainder = -3a2ea5f9d204dc31f21833\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3d3c79a115d9071b573d2d\n\nQuotient = a49dee54430f1737a04543d5f549efafab25f0f28f5e304f1bbca191f99521c2c4be1b9927bde19e1ec2060bb2\nRemainder = -17d02758f8fcadca911a95f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1cc65a75211f2826c9d0811\n\nQuotient = 1808ab7c0ccac2ff8f7cb61248bf4624fb60352a356fdd1408904f8c6fb0cc52b7642ec59183bcaf5dd89ca0ac\nRemainder = 5c95323f3b8861261dc31ed\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = c516e6e3fa6e3dc52cf5933\n\nQuotient = -437e04d7076794850aada0cb4ca7a1055df103e74e00766be6a2fdb2631bf294cdbf2695d0a2f8f9eb5587aa5\nRemainder = 1fc63797594c56160536faa9\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -462ee529b488d1db2b6c60e8\n\nQuotient = -5dde5497accc4575a412e7232ce75bdf7905936e09e382d5c9f133faf82a05ad9dcc94ad858aed34cc14c714\nRemainder = -15e79293d5e055f906381a899\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 32765b0a34c88864d39bedaae\n\nQuotient = 11ac52a9287472e1d3b8577b3d50c95076e190714796761322b3ce869d96b44387e190e824849ee345d0a22b\nRemainder = -a158ccc7c055d64e7df3fbcf0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -10c061a37f6cbd11bf0c327643\n\nQuotient = 1ff5cda1551867577c5ca72c86516a82fb8fc5f59ce967b73c6bcc1b85168389872c9a747ddf044d6dba174\nRemainder = 21e766a0020ba429b330a325d5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 9435cd2dc2a92c950bb9e69b83\n\nQuotient = -2719c892fa3f4dbc9951b2095056a16159adaf32dff902e20a800a0cc2e858ccae408f2161aae25d3e1f6d\nRemainder = cafbe9caa1f83fd0dd3d5a6881\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7924e4dcf8f96da61f54bf83870\n\nQuotient = -5080dc99dba295f4a2d9a474c2ddfa3b232a82fe629fe62177514988983eff8195b37d3fee3afa343b497\nRemainder = -94ae72f78982ac1ff83f300cfe8\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3ad70d4b6b9b5f5b2eb65da67e1f\n\nQuotient = e475eebcfc53d49ffad2e0c2a4ba48fe7ce02c42ff107e01ab3fe5b26eee45c83c4f58c181d77c259155\nRemainder = -c83ac7582a02b47ee734e0f24dc5\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -14bbcff5423a260b21895327b18bc\n\nQuotient = 201308a421b85291d23465d648ad2a8d6f3393efc16fb675a42ea7bbca635ddd8c2449b1b34e5db30a03\nRemainder = 8e07efb8ae4c9df39533042362081\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 93aebb72a81ba68e8881fd1a56a90\n\nQuotient = -2584cc534f88f091fe471c652ac66a695906a7cde1fc1cde9be3ee09026b690c1a899378ff31f6acb90\nRemainder = 794801d9d5770a60e312b99d6b9f91\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7e408caf387a0ce9bbf4309c80755a\n\nQuotient = -63f7bfc0fe5a5421bc0a19fa6c87713a72eeb2a33e5eadee8c2f32c20d14f403ab8bdc424b9e8e0c68\nRemainder = -24227c242afedee2473c1a66a5cc29\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2f622c665af7f8126eabfd90df8e9c5\n\nQuotient = e557e6d2180aeeee5d2cef453fbdf38e84cc148f4608ade8836045498be2d318520ffadcea6319432\nRemainder = -dd290149e0e159f9ba6bb9f5a4b003d\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -14a7623d1d9dfc177e913d3119d0d30a\n\nQuotient = 1651d852316d472b41ba0460566e43fabb9257861859ad0fb6ea5a6433a4164299e078f4d50c58afb\nRemainder = fb60aff5fdd2a2b794b0d973ac4d92a\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d439da27b5e70342aa5cb365ece15665\n\nQuotient = -3ae357761a8ff43d3b1bc53eb336260342a39d22f8fac44eeeac96c2f6de32580dd6a688faa9c515\nRemainder = 4fa6f7ee4faf2f6be99c5ce4b65cd642f\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -50700f9c0da59482165a47a3eda2bf07a\n\nQuotient = -543b4390e4e254226683aa0b83b2ca176ec27a373969fb88f766ac72adc9125ff83b2652e46afd3\nRemainder = -12ff398d9a7d9e97a7f63a0bb293c8fb0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 383c5a4f1767e83fc382ad4f1c7c2b7ddb\n\nQuotient = ecb72c14c59d49287fb6b2cacdf04619ee617d5f3f0f1b2890fd4e79746a4fbd848613cf5eb437\nRemainder = -1035512a2717a89062d48f1bfd213333ed0\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1402b751a1e5f3fc46e22b43240d6ce9b27\n\nQuotient = 1e800ddc5d5126f322298383f32fd593623eb88a91b2d68c5d9f56e20c16ffe2cefabe87357", + "0ab\nRemainder = 72935d534bed5ba557b91ea023601f50b1d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 9b4df766c608ff3efe5ea1f65cc850fa73c\n\nQuotient = -2c2dc2378abceb983904cdf6728f361d279b4c821710ae785724a7251c43fe4f705f023afa7e2\nRemainder = 249f6433af4e8e224eb570fd438197af62f3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -6b382f812816c77d65c94c0c660b31a69b8f\n\nQuotient = -5f3ced1e42fbd3c6b2c6f1e16953e0c1bb6efb4e49566f974a968f69a1a66a3d7558f5a802a8\nRemainder = -317a7fb1af65982fe4641fbb1e5837e6ea3e1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 31bc97372d17038fd842b72eaba2abb26df62\n\nQuotient = af3fef8111c449b9e0858e7e53e1d00b764232f7a077d75043249c387ece30af351c8a40335\nRemainder = -a1493bcbf57a8480461d62796aa8f8541ece4\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1b076b2f7b78b4a0f0e24ba3a05d6c697efab9\n\nQuotient = 196734cefb08f09cb32ffefc07da8d9545d3451d5a08736757184bad94c73be71311cf1e01c\nRemainder = 273e33521f4d74840a96b3fffe169f79d32855\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = ba7746f4400f812919a3dc86b00642e1487691\n\nQuotient = -3c5989cf33145057a9c8e904435d12939db519cc6b9ca1c0a11934399cb139a73613950f2f\nRemainder = 456ebf56c636d54e37709b9e799e83b7a08cb93\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4e7d4f389423f42e980eda55b4a6a45f6f4bdc2\n\nQuotient = -8432cf3338bce1d12586f83025aea50cff3864af3eb2103a36bbb0aba10b0ba4831641633\nRemainder = -4f62c678137df301c4bef216e6aa910104e76ff\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 23d4c57b5a8162aae8d937be12efbcfd7b96ec06\n\nQuotient = 9f94c4399eef16dfc65a1e015e0786c86470299865932c4d564b71c9b1551a9c0308af38\nRemainder = -168b74a6073b4a5b54fa14aacb5c3bb7897ed0fe1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1daecf01ec633610373b79e04c22cd7499012bc66\n\nQuotient = 1d5b838dce6c0324f157ad125adefde6e1045dce9ff97cf8d1d39b79bce02128e3433ffe\nRemainder = 3aa816216d55fc3c910a030fd10fbda1e12f2ac2d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = a1598a12a84e9cba42ea0e200e88d4599c9f615fe\n\nQuotient = -3edb182b53890ca8762f3039d2d71a8a27c36cc884d0879e0635e6326af0182bc47cad7\nRemainder = 4610b2b1305220bc0de584dd3f87d90109012a8077\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4b5c2f1ba3a82047c9de61d47cbf1bec86b6ef90d6\n\nQuotient = -7571ed4c509630886483f6ca0923859e644063acb38cfb338bf3a681fe449501262516\nRemainder = -21c579846594fc3e5efc53ab01576a7b32d69faf41f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 28550e1f7c6492f4cb682c37b105f92b049c13fc03b\n\nQuotient = 9ed8fb31327a110ef4377258681c5287de8ef9dbe62aa4fe84a7f2a94bb69607cbdb2\nRemainder = -1b7bb759dd0ebc346cbe216e56be8063f063490c17c5\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1dd1e61caed1efc07d21ce05d889de1ad65808cae026\n\nQuotient = 1aa716227d1ca6af68286062b2d6dafd7ade16abbd5d6fa4ada0365832fe18f73bf35\nRemainder = 32e714b0c4ecefb38735cb88cd5e07c21c81be858cae\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b1b959a7b3262d7f4dff488315903aeaffd982b726d7\n\nQuotient = -2a9979a530046939e0b43a25edfbea6775784eb5cf346a9fc3a2d22e1aad473cdada\nRemainder = 4edeb91a2472e80068b1883cf2cc45d68ff9bbed1756b\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -6f31bbe097587a68fdf01d0bf93830bd03a23920ccc0f\n\nQuotient = -566ff76814e1c7d31ad53bfb9f3c0607ef1f7d1cf9bdee6e1cfb78b3ad7018f8bbd\nRemainder = -1eac095d6d84021c33aa9b219d191bd0637f20b5920eed\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 36ccf5bdece624b4f54c729a8cde13325d8dd764f44894\n\nQuotient = aee4f377611179d8b6315811dd94639aaaee63e99bddcfa8eee297ce1dc04daf8e\nRemainder = -59cb3ba7efa1637c46b21795872e8deaff90f13402cfaf\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1b157ad838684b45065aa77ca3238a4d8c5427f719cdfb7\n\nQuotient = 1c72d32cb83cf4a9043d3bb5002f61b03e29c34e44a9fc5cc4d613726f5e618546\nRemainder = 7312d11fb5828c7f1a0060a5152a7644fc1e6a59de28d03\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = a681444c4d47d829f7b629b561ffaa0c3be1232346c907d\n\nQuotient = -2702afc4095a0396215e3ca36e2a59725f743b30de0dd8d4ec4d943fef6c37162\nRemainder = 223dd3080ede3a64744b14df8742cedd71388b0df99073bd\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -796c9ea38ccf516a2054a1e584c18b64b996c9679960585a\n\nQuotient = -805585c6a7badc933bced6f8373ffdfe9796e963d3fc90e85b1a22c38f842062\nRemainder = -a6ebff3f651644915d5c466cc2915d104f0f85a44e08fd6f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 24e8fb7a6a3057ddcafff92916c46f7e4038b98c3104ae831\n\nQuotient = 10383ff8feeb180d4fde925b534be97ec3d5f1f1dab5d8cd9ab5d8ea646cfcdf\nRemainder = -a7efdd0401c74a69cf74442fe3da907acf92e8edc51668828\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1240a71ed8d81e86fd9b16e1d64f438b35d6f8eff672494017\n\nQuotient = 195d95a520fd22317492117dc756ff97806c48c1aac67a41ae56fe503a60cec\nRemainder = 8b8692bee56f8a1ada9ffd8b3583eae33a0df9b73a7d8585f1\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = babe02063b61cb90634ac0493174073d2419e00728d46ad2b0\n\nQuotient = -37791adae674b866e4791c107a697363847dee4a58a37806391426ea48b8c9\nRemainder = 33986fc6a5f5c4f4e31458fc7de55e08a4e9320509d90299b93\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -5563bb852e7338c65aa21c516eecf47f498e5788c608ed46cae\n\nQuotient = -68a30494eceff55e4f54a556dd9b30025ccfa22c0952fd746adfd13d31d00\nRemainder = -1b511d0ab81d528d00a1058850bef48df2e9ae9357e779bb9231\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2d44e919fd27bb3fd2093062d11830c30fa77febafe0a2082cc6\n\nQuotient = bd30999592dbeabb8871b76aa04cc1c6c3794a83f0178c2ad505d8189485\nRemainder = -b0dbce286df5faccf0bdb40ca60f508d436f9410c5e49c3f1360\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1909930e2d16fc877c15895a3ec8b2125858bfa1c5a1b8776bedd\n\nQuotient = 2171694ef4a9d57b83b09357a511d4e11cecbab5e9387928b480d686a0e9\nRemainder = 29abc8898d5ef85f87323c2a6fa36ab6e1bdbcc0ca742b1a2347e\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 8da37bc9c7c9bdc62f49cadcd40e156e776b7f4c8f7ad543f463b\n\nQuotient = -267d470f32911150d9944e684c14e1834734b15475bee968748dd5f6502\nRemainder = 53a2ffef61709bd7143c4c876e021f20a99ba481f2b11abcd45da3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7b117ddccee97816c2ca2f1a612cc0d94ac67f5a79ed41744c8fc7\n\nQuotient = -5a21a3bdd3a3d4f1361a978706ba1cec409c296a5b3c369e91fc8317bb\nRemainder = -2cdc818f1e445fb3772d2a56833aefb2f5565a5fca80662e6fc1845\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031", + "\nB = 348dfba3c793f0018d7d3a70c4060c3148b4a3163ba60af9d6f8b04\n\nQuotient = b301b4050fdf4ede8f9c746b26d968110e1eb119ca42cd9c9bd8d4fab\nRemainder = -17993daf81711fe59204ec82e363d2b91971129af9206ff9506d3cb1\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1a76328184b9bea8770c91cfccf8ab98e75b2224d666af58022aca80\n\nQuotient = 19c401336dd43c221a61264f8b91791d250e6c99c61850efe6d1e3532\nRemainder = 6c9e547a77c98eaba1b021777dbd98ea88f7fd37c95a2b182f2b9067\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b7d7b1f95f4fe2f267af88b81af88fbdf603e54ab6de73ccd000c32d\n\nQuotient = -38a77853de88a8db14612884b515e3cd7c673175779d4ab71ba58f83\nRemainder = 51851549cfa00dbfae388cc3b46fd4824268e00e12fba288acceab339\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -539c0171f48e4160e5c308ee9e74f35d8b6d032e946dbcf748b1335a8\n\nQuotient = -79a7eab82e5b65f4f6734e8803fa7c30852ea3ae56e801c5dd11778\nRemainder = -f89592eedcbcc68d5df80663b3cdc638d9d779707d4ae5a552d97d009\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 26efac15401a945ffd37066bc5af23191292765164a0f1e4fd537fd64b\n\nQuotient = d33afb58753a21581c5b2351a74f3d220599ed56ebeacf1d43eeb2\nRemainder = -f699437f44af44b3ddc080f5b74f753d35f70baf3866040ba3c64b30f\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -166cc6a3c60facfa0d8d318f26c6514c7eb9113f6b625c1de804ad379f9\n\nQuotient = 19e55bdaaa5a375c36e6869700f8677db563e5cf985be2a8d1b012\nRemainder = 7bccc3a653f29f3f45b52b8de2449c868c64d976666c01bff2dca03a8d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = b6eae7a82b5dd1554795573cbf558d7cfed813eec270c326bf290adccc2\n\nQuotient = -297530094c3e4270ab5cf67e60fa5af6a32eb41b18b050fa6d46d\nRemainder = 62d8b502e172da7bce53fbb7c1ae376b6c21b3a3a47523aa0023406e353d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7241ae5f1aaee9340d437ad2dab94b70dd29fc6fff7fe31b100aa5001644\n\nQuotient = -640f3c38230962c6d6fca459afe0e46137525e8d62dd9b84da73\nRemainder = -16fcadd5155910764ecf0b4bd0afc3707e2ce49cedcbd5414f1c7d860e95c\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2f570d2da7a4e62097eb494ca43f7bde33e36525308dc864ffbaeb5d48f97\n\nQuotient = b3895ebba13c8f383ac0482be02e1f5518511420cb4513426bb\nRemainder = -21bc847fdfd48c7a4c36c778681ea20481081cbb7af6b281c8b8ebf2b2c3b\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1a6233954b3480af5f911a6bb8ad33967d5e0446c3e56f521e892c986b6b82\n\nQuotient = 243f3fbefbf842c79c5e96162fc42fe4f177a59d27681c54b3a\nRemainder = bbfaf15a90e744dc4a1caceda3cb339e5491e4507a1118613c5e9739f976b\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 82ae783b8a13e2e65d52dd3a6d6b057163347872f4d72245ff364dbf2421ff\n\nQuotient = -30f7cef2948c9ebed8fa3c5ea9a9bfa96ee4e9729c9b18e9d3\nRemainder = 1feb3fd887629cca60c664e385dddf538d9bf7fff2d34ca9e0e7614946d807f\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -60bba60d69093c0134fcb90aefdb9c190e7bf037ecc13dab3cc7915d7893046\n\nQuotient = -6b6f0183c1f598a68683ba7435c05d700d74681fe472669a1\nRemainder = -1f4d58f81a8c18523918d31791a00ea9aafbbb87792d90a5392273ec4e405da2\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2c17372a5128d7c403a3b94838072ecf9aff88d164764b12bfbf6261df957e2f\n\nQuotient = c4347fe42b2a7d9d5a650b72724369c5c1f59262a7be3fc2\nRemainder = -1103ec9c4a15373949cae4e34b7b42e242da41edbf5ad8362ce5e5426d3154a1b\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1824671758069b7660bad819f06c86fc76a9344ea38412058380363e5c5b4086b\n\nQuotient = 15e8c8d6847dfe974cefeef5fee93da9e58b74d640c6c413\nRemainder = 61dac240f2b39832903d5ecad9cfda5162bf8ebb0610545f259b75c3dc6ab8771\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d83386fb9682576cc70cf84520c53169e391b414f5421cddca6e257bd77753c40\n\nQuotient = -3572711bf994e6ad48535cc4d65ac323ef1ccff530b4337\nRemainder = b5899d4cb879e37022c539962959339d055900cca16153da09b54c658753cf50e\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -58a05faf5c61f85ac5a090b6bb045c851ea17332d9bfad4309ce2b7a79ad3cc575\n\nQuotient = -6931ebfc6e34305e5d7cba5284829d088d1ec0abdde508\nRemainder = -1b09eafde481064bab3a5c7fd895edceca40b1e62a9cf953eae1061dfbe00936391\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2d0769f392ca9ec629ef1bfbdf08cd8cc9219330ffe3c05343df792dd94b1147714\n\nQuotient = 9a4800f0cb2bfbe8d234410deb510103b7da30cbac7d9\nRemainder = -971e4a529e439a1b96b942001631027ff2fbe40b8939e224adb7f2ed30faff64d1c\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1eb3d7971125a036c3a67d9f5ce580a4ef4c469a492be53a55bafd2eafd4032b5b9d\n\nQuotient = 23116704b7a1a86cfa2ee5707ee46268634db5d50dc0f\nRemainder = 467c6b64c8121e4f250492191ea36a27119a0a6d19af519bf7ccdc2436c885c99d85\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 87134e98f73470e23a96c6a9139af3d4d21574de8aa9ea1d720df8940bcbda343694\n\nQuotient = -3b7f72ecf4f55c02366c52f38a827f5773b7cdebb9ba\nRemainder = 194b334b2046a66be3ddd7c6df01c88967fcb11e97b8206d000bcf6043c6e9ccb13f5\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -4f9d0341cadfb1f0bc38184d93503faa196fb8170f8ba2b5d3b512c09d39b7f79a5b6\n\nQuotient = -6db1d69019dd4cb26fd65d5b88a31bb6413b30278a1\nRemainder = -2042a060391e181882dc0c8d91c3b03c1ea35e2eff01babb3ae876ba1e57a505d44856\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2b2e8f445c0c3aaef0285945e4ca37a700310e003086f34d02c891b94b117f3d3032fb\n\nQuotient = c0e5b9a5853bb21b5e2e37f469764579d5cb2bf984\nRemainder = -154669d4bce7914cdc8d79f2b8d1faa43e8cc3b20fb0767e1c9a47c9e1daed4b665cfdd\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -188e619dbb719381e701363de874fe168529c10f30d3ff184e4356991fdec1649f72235\n\nQuotient = 180054f8c36833d44cab9dd61e6d89d28605c564af\nRemainder = 59192ec5c6fbd9773b8b7dd7d8ab1800dfecc8eb01c29997d15ad75b79575d9e26e1fc9\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = c55b5eb165c63ac2794bfac21980ebacadb93f1e059309fd2b855621572e8d9b3f29018\n\nQuotient = -31412e97045c19ec38951b0e3884c66d1d7479437\nRemainder = 56f1425227bfc6eb1ecda7bfae0e5cb59e92a2cc5306b28465c8739e40893dc5c1e94cbc\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -602b8c25ded1ab3877f58cb048c733649c7dcadf87b2652e35c4e5544d2306107ebff7b3\n\nQuotient = -8da1489ccf7203ecead94c67a5750884122b6e75\nRemainder = -15162026586a1e55dda72785f31c9e6140d166a1fd34c87a7d8c78f8d8f87bbdcf8f75b1e\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 2171ee4a6f7f67d5a33d0a08c367184d70ffe39da28562655e75f6b66c866b1c2ac93e467\n\nQuotient = e635f8bdbf80e99723aa5718d3fade4e573be2c\nRemainder = -ffbd73bfe05f95bc2b135f12682288c620215eac3d6d56503d93a90e06f236e597d1df975\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB =", + " -149375d478a096e724b84faf795c589ef0d772c4623f5be38da99006cd833dc5b28363faed\n\nQuotient = 20f76f5c6d0c8284764a10f6936c22bfba5f851\nRemainder = 82e3fb3f7252dd87b5370d26d9e8b9e98c7d333701f0ce8a05c337054c7aeb343d04d7e342\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 8faf8c0a3ef94ab1069394998e5412a7d84f44aff97edf63abc46d96f897172c38faa0b13f\n\nQuotient = -382586dfe93872abbe3a504fc62a8973913f96\nRemainder = 4d407323ef56093eea2f3993334215950f4e1a85ba18cdcd77d819d92b8b292c3ec8edea425\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -545d81ed25602b158bc79aadf98a8f655fc399fb8652ae94333bf54c8c9ffaf8c6b3f2a9d52\n\nQuotient = -7d179efc493eaceaf46572a1f3a62bdfc4a38\nRemainder = -3de3d817a9cf7d529b5229a503e8ebbbd2c53215ac3c584c010947f780198dee16ffbf47791\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 25dddb00f65d6a1ba8caf7815a8063c5da656d775eae9e0108c68ce11dc925183810888dd04c\n\nQuotient = a9f7e5f235bae0e3e29393ac5c99d510b009\nRemainder = -150478b4a0df3eb20dcd1be8da283a00636c021c5c6337e7732aae9c4b49853b95f6d2475ea7\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1bde6cae7f5ced9006c0b1a61fb50982a433e4e2050aa486298f456556d8e909e96933e2ba3ba\n\nQuotient = 16de125df5936181981b4c2d0051a8b4d211\nRemainder = 29ac7c8a11f9beb9ad649257994216146b663bf4f237c561bf315d95778fcdb1010283475ebf1\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = cf24735a60ff5906410be5c4d98e3c9247919b57e404aeabc7eaefbf07bd64762bc61b96c9040\n\nQuotient = -268a52cd10ab4814268f66d9f44f71a98eb\nRemainder = 20293699f12fbfef2e391963866fc082a7884cd13b1c9bd8d5d203558feed2b889720be936451a\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -7ae7d548212830013b7d653072c33f0dd54a6ebd8792bf75809d29a8c798dbc67c3edd99a69b85\n\nQuotient = -8f051067ccb82b6a3dffedd0ff2ee97c46\nRemainder = -100dac0d3bf5aacc5fade281c071eb2399560a65349566567ce1c0c34e43f175a575ed1eeeb3b07\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 211ebb5dc59a051fdfa3b18ac491971e863f2086cdc099672c1215af4ec877e29950efa4f487be7\n\nQuotient = 9b7ee4c499386f922432fcb1a453ee2ec\nRemainder = -f410122a74386d724cdd45b2e548645ac5ee4a44cbfecb82aad34ae470526674da44ebbf557bb75\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -1e76750814dec1ecbb1af0fa2281ab3185e94e47fc16a77fed312f23f261ad7709ad7c9f85862c1d\n\nQuotient = 23efb26228d7bcf281cd45f54572e2b3a\nRemainder = 65bf2ef1c2f8e94d98060aa305f85e6cb869c74eabad99877010d30654aa2e578ef6aa3c5f1122e3\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 83cfc25e90a61cf8686e3d5857b2f958674d478622c54cf8427275ca5e9312ed24e44ed4a1b5e413\n\nQuotient = -2cfcae0e922f2d884bfa0a3346dc9812\nRemainder = 14de2725b11a9c6784d9608c52770d29b9fbf824ecd4890bf28f3ec0dc6c52e4df9be540332b8882d\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -694b057ff381badb37c7c15c81e74cbd6774e8d61c9e7d450811c36262ea834fc1287fa59708ee072\n\nQuotient = -4c0238ff3c18d4d58e543f020002802\nRemainder = -2ddef796c50817e82ea6f64a02a8c6b30ab40070ff5401c2d39ca14b9c4d99de33834bfe566a0c2efb\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3e51c9ab14f522b55e8f9d3ba995c0846a864dfa2d568ea211b0cac1463ce6a1da72d0a15746fdcc9b\n\nQuotient = d41f9102a7785ce64f76b7d7b870b0\nRemainder = -106eaafdd518c658bd371164ee43ccd915a01b513fc7d220900039ff840ba36450e16ce9987e08e7141\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -16549c5d57b531528dd4d781f03cf275b66cb94eba038b782b739c3ab30b8631c8706abac06004a942d\n\nQuotient = 1616b432b3277e774aad92b0cf544c\nRemainder = 2c89373720b834d718ff3df985ae47c3a7cde0e0309f682f5fd48dc97a1ff3d69fa0dcaa1245e956445\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = d6721300e877a8145d05f4f3d8085697c2ca5f34a5357fed0bdb7169f83b6f8d855232eeea594846b79\n\nQuotient = -320fd6a7375a42a3961362ae196d1\nRemainder = 5336711bf81237ea3449f4e9f4e6358dc250f8ebd86082cab92a8079f2c8f835bc783082efb0ed7e3f66\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -5e9e8e1d446fdd314d487cac1226088696e33161d923acb67d3c75e87e428bdbc193e02f53200610fcdb\n\nQuotient = -4bd06daed3f30345d269f51e4381\nRemainder = -1f3513bdefa40662f0f50a04b418a833aa2f85522dc6c399298b1b147662ef2164ddbfb7247ba9511b8ec\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = 3e7ab7ffe5f63a6c1e109b95b83af470ff820cdedbb3c90c398ec42e44a45e1ca894870a7fa51f17ad5c5\n\nQuotient = d6fd01a0c5b55fbe36e58bbe77b\nRemainder = -c51af3e8b430870388357cb366ea888bd7b4ccde09ad3a1d2ee1426af060245c6d6b5980ae87fb66c4642\nA = -1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -16086df3dd5e665f2631a294563c68931faa19ee67d6a2153d262940a648ae71bb3c1745daca5ea977331d\n\nQuotient = 18bd9a8f5678d28cefd955cf99d\nRemainder = e193f2fece67b7abe16373c3f84f18dfedcf654d951bf47585fccfaf67ee04f5037354d057c9f5eaa8eef\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = bf758acacd11f3f3e6665cd740517c9ab2384266f3c7ff9afd0888cdad2f6c9401c24d6c11fc3949aabbaa\n\nQuotient = -371239db55c79521206c9e60c0\nRemainder = 93773085af7582dd298b09d7098835787978d820289ea6850f27d0d77eecce8614785e32b228f46ca4b371\nA = 1280d26263c574f655ea63006f2930666fbaa4de1b09a11ab0e47dbe0054711aab90a05b1185454dcf046745436391a673426000000000031\nB = -56033fd85be464301f10177b58d895fbb6df6154da5c2a2a7cfc3a24d83a96f5295fb17a08148a4e51dde91\n\nQuotient = 696d8e378d12221e2d970c53bf63a20ef381db8566701972c22fe067cdba99c57b68706a5c6e52f21bb3de861e49ed2141b3036f07d1fd0ee\nRemainder = 9f0e50ca76031b\nA = b2668f5fbcf4170820ed3fc9b12a61862acf8e3cb17175482efe23c5cfd3556e77634d407b6d1f98a73437a8d6066319a7a860afcab2338a1b1313037e30f4d9\nB = 1b1313037e30f4d9\n\nQuotient = babe271ea266bc7bc16d193097903037819f82366c7e9ff8f2cb14157b40433c6ee327038d5dcc44140b070d823befaefbee5e13419f6f17\nRemainder = 93d7c547a9ba0a4a\nA = 74b1a591f449377836f378e05d2902b29964df59c6926e5a9182cc09ce3111783cb7021a185340b4880d56635de268d6f3855c4d9997373b9ff8df899ee3b3f1\nB = 9ff8df899ee3b3f1\n\nQuotient = 890139fef28aa3b77814e1122b9c7f26e746ee3c507e6082b508fcbe380de83b06a01f735239c6847c30eae44749fc8c5e3bd97eb40ba297\nRemainder = 6c97aace900389d0\nA = 7e89adea82b4cb6feb41297b6dc8d948e72c3d5554a987900e7fae48cfb38fb5282b13d9a1f5793cf7cbf1ef551865041c3ffe0e287714a6ec7123556af55a48\nB = ec7123556af55a48\n\nQuotient = 1fdeead441e2d7a6ce3cce2389b2a22248ddca7970ae3f7e7d8453052fd08534ff7c46f6a4537fb6f28df6c5fc8a7d384336e679b74205315\nRemainder = 2903c7cc2651bfa8\nA = 9ca66de3d83f0a747fe986464522bde5e42aeac20e8ace1ea13fa6bc9514c58517479a4281d4128c6d775489b85dfd114ad184613f308f6c4ea484a22ab0ad1e\nB = 4ea484a22ab0ad1e\n\nQuotient = 12f16c8f9f898a08853982e2ac5a906d784c5ab8d74007ba3ab311e861d7c1ac115efe694cab7583f75a4a59ceff2887dab53b2f1022aa452\nRemainder = 4bdaf1f352e87aa5\nA = 6e6a97b358b591b78db43772378dc084a11836ddc9dd4607f263ce620714e8fdf6bf67387c163b6f2999f84270802b4bd5c0f0377e949fbd5d42fe145e66ffeb\nB = 5d42fe145e66ffeb\n\nQuotient = 14e0c06c8cff1f9f5dd8afb6fa6c340f0953a18ba7d2b26b22d8e7f946ef20fd5ac277ceb59cbd4ce3e8213803c3b5b0452ed449e22bf2c29\nRemainder = 55422f1caf4a9a00\nA = bc9c054ff568af73e301e0751bc1ee055e82826cdc53449f2d9f45feda2ba227bedd6df9b74fb58a85917d60b087bef04a156a571716e9bc908ae83784ee35c0\nB = 908ae83784ee35c0\n\nQuotient = a457ea94da3237c0dd15ee30e9c13e7b4ca1dc90fcd67951b8737872", + "06babaed837a3eb17e298d74cae92d1059636f9aefe11aef9ffa31053\nRemainder = 124768541b600598\nA = ea6dc82b1906c277526ed867fe8b0fbe32feecfb935dbab860aef59a7d72799fd4e952e70b4c9304c7b2a06af8badcd6cfa12d0b6c9db38d16d2c4a24099ca14\nB = 16d2c4a24099ca14\n\nQuotient = da0a37eece8972a0e2e8817c54e67c4d9f92373340488539d5051984bce0ae3300ef6ca9d0902daa4d485dec3b4db6c8b1ffd2c5d08b18ae\nRemainder = 1ba15c46023500b9\nA = 36ca8763e20e6ebf07a55cdfdd83892bef0bab68ac092093bfdac1a49c1da015541196a24249bb2262e70f7ed53e0fbae61f02ebac4b61f740548136ce50f243\nB = 40548136ce50f243\n\nQuotient = 3d8c433daedfbf681b528f88d610204d33bbe74d0b13978c34a617ae94177e07a757519b5a8f1a93a73d0751c7b5b72b4bdf475a9708fecac\nRemainder = 4cdfd72349c6110\nA = e0dd7e73b2a64dc017da65992176e2535c43b6fc14f2f7b0a7d894d768bbc77507eac0112b2dc3ca83d70989a1b949ccf374be6a012d80a23a74bba39671fcd0\nB = 3a74bba39671fcd0\n\nQuotient = 39d084b444e39c32f2883e9968301151802da15141f65893f37b8b834eb01c074aa1e1a978c5c99732c87ae106bf8db09e1728c8bf2aae88\nRemainder = 2950443357cd7477\nA = 16df31dc290559c3b6a3d192cf15d825cfe79f8dbd5c9848eac7fa90eea5d87f8b430cccf9baab3e8e4dc33467a4234d8551ff25e33af175654686ff1368e96f\nB = 654686ff1368e96f\n\nQuotient = bbead8f70c8e61114f22d36e97861f16037efabe1347613e78c51d7f539065421a66c907faddaed13ad2a0f0b00f8fd594e917799cd937e5\nRemainder = 3013136f5f728b68\nA = ba5e688ab4f8ab5c25592bc4334b6dc2b7a06d491d0f919b716bf1cf109b62a30d9dd59dd4bdf870dd2687894edab303277a5f3e3a537cc8fde3ee3bb61767d6\nB = fde3ee3bb61767d6\n\nQuotient = 42aefe467ff2a5614efef1edce25a1acba9c476b3abbcd680140a3aecf8f51c1ebaab8912de217451bfaca2842c0bae717b8a030b6318c0\nRemainder = 1f130dd2ead0d35e\nA = 17bd50b5322c51ac883852ad2a4446c039dbc210ca3aa0313065fc88cce6819b324e93b036bd0c71be58586cd2b243d01a4a918c10ea0cc5b22f9d795df09de\nB = 5b22f9d795df09de\n\nQuotient = 13de73dcd72a3638fe2a907fd7f6574bbb228698fa60e4ecffb082911c5f09c74bb4f50564d3d4035d07eedea38b634a3e3acc26c8e9aeff8\nRemainder = acb8702f0113e0c4\nA = e0327b2e59236a3f91ccf960490cc69b2afc854de9299ad2edff9618f9fe24251886afc65f5c581a9bc86013f356d599e98b8b10f5236a51b48a6b29025983a4\nB = b48a6b29025983a4\n\nQuotient = 27d11481f00519b786eaee96220afd45bc51700f7366fb5e7da35bbc84891aac3d9d2b709dddae371a6b78439fef810c68eef586e1d68350d\nRemainder = 3d1890c5e1555d74\nA = f3504d5d96c9e27a1527725ced337f1cd0a183531642051e166507432c01e8d44c4e8918701c2a05eb8a9d7e26bf04993f9adeef2826ae4e61c602477f849121\nB = 61c602477f849121\n\nQuotient = 10bdeac209c67b023044186704735c7291423054bcddc24b731ad601b49372f4d5ce6e9d85002f8dddf0411efce943f81a5e42cee2d0c9fe5\nRemainder = a93a0c5bd51004e4\nA = fa29e37b0d0410d19fd180149b14f94ec2edccd347da65f6832850aa06a61b7b78c96faf64dcb347893c93c560b8043466419864a382c6f2ef1412873b2d8cbf\nB = ef1412873b2d8cbf\n\nQuotient = 1c9b6cffe44241292320c0660b89f2f77aaadc8d36e33f5ac3da0f12b3c114a156870a92079f7192d237f8bf49aeee6282531c929cc56d75\nRemainder = 1ce3e5eb13ac7958\nA = 144325a641463ed6bddfcbd73e50620a44c606d71fac38efb1c9d2747b4903f7b51fdedacfb66db022aea09b43c7c2ad7b851035165ebe59b552d4f7eee617b2\nB = b552d4f7eee617b2\n\nQuotient = 1b4ad18dc0e634053beb3cf840b53e35117ea06309ea8ca22e37123fd7e1d391c96c792e5125e322c27daa73301024080d73ba3491484b659\nRemainder = 3286bdce6dc3a828\nA = e3a2b90d3ef446f6bde30d3e726cf3e78212324054b40deb0b18fe00645568fb0a6234b6bded6240977373731bb30d1349e25cefd54b7a9985735e9b78002691\nB = 85735e9b78002691\n\nQuotient = 28f5e8da6733240cc2f18e3cf4d42a50d92816062af33a9e1871fa89bdb39a0d905c49faf51cc1c1378741bea34d25ac2c8e522881a6f6087\nRemainder = 135784870eb40c68\nA = 593206f9367b72f9cc59b3e37d2eb23b2061422859162ee53656899c2471017474f500c6e23efe1f6b1e57852cd4229329dc182ba01a257122d76a26aaf9b844\nB = 22d76a26aaf9b844\n\nQuotient = 1ab276448d16c533b6e90b5b5ca266e13ec27b5a58c80b7657df963ec2d1fe4eb1c1d24873eff6408bcb3d0cf97c31e85240eedf0efcc1e5a\nRemainder = 27b105741264f875\nA = d84fde3d851b52ed3b2a1268e9b765ec6c09c5768bba709b3b799802fadac30a6c3184185e6d57249b1c34619f3c9d2b90bc0c348b22537281a39fcadf738083\nB = 81a39fcadf738083\n\nQuotient = 84a87678485b3e60ee1cae3701ebdf0a29ee44115a492c34a0c8e84090e14070eb2ad0abfe2c339f26b5099327515104fe3d1c5546feea98ed\nRemainder = 95f7434941f9d8\nA = f79a0643bcd9c28cc22cc7b4178b3340e4685dd2672792516d6fc08567d2de2d3e25d43f100a58826edb146ac94acac4213bb09bdf8a258001ddd0ab110b89fe\nB = 1ddd0ab110b89fe\n\nQuotient = 516a2ac26e5b3afa502c7f3c6f15376f7a380e5842c229443343b5b74dc3de84db3ae99a0c57043e32a504ded19943c0310cababb3e92cf8\nRemainder = 327cf78eed336523\nA = 17c0d5814e1020d5d69674bdf6b9df193a16c0c8567a589d014e8eb7f6c9c36560791f7acbbbacee7c456eb51a4cdd7ca88011e9d8d9f2d64ab08ad74f7be5cb\nB = 4ab08ad74f7be5cb\n\nQuotient = f0da0beebcfaa716f494cf3fc81fe65117c90adde3b3942e8e66986fe8050fd5c9ebe1c88c5db04cea4c4c14779555d70cafb53870671f95\nRemainder = 3b2f844440d7be00\nA = ebba8c393c2a22b094d824ed95b4acf6875719fc165f73ee6d359e1134949169fdacbb42d5deb8cea96e11e3aac985635b5bcc6c02a6778cfa8e03d9ce6fc680\nB = fa8e03d9ce6fc680\n\nQuotient = 56527f07593774f0fa642241400985d0bb9b41d3dc9e025ca069130d93afc972d75e3fe0f798e127c3e1b4e925000459a3a5a83b15186e516\nRemainder = b620b7a3b752b78\nA = 5d6cad9e26267abb480b2b9ac5ea323bc4c3c53e0de8ce40c89c85accf0499aea5b11703a04296519047585ff12f8795f98da0546c20016a115100eddabfb468\nB = 115100eddabfb468\n\nQuotient = 294dca3b56ce9529aed2c132a9bd6c0c61de7a58ac50582f396b4fadcf7873b502bb869f801a9ab1f12384631cefee72b3e6050a7f69eba4\nRemainder = 53a0fcf5486c7a6f\nA = 24aa73803f270185d23310df2cf3ef67b18d7800bc41aad2ca13f372a27ef0a9217194f3f512e79f545a903895def195a5eb9a1a1b6b3f4de340e9da9b305d3b\nB = e340e9da9b305d3b\n\nQuotient = 16bf4dab1c29bd284c9b6649de65a4ee58f21d6a8b51627ca133fa817872b1a4a9956662db0aead5898ed0eda08511be7c47449638f2fab95d\nRemainder = e7751deb047d98\nA = 77b04d93272491322ed2fe651044e28cadb2ae7825f02b55aeb0f73b8b8a8b336802416fe08c718ab681581ac04d87116323f61f50bfd2180542fcd4a46dcff6\nB = 542fcd4a46dcff6\n\nQuotient = 388ae1c243bc9111e663c0c80495c36e8767bafe188b532b7ac84b5160d902af1b638aec6e4c66955d16bd8ce94ce6027a7bf95910f705ad0\nRemainder = 7c667ea307017c2\nA = 52f357e9a57722a867d8199242e100f06e8df810ee913d6992bfd9dc03ed78bcf44d692aaa7be806df0c9e0802851d7ae8405f76114e6322177907198f85cb62\nB = 177907198f85cb62\n\nQuotient = 33dc2fcceef7dce92e3a9df58566c6e28d03b58ff6ecbbb31e43936cda6380a56788285d37b5e8f11487afd78c39cb2150cc98d9d78a0c6cb\nRemainder = 429a380c9f8eeeba\nA = d99cf9a0bfc347c9631ae8c69defe1f1509c3ecaeeee5dbc61317bb73fa5cc6e704f64c865cf4d898f8a2f63214dbd511f61aa6e09856222432376698f8d2f67\nB = 432376698f8d2f67\n\nQuotient = 18ecac9e5539a014cffd8310ceb1170577cb23aa9cb3c523d57ad83069d1609ff743cd3c275b67097a038b85afcd7105ad21672f9ecbbc7df\nRemainder = 37924fea665f5c92\nA = f87aa8b6e62b09291e0e9b832ad71d8f85d60501a8d89d2638dccd4022e89bc4932c186a198557282527dfa86dfacc2f90fe0656695b61429f8220509f5106b9\nB = 9f8220509f5106b9\n\nQuotient = 37c0649a53c8cab91a7458702870bf64cb1de9fc1c6b9a3b92444119d368501b62d3a5138af72bdb7752eab8af6bf4e3bdb9e3beb1805b88\nRemainder = de179463e3e91ad\nA = 995c04c1f24c4efe88393bab7a7545e39193662d5db7c8e557d6c554ed4367f5af82c463d0ba6bc3148620481140add5677937989e03fb52c0323980d8841d5\nB = 2c0323980d8841d5\n\nQuotient = a6d193cfe7d8983768ff29908ee6e07fee99927a4bc4ef41d01f63f3b4a2e7029630b7d925d0979458cdaa903771286af672253cd99593b3\nRemainder = 6bf69921db298b3e\nA = 55c856daa8110599cc4fde0a44acbd69a68eb177e0438f7d843ba0fb74caab2a7e0c8a6f176f5555779e65c555e9157a16a1497edf36ccb583a458f0372a57c9\nB = 83a458f0372a57c9\n\nQuotient = 63f379bef9866b59f8bfd6bb0120a75dc03506b0034e7440764afc8ec14d8d735aa6f03a568ea98d0a74ab9bbe9c6e11b288467e5f79a2539\nRemainder = 11c077beb8667d88\nA = ff1fc3ea60fb37ff23e2f2f4e207a86e055cca41eebcc5bd6376904b51fb3d233cb04666fdc92be33239b5ee552870e45717890e35fdbe3728d6ff55d5662419\nB = 28d6ff55d5662419\n\nQuotient = 285ba8cdfbf00b112e496ce65cdba2271c82a273b3d30bed82ef2d360790c5deb97f3311bd5eb9876a61e33b3a37782d00c2d5ffbeec752ca\nRemainder = 1672a8aa119c3a1d\nA = d614352268930d301aa4046cd38e2eda4dcfcc52eac984943f2c863de5c4f8a44473a8ecebf12cb8f4da4722d305e5c9c3eddc0109d416e854df334dbfcfdd4b\nB = 54df334dbfcfdd4b\n\nQuotient = 358178128648fa9ea28dcfe68b4cecc7071e129e3ce4d113f5d1e387f7e5a412e9d2dfe5ff16d9987a544004d213ade9c134cc240eeb6871\nRemainder = 44c3fdb374bc0c30\nA = 18b973dd011969e29a1f4a5b8f118313f715c2e31dfebd9fe0957cf23cf36eded89c38637a8d3512bb23324ff", + "2a3627d5b942300200c823d764b7a6c12d1c91b\nB = 764b7a6c12d1c91b\n\nQuotient = 19ea7212f6604d423b308fe3f2f4986f31aea9d6a117a3e207e38ce5bbd8d7a866285ac60433630de547fc84e364c451457fbf864a82c6613\nRemainder = 2718de2dd0796f08\nA = 83577f755a448d5586e19486b04de7836818223ea920465c4eee979a9ce5696ad8e2fd5253b5d5dcfdf355465e8c0819658ccc5580fd29b351169b54c62b779c\nB = 51169b54c62b779c\n\nQuotient = 13e0c5b9905770b60a6f978d1c983cbc84dccfaed0f4222f534df80c7d3d129f5e8f74f19581332a7f6d383915424c71db4ca19bde2591fcd\nRemainder = abf5f6c8ab6ed4f4\nA = e2bf43c91cdbb244790eb165cc13feafea36f5187cc9bf8aa8cf202042efd5441e3822a1164992da5be750aaac0bb11f09375bdfbd4a39e3b682c7ee6ab5f5f1\nB = b682c7ee6ab5f5f1\n\nQuotient = 3919f31521e87f90df3a4463d0c83fa31e3f569449009d307962d26f07d854e8d3f0badbf55311c206bf34e6227949327a93b1a5ada7a930\nRemainder = 6c3802d44dd4668f\nA = 2546880cc6f97fb379afbc4a2664115ba7909414f35a5bf88be2ed5187bd1a24afaf82eeceb0b438d4999ebf9b7ec752236669425bd3cce6a71d9ad67ff2ff5f\nB = a71d9ad67ff2ff5f\n\nQuotient = 121d5ad4115c2768b962e51d09f426d61624e0f203ac6c923289b4e7964e165b34f3dc1ff938a7cf37478d407de251c64db71d3ee629c1035\nRemainder = 660a35e1c1245910\nA = a36d3250c123697adbbbdf489e6cb40be57febaff654ca951c9fa0b396b1714c55ed6e05e468153ac443dabca29de9b43cc0cc4e62cdf24690593662c86fb5ac\nB = 90593662c86fb5ac\n\nQuotient = ad81debaa02f6e60da58b46e76ce041fc4da64138634ea7b3c165b8fbda027eb64b6b5339e70babbb83430d60383c2cfe22029e617fd03a7\nRemainder = 2e4aeafa2ad76832\nA = 8992cd131757ba5cbe54aa58be115723ea3438ddc782a4d1996980b7b312fa76e4483584df744b10340e5fc9e468690cef538920a732a8f0cafb4e30846cad1d\nB = cafb4e30846cad1d\n\nQuotient = 67a71b9ebaec91121a8cf6bc2932b6be01af7954eca69c5202d771c2c2d13683cdf90ec942a3445771ccfe484f947f078de825ea88b3c05a\nRemainder = 8395953f744cfb31\nA = 4f8ada84096198175174896167405b85cbc03fe0642f6b263a70f9a22f19ad6c9aef38da8ac036d409e6fd925023c95312cebe04eb653e0ec473dc8dfed98967\nB = c473dc8dfed98967\n\nQuotient = 9416326e2347a541b777a0fa1b0c35d8fe76c940d24c6f6806d6ae8ac1e280c16e480786478bda3f780ee92f3f3c361574efc2ed5ca98e26\nRemainder = b8ff45f31bdb58d8\nA = 902f5e48b96b9b1fd16c3b21292ed495987ddac4e1d92b2ab10378f2966c4399d6a41eef622a4991ccd1f647531dcd145de4ac99b3036779f9414ed2f4ba7e08\nB = f9414ed2f4ba7e08\n\nQuotient = 403c651b4e571e8301c4158fc185396554bf61d900708d2af5c2bdf495b3cb539b0b9b5acd0d71654b3aa68024961d5a7bc9e2788e6c822b6\nRemainder = 7856ec047cec8dc\nA = bdd6d846983fbf140173a26d2b709b9f31b4fee1eac9d25fdf0ef3523be0e6afb372acab470cfe1806b36d84017ec99302eb9eb5eb2862222f4916d8b6201d14\nB = 2f4916d8b6201d14\n\nQuotient = 1b6d967173f9777cb6194c8f69289b91da731456fe5a1515a49e4463cd906c84f97381cabdf9f358d97fad5d3cb140e3a3de397e7f9f683157\nRemainder = 83649246ade8bb4\nA = e3da80658acd53ada7c2dc57178e697f2907c5b0c64f4a87a794ca7521105a0568a32874207646df3768ee60964b7d1d2e29ea6bf7fbaa7e084eabd4ea553a72\nB = 84eabd4ea553a72\n\nQuotient = 27b8f1e49e404455cc68217a20766590e749507976a3a6de25a7cf2c32593aaabb04d84deba1ec6bbe048a2959ffd747243c396dc53c9c811\nRemainder = 3daa032278ce53d0\nA = ff3ead7c7b27f607d16f1ef4ffa91b6cc28301b9256cfcb0c22b6818371ce648ae8812dc50a86e4bdc0d0b1e5b0d55c6ba07b240886a6d5766cfb3ed0937a543\nB = 66cfb3ed0937a543\n\nQuotient = bf987f58700508356fb6274f64a9f78d455e4c436fc6fcc980ec0800287ab3789b91c29a8a72b16645ecfeec926b6f8242f3c7dc3adb40cd\nRemainder = c007da44faa80584\nA = 971aa67c9af10f70977f600e10f9278b8e66d2471956da38e5f4b3fedce9a5fc7ff42b800bb4a78314c70bb59394d0880383f5182b6c1960c9e5b47ef8e63be5\nB = c9e5b47ef8e63be5\n\nQuotient = 7332104442474715d7c4cdac15fc1731240f8b4dd0e6ff3284a15a62a8f9a071dedb87f2220efcc5839cb7e6933a8f65d767819db26e134dd\nRemainder = ef65a7789f54174\nA = bcea2ae4b1edfebf905a5820f0481b6c58d76a69df9dbe84764add3f49496a5d7005d645eaee3754e0ed105c13a114e6a0eae5cc4efab6aa1a3d3a0050fa86f5\nB = 1a3d3a0050fa86f5\n\nQuotient = 3f6182804a7ff12fe7ed3c8521b55564559b1a47a78e1fd56597b9470e7e0f6e7e48c58bc8841c9d118718ccd5e0c0bf9a08d8e244ae60da5\nRemainder = 398e30aff5bd284\nA = 2b877181a960c5e29ab1b2672ee22539256a82369e8f6cb5bcfb69e5e4a41f782e89b58fc0ef6ca336469ff929729f8492b44f12199f0e1c0afd12b2c999e787\nB = afd12b2c999e787\n\nQuotient = 1a80a681d2c42edbcbde552323dac3a1c03b43251a99b5549da6cb39ec6947daa0d574f0df68512984fa8e269b0b27a5576b3aaccb76ebc23\nRemainder = 378e44fdc7a5ec4c\nA = d37e62f44de27a1418f348139eac5ab9fcc1ada21ea6d7695273daf638b4d7eee6745f54b99a9678cf742d304736ee356f66d16d874f8cc67fae9be5dfd41a3a\nB = 7fae9be5dfd41a3a\n\nQuotient = ee982a63816d56758c29d284c19b9b984908cf0a9ae3f1f926e162a2cae4f88703aa477c5c14042247635c103494d11593c2c3839baf4d93\nRemainder = 39afe3275c01aae6\nA = 9a0b0476cd33861d2fc3137df292728e1f636f6fcba5105f384533723231a3104e7c77df46f7f34a4bdc63d5c67b418cafcf106b26ad020ea547d34edac1d3a5\nB = a547d34edac1d3a5\n\nQuotient = fb3f4a39a661e5c31228a6b7b4c27e6e52d1954e8ce262b98b61650efffd762cf2a1aec228bec5d5787683cad6b2e6e49a0de91c15c81874\nRemainder = 63e5ed36ff73a42\nA = 4453712f56467328401a69d4d749a0771732734a760a74094e50a62a030cb604e735bfe0bf0641754edff94ac0e0549e8c10941255f0f21f459e52a6cfe4d9ca\nB = 459e52a6cfe4d9ca\n\nQuotient = 7af60a7c0f995178be76c070cf49eee311e6d1e3afaf50c8c93ff200c1b3fe742b23259b4fc0b9ed0947be4fc9a6c212d86de9a0f7dbb5279\nRemainder = 19657d8ce516a138\nA = c9c92a31ad0f3cfb56a294c42a26eaecb77edf33ed40a7e6797927a0c996a7c0a701b484741163df388bb082e3daebf4e1b7a99002632d6f1a41c1d517238557\nB = 1a41c1d517238557\n\nQuotient = c890c55a8e2a3105b9bf9344a57a9b9fab5fa1fd57083d52431b695553bfbe7a44a9b6cd1f83958224f351f8511b14215d1648e88e938573\nRemainder = 1bab5b03c372daee\nA = 88341550e470016c7ab600b9f6cb410071a77f907a58cb6da4ce3e955d1e859534c2c1098fcfd91b9fa66926e51896733c36a824c3a20844add94e27f30ca651\nB = add94e27f30ca651\n\nQuotient = 34c240c42da400317f66f5151630493a2f200ee418d5ca3300cab10dfb429c2acd7280bf066fe19115f86db83d8f5b93cda714533b16abfdc\nRemainder = 18cd326996ccebc1\nA = 7e96d7b90ff09b114dd4393e9bdfb13d8ff517681126c566e18dd6369d87d248734d94bd02a1f19cca90be7642822b636369c51dee441a9d2663ec896e1d6c6d\nB = 2663ec896e1d6c6d\n\nQuotient = 10d18159e75efa8204e325e6be830b4ee8d2c07419e8276edeac6cc286488fc0c888300db3ebb5f935aa82654d3b932540f0093d1880e1d6d\nRemainder = fe9b6b8ba7c30f8\nA = 731aa6e2fb2ad1e1f80d7668c7b0642203af24af382abd207a5ffb588209e8b5caf953e9a96b478f39ec03a397d1433998e3c95e382d93376d80cf0c957788e6\nB = 6d80cf0c957788e6\n\nQuotient = 450d1f4a105ff8d1a3efbb12165ca98c67ae70404472e4862db479e03313b08783ecc42104780c9d57df0ddf19c5b4547ee9ba52ea82dd0c7\nRemainder = 169e15b4d5aa180a\nA = 902bcb1904b80183656dcbd51879e2982e2b46a547c9ae3119ffc12c6a003e4321b519289b7f22fad19d16480182d1d797c3045b2d29dcc12167f9ce5e233d89\nB = 2167f9ce5e233d89\n\nQuotient = a426f71cb3d75365cd076a6c35c10765bbc3f4bd317fb83a70083b0f7dc43a4e0b95508e60dc1dedb780e9b485f4f7a8870960de669b73af2\nRemainder = da381ae5c97a506\nA = bd59dcdefcbaecd9292c4c3685fb87d3a94c0f0ed01e43e63e1f36fb65d6c5eab3b584f3d1f76d31458c9f6b4c69869d96e943c61df102771274c5b4d821469a\nB = 1274c5b4d821469a\n\nQuotient = 26ccd4b7be090af22221729b0ca51a5e66435c2d33f8d88f94405f6c0123ccbbbbc8080cd8448a977946019ccbf5d267ac3f151ebe686720\nRemainder = c41f9e7bf20b376c\nA = 212dbeff03f14b5825f0d7cf8a7501db21b60581a01a26d522ee44e7fe69545cfcaaac64dbc76c7e3027ac39ddc2d80af6f3fca1824c6ff6dae90967d9ab48ec\nB = dae90967d9ab48ec\n\nQuotient = 801df28f4fd987b4e980760f4f2625276a2a7191d453095c82aa98a2253324ad2873abae70cd98c28ef3ce102fdd53469b9f01889f3ba8b0\nRemainder = 8e435da582e59809\nA = 48341b28138dd04807e522e341f74ac46b0449fa45f96d7fc586997c056a21eb3c399752a6a6c023509f042cf9e879f397a34af9aa2ec2e8904674f2ea3ff739\nB = 904674f2ea3ff739\n\nQuotient = d3857b72b70adff9b5dec3cbc63de7c90ccd7aab6595339b2de39bd6b9789045141d224aa4e6bf9a06e017aa3edd00e716a771b3f5b97771\nRemainder = 14135c686d2e9f70\nA = c1cea45dd46409d5e24fb7ed7d849dbb079247af2d312e01083754ed07f65f090e4dd50d23a973488702ef00936c5d78af603ec0fdf03dceea8f939c922b1e7f\nB = ea8f939c922b1e7f\n\nQuotient = abe20c90896e261e7d31bf40e7f3136d36b0b78006d12225a4dbef6aaf2062b609379eefe7e5af5bcec17126286f196f1330da8477096763\nRemainder = 230307c44cd55896\nA = 19a637e4f3051be0f7c4d35513bca4a91ca9b8082fe3c73899b70b6805a7aa0458512495cb6ee1ade55ecd5851be1dba96d65202f06bc7122633a0d905017545\nB = 2633a0d905017545\n\nQuotient = 5ed3765c4a777a903e182f7c9ce39d19c01460f389b904c3ce1d3525edf25ffe7dc0f4d9e24f0bc8b7e01bef19c83e74f17884bd7", + "bfabb2c\nRemainder = 40f5346f8775e20\nA = 546578393e914be30581e24508a33f6560a5805dfb1c675d1ff1d6f5eaa7ee638b9e0265f543413e04e3f1f3b0895dec271c9897a48d9ce9e3d7df32c15b75a0\nB = e3d7df32c15b75a0\n\nQuotient = ed73a67932746985465fb0606fb0e81595514f1647c911c303d4d31eb0306e3b2aece07320f6fea57a7071d73150591ab2a82a7d53968a81\nRemainder = 2e495a881876da00\nA = 8976445bc318921f7e12c8d4e8e50596849a1503b5efb65e939c291de136597c05a1fd16137f0bbbd7197df943cd612118d1e55a50ee097c94331c1cfb1e941c\nB = 94331c1cfb1e941c\n\nQuotient = 5dce24b7a16d847b0c43cf365ea20bee9679fa0e8732813e827cf6ef3c9bdb7fd8846b5689ce8b80a7dc0dd05721cb06d2700aeeb7ff04d6\nRemainder = d8ead1ae3126aded\nA = 59b99e5d028e6771d27004bc19830a5fcb347f7ae04c0ba7c49130bfb198c5b16821e425c979e6d2dddc14889ae58475bb52c6cdefecf2a8f4dd6e462bbc8f47\nB = f4dd6e462bbc8f47\n\nQuotient = 170e10b399a4c5fe354b536fe59d53602102f215d5107493680ab6e181f67d75ffd45bf49ffb23cf9269b856156b5ac6b1c5def4ab1abb18a\nRemainder = 57131776937c5df9\nA = aeb35966e2a616762768b7f63ce3aee5e81561080617bbabd7846b3ca03fafaaef83dd05b8d16cef40db0a56f3b0ef6eca5e236681cb57c8793dc0907d9aa30f\nB = 793dc0907d9aa30f\n\nQuotient = 1acdb88f047f9bf679c50ed67ba01dd24dca92103f8ea2677215b6142083b64f9fd2a365499dc8f2bc61e29fa176f7d76b55557fa58e34f9\nRemainder = 5065b726dc6b3758\nA = 15a6292c9fb66c6770a8dbc6fd431d2a4b57338581f78d0860fda90182cca563eb2272a79fb4f5a6fc72c90dc23e8a95713b65988b5b3f9bcec4f0466c1c47cb\nB = cec4f0466c1c47cb\n\nQuotient = add8127c0a27c961203ea0351aed5b3c75aa816e9c2684574e55f55c7140adcbf69d2cff843e5f53c157bd60b43c45c8b6658de72062fbba\nRemainder = 67f48d3584cf4fe5\nA = 4e8938c8cc46d34e3369c5d8536b18c963dbde56020678f77cebac5f8777e0afc62ca2ba4f533cf6cf7561bdce77b6f495bc1b05f1416d1173a6a288012c7c73\nB = 73a6a288012c7c73\n\nQuotient = 688ddf883a0bcc1ff9bd582119c2fea7c059e19aded8c048390a1d8fd7d769666987418bbe0d4cf4b67009a342958928769375c1c0d558acf\nRemainder = a5356d04b64ee12\nA = e0c9e32056977aeca72e229d83f0d320fbaf5cd8bf3e033289f46101c75ef59a854982f33bcbcfd200034e8ff439d669a03fa404e7dbfea822664967d67dd5f1\nB = 22664967d67dd5f1\n\nQuotient = 39d4d94587fd1445f31457c275fd6294fcb69ba155e7da3e6cfef38ed1272d6c95755bca49007ca62cc101b038d264876f18594b8fd4c329\nRemainder = a34980d5046e2ed0\nA = 2efcb12fb55c923f5c6ca7ae076765059e15d9e75240a6e5fc3db92de184143fab1934c7450c3a380a9851846c9f43d67bc199a314e82e72cffee795d695f82e\nB = cffee795d695f82e\n\nQuotient = 145ea82eff186b7db4b11fa1514674fb9d41c698efb33227eb1abbc4eb78bdb2a280c0c4c47adaf4e010a4336cbb5650becd1ef544e223e53\nRemainder = 36052bba2867f5f4\nA = f6a6c7e33fd4c664652d696c495df387b85b132cfdfe34bbd35759477b4a3c052f610df57e49e85720489e4bb8dc923696400a4a28dd000cc1bd491446a50b96\nB = c1bd491446a50b96\n\nQuotient = 35d0c9d870348b113868282aaba22b21ec87cf421519a23b288b150604729356f924090ba038d7400c0ccd4932836c65902b4d3c46a202a0\nRemainder = dc8c7d087bf24b0\nA = 22228c8a5966ebdec64007704a373b0596ae702d62e29e468653b21a890ace2f02c27f26b043f48495687ce8c2ca8092ead21aa250ce0f6ca26129615a2432b0\nB = a26129615a2432b0\n\nQuotient = 52fc995a486c4bfd17ed9722948e9ede1c4ac2fe80e6bd7482fc47944c4337a185a506a9ca473d49073e1b813ad742f19b13d57914888d5f\nRemainder = 75c703f654ad630a\nA = 3473041ae301dd2806da30dcf06b9c09600086d6873cf3ee9d5a0be638849afb56bce2664f797de4123f6f8fe3e12acd32e33a285bb7f493a1cc13a7108327f5\nB = a1cc13a7108327f5\n\nQuotient = 1744946730b2789977620f2e7439641125dd338d1b31fc50813b34dea70b83d209330bd17fd527db9a402ad9752c26b8823082ec9971f4ae65\nRemainder = 453a3d59303ec3c\nA = c0f592d83649bcafb7e2de1a8a71fa863c1f51b595bfa638c8fe30731c6fca36da975b6f19c657e3ca29efff6febfb311c003ec68189998c084afe4979b5bb19\nB = 84afe4979b5bb19\n\nQuotient = 468f3eece20aa9d6473f3c559760793e702758a3d9cc19d7817216392c7cc7c3968778cf2fe0c3f0c1424d7512cee19ac0717952f18aa287\nRemainder = 5904e71034e3a02\nA = 1f0c99a128c757d76ae6dfcd01012f0453c8f89b00476ec46321ecb872f99a48b4da29a4abffd0bbff2b727dfa182652ca85350b4ce100fb70a6a40ab6c41d95\nB = 70a6a40ab6c41d95\n\nQuotient = 12198913ef16c1cfc7c1be13f1cc5991a61ff74935e09f0c46d26456b7cf2825403b9851d07d27e0197c1fa2ac5e32e836979a184f14cd94a\nRemainder = 33431c3df719f946\nA = fbfbf5494a9c5384c7ae3df6c02a5e1f9f32dc31cd7f437832696bba164bae1a9d95daefb8bc08e0e8e637436fb747084460697b5ef5ac9ddec06757dbe61aea\nB = dec06757dbe61aea\n\nQuotient = 376c2f902566d83c21eb7c3aa3a6fa0482ed52c253f67f00d5b915d0183c2d9a2891c2ff837fcb426a4c990c48bda4f90e0bf69d13558696\nRemainder = 31540f5e05e8b4df\nA = 2527f8cafaf7e8319ca53104229199188ab1ca5fe592bde8ecf605e17ca6446414e06898a85e177d6985b5cc6d4eeabd6b222b5f44b4fc1baba050665c090b5d\nB = aba050665c090b5d\n\nQuotient = b8fdd5cd7b2d9295258bd99e2780921cb2ea70627a79088039fc3ab1c62bcfc6307e86db4a7803f18e5339f152063f9e41d370e97b1ba2f5\nRemainder = 4ed4f2d12e4f4ba0\nA = a25bd113c5a8c67ef65aa80f1512de43c9441fec0c41250048d29c406fbdae80912eb3970457d621c552e3af7ef2d6bc1b5448e7df5be724e0adf6f71df7eef8\nB = e0adf6f71df7eef8\n\nQuotient = 5421daac8cdeb6acc2b8b0dd85b592f255ee4fedb3a9e90f2a5bedfb0f9f033d7c562c96958346bcdda4664c67848b9d9fa7d3892bc4e9af\nRemainder = 7e5661558c345eea\nA = 490aef65c81b32f5df76dd58decdec3e3f73bc1fcbdb6aee0c93cd98725056153b572509e75d2cc4b042bbeb0a77d27fbca1e39efbc765adde41a7dfc5c3576d\nB = de41a7dfc5c3576d\n\nQuotient = 156a8a24e7804c5f576cd1757dba44cb4185bc13cb56603b54ee3b70fa35cd98db1992904d4f7d99a63b3a486e6fb31141a9d39cc0301f897\nRemainder = 29e9c1627537e5a4\nA = 5e4a10e772de8dd2c96acd714f7d3880ae8ab460095a01038f3aa9b8ac8165889403b42019a1e70e0e7f32e77fb388eae3579dbcb690729c4671868b0526aeca\nB = 4671868b0526aeca\n\nQuotient = 1b0eff2ff0aeb2c02ee3cc9e0bff808f4d616eb290293b13a6b58a84127972bb417d55e1d001a9720ec72562ef3ea688e64c4f32c7e26cc87\nRemainder = 664d57c57d4952e\nA = 806b8504abfbeec4d5923f83ddc071be88e11c4394168854448df96160b95adb1fd9c288852e2f3df3e36916ba5118815ca2e83a6a7d9e074bef9c961e2958e3\nB = 4bef9c961e2958e3\n\nQuotient = 2e363b13b0457a0e9effc2d7e297df78f35e5d24d0f8ad4525b573fb2f66f374871291ee8a8ee3d15a823b560156d474c678f79ee480bbe4\nRemainder = 5ba8f49e0ca36ab4\nA = 2e1bb261d98ec405dbb068daac5efeb0a51f08149181864e9dd6bf6cfcb617b76d8facaee2ef468807e0403bc550d58e8ad9e5cc0f094b02ff6d0277fe642f44\nB = ff6d0277fe642f44\n\nQuotient = 149a5b1a81b9e47ed36be76252055bb202dc25f8fe7beaa1ce59c279b32941cfbaf8fe4555867850b2fba43b10b74534db82398320f9786d25\nRemainder = 1ef621737e81780\nA = 63de892cf5df40c98de78c755c99e94e0e76cd5dc0b49b8856fe69dd0abcdc535bb1416f0d02b4eeb54e8a939cf7ad4edfb7de4dac87523e04d8ea8637e50920\nB = 4d8ea8637e50920\n\nQuotient = dea8a9211974758752d89965eeeb93cc616f88ce757ec2809f829cbb8d99b4ffdc3f0f643779fc5e0bb53b5273a5b15965f4a364863592f\nRemainder = 9ae7de3edb6c7edc\nA = acd5cebd069f7febc38c318867ba3a562bbf8ea9b19a6b33538ba107e49439f8ac6e880c6267c29b39141dbe2273d93062464de307efdb7c6b738c0bb282c3e\nB = c6b738c0bb282c3e\n\nQuotient = e9149b347cdea84d740be70060b239af000c4336ddf36fd5159083b795c4763588c87a959df0104212a04cc928baf60b0ea72e8cccc6d477\nRemainder = 3ef5c6ee67e6f5da\nA = 6ccf1b8b406e6a106160e73ac4122a04c0814ef5a47708a6776eb52002d52772d3fce3fc05398172bba191390aba925bb23aa1eee626410877822f27d1e3cb09\nB = 77822f27d1e3cb09\n\nQuotient = 1606c2fe44cd0b780ee474a9c7daf0b2bebf62db0ba8ef5a99fe22036019890a4c7dff73e678965bb0e2a6e61d00a74a1d33dc1106842115a\nRemainder = 7cf920ba2897f714\nA = ef9a3983f26237576311a871e4a3df0538593dd0cfda58ab90b889fdb35c700f7d158abafad127605057ca0532e846992c41ec06902ce58cae0c1fe238c726cc\nB = ae0c1fe238c726cc\n\nQuotient = 8ccf17de5068451fef1c2808c62e19997c7f920d5cc0fde1f5a247cc57c6d730df553cf33094b786597a343a0ce9e4bffef568247e904343\nRemainder = 2689c40a54df34bc\nA = 8435babd279b7a3833d01988c58005d4557f7689ea9b7168ef42ce2b31a1a3c32a982aff654f271a651085335496dd826ee4b3bc27f58920f05dc6676e51c662\nB = f05dc6676e51c662\n\nQuotient = a9e78c48c779140b1d15843089765ce9ece3855537ce88cad3eb7aa7bd6ec72df65adacba2bdf6c491066406bdc3dd3dd734a70e93eed958\nRemainder = 53da0b15ac079ccd\nA = 78550cb7b58b58d6878b615dfa25a5b90a1ff631740e631c7f8829962446903c686c810c46a1551b6c1f7a89ae898435bb8e36d1bae24a80b54edbf4bbc9af85\nB = b54edbf4bbc9af85\n\nQuotient = 1e3b41304ee07f6baf1ca061e0e28a3740991c6ca2749eba70d3ea1f9cba8adec45cb69a31cbff22784a9e056e884713c0812e8c7981e49328\nRemainder = 3d051148ec43a72\nA = 76b9453d315e7a9c592e1f2640f5b6b90a65e7f2ff8ac24b9b47e35abb76fa5d303be6d501b341a882bdd9d2a1c81a9280724673f87fbe9803ed5a2e7edaeec2\nB = 3ed5a2e7e", + "daeec2\n\nQuotient = 1921410e1a538a71d33d9c5de95593fada116200c399fa7590ebc374282570477f5f4abdd5166784ccee9671a1a23b96378df62168049f6b8\nRemainder = 1a1f4aeb882d7546\nA = e4aa84f782a65d376b10e7789a7d56695885aae274db6cb37e0a34414397a57b4a5f76dced11376af5fd11d31828203e685861a6dea239789196fe73d0e46116\nB = 9196fe73d0e46116\n\nQuotient = ed2afbd2e63617a651911017d9d02224d521e99275ab642ad1a941827983b17ef0f2067b5405b20e8e97f2ae6099150a1989df94276aadee\nRemainder = 4578107045b9cb81\nA = b547cd987638ff7e3c30fec9b728bc10c3b8cf16e7040bfe0fe9a26e44d2898c4c4d28ef525cde2b4007b2ffb3aa80fc4514a99b9aa2e112c3acc56b72ddbe9b\nB = c3acc56b72ddbe9b\n\nQuotient = 56181509251931afca3bb9dca21eedd6ed4226be67497d8d1bd0ec052af146993e7358f132e842f9b6c4934cf1b4501f5d6c5912e65c8d3ce\nRemainder = 1b9861df51429a6\nA = 32988a4e0769a5aca200f6f6f1498512e13b4904a9a311cd8a962fdd688de0c6e50b04f42cdd2cf8bf9b0a6922657f9ad195773e1250f85509672452618da9c2\nB = 9672452618da9c2\n\nQuotient = 1fa45bb973dd1d2df0002772afba55284a1e41f6aa4b0d1a6c6a4beb8ae00b52e88a9889037b8bfa9b7ee38036c57b713b48af156c3f9e8d8\nRemainder = 2525d52ecdec8814\nA = bda657ddeabe24c82c883e85822941bf64448b7cbb368468078101289b6fca36680b3884e35edc1fce5a5cdbdfc11359a1ba8ac0785c09ba5fe5cdbd30726df4\nB = 5fe5cdbd30726df4\n\nQuotient = 63e21f5568d07976aa81a2690b9e81b76fc3291cdeb010d1693d0e80191186815c7b2f83551a5f1b172640425d4733f06f4df1b2c8a7e6ed7\nRemainder = 14781a368471ecae\nA = 9f3dad0b3b56de15ac46cde1d79aba6a2f3b34d685cc810e9fa3f2d865bea4afb480d58653630319a258e9e8ded9be93cda3bc52b80a9359198221221724cc3b\nB = 198221221724cc3b\n\nQuotient = aae37878db016dd758003b85ef52acc7288b7b74c4723e3876a710baed4751d3be2ae49123b248f2b2c55a5be702c4428b1dba9b8a6ae8a9\nRemainder = 6c754d5c167e1228\nA = 4b93a98eb7b92cea0a4f5c2223e77abdfbd332b39f295b4ac40f71625d88e4add7e482adf3010082d8dd8854cf714a54fba0887de87946e97137cf7eabda038f\nB = 7137cf7eabda038f\n\nQuotient = 9881f551c4b7e67611f37df29e77cbe4e2d9fd5e17b7da3d013d6f3d4312e53dd26dfe3a2a12525cfef1ef81e6ebeeb7ef8fb4f918bf15ee\nRemainder = b14595005716bfe3\nA = 7737f8e7337160c14cfa8411236ca0354d8aeabf389b9fc4b14bb2ec3bb68286f3d82eb394dbd8062862b955e9fc8e86eb646317d1315d09c81ef51b30288cf1\nB = c81ef51b30288cf1\n\nQuotient = 4c8519d4d85ccf845fc5b8f31c27c60f0893ffda29ba86e8a3fd5fe67de5d29cb29362679abde996039b8febda2ecf71f6b9e1c1874361464\nRemainder = 10fae644af084f8a\nA = 900f7846e927760d9986894de6489e53cbbcdd59f7707917e7581422508f2ce79b77bd2c56d964a41e60baa927ca679faedcd9cd8102dde91e1f583ae834b092\nB = 1e1f583ae834b092\n\nQuotient = 16ef17b40bb73063f3cd0929cfe2405ca0ff2d3d426ac05f8a8dfadc85659105f7f728e113baab59247c4c7936ab975c08d6f1c72c12c532\nRemainder = baff11e6961c72e3\nA = 130b212cb6f3d854e4f17524953fd8592f5e59dfe92fc7d955e2899d1dde1ae4aa20d749caa349ca8d1bda7eeec2310532a7af54660e2a1fd4929335a1623bad\nB = d4929335a1623bad\n\nQuotient = 1cdd7ee2eff733b83beda5b862673177e2f2151ee0fd9ac0bf0ec5b7e05516f1d1b59ea754b0483d0e4bfb7668bb99117907a58a8ceb78028\nRemainder = 29e33e0c2a515780\nA = b0131ec2c1ffe9a523591a9453d2fc740bf885e7efc1a0158905da1e646745ef1bbf39b406564cb3da2f842bee307b36219bdee5991c969d6199279c25d4e380\nB = 6199279c25d4e380\n\nQuotient = 20bfcd06f9c54c537ae563e33dab31047aa30a6bc4e7eb0902bfbab3bbb7e65df442c46625c39e08c88310116348e9ebca2450ab463727f90\nRemainder = 11d8f2f6d4c1f55c\nA = cefafbaa2990eaa88184162ecb118d20e5999e5a8fdd25ae7f6248650ea74a8cfb92c58efecdd5d31eceb618f1596d7a6bfd31d092cf86da651f629975faf91c\nB = 651f629975faf91c\n\nQuotient = 37204c5735e4ba5e47e845d8b652cfc2b1dc715abf21ea0ecf5b1c6c8b9e596591fd7a7f41787be1a028c147a721ebb891b0abe3bd079b589\nRemainder = 1ee700ffb0ea02d8\nA = ce22d36b3cb913b32bd0e25cc14c7270d3f7b8e600a9b6732377f846adafd7fbd8a09d12fb7011f2283d988fc29aa25948dd4a0f24512b4a3bd460ee19887d35\nB = 3bd460ee19887d35\n\nQuotient = 191051194e4362bb201f5471d4bfaf92f79b6fbd119ca3dc1afffba334869ed9f8acd14fc42a2d8f616d652610a483ad90f5140e9a5ca4172\nRemainder = 74785b6874d8fa37\nA = f3c79f9a6af1c5bec72218d969620149afe8bf068cf7a7aceda977076665bb5a2c30729ac3aa976c9be379c6a5458f1501db8802652ef69d9b9f4f097027ddd9\nB = 9b9f4f097027ddd9\n\nQuotient = 6c46c17fdb03d192f75d636e1e2ab4e858d55f0f205cffd75550c4347726b5cfe036c6c901782cbe5a04f1985d9fd1dd39d747d25a6a7a88\nRemainder = 9a836be71a24e72e\nA = 4f6cf6e357b4985442a25b5c84e2cc0a5e685e2f5ff71ceba439b81f4123e16db2296dd4333fff23eea92bdbb812daf1d27c721412fa9847bbc9a0bf08879b1e\nB = bbc9a0bf08879b1e\n\nQuotient = -4984390f93e11c9a77880cfbe157dc41d43fe901c8895ac5091c5367a77370b16d42e8cc260058adf4d3fc8ee8cc6c0099804f4c319f15561b0a2b1caa7d703db82a726c9eab569c\nRemainder = -19374dcf21822188d720d6ec892bda2c084e8af84f38012da7029a3c3660c7e813fd4f7644ca80373575ff98ab6d743e939269c51bf62e04f\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 330af318ce0ffdaa92448777ed117de9c104e0f975651322c8e01b1c470f3cfb7a78b11f7daeea57614cec37d18b89155f19babeda0016171\n\nQuotient = 1a56f7d6c06a316a9a466319cbd558a99f06843782673a54775d859768a61933de3fc410068d00d5f6ab13fafc9228fd40ad41434501f8827bd7461441140eb6977f18d102d446\nRemainder = -3c3d566cd48a909292be2ce30f88ebb68e9122a3359f52d1d7b0189c467b829a9f226c0b64845715020dee12d179913ddb7f17da2db86d854bd\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -8e770450768d07ce20ff8f5f6af464b1ee5f1d0e8faaf927a19d3ff801f6089378133e822b8e63cf29c4c9ed721adfc91d3355a3c7bbde77bdd\n\nQuotient = 42131cf8f52a6a3f189697ce402a8c9439bf05cb3dc1cf8bc49dc2f07cef15b3bf0102c941b5b3bde6440abc6eacfbf77ea8da06ce932fffb226b33dedf001e9657464b0f06\nRemainder = 4cd483574fce075404dd22072abe61200fc455c15b382c7f2962ffd82c38ec1e2c60f71267cbc35fcf77fe1f9301d6b5f884f1c416304aa9f4d4b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 38caa64e74b29a7e9bbf341edbab112a730b17103831a9ecb70ef077e9660b2dd1fbf71d7f6bb4cdae2ed7cdbe9070ec9fde996c91b9bca5b83450\n\nQuotient = -11d6883fcd705ac97cae5bb7f8a2929d6f636f4f232ae9a4af9769183dfce9a9296fa0714c3f4fa1eea467a5c96a484a59d0cdd87496b9398e7a818daf89a58add3a39e80\nRemainder = a6b7984fd80d719ffe2e6eb756e4e3bd7ab51f6088e04ac8fecdc744b0385294dd23b5007910109abf40cfca814c10addcb5330e422b6f5eab6efa2b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d25d50f53c694cddd56aadda2654ae5888603b39cdbace93d19c117af5505750aa24e615f95446862bd693f5b444e2a876eb2cf49f6c7acd007eae02\n\nQuotient = -3fa898b02c621915f44b213ba4e80b8e85c7a2f4c78df2bda7d99494bbca3eb2d9354965d83e1c9001f10aad9b3f3ed837a630b329f5a4b28935158fbd9d291a120b08\nRemainder = -320d41a3875da2e83ea9a83947f5abb1a7026c84020e983381722bf7aa87d5987ab088cb2c37fc3781c82c81bef3263fec560023e236a747030618e9d2b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3af2721aad4b18db27842b5e539d8cada9dcd7ac4c5b885065dd2496a6f76fa73c8a51b239b5c068ea6feffda22d8ea806fb488ad5a94210264597edb40\n\nQuotient = 179307c3e14de14a744d082825ed723b996a4e15f156ac473960583138c43f4275b4436c50ef8f21a7b450a969819b81c15bc355fbc5fb55cdd8e124d931d142851a\nRemainder = -9c8eabd36a25e995c1811b79a2a0357f6aeef4477cac0ffdd130046cb2a647f928a34d91d9b489d394965719cd58604b957c693a93145328e5568d33d88a9\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba46638", + "1056a3b92c35d9b8b71372b\nB = -9f2d3da1da77914df66bc889a40847a0d705d4648a11f282e09173d170e96d84b5a45092d995318fe7a954b54b88b784423402519a38bb521e84a4f6c5485\n\nQuotient = 6c0f316406afb4cc2aebe34f7948422de0b612a02dc47f4ae59419c579fc465ceae1980a3e524fdfdbdfad4862f168a9851664688c9ba01a8bc1ac156a6276643\nRemainder = bf52a2fb6493eac22fc8b334ccd8e8fa347620539d9189d535373f94503310a027c5423197c7279bb51ab8c459e27f548d57b55740320e80b753290d077aa7f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22b9e55639ad3ff4f071a49c8bba6bd9047e162fb31882421db8ec5ce46f28fbc35040bbc74ead5a948c47c43e9c7adc32fa52046b53f12b07b5224e0d8e93e4\n\nQuotient = -1008fcb6894d8c411905136fb3e05b38ec5d8df35db06379fc2d6d3e3579bcb34fa6e021b98b899d9d082c111b1a6ac8e50418fcd5968ade6aff8828d8e4777\nRemainder = 3d7dca387b00c677d855fc4af4d86d86331fe4309929039e828765f0937990bffa964d3ffc5d4f2f4b8bea978329e7cedb847c7cc341ee52217f903ddcf9446ce4\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -ea045323f406bd7ce25b3ab4993b5f6dd92ca80e3a02607a862deb13470ccef229fad67ae958cd87fecf4f08d9609595077d0d1360d9fe48c4566e237aa877e7b1\n\nQuotient = -42a50301031962754ebf9c4b1e125e6df3dd40ffbe09c044b1cf4b62ffb4f92d298b05933a450bcef65e86398da80740a610ba45928000a5c12d26e9f6a4\nRemainder = -c5485b82cfefb3f980e0fc7c6cd89b1345a8fb942299bdc36ed4ff8916016315a0da84ca0ee2824dce3c7e5ed49d517c45173c9c8e30b224940af6cf828c73db8db7\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 384e523d5a687bd1a90101e43334894b6a27e8c6809a8bf5bffabc34d558a8309997dd6f2a3b7c1a63100dcc0b6647b444ef7e5aa4a9c52c7caba1ebd096c3fae6f95\n\nQuotient = 1054439945ccb5bc5461fed04e364c7a36d5dd2c0428872676debe07654b2ce31e435a90c81f2bac1032143acb0c49ad101398feee8426bf270bdc0229\nRemainder = -7bf919e14b2559ab82b3c1bf428d083a4c851a7a1fea44718377e9e945caa5cf48e0b1ad727e251bbb330292402a75ecd96a56db4ad07146533a3ab5a717d0a25a3a7c9\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e5cd83a644ec86b94f5e33d4dc307a2f14ee8653288145dabb2b5f894560c164470197fb9e37749656f47df343c245258627aeea17965fea10a57336bdc6b4a47443492\n\nQuotient = 62675274798218da426a54ed7158f8f737b7b3c328a9c351371f0cf61f41712f9b28741f187eb635ce45866762fb5fc5051776151d202e2556c5845\nRemainder = 1aeb5d1fde3c259917e430e6790b00484d0d9508391ba6ebab0f6299190d4b34f5f7d8ea2174974471a1e28ee2c15e05da645db971f699d5d0e80569b7eba7908ae579f5ed\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2622350611b486e6be7a7c1c073c230d604d782c2696038a3233ebcc3f01c6a711969094e47f49e294f2c5bcd04fb1b7c0934f19bf6e7aa519a8d4ec2c172ac59cc1a57b26\n\nQuotient = -12970cdd96b92c37787971cd8dd166999ff241be881eb9543ff29165a9c1a3beeb38b1910a5724ffe2b73ab95ac1ca88d3989aa531374d4ec6122\nRemainder = 627455cb555398150e5b4c1c53ee16dac8d80d9616ed1ef40031424287f8028a9cad1a10bdd8430f6f65368cfd00390c8d4355aa5ecdbd1ff0266a1ade235f33cb5309446961\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c9dac93cfb7abaa3fcde359e09a92ab0b5c06359bc09ae9bade3c6783064dba90b233b4c8d5c6236a13ef96c7a223e37bbdd931eae61e845e5a10088f75b3ff5f1158e833b15\n\nQuotient = -6742b3871dece5986d4e219bf5f43c101da8896f247521fa286fde696e0b71ffeb3b6a3e4f33710c9ab150b7a1f747cee76839c5e7f2509f62\nRemainder = -203b2d6eec9d485f7b439fe9d4c640bb31170af38418faf4daad577c30e44ca06efda55ceea4fbd959b3809fa2002b6e2cb891decb09334ed89ac66ff05502036b2155ff62f8aeb\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2457088096865cd052e9cd9349c6e5e34e46c89d6e860a36f8e2a0bb1e5d983e07d05e6f6b31edc67e4793cb4d40979c029c80a13e654b66c8acf6b894f615a3ac800bbd09ce020\n\nQuotient = 15eafc416460d757d0abbda8d094eb535262a71dd033c25e704a6df54265b6123247e5625da476e0c220ba88582a1ed94265135bf8bf1fb1\nRemainder = -64ccd9a0ae0b0abcb5507d51b2e6c8e52e67907474605c439796febda06eabd8a3185fdfc0bd088cc49fdf564b5b45890b07269c15b1aa2f993cd9872b97aa6cc37dea2f03444b3ed\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -ab34d3906d8a2b806b22c73d44948d703c1e05a9337f75cb0b5df5205c5e2d23f8a92d8381372f9398c9ac2f7b9302b83e48b26512ccd0b06e6b8ef1b930ec2678d71e2eddbf7349e\n\nQuotient = 3b22916d9fe3145fcc3b8872bebf5aee4e14235f618e0aed09199852c6bed80df39256d8407d334c06f4479f230913370b7d451fad99d\nRemainder = 1b02a7b97f9ac1f6306aa00fff0e59f55fce463ffdc640364a950df29474e08b67cdfcec0628e973d42fa1e4f98e988ec4c47e4915651a1731b71d5e36a10a0d1b3420427dbb79ba7d52\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3f74cafe9ab0c1b307cd7571fd442665fa3205fb2f45b3811b92d1d38b096a2025b8170663a29c52ca84da102e62048e583fba96a594c0b23952fec587814857c25221ff2cd0533cba6d\n\nQuotient = -12ffa4b6fc369404968911c17358012b993c18c2ff34122e06f450d3d441926b5f5638b40efb012d76d8bcd3c0012d0a0ce5d55c596\nRemainder = 64548684fd5f6c816bd296234740a4eed772570bd4a48852462f9cddf14f1350ce7c7c6a58aee8f66ad7df87927458db09e3af08eb5376de08444f35e5171cfa0992fb27f70b81574f6e8f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c58383afca9e1c480ee75d3cb6b0b99ea42e827d39fc96bab6b0dddc97e3eaaaec02a74847f9f7d49937f5ade3580bfcd491990737d172d4079437067251ab403c36a9826e974b113e2d2a\n\nQuotient = -4964410c2b038573107b0151b36177cdd62495e0dbef536b59c8aacb8836bb45e7bb014e5022360621e8e82a273d0d462b8eb6fc\nRemainder = -1250c42f8c9b129a5c477be446b86356edd1b19409d362c3a5fb5d59c30f1c3fdc1424a88a0d6ce20bae885905d98c8a5a6495931f73edf4c60112ed78834e3bff6de3ed54c867fbf16a1cd53\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 33212ef4a8e80daf1049ac6f639f8e1990142ac32f7ebc97675ec90f8eb1a2814dfdd295ae67317253d0187ad33f3932a3a7efb056d0a3c87d28e64e23e9f1de751ee6f0f61c6f39d08d72f0a\n\nQuotient = 17f77efddeed52ef2e423bc2c10d2ae15c97384b766f4108474964c2a44789e61249103d9f5fe00b4d612772dc6ea12a42e395\nRemainder = -1ec95323b7b95169d5ec0667f3cbf683e98c15dd0fe44df4ed9de9586e43f1f69337e41a6d11d889452665dc0b03cf8d9ef2effe0b350eeb9f6468751b8a2c42608ba2a33192b770cb62381a966\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded49", + "9ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9c91fdf2dd1827ed103a102db254630c278bf8b47bb12a342a92f081acbdd8ae5f5476ae194e24b187011ac25b19fd09e6e690777f9d3efb6b3a32c8f5905e1478a27fe4b1adf17a70abb4e7571\n\nQuotient = 4f5dec525ffc737094f40d27446ca0be5b7a2aff02d51d99609165c4cea0dbbc1d92bc0a8680782b616c149bbef7f5ca912\nRemainder = 1bc84ce56a9a0c74962681c02ac927051c81f3824d9f3f0f91465df333ecdb449473d9c26ae3abb9509add5795e89ba5eba6ec7c89b114c86e6991ca0c185b34d6e66925a14fd82809dbc4936d273\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f47be01e6dc6a86097676fbd472c2af0c83a2f743fcaa885e44fda7e9f350e9fb7a8cd07fda59ccb7963f1e95e6a1236f5f94939decdc85afc0e523c711b24641c844cd3113c17fe35ca988ba407c\n\nQuotient = -163cafed5bcfdeda88555f30bd4cc2da2cefe2bcec9a7c19c36ccd04a45121a5a0dc28d0bf6ab7fa4b78933c47a5d5286\nRemainder = 93f856077f5b2907cefcddc4d767ffeb0acb7af64bb9dd8a15dcfdda6c244c24fb8404ff9ea2fe1dc337faa05930d33cac4f61e171d0236e222374cb3da76396ae1329a407fb4ac652fcbdc568d0fafb\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -a8bfcac452a5e48fee9132b73bc2fef771450143ab80aabd8690ce54c9b52c2b5a669076a7a35fa6d926268077bec6d90b722b5d074f28ce3843fb0147e567c45f4e91a11416c082762e71b5c6129c08\n\nQuotient = -617dbaeb8c6f9d584e8eae923c872048f9f9bf039ec6b50cf8f09c061bf79acc3311b37c2502e560848c05ab316fe8\nRemainder = -1ab4613767c4f1f7d127e848f2bb7c72a3a9e1dd6173b63198b80d3bbebce6a31494f19b53ad9e3a77248e6f9b26fc59060e2759a20dcdbe785297bbd912da9a1819527fac550d64bfd20ed1f96450c30f3\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 267d9397138fd0374a7a58593d41627ba1203a646ec2c04997acf607e9d217b8f40183d2f9304447d6f7e727a476e636ded4697a5ff30a9ae3d249baf97969658209c1b32ddc0edf920b0b278e9b5464313\n\nQuotient = 10ad85703fd51870306c5e36b51512341d6d39e0bac47a03732787b2f62e49c76666f7f49b2596de6cb5c5b2f31b\nRemainder = -846b4479713bb19ebb8c1f1b75d2be0f39fc1095a3d2ca149b5565146bc19382b86e5ab0d098ab1fca1ce701d582400190fee34b602845c3c0c498925710f0b9e3af2412ed5ead1fe03d77e9b2b407ac83823\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e0ffa4e120f2f46fd1430b6022fd03f71a22f9b120f8d40e901279be235b32d94760fb8c2403d23cdeb728ae73e2b16af7322d6ebd5f5673187668c99805e700f1e997423886bbcb851448dc1ed4cd66d6598\n\nQuotient = 41567bbf616ab41da51108d7edcb5a8a4877c5a8663b3aed7559421b1fcf4b535a54989efedfcc935b3917fcd\nRemainder = fc026e554a0821e0d36b796fe6a676fcd7383a55fd6158d78ace4edfc3d8aa87c65f0eb41baa2aafadc51218b0562ff4b5c9b17bbe84afc491d9e309217a5138ad48dd51e1b1a9aa51d69963b608ec47d63fcd3\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 396e9b45ce43d3f89386cfad8ddef4b483ecb5173234530c67447ab74629d246c18b9da09522c77f598957e3fd2a1c0c9417399912fd547fb1023ba6b90d63d223bcbf3e7ba155e51bba7e8635aa5c39d2b9dbb8\n\nQuotient = -18f1f395347ce8df530d9330c61c0e30ac9531b50a0af2ae7809db1258285c15ba7a436121287990fcdbda2\nRemainder = 51417b9e9995de34316a66a2f70c146df8e36952fe64124819607bd8691a465f4fde98e590dcd56f0faeb95d1b67751081c2393626713c27ec2a2123aec2a4ec3761e5ace4aaeb612d46e52e16d72a186d2ec8a7ff\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -966dfc779cbf9c388a84e947d1128e2392399ff45d9491259c7cb19589154f82f41e852e0c6bb5a728f6e87ff4ff95abcb9b2b57af1b6b7fc125497775ecc1338e4bbcb5315f7afde4e283347184b908545211afb6\n\nQuotient = -3fd962e88dc1d501fe9335fff8b6b2d50eea967c3035a3dcbcdc9599b81f9a445ed5a6ae7413b8865fd4\nRemainder = -97f06f6155f8d0ee6850728192e0b4fcf55fbd9ba982c5f1d598ddcbc4e1c4be0e209fefa6ab3b7eb2b4c645e4dc40217202285ab0a7270d085dd9d4fd24e5293faf6797b4c3c79bbf3ec63fd82942549f9e8f862297\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3ac566d6b2d18572360fbdc626ec488aa316a74f33d71a17a2d0e1d2bf26395623eb91dc4abebf2f944e9bc3d669fae2e4332088e9ff9d9f43927a7888b1390ef60f05efd6e63ec606ecb3e164ed6dbdc9d088586aa71\n\nQuotient = fb5ce21bcf28490afb64e6746a1a81792c90eae17407c0b4c5ebf2464eeea43e516be2c615f84901d\nRemainder = -3d255bf94c3d610c32266fd472d070c0f5e7dddb88d32723b2e1a20709aed2faf28701e0d0227c2b33ecfa9e708e5ac354a97be732b786210d86f1f05d191513386c580b1ad1f4ac6890f87fd0d4270f23cc5c2064502c6\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -eedb64a6e204ee3d6df508830704f1d5b2d2e627698d38a114c07458ea0befd593a80dfd2e08fcb1893adf57061ec4fbcd3130692de7c46f5ca51361e9b79bb7a91963618b8e5b7591392a5f0e3be954e8b9978c97f12e9\n\nQuotient = 6933a3123d0b32693351a834751345300c49324b861a663e8700bdb3b70ad996747b284a8ea5c02\nRemainder = 13849ef93cbc77460c3c496e8f31f7e01a98c21cdfcd6877547161f9601680665b394933d3a0824f0d32854508c89f0e4a0873280c779c7ca636cd89cf6ee5d42a917b4f382be3b9654039f623c11b43164827f870fa0f0781\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 23ab6042240a7709d43de7ee17332a9710bd0d913c42b3591341527bf48d5bc30abb962482292d45a15cb03c9457cc8d78d1e00aaa63358427b000e59e4260bfe1e2cc603e175d7fcf02bd9f61fae3740cb8e10a510ea3d1d5\n\nQuotient = -10e67cbb33dc6e24765893a047252766c2bfad8385150689dd4fec9ef495dff63ede1fdf78bb6\nRemainder = 9dabe2cbc734b910fa1bd25616daee5657d25b6e4dbc2cd93cf8549715c87974a8336fc5070d86c11f6b670d4b3bd5ee8ae3af2bb321fbb4f8fade3f5c6c2d6c366b4d800dd13ce897f13b0d3fb79f1d9ca525b4e7286c56ff29\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -de093dba98747499f2876c8b6b7a6b9587284835ae35f0716dd594c826cdf5b9179f2c6b08d800a77a6936602ff2b64ee0b7c94493bd5009633f5bbe423454b7f018ae96c21230510ab4bf5db394ff153b0e9eda3ef90eb4c253\n\nQuotient = -521f5e35300b9ec2742ff472cf61235dfe2e449772afa638b1adb812cccf269afd164b7602\nRemainder = -2ad10e8758e1d358d4744ad344ce319617027107c0b8db195d1b58c6e6035450c9b377f026fdf9e5737750af5615cff2ac3ccee623c060d779373136d48a735b353d64bcc5f2e6ea1e46083fd799b5f57dd5ad0ff3e6df9764af977\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2db1990ba1e353a1a62de1b914ccb691380b6ea937c13621a29f0a40ecef460cea52cfbc77d98706fb3c9939ceaaf962fb8003b0cfb40535e0dee22e8e7d04b5648fce2e58803242c199421cc4b26cae776d3603f2ce410d", + "dd1e0da\n\nQuotient = 1d45aa6fe6837a1b7ac95efd55d1690b66487202949a286fc85da7ac0b50b860215e44fb\nRemainder = -7984639b596f1d4e6efea9d8b4719215588620ac959034b303584679a44fa84a4be0c89fd2e29f54e62959f9b7a858c06b0cc051176af82d4b85e7334555ba11c39e6cfa1829995c383ba81dbc220e527e90a1d440c1d069703cc1370\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -80316fdc405bb002990d3ef7d0e98defcd1f0e370d1e51db2d21ecbd96230baf69d00b168afcb7b8da9edc3ef7f6621ae5c5a0d7797e5c92283342e42468dba1036fcb2ffef1f493ff97826477364f6b5a41dc56d6389a01b83eee041\n\nQuotient = 3c0c3f7a777e611d1bd0d17d669a1ef7920b72ea8de06d4b415a73b836e37d6cf0780\nRemainder = d8c77134a75584ecd5ab29e97a909ec139464901f9cfcb1d3d9e29a63d204615b6845d466c8710873980f107c40ab54eca9f8933ef6d726f9bd0f3e9e97eade5eb1a9bcaa7b01b6ad51ff3ecf67d6e4d345f128e990494a2db434fcd3ab\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3e7dd961be36c0c286eb9e78bf3b33e6f9bdf2c2137a0c660f1d21dea31ac9a044e526bf47ec8190e137a60f1f55e947046b9cd04a2485679e48cac80a1bb064a915208889289d63a6e338cf7069ad799861c31ec6eafe02a4ef2c2641c9\n\nQuotient = -178d749de2dae3a2ea4898c59aaba98ad9f340762040f5aea13cad45a793f1256ef\nRemainder = 6c5d9b19aed9f099255b6e3d251aa50d1e534e6c86d82eebe097dc8dd0748201e48ac62eec070a999c21f5c7684e5a700212e9079b5fb731321dd1e16ca82ce80c1f5c17fd1720f1353bb90997f47f5fce335a43a6f59facff0b3724423393\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9f52ead13916f9807d0cf0c6699578af52c54816828f22de62328fbd7b4fd6c3740ffc82af4e24892092c7ecac44b5e775944445e6615fce25610984030a345731f944128f5734e6e315a0ea97aafd7563105695d026880d065761687b75e8\n\nQuotient = -4fe43bfa9417839ee408b254603c3dd176653b6915a89de5b781b400162fbed6\nRemainder = -1c15816e03751a203ae23c48965c8541849b09996bc81d28e28d7871fa87d1c3b2d383c056d3084d7d01d853bebe270fe2c0839e71851e169d417c47caacab2aff8a8e05f65dfb20eb17ed8f67475702fa83087bd868246cbb885d52639797b85\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2ef8419306ebfd215d9079c7a2b959a53ca2f4553845e3cd32caab2635c0e77fee8c5c016c121e3cbedfac57f810c132486ba78df9e719a976e0112516893f14cf9b89f95a89aaabf31cce509ac8e7e62ec3833f0be4336afe6d7d73518141d39\n\nQuotient = 127e8c06e12943017f9dd57ca24dca0ead230092811d307386c81b6efe009c\nRemainder = -24f3431858d5aee412443feab243b465b849f5dc97e4de4db88c7adf774d9bdda65fa0a28cf6b18eac6078b00cbeed2ac406f8426aef868d4b59ab045825d4b0a18af6c9105e32abc72fadef55b221278d329ff6fb9019630411bec143c4156df7f\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cae6399216401dec0f8ff5eaca884ab061469082ee3a18e49e0b4d5f9cfc98a598c373249a8ad2374e0b3de71370e93a98650684fbb931aa5d8b4482cb0be142492bb71743c251346df66896806f926a4a5dd4c16ca3294f01bb998835e6583d29d\n\nQuotient = 3f180694e59df85f48ac02b6d4faa26278af9641db18d79f198da5d802f\nRemainder = 36cf82dcf8c7ec783b4de68e0627a4a4b2a508637c176de09feef62dcf382bfa5d8b88539b5ca2cab6cbbdbbd0e54c092f00ee13f4a352cb570034cb0a012cc0fbdb6ed32967f3b81d146f352139bd3d9a5c27789468b7d79b84d6a8f6085f859532f7\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3b7983bfaf565c5ca444367654a07b8bc2bf7fdc04ef12128c392bef2f6b67d9475b4d2f0ce1c380913aa98616fbe1d74dc5c9d64df15f5c9b87a8bfbcadf335a6e8f863c7a01ac175a7d79645ababa5f961fad7d1b9926f7284e254fed33765339e0c\n\nQuotient = -11f635baf7b7d613e84dc38978a21ade2f4cd741d0c4f6ae592d93af9\nRemainder = 4317c686dfd56216bc4865f8dcb6a3446e13d8b33861e74d6c4a3223c387ffb8caeea0141049898609ed1abfc2adbd21756cf64a72272aab6c0b8f2177419abcbf9086635dfbea80a7b884181f2f2ec9a402cb0505e8208909fe062d5e6dc7094d66af62\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d0ea50558197566f22704e66a70328cacd6f4b7ca9b00c16b7c4b4e7dcbd47c9b2526b3858ebb4de7a571ac570872f3b44ba1fec655c0778a8a87ca24851f6072c5c0b7591b5e67a8cdaca78fa46f201e02379fcb9a8470e4a4971acde36cf501d369751\n\nQuotient = -64a078497f85588d3402355bf3e83d25ca1f0ed2c24a395ef6de6b\nRemainder = -87fc31ac66a24ebd629a26209ccac1b2c85e52dc83c5240269ae5a27333f33d31152c9470efd41472af034e8536bbe94b0a49e892b1d23db3c13fd84b7395d7e3f19d7d4cb4a4c07dd1860826696cf7202483446452aed2b4980388e7eda0ccac792d77a33\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 254a85bf512d9159b00a70678239902ee7e15ac2790ce5747c4a4743c6a0851e6a179b64c75acf312dd37a7b82a729246f79196b8a399ff476c48a05f89c29fb106bb06ef0300c4b330a7b2bcd4ea1e82584c7a96b99ec2131c885c5851343cfa6ae4d384e8\n\nQuotient = 116a06b1d38067cef9f55875fee1254c8ce39b42c19fb232a287\nRemainder = -c15a797fed3810e4f536e9509564b2142ffbfc0c961ee5aa923d43a824765c05d2a99fef79bfcb6310c77a91d9bc6d0762bd687493865de270c99989e891fbf6da7ea5c7c7a1032449457eb73222a011bb755ff44e4bdce8e86f8aa9f687840c0832f7fd8ce48\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d77c14100d19fbaff6334ca6aa504001a1d56f274632dc89d48e1d517935503c26b60c047cab9e186a55b72439761c884f63fdd2a38ca1acc653f6ccbb4b7262e6215e6d00c8829b448b7ac8716fe0bfdbf8088c8c61eee8f8db43b7b5551f6278081ac2eb1c5\n\nQuotient = 6fc9533f6d0e6c55494cb1b319ec47bde8e621aa92d91155e\nRemainder = a1a70f674cb141a896c4adace0dc58cdcbe2503fd0ad36ce348dc5b8afc96d0f2f8c65bbbadabf2920012798b7ccaedbe8d896dd2674082ad3cc75b54c5c190ad56ff34e8cb5dd29c031656497d48571295d6da396d5f4cdb652732d874a79a674d06a1d7b979f5\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 21917f48bb8e65646c618068fd9069c06e22ce8c679a845f9c4ec843849010abeee12e2d3c61fb963297abca30813c446f2ae82e909ca6ac7839fb58974fa65f3b5d91fb8b3f99d948519ed56653d50026d694060208cf48e3c757f64885b4ed4328c6f071e9f5d5\n\nQuotient = -1abc689fd19523d2e295f260d248041bd00ad3009cc7581\nRemainder = 1ab5af1478fe7373d012befb319b53ff9e36899c1749ea763fb74f7d24624e70ee78faf3115c2a423629528f45295e4adec7b122b993b5c29260558be4831df06468bb1c63e8afcfb1b9b533ec6acf754563d2ae25e2adb4cfe5ee3024611e03a156484a130ee01f3c\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -8c5a7b6bc8ed6ac015ec24efff607b0446c1b736dc8b409e2f433e69d0ca015d70c64b4c924175d0e0102ebc3e1dd96dd4d5bb01cccad229e699f9d8f9ad0e04339d70cd113e93d50c10c03083a81264396", + "f5db2d979d272798ed30efa15d52289d0c72f42582ea56f\n\nQuotient = -4aa210fbc0457fa7366a8aa9a3acb3f9fce812303ec9\nRemainder = -737bc4fdd3d5496fc7f936ccf14bfc3d93f5b7caf4718c444db7a3228b41015c67aed304fec7704ea8238ba6cccb1e94cac3bcf4764a44bafb49e5fcb0339ae44c0114cc304b9c4370363657cd2bec09bf962ccb21f6091b081e71d2bff8556600576e18d4f78fc68b12\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 324774e49bb429553c10156e8db122670d6dcaf6ef5291f515c517d7ffaee36ec5ec5ccb4d12dff71ae7a05bdfbb03ebaf4dc6c4e8bfdc165b77cae20153c27d53bf27d92ff25643b4888cb586e773955a1c02ecbf0fa6958a8ec0b832332eab2e449be6e72c48d2f1ad1\n\nQuotient = 1c8631a18d189f1fb689f896005f2dd2098e0dae9e\nRemainder = -1a1ac9612fc3354056a5378de5b315f12591ee71f0fa9d8a6b2ea2b1c4eca9947e5c4f5ed3d4b78e69ef7a1f5a9894b9c7d85f6e2244ae76881eb06584eaa98c78b60b46084b517f4882758691f91d9e2acfd580d5e901dae14ff4a4fd6b0d7c73450e4928fc6f02fb5463\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -838df2a27bbb033fa0e581073b879d6e8747fff38539801a1870f2e52d91bc84cf10f2560e93784650fba080304244dbfe9da679f207b6920be46b0214a1e490537e56d99beef3f58b30f311a12283501ad79a5407ff209d19a6efd0421aa144e0cd427380d89bfae5d1f5c\n\nQuotient = 4213d04b9f0b30026bd355404bee887b22b2cf9\nRemainder = c2bc097d1c20f050e88912f066b658446cacc7a4d510343a8d88ed007a8c0cfd5d44fe5f067a0e81536d121b39f2d0feb8dd053bb5632e3f9c04be5f6bf4091d646860cd38c96271cdba466ef8b7e2377a51d5669117e664269fe3c08a51b10e1e019ac063d670a3c7db12563\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 38ca0c2f03a5c56676a2f95cd7a69d4aa2085343af6b1d2a71e0d1c54157ec0e8f9125df2a499cdd484c04feb23b1e0042ca908db74744584036c79f21c25c40401d551a65afed0ef35f1ea000fa1a99cb29e6307f6ca0304145f7e483d008cf9efb028ebb654115a8c6b87a08\n\nQuotient = -134e043b3b88b31f89ff4bc709cfa1bd2c1a8\nRemainder = 99c1c846cbce5e9a26c5afcc0186bb1e43b2501ab3205d13fdf01dccb9b1a935bc1cf8adf74d58f1c316381577366b6d126da49991a0d5e02acaa678085f335ff8b8e975e5bf2e52a05488ebfc21a3e0d0bc5bbe67442f77bfc3c1f0c03b7f7ce42bd0fedd8a498f018d8cbea47b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c261a6c562fcdd56e67fbd2b91027f17c95da43175eaca6e4069c16d240ebbd240582dcde953eea739a4668fbfcdc6af8ff3ab58674c95de90fdb43f64a61108b030d644a44b0319b912bb563f61e520dca9c88f411b32e99c872cf00a01f5badad584636352913b7429b99ecfbe\n\nQuotient = -448c4922b7a7d5e1efec2c3f41d0264b76\nRemainder = -2599e928027d10d3a11056eb719768e5edb1a625fc0b8a1dd4439ebd30a82bfdf89e617ac7c71622058cc64ba32dc242d96fe3ecb856f1b146f831334af562cf88139a99410dcb869b9ad6ac4826563b400b59f55d8fff262dc920fe525b12b2fa167ec237028a098c9117cb77bc3f3\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 36be11eb72832f8ae7b6bdf689f794f62cc1c885e64706d14a77a11df9761c2e9cd81d8f6a0ad0cb1696c69afd80c8bb992cda5100cf1162d600515568b9dc9c81a518da9d240888d4984df65c129ac0b4c557b4e63ee5be79a27473ff5bca58e559cb04c4ac93b61545e7351bb6514\n\nQuotient = 152474a1a76700598c18d9301866ec00\nRemainder = -274a2f9e2bc5f9d75f9897b28f840b71bb10a3e4e7a35ee1dc1150be61130b4e0e987e8742c5edb75a1ce3158eb8bdb7d657b8ba39436d7c88fbff160c7488ddff2f13b3b95ffe149a3d0d2d406b1737a7671f69c0e5d7074a151cb2776b2d13ca24bec261662f2967fd22339ed6c3f2b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b17c79a31d5085b49793b6a6d628109a6047e3b1afc947e5212d0a9ae32b1955cfd6fed07fc60634ad15f32a9e402d7d5f750fb6d1ad958211f9e8ecda8990689e5212cf72b24e9b51bd07a6e0477dd4c02381d0ab6c0ad3cac1f620f723ab004880800736804751349f6bb19d3db48da\n\nQuotient = 5665f53d5a7405c83a5ff382ec376\nRemainder = 252d055186ec896cb3142c9e4e49c441e2ddad365b86ad21ae4ef1c522d3306c2834d6993a5e1f8c64a1ed582bad8ab746f7e773fc004b1c47814f73560db72f7237ef6e2f671d3b19a8777be2e4c662a76db87ea64f32c48ea371b1ffb15df26726854a417e18afcf49054c6d2e0e337e71\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b6eb2caa3ca650be02fa199e9ea6c48646a76434e268713753a547e49571f9817ad396f2cb7b16d307801fc8892f0af3e7f93ce08f7955a8acfbc0b56add4b4c7ef7351f60e402b9a8ef7fe02ccdcb4b00b7ffe78c7009268dbcf1d606c3a1b5307d9a8ee6121c6a635a742b8bf36b56cc7\n\nQuotient = -eeda035247bb13860f228d8f2c\nRemainder = 3976edf710ab42bf069e5829de7e16962d1b765f6ae6ad0ffabe723e21ab01cb9f3f5f4edb1d8c13cafc0556c0aa93d72dbcff754ae9260abd294647b71785bb049bbb865a26bba22defc458a14af019a796e942e77d03484028aac2b3798fa730ae0193d89728bf80a8728715a0807b3c497b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -fb5e55f261aa96f54983869d58b3e9f0757d363b9c43aca5580b7c0380096f396ec79d1b30037702c19be5889fc6376793cad51975100f33ebf43e0897dfabcb9adf3adf8d845aa7589ba1f6d155b25f73dae3b2f835595ad6050401fd4e6392012d06194af415b810b0c10a53bc56350bfcc4\n\nQuotient = -5b37eb0c3e3f8f8d9ac6f4e4\nRemainder = -28fde388257b9a11441c592580cd38caf2d69e2ba57d43151c77d26535226e05e08a9e6d8ed470d4354e9f46b7626e5f2b22b652a2d78f817bb51598c727a765941fba63510b58fb3dd5f30717f237da43b42d20bc260b06d488c9c912bfcea1e7808544c58960a3e1355c50c889cefe75d4d9937\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 29232a3fb059242cae6e0b419ff13c479048cfe46a9063188706c6a3842674b16a1aeaf771c5b0ef401d2dc8a57f6fb4fe1b3c7bb545c18ae763e39421e6a07c4469d234f9fc737ac21ca67a5553c7ed693eede4325dbd132dbd9889d815c02f426801eff1f46e7a52f72845234acc6c153f34065\n\nQuotient = 1c7ac058af2e7bfbda9484\nRemainder = -54d7aa6dace87e61e24d87053b9d094bd160916b720d7cf4f740a4fc5a7f03909773d0456c530ea0204427146fd44d3ecec51d8627b5768de1494bf42081a8a4fa97163b0b93b59e70e533f3257723e441cafa4aab471ec4086601021c4462e1f74bebf298ef45fec98fa8e6ea97415f84c93c12633\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -83c2cdca7577b32c20e9e20fb498a2bceb7174ea9aca09d4dd2fc7a1d3b922797b4e9640c7eb9dbdb4d93c7fb9daadd680c1c7645d8102d77e9c877a9f65b13239f9a650dceefc1fd41ea9bd2b38a622bbec99cfddbc6e88f377cd51cc29fd17a27f3d0d970403a2aeeac6ff9fd69c3bbc5c2b0fe7e\n\nQuotient = 472df5f4393f33cc382\nRemainder = 16579a289cc776a47611353e158c43dadf0a78833396f8419fcbbe47d90c7e840e2c90e73e563e6c505bfcf691120ab0f1e9ef9c31db608cade70eb8e487b1113a46e2b5c7f4a172ad99b502eacdc0f91c295fe608389e61d030607a94d09d349fe1a0cc46d1e07c8db533cedebcb4a3b89afd8b924993\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932", + "725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 34b7f6780620246f5a0a92a768072185f02e57a52db1d865c21c952f4386ddb7e2dc1df076316cb4f2f394397cbcde1af0197fcf33e6428e6f5d42a9ccf623f75fae5940873097d4591d9b1a4cbd00074d134272700ab06d901742da695c3ca9d4f917a808113336f883e769fa8051cdcb0cad7cabd1cc\n\nQuotient = -12b4e74d76bd306d9\nRemainder = 8768fbe8ddbf60b548938d8b4a74c4a326ef335257e5f513e65a7d2cfbe9d456425ceb719407bde3cbc74c9c978970597b5663a0ec61962e77eb351adaee2d2d37f1fb55b5d2ceccf282ea3a0d398be1dd1b166d55dce04a39ef434fa392893618003adcfa61401276ce4e599051ad93152e3477ff524f0c\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c898a753745f0fc178227a7004d917557cf3dcae2e85e95aee51e137b29c895755853ce2d61f214b80070174cad8ebc2795a7d070790acd335b383f9dc88c01227eeab85f1f29d76c1136ffcc7b9fdc073a3a03d8812c7c561b32d8e69754fff64acfd64994b7e9574d2a7cae6bfd5a6fd61dee7ee993bb7\n\nQuotient = -548c97fd02eca7\nRemainder = -939e90e281f97a433eb1c6510668d0fc448f03d737d92693b6362c692167add7e4442105d60ff3db29c03ed06c3121aa4a53c4625906519a4092e4821c918d2264ed0cf088b7da43a222877f3ad9a9fe8ec06fc66b9cfbb44e0fdca1dbe4e461dda9b85231b5b9733e0c78852da83bae557755de3680ab61d4\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2c61dce04200e725ab0ecc5016f66044218391bdf650bc0bd31f3749ac06c24707e79526ee459ccfd4bc22834f8d23f391f2e99135f92b5abd0b04079ab75a263c0e98e46edfb440cd865269ed7872e8c1ada312df1bfd6a5fcd2ebf548d7b7d1d75bc36f62e5e9d15262bb8652a8041e5c8f4d673eecb777d1\n\nQuotient = 14622572f311\nRemainder = -6d197a84d2ed486327790059adb5c073218c56345f48c15caf6892734fff0aa7af4782738bebf24d984bc8adb3056f67e57f9960001a67fa462afd8c57ac9d60ae6517d58ffb4773b637ebe6bf2473a5490511fcdc576a4c40ed03b3afcb2fd27c57b66a26f6d3f9b2bb101502b1117ba3ce7214c9db6302fe20b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b818674faf69bc92085b7230d9335d7bead0413f2905539a54e8d1233843ef13f07cb5538e0787097cb24f152cf54a92e62ef143e31cfbbaf3c09650b14229a4f61a783eead26430949c88a87f1618788abab9728aa52dd8419f5d568e6a109f278b2afdea91cdedca43e562d4bb8fb7f1b7aef13992fa7edc320\n\nQuotient = 5cdbb03ee\nRemainder = 1cfa68d5da7a600a7ac598b9ca1a0759f972fd9a46ba62e5e96d8f6f00fbccd0ab26ca03d14470b43793411ea9803c9409908625fd74ef8f9b2d7c2064b2e3439adcb684e6f01432a1feb0f492fcdd2b8b5a6cdbd0bf460272218bcf763974be8784e5306c219ee535baf5541b8580952e3690b585fd99f77c46d69f\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2869338cd16322409d3efbd328b27e2ba53cbf71816ff5c093849b1d866b8cdecbd6bd8ffea0b7787251acb760f85c277ded21e56acef05d29bc728cf44f55be87cb4c8913408a01a1ad53461058a1cf94538f05ec14a6d3eba804264df957de7eb1a61b794a1141218966463dd42402c260c229241ec46afdb5a06a\n\nQuotient = -f16da1\nRemainder = d8b66b622b5a54963c2c84aa186bfde5b67a3562e07a23a5f6843bdb615a3c5d4f007ad8b275ad7e4c5b1436252efe35699cff2e0546e6dd8c7230d6ad560c51cd54db6d312be32ae4c708e9047c3a25c211e2566c58d6b9291de31612006d4e847c6916702be99b3f7ce40e1ac842908acb7f03dc120aa8998c60737\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f8af8fb7002a9d2218dcd0f0c139b8e3dbbd48e25a5c910f6d0b6684bca224f62768b64955580306bac6bfd45b99ad77483563fc7dbe015edc06bee3ff93b0afa8f5866c23c7a7570b366550490c97ad84062c2495cff30717aaa965a8e15e270b504dbd4fa943be4f97a7fd1f3b589bc9fcf4f907a7690d99c978a374\n\nQuotient = -71bc\nRemainder = -13316e9b053a06520526f579718c326402d2a9686d51a340375cb53d7cebba99c8d1ae93388db0a41cf55d5753dd1174014ff3305fcdbd5b02de9e90c45ec0d2900ebf6ef847c2a045eab7f80f07f01c81b9fff093a779a280ae42239df79de8d2ec4bff6723788c86786fe276ae6a4dc1472442b552258e1e5b597305187\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 20fe256859a2e4c4f77db6adef78b2aa4758b29ad0787ce7e277bc68391d5949bb4dd07a9b1a79fe890c8a760871d81adfd3858e27d1bd6de33fd31b8aa6131fef9130a50f995c3be1d615d1bfb9878804b7f6494237d8ad78ac219488f17335ae54b494532f03a3fc8e9576cab6facd90c662658878fec86db66bacda3a7\n\nQuotient = 10\nRemainder = -23e09736f469c83f280052ff01071b1bdb52b7e2b061e8a1a8c6a4e091fcd7ca0b33ade885d928a11a3375599aedfe554d1c2289795daba08f07327a19a8adfc219592bcdf9fc5aee5961a48b3b1b5fc380eff5ed2ba7d7e564462397fb6c6187254ee41c74602b141d7adba99205d2e0b35da57efa96397b3a5d112751cf7b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e849bc0bfd9560cb90e42c8e4e88df175133c14466e530716d89ad0326b660b0e617b4efe8df6b000f517d3cc24d9dd4cafa2773dafd4c6bace0aba54e43c17e8e3ff9497a97ed83e6408aa0aee0e6485dd1d89d52520d1acf4d587422b0c5cd2d5e7e81fdcf842d6331779e800f96628206e8be020ad4021789008a641f67b\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22004040a65f9b6f120bb7243c638cf3a4cf6fc58c230da932c79568f68e31af7a7b8569aae77af671f8335ae68d6dc1698baa9d6ba9cd633a662101b45bde51d55098b50fabde8546f317ecc2ae7a39521bc075942e3751a349f51ca3c371f3b8a6cbbea3e11a334d677c07612bcdca767194c07fca78ea8a06cc3b0dc6dcb8ba\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cad46f410062dc33ad4d712c3b743ae2b7613576b2bd7c346a8479ed679a08e3644c7ee4f23b95f1cc9111905714b170abc37ee1003956f64f0a7e876b38d524fbb2436ed56069479d8d2e4029770f7801a7278fff99b3dc76280f35c7d43ee594073f725554a92eaf4f785c18a7cf6669dce5adb0995233241f3294cfb5bd8f4741\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2fef69f9745646aa13e0c38d77951161a1f881a7ceef", + "032698da3fce00764959f11140bec7d7f53d6777c3622453d4525fb068da48047609d18d463a8fbacde1d21035963b668ca11d5b9ae66db13de7a7a5b66a40608dfb56d9f9f0c8880426641083a05b5ff9e6ba0d6da3a04af1af01dc218e9b4f6ad7b1d3a4d1d26a5c906093b2c\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c50a24e5ddafb768f64677233c5cf09da1b4f06894bd68e194b23feb5c5d6844320a12a02d13ad012f13b1438eedd6313bac9c1f9bb4548fcd314988d8fe0ce6458306735307afe08a96a0c2bcd9cf126f529e48b7ff4b8266caa28c40b5c3d2a473ab8805c860d27d7ee9c032423148d96fad019490ea019d40679de7a2a3323e80979f9\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3a8682d0e5a4efa985dfa8bbddc2c0d72a4400b8b070a8cf7450aa8f831d8a91c9ae3542641b7a4ad793e232a0d301b82664fe2c7f20bd9bf8275828a2a20027d6056b211638b9b0220fa4252d058bb485dd3c4622b1eac97d54b9634b558ff1bd5bd11085d4f3d288f7965af52beaa922b23ac0207d5763c24c085076128e0ef7370eeaa19d\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f00fb238bc9383079c7ecad9b9f6efc622d58a76f2d5d40ec7cd7c3c083c459fbcf3d128df4d20ead5f585505515aab11c36584ca622d28e0cf037419a649d598346063a07e29c61b7a8e76d1949dbce3720d45576763aa0d391b39dd6b694c7cc60a1b4f4f107d87130402985695e1847e82cce39b8d0fb5c88bcf3b37d6dbb90baf5a8553c3a\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b809f6baacecf61198856d9edbb768ca2df2abe9b7b8ce1669fd9259732c8569c0cafde2e32d253094480ed281a8db230f84e780c6e8bbf3657c0b0baaf19ea973fd8daa2870c9d79f3695d78e063f9130fe07ce806a088ca267fd2820f10dac34b5b32aebec20e4362dce26eee0c29d2fedc1e020d452bc2499234d07a2a6e54314e3fd6dd85fe5\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -90ed75629073df816ec1d6dfedd1cdbed9239661e362db706288dc4d774d806bfacfd4b32c3013ec67d8c2af133b46989f12f809fe202d33d5ba53659bd2a9a85d3fa542de4a5c656aacbbf8899aa66ba816b809f2629f37b0444cd3a6dfc99103bcf2a5ee87790b8401be806b5d7fb7064ff0a6fc8ec769d0ccbddbc3d35f7dc4d388d8d28021c95b6\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3f60052c9dfe0bac797a674ca7f11377a24c28a1396ffa0f46acab7909543086aee1995cf51852ea4a21ff4bbf6e7309cba9848a7b2e3b33dbe660bdc58d513d16bc709f1f2253648b46daa7aa037332552db1da81b4ab9850ac4ec66621648fc856a71eee3cedc6617071600ecbc5ac8636233f288ec249b7ae0bac942a5fd539d03990c4fb28a46653aa\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c12fc156d9345cdfcff94bdd324429530ad8caf8afaaa1a82297eb3a8aecf2ac021384036749e489fae05e8776da0deca7e4325436bc8f383bed579c2d67a456c4e23871489780d760d63d0bc0d1d0ab41f06a091b44f602bcdc0bd4e817202e39ca6a934c0c9405adb5a14d24da895c58a81d1c7ce52734183e00d80a414ddd8869998822364e029b3f42cc\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 205dc6227dbd3adf8ee49dffd43f835882822b1c94f92cf38f5efc62f943075d80b33588973a0e0a8ff5e800ede21d394736ba98d4eedc53a9122f8c262cd09fe9e91cedfd0237003b0124d757797ee13cd03e7a3a257bd8df756940a4d22face9287edca00ca23e7d5e629966ef710b07e54241dbace041aa6d9f82687c3ecba818203adb376ec0b201894a500\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -82c30a9ef6a83d81b77825c71ddc563939b8508f1b7e44c725ae0f61006646ba9b86507ec9a4dfd3755ecd8bfb451c2d43a61599732b8aaeedff7a304ce0a9327e2333f75e9a010556ecbc3abaed02214f25e1c8373bfafc2c288ea36b8d5f848b76295a141d8f633609a6656c07f3d98177f5fa83833476dcd111aad179001f81d6013ca3a54cddcd8dc0ce7eb24\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71", + "372b\nB = 33aeafda3cfc20710f0b4a3d9ace4817eed80ca57ce6c82dc2e7946058a40983c9204ac95a1399fa633bc96cb10af3ddeee3ad2337c64391a42dc7794fca629e3e1e4e03a2ae24a000e7113b91c1b6230cce9592e45b6ee7984680b45aa0aabd7f56cab1a64ec310cefe5211821a75deef2e0c8e43eb467dea79dc8c03d2d523734498d079d5493c904a2ebfd8a3a9bd\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b897bc87a40211ef8f93645b1f6c981fa00ab3b12e117a89375400ab5f4c64bfbba01d265c7bc6f5e3a8e26de5de9df3b8f70f4a39c0eba577db5e4b7a68f751b4a69ff4a38915983cbf70dd7e066779405d572f5bbe0719c978b6865ea1a72d90d3ec8a8c146f20d98595036b3de88a7500d7b476644913e4b63e85c4e2632048e9600d553e560759770a902cca680b17\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 20604e080549e1c503049ebf4a56cf9447d90fe699a9773915b0a65588890e15bd58f55ad7b52bd7b7992a8b24704f1dfd5fd07c70aae4ccba5646405ff8a9cbf542dc334cc0c27a790c05420b552539fbf0a155861bec0e4d9e3fbf045720ea3aed58307d5738b64252a963f3fd5ecd0587cb4d7e159b4980dcb112e26c9c34f10a192e090ade157eac1d7a6f970871eaa69\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f11fc9682601cab97c25533b2599f50edb1ac65d46f1969bd9c3cb3717461627621c8cd401a0a0b91f3645b8804e095aecab31c1bab0c26df556adafdd7e7f4f0510e0bceefa3619e26b8c9a1bc613db03857f53e9eb5d4b8f75a8cd1429feb81edc705e5a779d5f95373d2243368ce17ef22da79a6a2672496bdf629171b7973fc4659c8eae9ae867cf38d6d7617029bf59d2e\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3cb0ffbd9ad21d0e86e4e4dab4d237e2a17d97356bdd305fda772fdd99acefcfb8309d813643c852f66e1c6c7fa41ffd44f8335ef7333b2b3e846139fa9be2c4ea762afba4e11263c0b5fab18c5efff2a18d83ee89844f5f4db2c1325f0f55e066a9e01030c07a85e2c9bbd37b5e767ebcc9b95f474ecff24df9ae52a19edeb66546a3a28980f616eb5a351cd399e5f8436f17faf6\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -b8aaffe779855c6ae51807f8cba780aa64bc22e8fa5e33f7f1dcb084fc476791565bc33eb37b4f791ef5cf46d64576f48b5fadc9f096f20c798355861ce5d24a7be1450bb871f9821099f98213d74a5e5cf83b895ae65e0e0fd096698463906a112e6e169a1cc0769df7a5ba6812300fdd33611761b6339385e1a70f8f8b2be7679ca216f5b183140e69586a27aaa9f2fac118118875\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2b7ee3ee34347dd89ba4a81415aa1269d0390346597b07444f0febb71d490a01b6fee174634bd88e8aa180409549b2726d044b4690353de2fb2294c8f69c612485aa066f68fdb89466760a85901cbc7312bfe5a6f656e67dfd2d4ee099ff97694b01d6d5b8626ab1650eac5267be53f5f3ced5dda1aa86bf42ae132a28fddb94902a515da40e0fd0586dc8b17a34af8eb03d06f70ab89df\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -bf8213944ba785e01b8d37a12de77b2ce1492f34bf6f67406cb51da89675b4f70f4d4f314f30ca8d65cbc48ee2fa1f0a3e4ac0de3a87d2c4c589b6812e850623d78ef2e46fbb555f6d3c69b211892c11a4a2dc3d8a9a19e96a07952602ed5ffc0232c140c3e828acf990e5425d8dd9ce0c1107ad1c6f96c8fbc90ffa457abab0d843094dca3c8a45ddad81b7850190625613a4851485f38fd\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3083421e375f0722b9397e156de47f77635d62ba1d51794469371b473b71c02e3722841bca2ca06b5d1cf1492bbacfa0abfe394dfdaa7bb8787550ddbd953540e9c97631d9a1efe0c8f8e14f395c82d20245cec6d8021f8564b4d66e7779c3245734c56fb74481172f4e349d9a113cd0ee5263c69ebf746c5285cd4c0fa91d9531f769fea3610c2972ccfe9a22c00aa62ebf52b3a4c6135f3069\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -d736bce537f47ae4797faad797af8cfeaf8a4fd42df1f7e61febf8ebf6e47dabc48252ff7948f3dbf8cc369b6952dc58f64cf09b4c53447d135c7a753c21b6052a9726a47a61e13628edf0f2bdb357f2e780ac1ae1f28f211296c8961c2955b773d7dc2904dfea96780b2877af133c9591a0dd54cb20884f014f363862478ee7ec45236bfdcf0321af0692e68f744af28fbcca827ebdc7b210da38\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774", + "ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2cf1708f1e675ba688c0d19eb61a05d2c8642528ea6b1512375faa732acc59ec04ea0aa55e0049144be09eae1292b6cba6db7a9823f1e912df6a5032bb9674f4f26c0c8244ea0dde7acfda566574956cdc33e4a27bcdea25fe255c19f218cc4316ae8428ea61d1bf865197a066b959c5fcbd7c9596207997d05fc38e32322aa189ea06cf5139522571661745c0d72b740dc6d842f1dd8481e318b5792\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -a9180e44a284b5bbe72fff46e55869f749b626ac33c8cb17be1fc260d7c6f460f24a89e1367112e00d0da4d213a821d09f103f35bc4eade5605bef23c5d048b1cfb45dace8b9c637af626a85fc773cf51e6602a7a5999a030030cf114ed6a4ed7583465b9303a72e7f60824c12329517c6763b0f64abd8ba2b9b26cebe882a51f05ef8076e527d53a213db910a5f42be5fb78729a3dcd08d69a709920a2\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f26e156b3b1117f7cec542b20fcc06ca66cec03a19b6f5eeebf22b4c0fc265df5ff06fc9dcac569735135bdc142b526b295225711efb71577b10aacda2fa446f5208487c725407c2188b3185237740c813e4455a6f1dde4f62916237f23164a3471aac0fcfe24ad1ce1dd81a6144f5861ad0cf22dc337abe10fc4a88b36116dc4929602ab48eb971fdd7a5ff747d6b9e0b2bff75c59621550991966a0a19f\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -9fe18ae697576dd36ebdb621d14cac1cfdfd1f5cbb7cfa8962c5a7dace96f9f54fb4f4cf2e650dbec5d1ba89ba53d251ecef7dcc1cab8c2ff3d77903f5fb5f29a4e8e3a2a3c05c105d5733b5132f2f8d88f99d17de86ca1191c32ad8ed469bb649ef188306f69f183bd0fcc32759e4f855170f88c0a3f6745aa98f6225536821bfa056a42b37535a622f42b009859c974cabf2e14f75c749d0fe5a01fb3ab0c0\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 33ab185854b20a8126884eed85181b14e75d4ee452958cc1043b099bc16c24b9c2f3e0b792744f230013907844496e600389800e45fd55133fff0cf19c9c152b9d031039eb90da568f9c5212a3ba283f4d1353ff8ff9dd04d292c265bdcb77c3e411716f471930bccbb8ddb819ebb0e0036dc1a18457cd97f4f5909a725baabbd15e8ce33875895aa8dce77a4dbedeb0271a2a4a17f77f5920c3776caa4a75ac650\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e7ca0c037bf8bad5f8d9c5a2737e044d9f7284c616156d142612a53eb217f57f4aa00b6daa424e6c0d9163939e1ad0510a1cd64fbd576f3e54c59d7aa6228fb3caaba7cdcc951e00ed141ac3a68abb9780bf46bf544fe0e347f677288e962fb69782741df49b27cbbe8720c6f8f2e769147d89df6e17e3c592bede2e696d384b9f01b99b31c505d67eb6193a8844f8c4cdadc9fe45dd446a0dc572c9da6e58ed303f2\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 22b76d6973e37aff4a09216e57662f186c0a0748c4375d6bed370ea61d1f6fac2d9bbe04487a629118b6b0b0c8cc4179fff7bedcf048cc529498bbd9cc81ef3a103d6cac49d58bc41c83f961b6df7f00c7171fb7d9359e03c76e4364cffae5f67321ce646e9b05f9c04aa16ea65389e940022eda6dc740ddc070bfc7e589b86fd1559dc320701c39de20d54d0483fdeef6c4fd012850630b982c2e243ac1ff918377ceb4\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -e6e4d69a82b83e26ef8ac0f4c3a211153ea6655b7ca12840e7b866510d114693049c5b8b22c3a097eac832bbd1986e60564298e54dba3316807ad64bd6c18903a0f22660c9e8d5dac180f57cbb90b176b842d5b58d6dd9f47499a037833a92a18f397238a8bcdc4afd129382fd6d200d3d267ca1e6bcc2cc65950831cb8e30bcc01665c8149b874c9f11168153c187341afdc43e4d8652ce4fbed9f9eac75db40d64344ade\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 319a81f052db21ee213c536db2cb8a71e0dcd0a9b2ce780a9588c38b717c5e487a337f82b5223f638fb552e92b826192e6a1c27771d1e86584bc6c7cbc5d9a6ce6edf2ea2ccf6939485959ccbf3183b40e410768c4665adf90a0ae2792fb4b5d8aaa06c6294e31893620decc3bc72fb4eb68f1e56b48e39c59abe869d07509b7564268d0b7f178ef09ef5dcde6e7dbd2a20fd1d4fcd707943dd63adf590a117ead1ad10ff85cb\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -eced809145e696ceaa0", + "ee8f831eca67049509b31a1b15e7fc86cdd97a73a2ca05bfea5f4b283d287e49906463ef36f2f8ea23c2aa12d5534c08e9769055e04822be0f8ac85f404f5c025a6833b4115f78da9470451c852ba0f24062397d20385f58c5aca10f3f09072b2592e5672ffb989a390abf86cbce74268aef1f4ffde730b3b962df1088bf8745105a7462379ce142f819c2538d9bba99e094ffbc4478625bc54df16c5e1a\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2c1ffbbb30e71d5fa77b5473392f95297b489c85f83013262abbe948842473154e00c86b2e354278844083f960fd746a3b7cb9baecb9c66932774b3a28f678d50dd8fe52fbeead43d8c8adad7c0fcdbe5e02664b0feb0ce214c5fa007c5fa2d08c5fe96787b95639311cc4b7eb2a7217c9c38c6d93444fa60c1f52ddae9bb2ec1a49a593e210e47377d3623cd2c4994ad9343863443911062e12233176f4a65ec715b3c9731c4a0cec\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c3bf056b905c0392a7b5fa57446ed350f325eb67d59f1784c744b04c7f4d8f5397db913407aa8a7f1dd0225c1a9673828db0d8bf3d4908ef53307131bf5b5c4c6068ad73b874aab98e8db33b0a758532172acd8b2c830d0679a8226537090166317b8eea91e8ee4a7282c0ab0ab6f2b7b63d728d22b534fdc88294c376a8d036ba9a644c2489bcc84f6aec83afbac08067a7b93f3897f8dadfb68c327b751841927a728faba47dc44ec4\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 23fcf9510caa531a304eee8d0b2d49050fca83abbf287b6b6dea06501c5afc6d87d2924df1d45b1bf6c4bf77b563a3013cfb4ad9094f8ee9892d33f6ee1c70131cd5721c5af804a9da7654510e8591aa185ee723f8caa78046d9e6fbb891e6024d2ec70110ae61c3969995e35941d2c7f3779d5bb71ce5b693bc9ce4b087068adbb554acc4ab23624e060f7cea169ab512a06ff3d2a36c2b6e3bd9a75f1a9ad30a6a16b0256c42eaff2c3f4\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -c32d5e643b12db6616554116299c1da672efff1eee394378c5e9e5f702ea4ad64f0dac8904bd2751d2cef91adcb283599f6c661967dbab27059e94dd50025489cf74c6897a22e95013669aa3063fcdd4b73aa6a9a1ba5cad3956bb26346e22df6741cd0ba1c0ab87fbe74035618a394383823216df47b910cae495b8fe7ac5feb3b2cf0d0ef6c75db477160b75324db8eeac48a0fce72b9abbd7079ce6f529a89025a03a3777cc7d1deaf3e4a\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2a8f2c530342bb6ce683a760540e956a1155c0fe065476e400caec59861ca97ca71e51a11b3213b2baea1a41a29449998778e0f533fcc181698d293f05e28bff2750ef4095170de98a19a36ddcf59a65f3789a3808ead51680245070262c9544e446f23652eba47065a2bc4701c55378bd49733619ed2c213f8ed12a4a317c465f37efe07ff2df8e88fc33d3eb42cde9408dda28215702bfa607030839285a8bbf89b5e8842fa7d7f50d83fd4ab5\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -bcd2b2362aa146cd120b729e81c98ae598804006d046a7ed0f9782baa10a85e37c7c22288dc61c24830a1b42b123d63779e88d7555028292fed5ada1793264b35e961b608bdd7398e421c5474c33a65059ef13787e0cedf4f8f032beac48c4b5e5a67417109142a43b198ab617d1de1a38d6fb4922c6ef70a5aad3faf6f8d5da3af9679c94cf61ee760ba792d2972376425e2ec9c4109e969e3d9c3dd90cdbaeaeb7382cb7bd024b75a1fd6d621c13\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 3940430ace4b5b87bf4baa2673582db3d27307ca4cd8e55e976ea3e10da72b6deb7de932253bc9228c85cd4ae7766cd0264004c658a66d81e60bb9bf4dd66e2afe11057b7f7b53a1ec222510748be53a93970fb056e8082631b2b77413fccb6e61cdc6f224b7903d75345afed8a4f194b4bcedfee1f16dc256c2bb9f4a129fab6a9fe752895a93937a3d087ab7ca212991ff34f1bf1c55987a574674af43986312bbc3bad3280bbddf4ab0217440f851b\n\nQuotient = 0\nRemainder = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = -ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -f0dc20b88450f45381791e85d080e4f2cf38837391e16e608b8cb5e0ac0ca75e9f72cc04bf2f56f130d46aff31efbabc0ab14f0c0ad680d6899797297152be85ac012644c8d0927b5b6c70dc3e5a8d79ef92a0873ec22af3d9683bb5db1ffd5ebfb698c5ea64cbe2b6a8b9f14d4c18624be1b78b19eca14942ae9542012692cd0d5289ebf75fcf5486596f92659143e9f952af3622137e633376fb95e628055e0fb1ba3a37ccdf0af69a4c0d6b0793078e0\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = 2f2135850715f623909e41a745eaf7b37593567fa8be2d1ccf76d10b93a096e244b91d8700cca37a2ec1bff7c3d21cc3211ea8b03a3594921dec32faa185e7f3d9d17e98cbf8d881fd2abb9", + "44181659242ede21df7e5e8784f541cad678df1ef6ca4a5fa91f7856c62fe593c4d24436810cf4fbd11125bcb571f6975d82afeb81bd0c7700e053fc175fb5fc7b329c438479a863b8d5fbe6b4436b67355c51d0306e8847a27a30c9e61f0e08232673cdf0ba4e0\n\nQuotient = 0\nRemainder = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nA = ea87c57f6cdbfd4f836431be3e9950c90ee8ecc291eb4efb881617512fd62e2d86caefce713cfd8a20f4b4925bfc7dba1fcbe99c72932725b5d11eccefde4c5e505952754891e9ded499ec453a1c01a82152c8933f7db4f2b4b19e97baac322eb483cd661a43e458774ef27a29a19c3562ba466381056a3b92c35d9b8b71372b\nB = -cf429f101a2e19a65af1e238f6745215cf476ff2609c846f10289f1ef21b89af2aec53def3f4ec07ea42041f8b5862dc37fd03b2df12adaa8c9f1933cc69b526d47797b40f49545fd093b8ceddee3c55721d1fa19b336218de0cac56d410cc6cff4e620578cf820f5cdaadc367dc4d6372aab1e0ae3831a6d153c14920b1dcf09e7629b7442a06385420d79742e409677e3b82ec58bcbfa668ca072e981e20728a983d84a432605389c855a6668e0ee0d2b67449\n\n\n# ModMul tests.\n#\n# These test vectors satisfy A * B = ModMul (mod M) and 0 <= ModMul < M.\n\nModMul = ae2ca2ce7addaee2e2b7752e286b2bb6a58b51cfbed5c924f00398e59ec36fe6341cd83da43a33a12410f45f6228079c4aeb3912be87e2e81fa1799151bfa0fea29873097475b2c3efa312145d0bf7e51b2a7c9bc961a4f4dcf0c883ff90b919b87c21099fba40257645be31f95a3a277\nA = 6b18497fed9befdf22a01d988d34213f6687d8a96e86c188dea4172e7c6095a0d18d3c86c0f5a1af9c6e3aaeb6baac2a510930b3ed06ec78ec2e12b\nB = 1a058d99397db0d209f01212dd4023ae01b15da04fe62d1f76f21622b2695558c67d706c535ca7f19b36f8ef2d508ffd6cf6fcf25e5\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462c7cdd79b7604246a0cd97b40ea5a9a77408f13cbb548b56ee713c690dac0507fd988bf28e77462832f4307b08564a51510d4a951c1ad7564316dbead2b53540090827a8ade8092a6133af0e5fac7310f787dc1472836178ed6992b9f71224da3e884bef8e8379a58e6d4be0fbaf59bc520f786631857213305e23fd5ca65\nA = 16c92f77c139706430f396f72ec7adb045745cd9f5899b0074d9955bd32de66f57c05c7929b575312a7f1c04f19e724d64744bff7b31ad0e6171437763\nB = -8734c4a2361fc530f60b28a5f1c7e93136c5ff6bfc7553965eaca54c61e6befb3c0f8cef4280e780cc5940d21a740debba31f863ded75\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462c7cdd79b76042469eb41a7a83115eb84103da4ba438c3e33227631dc185054ba4e607141d1e60990d8aad4e0bb0ceb645ce9ccdfe72d4738cbe1f6a73ed3e070194fa4feca6001c4a853940a227d15c1f1cc153d8c96e90e24805929fb11e0665e0c41c77d5a97fc5903a8b215360e26f6a19922d650f460f7056274ee92\nA = -6715098ab2ba3ea1e6341e89936e3ae913cdd450dc831c8534071f3c362841e47d88f2cd29c0d1239aa0949f3685f12f8519625bbf10b2c7a515e6d00942\nB = 536d4b3e4815ae5ed55bae6950f5a8a61d52439d2800ef1b5ba2285b85ed0f6ec4af9fa0e364a6b14f6f6b8bebce9200467804e787f9f3e9\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 309b3e30f74c58beca8b2c23f64fe1203830db8a7e306e1fa2e2022f0d6d422851da509d1b2936f088f0e35effe12a7463f47ca369bee2f2980bc48dd8e696b2d8c6f35cf55fb8baafc2e613b4c684de26129cf196741aab873f81e498b1e03018a539b5eadffeb5953029f31f8579df7ec0ff3f752491910\nA = -11fec955948e007b59fc50e729941ee9d43d552b9411510b73f6b4faafc0465f261f8381d96f647267f72175883172918b5c866cf1f1ffc43c55f3c96a60c01\nB = -2b3792f39499767e0a8b7a6a406e470a78f97ebb36765beab5fe52e95abf7582736db72a2ebfdb2405e3954c968b350a459ff84ef815dbc5910\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9143ec3e9f74a8eec476cab17ad8636eaa7c60e108e89ae0702dbdb2b255a217ba2530c6fd52658cd931b962054a9c20c8713976ef3b7989c40611cd25b0a9ad0635d61f6dc95dba6e0c4a7d53ff539b623b97ba3d66344fa324f905abb861c6b1e830c4b0fd5f6a4b01f09c8e1408941291b2285c4625267a108c\nA = 7713413d87f1e50840255927ff27bad79e5de5898725a876e4647913158cda9f5fa031dd7fc11d2e8130a0ba99e8706341c1a98d5fee3218763ceb1d131e9cdcc\nB = 1384e60753dd4bc20cdabf398525e7c4aa40065255c5058cae0b2ec90a3821bea8de672a712431aef5864eab719ba621cbbd8b46fe86fb31286091\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c462b3b4a0432890d141c0f46a28190a2e30ebb2e4ba90ed132169cd72316b290dbf5c261984d98e63eea6525fa890bf52185ad7f164cf49f67ca91c2f35511f3bef6eb7f3da31a602a78e4752e326d79dea729f4ca6438f2aa65eff44bc60979b42e44f6a301cb5de8fb42abb47bce5633c6ae9479d39c9e8b507d96161e0fc\nA = 17d806d7c76aa8acb051fd9c0c782443f1b1b6387455f7cfb737c41658d0459bda5d13587055eafb87ad8d209bccac1fdc392aeca0774ea48799511c1fb9141cad2f\nB = -d7c9b6574354e131de4b8643d766641e98554a03238ebfce1112c3da5f049d6c410a7f05758571aa2625f7190b936a214797570539317b32fb94cfd8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 16c84ed15ec6352a8ce6d5c2bdc0d9f13b333072fc7041146e944a29391f83e346b8ac0bee6dde98a420ba4f8852801d7c5bea6f1177a6cbf799edf2146f8297013e0e796917cc967786788ff12d9c1d07d9ce4b897bd22a1b8a391d3b4ecaa5b5c85d0a03aea5145db6350c42a964a41ee5f83e7d35e14cf442e5d99ccd0ac8\nA = -6d84cdf18a2f53fe496248fafef183914d55c42267af3dd42a39515e80cf29211fd58454986f5fb6afb56170dd9865d3158249090270bb9af341c830522a4dcabfd494\nB = 6f6f3f74187b7d74dee92f79be864d0a2c56d4bca3283742e9cdf15112c8f4208e3ac8ecc98b44b4ad74b0671afa4aa9e48dc31d34224a1f66bb2b4658a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8fb782e4883ccf3aaa2d3e020b08993d580c69ec8fe66ecac152c5babc8aeffafe406736cea492450fe6adc25dfa2e12723a3f9baeb02fc0f785b3db760ed28048e1710a78a2ae0c96b67c109c5034375a512b6fc7906847253f66316baa0ef90facc9ab992235153684d49d6939ab9e91086529494d7386f604ed69aca2f53\nA = -1f745c8f0c8fe6ce3f893d77fb274c61b72b2d9f9c5a2eb2467bc00d1f496d0ad469d76bce318bd64ff1107ee5fcad4469f84d658586a5789c068b0cb9b866d8fdcbcac5f\nB = -3a2347b491813252e8ebef1bd181534b074a368d076b8c80bde2e54ec3b4ec99001f43080c7857427e069d99b1b65cff998a141ca6963aa5fad1ee632986ad\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7c0c1c05ae1d6420bd93596a01aa0153000ecce660a8a14d6fde7d4740719cc495fe6681a9a08163b2dfd51659b3ae7db0fbe09504370bfc695457d7b32665a4df53e879ac817bf715d5bd6ca0e242b1ebacb1ffd6698ec90c442910a92b35ec103b345f9a9e5c7b005f8028da4dde80f36f6f6e5675040d19e46aef06040eb3\nA = 4c09264420a9452c6f0b55baee42c076aae5a73697cc6bbb88b7c922f236ee4c18e477f88e2c40cee03f0bbe87d3ac8dffd75f635315f856a3881c6373e8b9a286c813325d3\nB = 10474ece7ddae5c53c4df5b594439124370932dd94aa5d5b4ddaa233b1a55634fb7d72e33bf1b02965fa9d1538f97e1cdb5ec0477cec8ebaf202aff8533211169\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5", + "f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 481543f1556df756ae2e422ffe35aae020c9bde9e9b1f760b43043a4654de363dc67f381c0df1c3c1b90edb4343c47ffb8345a1aaf5dae56f446fee08a0b9ee8c42fff57143e10846610a9925be96418c4c957b4e92af734b96fd6f21974877dba52a0db1fec4aa97640e357434f95ba74b6b8323cbe17118dc489552844602c\nA = 11bccd165d9fa2d8b01a48c0ec549a6e600396cd2023f0240056193ad27e971c604eda8aaed6ff6be8be1001f3dbdc8655f1ae84eceb963938ae7bf428eb5c968f584798c1bd8b\nB = -cfb6629ddfc98a242e3290959f4d0726c0b1770b52393bc7488a471a90f7f0951362c03e67f443c9ecf4987f5303a789bf65e0fd59cc5eeb9f5d4f40d3e4a14080c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2a770ccfbcb2bad207d0e2dfaeed04b6e7509daef00a1df88e57509451739a8a0f15106ce8b53d280a4b4e09900420714cb6961ebb0e00e88567c5df50d2f2908b4bf8e0a9a5a8b3c6120503c14f16a99297459543c467dcb67915e0a10e19f72ed5b6891a6121b66abaa602818801d3306630bb04ea57e6b31b2c05e368d398\nA = -442c80289bfbf00db06eafbf06109b55f99786a323fc2c6db5686f99094cc24aef50475841243ec3ade2a1e0ff28b4032fd8afb8bb5e28f3b2863bdb9fc8f033adbaeb5f2ab16fe9\nB = 6d43e3c46f4a55d49e78f40d34033a7f5fcbe50873930e7c5452b6b3b176534e6e70033868c85b4d63052964093214dfd0bda6a84e893b1aae3cc72aa83d039e51c014\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = ba0e8c91a86af1001b13deb115c77609a1e7a3736a6b807255aee898e3100f469ef6222be532dedb1b8d3db4b3b55aa4b5da5629c83e9b2bde76bf2f2a4119a5378b5cde000980b3e58595d988ff776f0388fe025625ccf368e20914fa90dc771c826e4a836b2890e82ac2274471d586b4de5dab3278f0e70207562ac6e6493b\nA = -14be403d28c8451cac4dc83fbf895a9d2b74f730c39b0fcb33d7258f99211dde31a78f182ad1d27a559031d67d6f2f94a741f141bab80fc692afb452ee2d502099ebd5760ccec7f7ebf\nB = -2742dfd02134594edc6d3025aba5ca4a34dfeb43821ad84164510b43be4fb95748f8d0eed7bbcbeca14efe843fb676882784bb36c889be29bdad9270e0956286552119561\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 20c691d6544912fadfd9894cbfd42745991f39a29cbe3a1cdd302bd0487bf70c0179b9579b77f8481bee13ddbe42f32d734b6118af92884c946ea8576f6dec867c1c251c73777cad7c7c76e90da00ae07f96c8d6a751e5b18157dac4468c05d32eb86e74e0e8312bef85905af8193a3f5c799c5875badbc9eb7ead1258e56d7c\nA = 7ae9b4d5151b11bb7bd4d1569a6f4804f3b4d77948e0c6300e4f28d51c9a0afed2ae7503e53489edca5359e2b3d0c82a9cef316cd7e1c1275c31fc9c51a8c1e5fdf23935484e467d6460d\nB = 1f46f88d39fbedffa8501fa1268bdf3460aa98e12b629da59676e61852a4d3f8c59f72a2fd717fe2faa09639bc651ba516cd39297e0cac67444ec57c0db47c2a4e250033d02c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bf21b3cd55c0df8d4d568d00f757b10ef3de782ae71b289cb2b59d36df1341382bdc1825ba13199f2cf279a72968b3bbf5f7e3d13ea9adeb96d81132788231fd988eef04828119dcca21ec1fe844998909cc95a8d01720e883df27f07ef4dc3f09081015dbbdf019b96707c18b0b1db6e689e8f86466a2afea4a9cafc576e10c\nA = 1243b14aa3d16a55935f6f8ca49295e35e7f75b03de7192e1e8a479abc0a430e0d340acc05eb9a61a5dcbfe3ce3a4c5c940699f5043e924f282bd21e341edf8b7a6741c6ac72d7587a9e7a60\nB = -bcf08b2153e8ca911096189e35dbdb21b77ce89685484f574c89f1747612f39340bf1b204a23530abb36b2c5e195940b86ef1252d6729393c25d4c73dd434b6dbc3057b05d3f15\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 460539d96c07e72acba5b59c88fe904bf7f1e1648612908444b0b08172d05968b31b43456918b4287dbe01afc3cb4860d9c2fe549a580c989b6507094f6c241eadff910d2603f747f8e289e7a8176ca4a978bba89288a4cf875bf3e03939af966c54e77c28119a39d34a2b7055465f58ef2efe7c82ac547fb675653198e4b504\nA = -5a44cb669c055ba7c28d49f84bf8d12179aa30bbb9db2a48d7a6b09e44dc0e0f7471e3629cd2fb51e5a53346ae025fb49f9591ed1d71bc79daeb3f1254342d8a2b091ae07a758c1555efe59e78\nB = 646cc0f766346aaecbc5147a4488ce157a6d844045b80884eaee9d419087285fa71108b5ab4a05689aacc8d2e3dd0e6714c55eb8f77487a3fc5e56c3c2df0c4acf28a457051118560\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 79b536f4f30f9f7483f90e65e6456ef8072d9a7430405cf8c9377ceea2c676afc338837643436d55ac6af2326ebb362684bccc5092367209822581700d641cb8d331432b761e4c6e22639a27335f45a25ec019d180fc53dfb53d69216d7cfaeaa07db8288adc35b7bbccf2829631c1eebb821e4d3299015c3d462dc17aee5024\nA = -167529b1e8668938ec02a68bf4d76c22dd018c41e19be25e2f821f63c2046085d0af30d8b4212ea0f3f9943be1c14fb2d2a944551107cd2bbf8dda5bf258957325f06277036282977db4575b0deaa\nB = -378e1be10a57e03b197bc2b1287d643ba6d89da4bf6a6170816691fb6529c602eced237863ee39659be3729825f032a57eb5de0a87b0894d1a1244523e85b6f50a3d9976dbb038490e46\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 658169197ddd0bfae101c10c3e6a2b10dbb456048e81160b47b197fef439b1e0ed710399cfc80ead8e436f1c0399064f92da50afc335847515686e055fc7bcc0ca721184435955b896b0af4f4d96672ebed2f154538d49fa507b945c0a6ae926793751231980274213c80046666c28ada213a2f87509d1466b8d1b2122e93f8\nA = 49136d37ae8f3da71a6114327833e8aaf3dc8b5a9a27e9d04c953988456e525263f86ba94397321c2093803b789f8db3ed7cdba19c4b796500b979e02952e1625246f8e977e01fccc133f94cb22832c\nB = 1dca005663385fc00b4fd58c73adc7589d15ddbcb8cb2fba03a737a320c447a2b21e576ceda73811a31d8277883fd31e22f776bff3261a098ecf8f40f2855b0c723d1265eeafb43f85323e3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a49fc8084f3e780537b4038bb769b8db3653a3315298a99c2ede6739a1732a636e9787f2e8b09d0b9bea08fac43cccca71a315e6f4a7d6417d171b4693dbdbee8cd9f95be0847ffd40ff027267125d67b89737e1d0365bef6c4429504d13cd8ddc7810f456d6293c0c57c14a307b94010d79d5c13b92a907f923966fd3c5c8ea\nA = 1e7d8de2061cca59d1cc19b356a8fcdf2ccf917e0d81598f014167c5a8de027ccfc8f2cb8c37c396ebaac83ba862c146bb2d551d10ce03de9528f97725804e8a6de57b9d9da811200604c2a032462b6ac1\nB = -e38592f3acd75b575f64ced439d5ef2377d21c61bc70625639b01bf755fa2c6de803ce155744993493debcd4de40860bbfcee86d0b117d7f8c3f8ace68b67cb6fe7a81a145535553896424f7a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5a99c8a6afaa97d8e7d84f4899803c7786b1bfd2ecabdbfbb3bbb92247ff91ac213a72f6d23c24699d60babe91a7d9cea751e686c027fa1c954474fa5680f0059118426c71299462b11de5f2817d190599cc4b352df4d2e80605f9ad1e32eb13712d3027a2b6a19d52151e37e7fa057d8fe59dfc8a943a42a1756a38f103a75c\nA = -7df29221e6a102e32757c18f87927cdc90ecb012ab0557e0ab855daba832d76ddf595b9c5a62988ca968b64fd5bba2a147a5991810c17cae7edfde38bdbb7e13a1fe5206724c05a9fc9276c8d4e503a860c7\nB = 5c586d1aff7dafea3b8ee42e0e8854712c95385374b5bd1fc8ec41a72b296e070940c4160509a4a1699a678533ff3d12299338fc441b0f01e29a48677bfc5aebc644555285756e97c74e1af6aaa8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ec", + "fa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 21fd2d881b6a52332dceea42664aeae1ca110512c13bb33e25ba4ec0f39f80eb73b1fa0834c998c23a2453dbff971eadb183c51a30ba78d593f23be9cb6b2b33a554ef31e4a36e0314fc2ec889f18debb956b89d1bf8172553271bd56d89ed0b30abb70e68abaa2c76f73cd5a3de93433747d09c845b5f8843f9fdf9f6c975c8\nA = -19fe3bdddcf08190a037768b77666de803ca4f7f0d7dbe6aaaf334a486dd0da7ca024d1b3df11e0406b0326595a171be30b04574c1a7d04f4d2ccd334663690fd20e4fd168386280510a00a70c1a11e99483048\nB = -33b2400173c057980b0e0cfabbda1a5cb5b83b7ae80708c199f28142237f04b071c6eeb63d42e80eec04b76152250c9e4d4c4f19a048cb9815dce6e66710fad1d27494db5c31d9af37d2aa779d12d7f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1c45cfacf30682a876cfe253f05b393a2cd4dc065ce73126508ce897a99a723cf5145187643ee62d746f6edf70269ddce3c348a1432316286a648ee9ac31ef87feb14f25c42f2dfc2e84bb5bdb4ec0124e249c526c55ff2cd0ae938555c5f86d856eb181572ed01dc045f1ababa52d249e56aba0ecccda905d7d1e64bf89bfe8\nA = 6a40d948eac2fe5bf6db15d7f6b89fdc0712e32d39a881c21859e8f7722391ce05973efc7c40e2c0d7f56c217d8a986bfdb08bf87bc0435873cfe4d01967c46f7d39464bec411d0369f6f5d1d83f42596fa47451d\nB = 12529775e8253ba220d890d4912fb95f91e4edb59610e889431208b6bb42b089cf2aaa12ff9ff98c2482e7f4cbf35b22d15fa28aa288217bf766e937a706fe1e600143087b0a67f668cb7b762c9b9f38c0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3b3b08e8eda8be3918bf648227eb0d569dd898729d9cd54deb32b1a1dc69cf7b2c4184c8ae9641f0f75950df263a5e236f428ca86244e617b14a04edd0f31c02bd4d84f25bacfcd4a2786825f0361251475eb6c7e99020dfee4298a1f1bc260d4e364a332bc6f651dde7ce5026dbeb0e5aa75ee98874da54c7930108ad28e3a0\nA = 149d36918fffa682cf90c4d3f3d48e6408e7ddcbeb44e78b9cc7fbb08108f65215761a61d79f37ec8f67cc51e0a9b4bcb3834b0ebcf6734985153f29a2778473b80147eddc813b4fbeb98843f5c1ae6cea68f88dbb4c\nB = -ca87f66182e271a69c0964eda92a009d438078b584c3eede28ce1a501838c5f497186d305c09922f32ba858fb55f2a0dbfc9cd0f93b789c1f800cf092726d6d33db19e4f26c7dfca69b83925db14544ebfe2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b199655160d88b6b4157ada0e5675f82b33b5592408bb57c46e2f7d8791bfccaa51436dc3b772b83e907c20ce7edc2835ce96595b78c0647d244e9bad6f4184e0003eb0899e7a47ba0be888b9bf795eba95e5073a85c4d20416fcd4a8d4e1e16b403deb38845fb8bf9e9264d68807acf02d579e8cd104cf2bd555e6cf73d0450\nA = -70ccbb73e33a7cec30ef2071f3b1f2e008e70fd6d00fe8b7aa4b9146fc6d0549c57d984cd014c7e0a4ed6d33376998b7c2c9778fb9580d8ca4ba795c88612721c153c186740c58df3fa63b6cf7a4de76e049217218c05c\nB = 6cf4168d44a8da8e8446b4420466fefbdeeaf9623a40e10b77547687b25f36916f2c18cf6060c03b3b40e0959479f6aad5e44dcff0ba799262ef53e280f4a7f667d262d472b2e573265774deb5ff8f25dc1822b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6ff91af444c61d2e2fe8ad73bdc5377d5becd55074eb60f0f98eca3d8f4be8c02f196b3afea12c36f78b78ae6a5ab677ffb7d9c0bd58987cca816affe468c7fb4b56055f5d2326532d6ed1c00ca2d052ecd103994e8929bce04e067082b4ded7e1973566f99c514b4e0d95b9a8a931ef4f6355066940990fead70208a63841f8\nA = -1c924bea12ad6f8b65abd1796e381fee2cfbec15138191bc22d57165928794bb080c83878fa5fd19a5d657b2fa91165459966f50aabf19440f7d75f027b32e999ff4d3f7a7ce878fe0f33a847d644d86ca19713ca9968d97c\nB = -3abd4b281b8f25f5957d1f2fde904457d49a3a7eeceada26b454ceb4ae0e879135d376571f08b5038b7b3d73a9a9fecbe265b72375756a715a523ba66737085e5ef7a4ad988155adc93eadd5d95a0faea56914983b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b9076229b1a1241e8b4da3fe143ac31d060785be6ac1e841c2fa9683d2bacff2e2b5dbac33f58b0b1718ad2053c37ee55ea54a9d258ddd8930d2784852844d85db24e4721762839a5c73cfe588efedc8932ccfa585e1b5975083919be9e32a86dbdf5cef84d3d4b2ccaf7a006c0cadca1e35fff2da9da7d7e779494d8f85bf4c\nA = 75eb0fe6c07559c2b0c7b2acd7d29b5798f6c4cda64a504ebabdf54bdc773ab28b218f0defc040016178958d5561796230b71edf49bbdcbd3f14494859843c8ca7a0f777cb05827f2839f3982832f4f3e3c5e50af17ecebbbc3\nB = 1b8aa718d61447003fdbaa748a9d86befdd2675a677cf34a1be7c81e4577f665d71135a8a243976a4f6ffa1636695567bde522f8fb1948033a7e0941f833d827e957781cb4349a08c6be418befc8959960fd5fc1b288c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9df82b7c34ca97a3a5d4efa28d5ed4f35484914dd73af9090c4bb31ea3496ece8ec650f4e7b07dc779c97e597e76e43cdadbfc6e72b61ea718c073be1cd204f8ad2bad0df1e530e75705f3d3dc285e9d793c8d42f04dc20773d3fcda8ef3ac1cb10d33d20a91add0358ab8658f49d2fe51d0d2d72684e31c0eef85e5695bb4b4\nA = 1fc2a171445ee6add5c2e4d29e50b91d83338f8d63c111e4d3e95f16d2a33be02bef24dcc3d6ce6bb8f1ef980dbf8fed409a0232c0566153014eef840aff58ed8c33e8d463d408f93e2f5381a26fdea63676c4e5397eba1d39f928\nB = -bdac7a177c77451104852bb99004ce8e617036906667258d85adcbe8cda21ab7d03aa7dcf62cb210a9db8fc750c7e1ad290b35473be0fd607fcdc686de0b78fd9f258f5b25e2ed43c2ad1a38859f882b9f6b293dc258659\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bd9f3d2e8a1086b177698f87a9860e3a5f030e04a0bf4ee9436ac55e005bda01ff4ac662cb85d39e98a41c723ae542a83a936c3bd0280c6801ffda080ec0aa4230b45dcd0bc5eb41cfcf272028bce3572847637a92d1543bb2b8408e880f5b776e1cf14fa28d15cfb584f025596ff10c9f091c837a3aa622d9e5c856db8ac207\nA = -7fd5357cbee7c5e31fb62ad03bd47b705b574d915200fc7f1013d836b9cb683db020b152ae9464de6aeb8baf14999ac7025dde6173fae6ade325c60ec310eff6dc4130a8efffb15ddae90d760cb7f76a27d0368175d4a44a22f7f223\nB = 5894a0223e4aafe4efd4572752fbde4952c8b09cdfc35137e7e6ed650f8fdcfce9de673853dbf73730b159b2656047e69377d7c5025a6b346fb08831e64bc8bc34b75765012460d8135a4f7a0f41d768fb85abf17f5e2f5c3f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2c61867bca70e8662c7e5435a5aec020faae86fb079b992bf49d8497fc5f96abbd38a6f04f6ca8510e0160e546b3f68b7baef4ef0f404e881771cc12ec5ed3e3787c2d2ad6bb957cc59f8d56f0afb4bea49cb671cb42f4e8a0ee1dfadb6fa14f84a5b3269dd33e20d658ea4cc39499c7a39a4b5650ad7018d32f97954610f676\nA = -1bf5ae15f24c7c14eb59605136a3f679f303cd5b81e4a27465281d17715afdc2c231d7ccbc59f80ad176f4e0326eb757b52e3695e27c6776d7936da47e3a8a904f735b151422029535045ef489e61ec93f02e6d588491c8dad1cc311f52\nB = -3238dcafb85ce557036d19e42e7e7e473de9f9da6f920e18845dd010546868d2652decc94596cd2c36bd16b02c02559892b9f573bf21ab18c3c75591413d046b385d08aa66d849ab8adc9fbf788e837b047a7ce2b9c63f7fbd263\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c1d04b831b71", + "2d0619db462c3f3fb5973f5984e9a48493ff273a5abe17a548e185d751628899e2851e425a7d4b2c72d4d908dc813cd122b8f497e08e299dca9166f19752ff8cd9840a70155ed9e8c063a3840838b3679f96f1cd5f1cbf0e037d222029e02769dce7fdaea0bbb5417f85497d77c76a387c6b970eac15dcd128ba\nA = 7aeb60c134e84f289e419b74f99a5ce5b4aed5fc630d5d591ac7643251ad32d6ca7f052fdf8857f67138262d221de644140e9018f7b84879d74883f8f251303f65e06bb52246ec6a912772cb698b47de41c1826ddd065359f6b9f1ccb0cdf\nB = 17f81e53d9fa6201e4d3eeebb32267929cd5258d10f053e7c021c4afd17094f8ecf433b1ca752f8740f6d6bd84f801b1b9fd64bc4787b9ae5e5aba0b4318a63dfe27e92d5a3ade192af7563c74c9d6006ae7701240efdd6021a83cf6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = aef89874854ed34deae1b77286f9cb0e3017e3ae77fe050bb244acf4f30dc03504c73c1a4d44b769709bdb53811a5d0f8a76a08e6a66fc2cc4e98537ad6a8049f02494305b89a49a55e71fcc3f5fc42d6b478456ada9b19ec0a03f5ccfac5538c0040092771660312be5e51996073ff1a506d7460c57d54e10dc2991c028606a\nA = 18d3af14bbffbfcabdaabe44074b407d69abdd80a6eaa5954f0e45fac85af7ced1715c78da872f7a8fabaad3207e31f12b7195cdb25abef0a1e54d3b13349d997f207fe130d7985e2033cfec899a0af310c9827749cd22bd062eb0b1faa254de\nB = -85a7d9f08a60031e689b0e611d7f7f46e1178eaa2e6459602e738990c77f4d3783ac43fc04d53504cf67fccbeb02f9846756f8e32fa4a9316b6d3b45f644254077bef096a72bcff17ffa17070a4355121cc5daa2f782fc0d0bb48101db\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 14a85edc6297763547702c212b1a8274b8f85d53ef35cd1b01ed51039bbe030d0a1b9626ae2f571a43f1224d723847a1c6708f2238f6f6fd75db6656e6c703a5acb57f69717efe8ed58a3713ba2720d8c001d026d83de0ce5e24b67c41daacedaadfe404aaa9b672f00562e6901fbd0710c4303fec41ee3338100beb36c9b1ed\nA = -44414ec207060d105f599b9a66aafecc5b232b55214c1a5e1922f6b59439b3ff77cd3a327bce4f7406871196b90350e6dca9aae147ce03027dc4de7563c734f111d95171f489105de5ca80047cfa43f7e932917b816ba7d41fb95b4106745d700f\nB = 45f2cea1b9b75880ac3ec206740cfe0ecceb488c9155cfacf5885a8cb49be78af8cf221ff8de2328f4880479c031f830a3c9eaebfd83f7de501b7c5cde03c4720c56a676d331b2a13c4689a2e34a43fc11f62825b8776e75d31225ca7ff65\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7670c1e2e141d8f8f5466de8ae2e0ba2eb3eb7634699eab8415d3a37f8df291d00def88361e9fb64a2f116433dac3ac2764fd62f3201dce4e48a3b7019e5465f82241ffda29d5eb0462fde74dea3168f8993ccd4d090b9c31a5a6cd7e05f725bbc89479836b89379b422250ab049f31c860110df5ed69089716877fb0ad7b0dc\nA = -15b4a2f808a85a5bd466a342c4853c04ac0ab73f8e53a4a0477f73dfeb8d7a911ab2eb5d3d192b9b084d0e38db491148947c66f838aa5f460c37341b129137614259efa531c0e6ffdf163ec6851737037a5299060418d96da035e6f583e6ba79d0414\nB = -3e94fdf22004384f7881875b1d8f58019ed8afb1b6a31f5d591e77b0998f3100b34174d6f3466da44b4c7fc8b92ccc5679c26c146b704198a65a88554d24291adcf897bd758a035361f671a82972b5962002c6a828792980f86a64547165327f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 35b49beccd8d2010a8d777c1ff69e28e01a1bb78c6466e717f0a934bb62f9bbcec5ed29f9cd2c14d240a6c33b28c986eb9c8912a4927605532483dcfd31a50876e1819f3d7a0f49bd276ced5c4110470244fca52d2611ed7e31cd8b73e749aa70743b39e92810b3b52320342a65cad3180f6e2966059d15f79e5574348f5f66c\nA = 6fd078e3cbcda6a71a710e99204da640edc71a65974fc765999a74ab50a0e4b090d57ed0ee869c8da2cf694b6fab56e87c4af62fbe73eb8890bc066ec3460beba04dac3b8fae7e4f316e8f954c6e8d934e946dfdc9f4cde0f26bb3d40d5c444b03bfc65\nB = 14d8041a3b83468d2f44f150ad8d8d0a1a22035d630f2a17b70d5c3d557d3abc7e4d753e1ebfb3a3ba465520b84746073d211a67e079ec7f47c2cff9c06da69bb5cbafcb6cabe7e0018867c42e07931d6797d4499463e3cf786c6d5d6c8cbd600d8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2f6e0fed8a9720fbd83ce950d7545d2c6d5b271582194570424f90309227a51777cac974bca0ad3c1289ceb91cf75af73b0645cc20d71e7789144876b8c1bdd550328d9907accc316189e8ad81310848cddd2dbe362c9398d814a048f93f9368fdbec0f19ab87ad2a59d4066d738c3da3cb71d4716f2cd2336ad35ea1438276c\nA = 14bda9e4aac85b0ab7abece728f61450b7779d3b5fb83be813758e742d2ad76597f132aed91e20a75c554f0d61ec4dd118eb733d04942b2548b1efdb4dd22fdb543d9bc1e4bf0574ae2cb2c46fb98cc4835b6a074d6df1a3bc5443beabdc784d542e3349ad\nB = -efd765f8ffd72d041ac3244078b8dc4482233e9411b289cbc2cfc26fed2cf28e286835010438ddc9e7021ceb098b10c68bcc4732608ec1f4052df9362176ee14812bbf09ccf7c2882714ecbbf92bbff61c06e9dc35a368208a05dde949fa2cd091ce0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1f0c436379f6dff55a59093ff2a0626a9b959e3e3e59365afc33c7a7893f04bca863ec910c446957baa8de4e35a1f4e9c4a776ef41b053f03b775f327eb7e5fbe68bbb478aa4339ae703ee4b573d6931e47e09271d40239d527fe77098a7fbe519f5eda1f26dd6a7d0ee6833efe37187d8a85844690fecf9fdc3a4d80b921130\nA = -51eb34de29ba24d2b1fbeb0a1c324f4ebc69cda2dff971a315c0c2775d988b03ca29891ed0790f3dd507a1d26ead461dade9284613e45df338dd83aebfb66050465d8aee554970b43f7d4e0428e1512289fa1f9b23867b67095c455b66d536b91207b749189c\nB = 55259a1122eb7eb611a69118d3d42c2f05dd228d71c0e1e42ae3a8d3d180a95b74150d844e916ac85105805126e4b995f2ed1cd3fcdf28e1fd241dbe3125dfb3e4d90556256eb513a2f7c9b596719c83b26931d92bfd3573560e8bf054138f5d6b9cde72\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = ac321a272d2206df4dcd6ed8ca194a1049c1e3a20bf325fa44809d302170f850721c077bb5d792f86f7ab03ca259567397cc2fa1429771190bb632ac2c92d3fccf6e05e13cd33149994cda5f9c57da155439663f6a13c66f9da553f5038fb92fdba186ed9ca04b8ec87cba4c5a68c8edeedb94e38a6dbe293340dee1a4ecc768\nA = -19ac99d7d51456b00a193b3b04693c7e5436e05763f0154768db078ea5111cfe9eda3451091af213b9c8cc649d341de66c12ab2803ea39655d3d7de182a77355ca444c5d2778f791d39952a7a11839e497f5dfd8a703df49ec4d7628bfc25a992e94a6477e6be39\nB = -286d1d436f113308be594f0f43d7a05120639152b7e2f93058cf602cbdbc016512bfd23f7aa937fb358b7b602d15998ecc150f2b9224c58527c0c1267739e065e24236771e2c683957871637468181e6e896b513569bd004b9845f0f0e4c26a5ca123365e1c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3466804a1b7d1af8b6060aa93a4c325d9cadb33ebcc8bd991f9e44cc2cca8918411efeed0f005790d649382ec40278c8cff903cf3db177d24466c58cf6a56ffc14e595c36bfefaa2327d37f616b1466eb702f5c49170598bc361d892e18051b8233dbc5b3fd6832befd9a995bcef3b0f3beda6efaf09f7306ec203172e78264f\nA = 6710c19330d3f974fc377e28039e0c0ee0a558621fd67fe724c326537c18c66dc5eec60980e07d401ad5556a05688d2dbe7b271f9d5eda3032bf7cb7c420e7b5d65a195bc037090b6fe83064ac3731624ce2baaaa62a6eb07156ca12ee51d4321988026cff573ede9\nB = 137ca18f47a151363a3e8c52dcf024262ba525ec8852e8e406f460fffc2cf88f1999b17a5821849317fcd84d09c88ebb6eb0340120f113d7ca5fbd91c6a40cd790bce7b422552cc0cfd2a6417add2501db1667f2802e5d0f4df824adbd033a90a155cebfbe0b53\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304ed", + "d566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6f248a70b2cddd9627b32fbd130f05a604866799365f94d97f1eb582b28192959692a870be7c2614536a8de84cd8c1364a75a3927ef9dddbb8c6c87dbf526f2d3a7916384f2daed96002831173fa4a51863c28b4378f99b1b201010581d5eabd66ad1e328cc4e647bf5e0588bb775e130b4a4d029eeeeb5852c5742862ddbc3e\nA = 1f014cdd87cb33ffee623cf454edf2c476e91df279b4f0879637eb6e8e5ccab305186de67585595d34ebc195fb150408c4620cf6c7a0b0d9695ba0e0e1d7552ca7d0be3dd678b1cce2beedd11939891a6804770f1c843e16dc2ea6aa8e4043940c37fd3d950caa122845\nB = -8d8d9dedc80994fc5db04d8c935301e47054250fea9020bde8d5fef01f2307cbf458d5afef5210a369c396287c5eb453637a2d721085af3de0d75a5dfb5dfd22fde3b229d438439af7b296b9e68ffc982efc6c825556c52a735f8be12a214a06c4270824d5268fb6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a35ff7e232f047e575b200b9fc4c9253de6ac04c612b8a82c275a951075eace5e7d6664fe8f78301d554cebe7b996c1f4ec3ca59d8d12d7196eb3909223de94c220f0445d24233534af1c93433b05c5924799d2c781fdb88c4537bb8d442e6bf76b2d966827bfb4f40378a3f135103513da056bc0d375b1339561700d15a0227\nA = -58346cc8a9a1e5b8babaed8e7f59415388e0db654ea7cd465d96781c57faae7a8af8e7578e46f3a8de7bd1027188e1cc32fd1c0d60be24fa3289a12cd822a6c9a77dcf8799624856c27ba88fbdb047473274e651760581b44457ed048cf76c166d38bb9b2afd3416ac7e45\nB = 61951a16dc6466a9fabae99df29b7229f1ab96b476092dca1e4f8fc8e7404e2fba56ee66486d1f27f89bb3f86f271307228d7d6cbcff943961e177300b6acec1eeb46af1c5725f745a2d2af0fd9642f57a09c9ce6742114be0aa6e939e638bd5c7a92a7c206b2d36e35\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 90b441d8277eb1ed454964acf567067925881b5db0b446a7d554dc61ae87ff979bfb0e58ca1706123453e62ce31284a5a2db1228d259e27abc7fb5cc5848dbeb9a6808fa1b4afa844ab39b652abc41423c2833e1209a1674db518b6df7ebae315dd7f416df54e73088762ef64cc2cd0a08b1cb01c49d9299d149cbe84145a55c\nA = -1ebb693ea7d18e0ff4a9a51124ebb78bfa3a4635b75a6387e9fc745a2325409f927324d1289be8a4f5cf2d5c04adc7ead20564f97e453287f03e5ab59a6133584f970446652d05a131d7d382c47b7cb97580ef6710a532dd4f5a0369dd3db500ae5a3c5efb587cf0cd2638382\nB = -3916ebc4653e7d6e0a4f1e234d765d41e9e948b5acd7ebc73cb595559c1b20b037a3c8da0a7aebfa5fd327bdcc922551cdb8db3fb0a581fa0620ca2d2559ccde3ebc44542b4d80926d061e2a35c08c09547e0cd587c396ff2959ee93ea64b1e6b7e2b624cdf445988e1f42\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3ac61c3a028f4a2df6645acbd36818a2f76a3229d229ce22471760807585a909727411e8b68bfa4e76adc459409a101a1ce83900d46918e8d0903a163de87c07bbafbd60c7f536a62c59370ea53b6cea4384345343146bbf529334b4201ebdc7585b6e5eee42696400c9be9f496406a4eb51d2fd1b40466224f1752b181774ad\nA = 5a16d5fb9047949684b80805e5d962bdb939d0d0368b48517a2a826679c37ee0ded4fa83e657192d9ae84294e450f7e2f2773d1f13395169582cbf95860891b9fdf8f3240a16aadd1198e884f22b2718219d478e2410fd4bb98ea534a3626201959af099fa55488f5390791bcc7\nB = 1f67066dd06ed4a49cb556dc2fce22814754885a7cf6c13915d974b46b0e6269c0fafd688f45ed2deeb026a7cbb772c080dfd577d21ed2c81e50e7537a70dd550eb94fcdf626500040da88c43dabce13c82a93769a9e0ef66a471661292dfd3b3af07169e2dc909e43678400b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7087dd62eed6ccffc7e1370cca9444dccc4ff160458941aa9f49dec1a2e9ecce4cf50ac2daf06994c5010cf225cc92238cd60e1aed9edb2befb0fb354ffdde94ef5e8ad0415bc95851d59095a5c4850ec52a74c78eab58309f395d3078dc481feb9d30bcd9f113af7a01611b94d085e32193dec738a64c5fe9bdfbf5dbc98cda\nA = 13596eeefbf06e9ead8d883113d8ae6cc3da8b6fa13ab66681db5a9c083ef9e49d905ec19c39b149cc09452eea0446b29cc92d4e865e6f681827336945282fa6b276ef552363229a976c503b822e6e4a9862d3fb30dd0c3627ccb97a7046a6a679050a39166388a9daad5ec5555dbf\nB = -a4e574363f2e5982cc087b38110d257019962fc166c2d6e6d396220bb308a8a0dc7d90c5cb2ab85faa19b07ed7dc11eae9bf2abde0a5fed279e77a717b43d35e70fec4e18445e37741262d0b0c20dc4375371d87d839d39934f1dc41122e815f3f37352d04d0cf514738b351f02\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8495eeee238164082240ae1db1e3c1e36fb6621e6b714c9de914f9de8a587d7106b8dc5214f7c60c0ee231d7441e03cc26462e71adf8e29772ac95d0395722d2756f9f64daa8ed41d7ce824a572d7f9fd419112ae823b5b48b8aaae09fe093e9ed05918c4ec88ab159890910837ad0691849b44be95993682b2da2b124de39ec\nA = -403f21e1a7911806747bb78a4f20c4e6572d49c6c4ce071db0c8c91ee985e68a16e60093e4628414b2673d25c9f13c4c43600633af95017e3846512197c9515aaf9953570ce5861620716b3d80eae7de0f033772fba82652484cb3ce7cc189d1fafb14e044e07a88da302547f2e623d8\nB = 689d1b4a968b7c00082ae3a29c8571f826c4630c947a7767fe4a71af43a5de84db9b5baec0980eafd0019e09de1b5c56173ede68c9a6acf260bef3d9a03f4c83a33106c94ca7e1a8615b3553088d1d05a62ddab0f1e5a126df5d960f67e3b92981022e1f0358c7970bb2fd5dce7a7c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 397df584bcd3b2e1ec7ed89de624e9d104bd6812901e38c5740755ce91bd54155c0b624c590ded199590be5d98bd1ad4acee56a62d05d6b5fdd1ade12f7db8e3eb08c4a5996450cc1204be7ba61b768af0efd563ea478033324731e24fedada1ad6e564238c891494e85ded4feb2165fda22f75bf120856034a9206511885fd5\nA = -19cc480d1e07523bac502872a971d78bb26955c5453386f5d51767150e229daad3ab2dc85e0fa0cf6e72389391fe627fd2d9f263f105508642eae5a095ec4d88545dc9d0a2c436907460e1ea7db174673000eb2e0b60d57163ced261bd0f6cd8ce54133cfa10591f1fd27996353110060cf\nB = -39c45512fc7c9620194fb7ad22abea8f6dbff4a137dc4523115ad7e262934143cf1f320892f8c097a400d4099e787ea7041d0d69b6269d191fcdc8ea28340ecacab71058cb39a9c7362c848826b35ab560c27113fe53c497ca452397891c81365b6e7f07f916d47961e50b8c7c5cab38f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 263ab04c98efac12210beb66b13fec7c260c5b1cbc20cd732a511fb3786b917a617d6622847f4eed70f25982ef5d0b0d13848c62dcf447e3a1d491f4c80e69cec03cd318f6f93134d582210bfa81c1790562053a71091333348c6624d4d793fd6ef971d284a4ebf0be0771efad302015abfaf3edba017907f10ea14a46d9fdc4\nA = 7a354753e39b9ad1c0ad6b65575fc7247487f3ea320fa82d1d333ba8dd5d0ff925331994a6961c9c603be5775ef1842159551f0bfb34920b93d90ca60e6abd514650f77ee8ffff2bac0eecd0fe8ea0fffc6ed0285c9f3c3cfaacf338043975457d62f9c8dda8cce1e99f34529435016fe2ed4\nB = 1a4384f9620567c698ced05870b4dae983d8f0df6aec888353f9dd6ac8ad54340c3ba8346bfa47bac38897f3963fce972f6d55f3407ae03f5c7637be1a34e483e50dcc27148b76ef079f117104162beb191d146ec828ad5c5bde5ee1683a031d554c276d837bf1f2f622cd11baabce10212e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 91cf4d1899e170bf75dda0d51a6481f79eb94c333b876382c9d04681073e949191223926523f6531f0a45765d7f382221eaa080d7bd05a3c19220ebe18802b15d8009714e8e4e9872223049622ca02040eb041707c7e525f698cc", + "361847c66fe3673a72e4d701466bc374f55fa5437216eb59375c0e2c4f7020149d0118ea72a\nA = 12f35c48024e8271e8f9a60a48b5a214bfb6595a837c041b230e6ac87a4c1d4b3f93a2d3a193c750c9857c8627d0f7c454d6c4f224dbf14a865eb83e990b1d9b8bfb729b8d3dedbbe9c95032e4d60676c2baa2aabafa698392590add3b83b521a7a5e7d6f8af207e44ebecd735374acd01ef5822\nB = -8fc18f92c0613d085cf3ee6f586b39b99ecca864bcbe60fffc63c585e5613df68f3534ad46e244916b1f9188507a3692526c9e403b8e93480b0a5a6297f65215f1a5d8e20631a9d559fa1acc15a98c9397761ce18903f393b10444ba51bc92ac44df90d4cf0852da9d75902230c6de6f26dfdb\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9af562a7b61c6c84c91bf979f32ba5d246d2ee2050f07ec2dd5cb3f9496bd37c3922ecb2b5b17085a13e93ab2dac6022077cc18c621cce3a2d2247e5e89de8692a36f596e5dc7a6969a4f3ff0d1580eed380e6550c6218c1938caa2b7ab401ae6f520063c811088504d60a19da3b5018d640ab8d340f35d1337a2ede8bc64bf0\nA = -63bc10b8fbcb391dea305fe61b404d3bebd035514a812d0e1d38daa3d67f9f1bb8f02d2979270cb9147aa51d66ca73d4b5787e472456a13fbe0d568e92b622439d33ad3c357a56dd26806ebda7b3bb592385ca5dba7e5eb5d85eed0a1746441e8d56e22decdbf8f4296e30d222da5af17c427e832b\nB = 57a602bbdefcdd00f42ed1e2cbde2ba858d171804da56b0ac87081424ad1569df1308fee7c9ed349eb496d5409c4c46921f09ff0830bc9f57e920e17df16523598fd90314141955ddb84a1522ff3ebfa812cfeb6670525123476a739f64ebe6a5f1fc805a880f8e5a71b908c483a121b38d05cc2c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b395c9f264172a3653af6637e72c4c8e564d1ce68032a5d761bf546e0c4b51b33cb026bb4256fa639ae98e54e5ff7d8921ae411497272b53d97c2c44b5b9ecc5aba43dde201f64f1d033056f19ceb0cbd04decb486a1d07ab1c64fd213d7eb6db9cd11efd743462e137f368acc4ca0b49a7f85587bbb5ede4be1616889e2699d\nA = -1e71df5f04001f6468c3a192086bda948aedd19c5da9a5286856f30524238d95b0ae71940f2af123315ab5d2fc61964d3e970d5858b7c1a78d0f2cfd10cba7ba4830a8c19a09b59794ca5d7da32cd8376b5ab06079b51cd9819c0021ea41a9e43aee147befdbb17a92cac7c7767705fdd908bcd291fbb\nB = -394c187308320ba1b14d91d75b8ff993dfd57f9c84e8185f12bf9924e046629ffcd7174879f9925bb643988259cbe9dc9277fa83a25012f91159b012f1964aefddd5a94ac6c2a55a22bbae93085dee079f84cea1d53dc4771901db9a3db5a14eb17c25aaf5377e2beaff6276cbce7cee97a9b8f32737\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6602ce0fb5002eca37e85b60cc871b7b2eed13d38c20a37a6e0886ee4814f3ce2515f8714c67ad81e8c3abf6a00464e6a51b15e55b6c11296ada43cf459e15915026d3260cce8fb796241fc2b0bdd2b65ec04bee3b7ab6626e10597f3b13b43d16c34afd5b43a219917626c88b24c6f8392bde1b2e65a50b7f1a8dc5eb096702\nA = 4855ce75a3d7dbb72a257f6291e9f6ccc158647aeb2f8beb3e8fb32f6f59af1a46617b77440798562d6f58bfe826d3ea7dd28daee8f5162d7d24ae6c24c2deb2669b15898689ca789e2005903f3a94e991e7d3c8f3ae6181029d959bb15e71d7ba94d2dfd3ddd10f6fc49a65798b5f6ffd64682c78b5d91\nB = 15b3e9992aa3f042fd58ff97a8c04aaebf46b75fdc38caa9224394a1805cc26e4311bfb498d5a04d19396e98d11c8810620979362df82b23a115fc1711b57c7a56b8408e2682a2edca36cf9311addfedd2d0889a78cc1ab170d1379245de6f1f6f4db815fea9130463dfe5283f195e6e81486a1d39634aa\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6a81ccd82f00d829bac186fb38b85097d52afa3ca83a026856bb83f94d6af6f6c6f3141d433f8fc159d11397df8d2f44c769f255cf8148249d8e9fc4f59ec3bc8e804d7d5189e71e20b8d0e540b59a2854ddd7feeebda5a95f17605e8bd5f311a63cc2e4ce23a51229d0a49ca04982c1bff79c201de6cc6150b690c98106a39c\nA = 1f1589c9b5ad9d878631cb03c23ea7e94680220856285668838452a63b726e01709588b38e578da8a4845aa5cc2e4723beafa4f81a1a2e463f67d9a3e432de7064ba8bfcb943cd9efb0e5a136649cdcf5e85a667917075804991b997f318752304f4946d69abf161625ed0c03bf9abeb4ef28034f818e2a643\nB = -909dc7fcbd27d0bf7d6a3d0e2937ce725b5cca0acf78c103d633206cb431e2e2c785aea4bfe2042df32417143de76b71d21587112f36d067f878e556b94ef63d59a07d19647593efdba7f3f5324d64c55f93a283a0dafe080167f6576053f9beb326994f4a1d53e18e3f3e770e69450bb70f276d128e48ecc\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 69139f2e10726f83300505d15dcbad5b5f284d1c06789181683b7b8caf35dff063dfa4968c35facf32a3628dcfc19b3fa4c30ba0e030b06773832a2631529fe0c0c402e05a0c4e9446a8b6c22754c70ef540f90d903d83a2e3592169ce6b5edf939ac5ff25b8bd48aa2425321602a9571661a1109e275a3b3039ff0c2f430b18\nA = -5d02cf3969bff8789850ac898c00fcb3ff1fc49a22cb243ad18703bb8fae25f83502bcdd885417fe46e8237fd0b444712c4fdb8f4972dbf9278a83eb305efc7a8210ce55167c069d1c4136a9b66d0c4dfadbf036c079d12aa082fbb42bfb0098006136a61f3da43aba3d3bcf2f5ac2d7884caddd0cfc28681d33\nB = 50b369234d993721288662d83298d99b9052a0a66336a5a31b76dfb20ec2b5be3aa76f78b2c17c63d78402a15aacb585be5c8d2e7083145e316e71e111fd34f5c79363c4591c247b1a94b20ee042d840c42a3001d6c8dc7cc1e1348e0e3ea8c6551f9d24af2dc2d0c38a54ef065ff048b148ce4f11ed2b549c50\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 87de406a6c957e85c759f8ff684023a0f98e93ad4ffcbc6fb0038c7a7ceed2486f15f36555d286338aab3283aef677118f7cc3f88a7ff0ac9fed31da6786ce895c3c08d3edb652bbc9ac2b44c4cd24ad281ca3a8e8e6e4d730f4f0c25487cfc1b2afe222934eca8b1e1572780dcc149422a88eeb1bf31065c929685a0a97ac3a\nA = -1878e0497aa1c2942a2e6956957c876dac73c4bdbf42bc92498f29a006bc92f788c24a4624b87324a7c8aedc6b2c0c8a1a442aa91557aed9bf2c02b6664979e8a9a21330dd839f4ba8f84515fa6f7db9287f7c20f31732b98fc09ee7796dc524870dc35851814bc57e1a8ac49d8935fea04bb08b8760df33a98149b\nB = -32f4e94bd073cf3f70810d9af7a873996a0510109bc6fdebb855f27dcd012c59507491152d30849d75f95dd868992c6fbbf29b1d899cfd401e9e7f4e0436732cb4cc9e6a6d6b0cb63fb0bee21e422b7f7b7b14dc5d2b6d10447fc4add390fd3c8e7b06f1d9b181adfa8d04459ed051bbdc9666623b00e3871e597be\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = b456ccf9d066dcf4247a21c7f3820e324ac9cf004cecf8dd1f6c3aa40c2a33e24c423e97190fc71bb9fec21d36c5a687065a7877237a2a05e64cabfb3b20bfff0b1f5ef2e9adb7edcd7140d1047b0919a2c770579ab44a08e5ad9f63a06f90ec7d5885b91de5e524b2e187937609b4b81d40a0b33e31a48d7b9868add75286a6\nA = 6c484e3c6b530dcd3644b19fee66c41c7c2c1dbcde574d87ee13cabef9dccbe5b41e25c32c6a56df23f2e87176afd28249e5fcb918723707fca94d7e2c9623a3493d395db802a1b49d550f52c29666f785652fe81afcab00a60a5b50cbf523cd13dfa06d5a5b0809c68ff7264a2cb35b8d52284172c62ee658e8417e6\nB = 1b4fc753d0530bd07094bae09a02b1ea684fb4e8519086b1e2ed9d59af011f61d1b94ffca6f354a5b428417b328bb1e8af3f6c7ac9121dae58de9f1dcbaa9c73a357f408b870e62b0c7db1a72c4c440f2e6fe90b199b9dab29fc23927190d3f2bf8a7ee926a152e64474283695614ad696c85ea547f5f51d02d1b823e3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5e7c63276f350f04816a6ed9f98507a78314f1d99081fcd906affa3b8395fb58d029ec657af82e77ef45611bc988095bba9c26f25f8fd404432fecd02398e69635f3315a824d6a98b33eaf6a91f12957a5e80cb48d5b086c795eb3b1e04da5432a7e8be3d683addc586a44b62", + "43ffbb7a979bf9664cc7ec41e75f267d58a7127\nA = 18efe267d4c62576294f4ba44c67a058cdc0bb44c48f4035682b2d6b8a63106081af43d99098ce133f8d7f9cd04d4dd7414f704e32871d43d6e5d73fa9f447873168b43b32d6ad19378d74a967f92ec7629a690d29a62a5a6e734e9ccf5b84857a00d97b9db846b057004b03d88b827dde717fc30e6a5246c752d65dd625\nB = -ebaa580d3eef5361547c692e107439c8391ac0a2d1cec0cd275d0be69133eba8a94bd186ff9a129af3f5a015d5ebd30215643554d7064635dc11ec7a8ed2200fd637b099e534237f0495d2b629abd4c8f84aa1d925d53e98490d02f9fe51bdda08b043f67f0903c0195fcb886c04397d3612e4501ab8c7b7db69f781e169\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 76fcb39f94dd2756e8266c025cebe8e801524a757b976e35ed45e3da3db720061cee9037fdb34776c704ad2059ad8920e400bfbf10eca9bb157eca7750cc31fda06473bd22d4def80189c47ba32e2824c721425f225563df2a2ea1edd090e01c0bf980677db5a5dcad37d21a68e2832d1012586f506480e929b2fd9bb4aaddf0\nA = -75f903ed9bb0b6db8e3be16e797258f6c18f6cb7b16f835f04e3045f7e4974d7a86a63f2ec351c88fadc0635b6dc83a797cdcb5cce1a1674f89e44190991e0930575b19e2aa1512bbbf2ef6f8c3e707b17516756fadb635d8c6bf9caddeba14834b5950a4d1e98bca79a4d15e5fa5fa3c1727d7a49b33d481d32fb14ae4164\nB = 4ccc582c8460f7def2d26167b68788a681c41bdf6dc805dca83127a18bff6f5ebea6db75cd959beb859637b200ccb5c7644d571f436e46a357d027edc9769da226278f7ab947963f7caed1e7e70e572980e960e9764a40c6db67bb526694b084976142471270b2331da563a10427cbbb38e76203d7da5d67487eff701d75188\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5adef30c67aefea4da3884b8a1d0ce6724492bc76b477f1053621e7d19f3cac15448e9401d34e05ac4b508b9d1db9a8d323cf43722e0af6e3c3b6d463c6007449c3bc3236d156cdf988dfc308a1b4911554ecace52938a7b10f463d14f917ec3d9fddcf6d33081745009c59b58aa22bcd7dd8c3bbd489997d4e0bff5473ab9d5\nA = -174e8e057a1d66e22eff88de26f43fde1c8efe5611f6ba4f318f027f5a5818df02ec3f014dfedcdfc8c143c5005c3c5098d409710967c93474f5854c1113fe4030e6682bd56d389ca8b9a4587b8b9262d146bc92fcd81d75c3bfa4281898f394f45d5dd11cd4c7344ee7a933ee346bdaeb6f5188967c388b919a0ce6730c0bbdb\nB = -22702bcc4f9d5bc6f803af6af8072780ff7de7a346d6b9293ca751d6ee3a81493fa86738c44cf2b7be4bf14a55a4f8179c35c09dcb1485f4c08ec5e9f9b1efa91f4b5f15a31a46e1ed71cd934ba6bd271bb22bb5703aa468d297f360ecbb48f9fd6c572683e83ebc3d432203347dc62e19fa06f93e087283347950829d4256bf5f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5c2f67b1607776c10fe2c30b112e541c4d8229f5f99f615fa02cf715d3f20556a28eff5c233c58994e9c6c1fcc37b3416b0875b9a62fa5a09a4b8f9e216487203b387ff97fad1f39f674ab19c5e34cb2f162e6b0b0b0084f0618e64928423b73b189c744e3de9fa50d66f45975f68b14866cc16c8c6c722a54420adf027880aa\nA = 67056e93b69e8a7b789f1f8b835d9c6ecb7762f844d656b26df9844a60bfbe0d55684f61debeed31a24ef4246485e8a1d43d49eaf97ed9e7b9f2d2916a8d85b8c9e8ad5575cf5a3fea42392e5d1dfb23f7ad41a7b56a4f21e2828aab38a602d560c99783a4f807120292ceae366b1fbfb4be8e5d4561bc8944e7f17ebbcb0fb6296\nB = 1f874f244ed6cff9f910ba9a58db0dc0a7435e8d99ba6412e976b8f64d4106d3c5c57ba079384fced1c261aaa538e131734451fe84fd3cc5cc8b3ab46b2031f888d95084cd3a35a61092672a9118eee4ed1a0df0409e3613b3ef45a8b16b71ec892755dc3f83c5492b67fb9a143ee6102d053078f4875636b20b536d5cf851768cf73\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7850019c6712f18eab877faa8489daba23cf34b512a3193852508185b13cd5a2e9f503fe8d61b74b5d3930021a5b8c38322aae9b9b1b4814fa4c2c5bc409b58f11fc8fd7854b17baa94a6bff5f234832f9468d90d148fa2bfed774ac03f2dab6a506a70db4ce363f932adcae202f04fdcae968f632dd674416c23d4e21345ef2\nA = 1e378a0f27e6259763890d29e112e3d8d2bdeb9994c49fb67ab680b6e71a52fa0a7db886d3baf52f36d943b5430ae8bcd82e229f4197239c35678eed254c5816722b995e9c311be942f8124e2f80c1e59658433a57f346adfcdb83202e55457308161d2f928b60efc39538a6469f90f1a868cf6077568c8241623896ddc2705cf04e4f\nB = -f4ee37e39d4cadb692bab5483ceaf0258b068f2c0354c540438803780c983469ea28324ce7e209c3bf55b91f0a2f4544bf318585e4514333eafb9b8c2f02170c620e9b5280a828ce1d8dfc64ae9c28577e15071825a85a59656c5b47d9a382af6b78a5b3dab1078dd647e0b473174b8415d401543d30a4018cc3eddbfa546d0fad9cbb2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4c8f8b671443a3af5ef5749885ce5de8e2afeadef9051bc49c0d7e72922d049b1accdb79d82288e472b07578e8b6d2176d6cbdd7f0caab593dc0fd9224a94920235410501fddd6001b62a7f7d8eceaa7a8e4c0de52029fae68656e8120972b5cc1c2e909c2742e836f2fecfa51e12e4f8a2ec7e69eab061c81785374ac607fbe\nA = -5769eae759dd6bf94468eae94189d3396886d4569b0ce264c22d39b623be3abb01bd5008b9fc86701a3373f7764118becadcc69481cbb134c20f669cefeb376dfc489dd4ee91cb333d06afa391dd322abe2b3b715d11ee372666473a473e29dd90fcc97e939049b455be52b3f288db306999019c1177ab5820d94859a9d2f050b7ee1d4a\nB = 44adcaf1e2afbfddae19b23cfc0f0ba1f940d32945d0b541db23f3a0a9d06fb1f67ade9a8e620bd96f4005ced99430c7a55eb7e93a701c829fd5b9e55dbb4d3833afbcaa0d9c946916b1a86af4a6393b1155c6439b8b82260e09ccf0ce5d1c4856f4d524983e4b0fa123267694a1c6118beb8be26113a02721a02d7b0ccb01ec6e9c0f9e19\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 51e25767b8d4d7b2b0c2652d9ca6bfdbfea06acba543b1bc8d3d25b2fe5f2998febe1a6e742abc3f482b4267854c2223a5918a9b5c84e0864278283bcb5bace0c046db1d0240443404fb62d70ebff3ccc655e5f5977958df4c878d9859a69731744f3d33978ac31551487270bb4fb56ccbf59402ef9fee42cbc329420180de08\nA = -1966812979042198f70b3f1238c93ac5c6e5749f1108c2bba869b1dac7680f910e56318c9b59be9212e713a348767ba6e75917fb599e929ea2144880d18d4fbda4f4663c7abb49b02245169f385e09098a4e01b56dadfca8c803acb7cc244f3c98bc17440ab2afce318476b80e1d0b4ed9a8d6f2a0be64633f8faad5eb48de2681a38a633ec\nB = -2e4f5eb92fc34c753c61dcc826abab6fc4f427c6ac7e73ffdf65b1037464b2a9a0b0290e713d81ab57c0e1dc30e76fdf96046fe10a34cc4511398319ee34bcaf73763a9042fcacf59a100c43d3333ffb3743048e8df0dc61fd0da3f935fadf882ffdfa9f0f42980c1af6edfdf161c4b16087e2b14277f655abe54582de79c51193e13169b55e6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 33539b5f38a9943b15801d449adabe02da6e21651d96acd9aa40e866bf65015fa40178399254e8af6bb082d021e2a05da0f45b699d193b70112e114f0d25287476dc0c733c5cf9df57667ad0d3ffc4ea2f85b43cd10459cdca9465b0974e578c00a6e275e0b97ef2a4c9886aab7b5947b78a88f84a3f1d8c5f26bd07bcc59886\nA = 531b891fe9e8db322cec59a2115574c7a304c423e6b11516906b840542b2c608785e2c18033262ab9cf68f63edb40ad4f073ce8841db602cf8fae0a6771d741c6392976c9b333ecfcd0c8e9997da40616ae2a9e0c6be93fdc7af0dc0668ded1e42a9f729c70f74500ee76a91d3d993c075c2f645b35792a20edf17c157459e35c0a48da6c4c6f\nB = 1a6fdbfed1054a0c5758f92f72db7e5737b0740c4d8c3ae4713366ef6709b21eaecb6b74c92541a9a0c99ae18ac6ef7de79d4c84ce39ad59cea9c203734a99bbb895916275e8778cfcf7fbb7b7d081a677769e4ab96bc7bcf23303100e629fa8e07f5b8fc2e39c7b5724c72907eaad09d3088783b3118e57c9c8ad1799b43a13f73864c5602c478a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b92982304", + "9d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2eab6018361f557ab06725ad90f6886d4b468ab1a193f8fdcfb4ad15fff781c8681329a27aeb5f03a81d7c404b8017b12fe23165e941ea767c733513a07e921aedf20596763f6f977316e37bed70f6a617e5c2757c229c59b3d7b1fe8755b5f65f7f407f13634aca7c8a267e661ae2f77fc5a95f56cd6c8458119df587478b1b\nA = 1cc779145b2b7bf9ef4c9692845e162329940f96eb43e04db8728bfe736698082aae6b6a1b3c32867c293b08547a0941cf4059d2d567840ab6ea526e3724ad59e715a3782ca656cbb739dfdf0c113a18f0dd62423d4edb60057fcaedbb852178d38f1b5a232842b4fc645cbfd97a8cac0b094b870064302dcdf23df2c9e9f736d93409cbb8ce9ab3\nB = -cbba16086b51bd83d3460e51cf193ebc79b826e4f30978274eac3b2dcb04e9d7b56a1449b7cb128bbfeff5c4720bae45271fcc64085d3ee501f0f21fe73cb7db5f275d88be55c339f9180ea21a8cf3755a875331931b75d23f57c2030c89c6f9c1ead431cb4dbd4480564c83f8470610e5673c7eb6c0fe7351ffd7ee460df5db7872c67041aff0227f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 96fd93535728b961b4167be8b304e570cc34e787c12a9a5d76e099b336ed6b837cfc246c5bceb04b0f4744c5da7071fc01d70e342509473e5bd7c60d6046c9b4f21c5ee71c4e678447f837db3a7694fc3936ca733efdb7d387f0f6e263b3ac0b89054a826da9716691c9d580ad38d701d08ca090b6c59be466e1b9833e75d820\nA = -6791fd686f46c3773fc8d7f4753d178a93f6fa4941f4305d9689c2a305bc67840bbef80ff05c7bc6de3a595f73846609327d28540cd705f5aa94a3ae5915ef55304c37c4c43a4b46906889331ee16585629bb303673d439de9c0236f708fd19a977e6e1032e0576a921853f7dd328979ad1f1aa945905dae93a82b3af9451a541f544c18ed2546b66e\nB = 6ae062b39c77bebc2fef05743e6d35e14a31c6fe1fdc42d8de2db94ce70a6d60d66263c7414b1081ef2fa6ab511b361b8baa9c71ec628dba5bfd772c440baefc2fbed68d40897878232d9715c4b7e7c9bdd41cfe7b6986d825f68be8cc16d04afb0cf593f3028f3dcd91bc94923f3d7211aa5f0f12d3270e8df8bc191808f0e266c4fce2af97ac7ce06b0\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 949ea5f645ffe5d0d03359d51a663c7dd6e6013812a47be309575e036503126f48677c68c4ef6e7b3f72d76657fa282ad5881263e649b5297da82e24298300d032af3f5e8309ac7eb597b16e257a6f7af3476a264415aa7783433e83be57ffb3fdb404a9ddc3527d6a9c297f8cb7b6674961b3af837ebb65f218147a46c39cba\nA = -10f59ba073126d92a201529a5374500612bc59a9e66322c6706b422d35a4f82d97e668b268f5527b4641c6099c80bcea504234f3c1e3fd29eba0f161da97c50aea542becba499f29d4ba5571873d4dd9eb3f48cb26fa6c929a704fe8e49791b2ca3293c2428d9cb453263935c9c90a4a2b39d23a0baa12535845f907d42b729033a0a1e74d18da30a88ed\nB = -34fdf9ae6760d4f434d09ce2a7760ca2dda14bc256015809745524dc49d841b07102aefe5a1d0182e3e09d4d45b415e46f653185742b9b8ea6960160752080e5c9577a12182ccf1a293407b534ea8ddd33ad16cd19ba537d8db5b542f86a2a292423d452bf18d82361240a7efa831518184572c5a8b73b108a81d5036b3b530d98bd47c7fb2123418f12e05e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9ab739ddae55a0d71b39974628d4601122ba6c5035c3ad0439691317f23dc33c0014f3e870a105e4dc1432ec79693bac658433b21cfc218ed411e003990b94ebfa87767f3614ec19f5bc30704adcaf85a9d3d15ea764c8f0bbd52ff388659637746d39859398c79016ace8c6f97d3a5616711a235b85f334fb889b9280ccbea1\nA = 76b15a0aa0f59ec804a5e9a627e1fed524320b29120b6789f8e71b1ac4e00a9a8c826919035b84f87d291e2f35460bee181342136dd9eaeb99ed00c6328b8e44c49ede3921d6275f6e7f03de179fb2374ae2fa6c58852fbb2649e214691daef945ead6c8bd5a53ad2b130e9eab6ad046ddd6b80874ca6515322bc171ee32749333669de0d9c883058423579\nB = 1fe2171056ed4585a143b6b2bb5f44047664f64d710dfc05c18be5840ef9426ef05b6e92e4ecb5544ee4622e9030153dd9827f2f01ef38e62b88ecd6c46b4457d16644ef6d863c226acfd6928a40de614a5853137124fe69127a7f05463eaa49bc742d8f7be300d06b302dfb0ba86801119bcdc01b516afa360aa8b22b7c6c1839cff859ca1bf26e3f7e030512d\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5631048ffdb2767aa04d59d8a5750016b38b983a2d53743ba4de5d93bcfc8ec30183a84bb1e290ef9c72c7ad357728acecfc613a6f9b3d712456d545ed54a337930937f4589fe41e66ee930db3dc10a4fe41481008c69eced65b9d1c46b8574c5ac8f7d94025d8fff00ced17a5e17508527681bf94c2dedd51502a2c4652538c\nA = 1aca12b1933f25ea081e12ff4a4f6f9ce379f96d976da2ff7b8eb8ad791fabe31c1148fdec22dfd67828e540c955a1e13f40c5b125e1c7e6bd839bfa84e5bfb58bfed76058c6db77af7a34ffd25fabd60e19f65e1faeeea6371d7785f2e5bddc8650a7492e06691d61f997483661eeff54a30656f1daacf31182486bc40647975151fc05d2f64b50e632f5d5c4\nB = -88ed894287043e7e5cd2eda3c1e5c97f85809f7a246b0c20891fa9a024f3aba4ec1f3d112580fe6ba6b0bdcaa1325ac7ec9508aa88c187af08e4f37631eb6cc97e4481b18f747ce6d35ff355e425a4833834ffb8d34a818bdb015fb818ac9f58feb87020234243aff912da5590ea3f6cba74f1a9fc3ffa2b4aeea25479c55a3b572621e75d86d8c8f6ee4f587e0f5\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6ce341aa4a571cd5bc110dd436acaa09f409661967de0bd096c77c60db58b2b0ec95cda50acd7fa20ea4266b2c579eeb6ac214a75d40abbb70845db74c4d6c93f8c545add269d45fb15d985e7e630d0425565d06dad4a3ff9835411e51fdd9780c24f466dbf29244cd1b8c3445af181d0928db399bbc8632f7ebcb9d48c0b754\nA = -52c53999b02a92d6254557203cb31a21dcb896495d1f29f3277d19129ee43e521ab9d5a297204a844a9537d63b74686eceba72ea2e7b98ee8895513395cf7c44c99348f5c4eb657874a8115f0027d6a416b8a04a1ec0e6809b7701ee7d41e99996e307bee9c295ab3df1faf674e0067d0ab3bec4da998580203e33760870ae472a3045bbd66e352b8f4d284efc00\nB = 4329d110504caeb71ce0453b0706ff675f646e70a6bd9575791a38f672eff226f4958f8b1fe4123c0001d8f8595d8030d0e9798232942725a9b9d654ecf50546adfba7103fed796b455ffbb4c153e70f941bef7953c8a210d6f2f4ddf5d9a79d9938503ae8f24d69d5d7df1c988630ed960e12dd877bb80a1ab0bcf6db67e0c0578fc0c40408f72b19052534da8d31ed\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4b9fc1e0eb4be199427c48bbe1b53948d0135bc1965b8aa5421a4ec704b13cf934c650405ba02ad611b0f29d46d82d4a1fc5a84651a29364524e37be2fc7001cbd3c792aa477802999841ff19620cf66dd2453c9b05aac349b9094d43b40e358f32805d87cea3cfa98e05240ff95ec57d88e0a12917628ebd34946eb1ad6799a\nA = -15a223b691d8b3696306b0ccdb52c1d62c7c2d1ac71e5f07cd8fba960417b42fb5ebed5eb9469be67f231b5254bb0fcfadf5ac5d2906769e8bf8292f0442986cabd88805a162c0c1f60f9ff0bcc2029ce33452d05f754375c0bd147fba745bf8a0008792d4f90d0e0f2cf391f2d7865705544f4a220ded44732321473c0ae7870394d4e625df11bd0923340cb70b995\nB = -340e5ccd644849d982bdd455ddb3b9a23ca14e168bb87256bcc370ffb6b7fe78fd062b3bcc1ad3c8c3b8cb549f2baaf1b7f0f6522aba02fd35b651f7de52b3aa2e0e40352bfd6ed0f84a2bbc3b3a396dc8512ca1db01cc69611925f1037794c82a418f10e0d994f458d1f19051e8bea32b90ce744d46718f42e711c094ad0a1ee96c88920188078f1b044ccf307e4cad7de\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 31c090e5160faff9a136a7a482b42a43ae3c7d00c215cbdad28804be0e7b12b0b3af820c1350b1622a22c8875f24d48ff16231c826d1a946c66f70aef92d4e6582e3ce9213d907267251ac74fa3cca9f1c8fd53fe9898aec19936a2b797fc345d68f0791cc740199be39c05053d5591d874b415e62653b04a3f41e263d00f230\nA = 5419e87e50b28b6d24927934b541d8de548a8f4ec7e9b00aadb6d23f2d33406177d3fc72d29ad2c2e141ab2916adfd30ec4791c626af61d8d192276d632aaf3b54e2ffe83b44f", + "6f1ac441e6823b6b58cc08fd7a0af945a02eabb5aebb2c7ff0622a17b38077cd0cba906ce23e71ac7f4da40ef6066565b4cb3a62ebda28f3629eaa251dbd9979b123a5447ea20331723e\nB = 184782ba4daf429cbd13ac13fe93fe5833f09915cbbc707feca3293e505ce9cf0b4b12ffc8b178e0a4617f809be53d4895a4182e7a8a65043361e654befe8b01429ba4b7420193d1d7d90930ee19cee0316f33a5795335f5fa517e1ffbc99b95101b0f936353afd3bcfec34851ebff1ef02fea991a01b587d28640c935ec91496d1aa3ab8d38a6ac75b3a4198ed27b9019bb3e\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5eb9f3ca660de481968a3c7321281f22fb9273b16fc10d8eff1fe34842364dabcfaee4993c1c8ddb7c8d6e509a8d2afc005075d5fd3c4471f0622753c7797aea900e785ceef905e2606f64f34e47239c40b74f07e2ca70bd5a18cb0a88780489f3e98232221f65ac9c5ce703a256b7b75eb1dd38778d8bc05a37ac9ad8d36b35\nA = 1c73d8e3d5db127a81477a5c4c6d61ac62af446981773ca15a9a01fd5175a2826a8763f91d68df28ee606e8ffc203305875a238d2095345556f12f3b5e10c5bb6ce3f90342ac74b9ac057195c863c4b9d28ca1d958a98649c7f8897bc6abbc39becae963f61b33bab4fd20d9d0e5464f21c2cdf06d00f597dfde45dc5919f5124f26888b12d72cbd2f57de3f2de7c014f891\nB = -e406fb60e35f0abdd313b8431f4cc89fbb034daf71fae0cc727e9a93cdfde53566fc74e48f4cc2111fad158c63293bca0b21b98416381b81d2443d0e91647679481cd6b6869b37112d3b6e575eea7fbb5bdea422558d817b49ac36a829926553202cf9dcef09423c085d26176a89be741ae20a434ea461def090dbffaf2e2ef97bbd4ec779041ed69ec07d125c7b85a2d215bb0f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = acf9d363fc9b76ecf7e61c33270031340e66595e559dd1c9dd4d2243819b660183521a4124558fd4b216dcf5c52c4127fe517c48cef428b9ee0f1bebabab487c968a80b9815e82c12e807c096974ea3893a8d5597f745365c352a6bc6ce92479176092f02907538c5e784bf26dcde7672338f402753b08de8aa21b9480df6955\nA = -7c03ba6e3939ebbeabd35cca277eecaec31f326ab75f1a29e05af50c4e62e0175d4d6a57acab87cf1fa3a51791e9a2b2d4d5db570ec3941263902b0c74544c323c106557cd5139d2a25f3c3ef81ca009d4e3c16f1abf6e2b5196df1b30def46d61eccdcb3741a6dfc8e8c5e6db68ec29c82b0adf6e35ce7aacef8da806b3b58bfa489d319869b20768f8eebb604a9624d048f9\nB = 4e021959da96ebeaad17f9896ed53010d80ed3fd4c3a826a266e82b80ad81b3032303e7c0e58034a652b8aac00c08d42a530039de60d74ad349438f5ecca1256342ded6f30e3bd2aad5bf2b49124cb27f45f697e157550dbbb37f5aef0f04839aaf1ba43bf1e77a1529818d0fa91d940904eda6b748e5c86cd1b37592542c43b7b4afe2b8926fef6dc01784fa431d43900edef27f8b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 24124c69aaabec7a7b4e7a82245f6cb14b199852a8b314a7b8d9049cb66096d5ac93ac75eb58a2004de8b0fc8375638c0878fb6a45be8bfbcc292e3571df1bb8d6e346d5595fa395fef983a365e4e868154fb3e337d47771419e7f1dd5e4220900c564d7cbe8e7792ab288f99d265aeb296c5ebfdaf08b88d9b30ac660cc3ff8\nA = -167c959417e9566c93e7e05d2a410f4850e3a313e516ec958c3d2fbdecbf58072d05691c68981e176a867d7467091dfeca11f695f750c8c44ebc4d08e39e679d96c4791ceb1ea3b89fa3ce26f7ef214c5368c03ba694f7ae592bcd8ae53a66cb3eb1e0cd3c105faae6eb7e7a8fbc88248be722406f2d35e46c751b5ceabd992091eeba15191ccf6dd61a7ee0c624d43b188c42b6a\nB = -343940f3b2a5f73a51d6f609e8af306f44ce7b5c2e79edf6f4dfc07866dc5c4b2e0ba48099b5503af87762a44ae451d166f8914ba25b3cc41a766583bf73d27e40784064582fd9fe952fc00e9aa2d4e4f1ef35818978e725e69c1bcf267fda4d635d1d292d54d3ad10bae9763dc5d7f7226f371184465695f2d384d749fe07967a1bb64df22f294ed88b13600c7068d881f713cb8e3ce6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 50cac148215963e58cf6d2ebc36fa518c63a0ab8fb136ab84c9657fee459043ee9f42aafec89e8ba5fd1cc5c4495a41e80590ce197e12c087ff7e6ea88ed798735f55a1634562b82f8514488ada526e5dc10700058980885000e266cad55948d1e080f6343f84b12a3698d9ad5427fad4017d931df77ed2e45e2fb8380b7fa39\nA = 6a9833d768a22ea46aab1a1619f30283a1ec254a2de5652981d73146aabe31041ed04d271c6f2e5e2d090cd615518a06563a94ee2b12cf9f142de3f15599998a712974d0ce9b122a2aa65bf8750f54c6324f12e321a888154330f0f9e1e5b7999acd70d4e6da95c2df1da2d19544b7abd2bd3041e3228c7cdba44f7d1cbfbcf968f8fe87fab523eede0485efaf5cc9e56095cec8983\nB = 11e782e2b3f469b1e3d14ccd1b8301ffcde7e371f6e9afc99af5809110c6d70e1cca5c0bbfeb95fc3ef8352581c11ba75c0f8c445ce2aea903769a24289581c95ae5ebd9553fee61a30d155bf6011278807833eb2ce7ee2a98fececa23fabaaa259409e88e3c4f4eb1e04176d44878ad3f6961e0615ade2fe86b6eb02adeaa7c9019d63231a28f84b7dcc8bb0e71e2a717db09301e1dca20f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 7cd49d72bcf5ff4fa2c686f21e1f0146c4f24b9ad2e900dca1c0a5d2fac5047509064e65ac582946b251a3f04850c9abd8b80c92af0fb11ac13debdae8b94927f1de0e4bb217e78f5d04897c6a0762667d3d883cb754dc610442c9dbd44228a7ae4f14fca145550d813655befe3bfeb52f1c76f989ea8a1dd9c10fbc7e9d6574\nA = 109fe33568598972063279b71ba0efdc2e03f770cdec331428fb8ca084c9b20d0fdb5cf9ad7ce90c8cb8f0fef10d219d7dfcc6b4599440db8cff9971da7852880bf004266886eced8763b3569720df3a1fb0dde2717ce0183f2250034871146628430f206c12f5fd87574c206b203d90c0f2c705cad3484c73da8bf4e9f7e1bd433a6f7fd27df63079d30c490aed7161bc594eefad4bc0\nB = -b95da952cabdebe0194b7fba519768e1b56149353cd12023b97397b59e0d7f4dd1d27b65b833948f58e66d3f6928cc3140cced835dbd612cc82a7e9fae1621986f71ddb6707ad57926b03e87e165d30fb145795a70627975bbf9d9ac9bce07492de5227c666663cc28b3e70b19dbaba7f16849535ce5fd61e91cd2875e0a534a10c60d21f919d566a3469d108a35ec3f023210efd5d318c7210\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 98a89cb3c9602fe503c32c44609bd4487b6c8323737b3376dafacc3eff96efcce7a31f1b61ee6799dc9561e77ac058fe5195cc013e72a2864f7e492d9f35244b321d46270a582f6f14f15fa8203d392e81b183a1d64d48b51d70e38d49c93869ffb9d7509f15ccde547d2d9c4dccd50eba49190b6e831a9f4f9000a95dc83f3c\nA = -67d7fc8f1766c40bd476cdb65d4dd161c3d4c2c5860a0c559f0e87ada213c9ed33308c36bb1c7d615fa69ec53656bbae6b57181a0134af23ea2a75f8fed3290a2f483392a3745fb57adf2121738c84f6d34325121a702c8ccac0090ea27fe9a5ebb6ba9d4f397e4a7e3151850b3d7d25643398bd3e4c1da081471389799245d986cab825a2e6ca72b38ff978a2753c835299ab4597bc65fc\nB = 676ddc4d18960817ff8fd2adffaa68c87d234d62d445d6ba3847ded849356d929d9e4ff01f517d7b1c0778bf90f475923517d855956f17ece1e032e2fd474d2133d6b8a591995454d8b587cb4f6fdd0fa29305f146d340cbe6b6efd28a926c73735621be0c5decb792083b3f063a43dd9f635e03f78c1bb56389a5cc993c8f36134d755a324d4fccc2ac3bafa270df67db0a4ee6ea4497aa33b5a8\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 76c31404854006a7d55554762094df6e11e0393f5b0451d85de2e5b104432df72023a35f44da10dbde01cebf77b8f9d3ad582373c5d32232564729af0d03c5450e439045d96a2f0a38871c922af2bd38c545d219adce0ec80fccd121d6a733bac09253604a8a0b1ecf0f24e44b818ab9e9974181cef10e9eb17684c57d72257c\nA = -134e8784878a8f3cf49ccb952075f9f9bcd24a20f8883955f262867045c11a9c566abee00638927e5de924872fb98f6376e321ebf3f567db6cfeede62e04f839617d78b7c9d3487b60a0d3897b3fa49b14c12511d04854bde4a9dbe5f31424a3d05cb75d23b46f6c0819536020880afa5a2c173f6881754b56f82a2864c99c820156f96b5cc4665d603597331d98d90a52f4a30c6215ee5eaa2\nB = -3c5c0d35de5fb21c84d2db228829f43b31132b582556b92b495f59df5", + "02a6d00584bb5bacd9b8c1a8c7eab91db0ea24b40f07e62a712842d5c2e1d208a6412a068cd5c6394d715260b67fbc03e3ae7eb4862f74f4d7484f747774fff03830c65fe022d579adb6737f6dfe297db750e6a58d1004e7e2716838befc2ea97179ecd53b7f36e3540e1c3a0f3e044bfe2d0efa9b89d2d308cbd0bd88ab3706\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 5b704b3181e5d0494937b4d6aa8172eea82919fd1d884493197a6a85ff047a7bcd5dcf072bdcef0287be20d4ac49918d1df550d184f86d7220f0a84fc4da3ad05e131c443fb529df01fec9fe4fa6fa2f36e791f9e16b4092759016d2f9b1ae7c3d071c57edf26386aaead767a3109c12a5004c7b9fa595e6d592daaa2dd1df04\nA = 48a0ccd2d14e14e2aa862d306501efe5de239e8ef36ff6251c861a0aee9f739411f402491bd99aebacdc26c4f30306f9137ffe4579c2f13efa81b979ddfffcd23675ac6307c0aa3ba8ee77a2e3a3c8e241bd2ade6484e6ead32ce8d752fb3584d14688f223758c5cb8705cea9c56136b219d87f9904bb56be2ea1c9a035df33455206e6b7972cba32ca4c3db41991117d88da3521780fe65c4023\nB = 160120a35ae3edac3edbede9ff1c6f317d95481227d87785b7ee46cfb80fac9973e418244884caca3211a3f6cd3bb419cf70fbc22d82ba5ab98ad80e1f6c2cda753aaf7be78613ef25577107a47ad1ee3c3645db85c4d29bd77900e99e1f439cb23c6c68662c05322f94feffcd9e37d8665cde984387093a043447de590e7874e6acfa37ed302040df4d5c3dcdf9fed91b3d17ab5c141d4494d0f301b508\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 448c3a64958b82ccaaed3c74706ce0a48c5e059c3610cc03a6b5a03a7de5d4f1d1e4b08a31478fa8edd58401f0171697f0662146ce2b371e335d695f9e4a671255f29fc0b9b7d1b2eca4cc7f8357aa0920b5942e31bcfae84e909828fbe5d02251ddf10dbe4c15351f675e96e2eae6d044da1f0858ce8ba9b7aa146850b85d93\nA = 1b2a52aefe44170376df29d17ae2dc1501c9c296f72f271c21f53db71247e72c3eb2b780190c45343bcc8f548507559ced3bd4a6fb13f9174dbddf965b9c4a56c3d88727736d78be9db2268cd02382e50c6fa28ddaf8eab9f44ad45d5882a5100b3027c150a7f3bb36f29d24a76e40f3820ba116d645800459f06c20679321cf5be72450879462f0eac99ab6ff8d26b464cd0e6d78621c9263394c15\nB = -b7d9bd08d7d8e0e9596851b7e03c78973a502afcc7b5fe5b0db6034ebb8a11df1ef7ed0ae1371eb4111cefd61c61935d768be3e3755e481daced219874cdf0d07a76e7144be626cf1fc21c8a0e9db4389ee213193775e95d4d86741d8d8fc820c239b7a90937000dc3e89b2fcd61b44e1c38c655bb3d31aa7e422b4406c9e4a88e6a2c18ec7c048f4a6b5b270c90d9fb378f64be3b5b351621db48a6c18625\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2192157490ae044a26c23eea6da51d3a3dd08c7fb67a9beb76d37ee24ac0089863aa7f00849b81bab8259f3a0e1bc744d841e07aa413c286e4bef2ff3356bdbecee756026915894584b4fcef7e49da4012cd9fcb5dbe3f3b867cb6a7ee959a328b0fd56a9eac1f4e40a22bf0a30073cd2d48f99245ac03c373810c54eaf3306c\nA = -598eef47b40d1fa1ce260edc561bd1c1ab286a7e068af412ec2baaecd07c5b9cd596505ea1bf0370ea961c4ceeb9be76baec74e6952cb846f20e5da406bd01368b85d59569b403b7a305cd7448f331f10a34def43c738fd633df9a3eb194c32d53aeb567889927271d71d3929d43fb9338248b64f7d23cd1b053239e09cc2ccf5fe9c9ce240f1a10fb151a8583e4b4cbc70ec3082dd20a9962d564544e\nB = 559fc917de34bd7dd7a23a432142ed79e3ac4a6caa357eea21e423eb9af7fd94f1eca735d2588ec4c2ff013520c3a0e209627217cc69bd5a07ca46a43ec1f1bdbee5f09ceb1b2c18bd388d3852e51070943f16152a73da624be680c671057677356c6f281a4ba1f7c60609125d7fd9086c907ca5c191820d80e483886b70c1074e2963c49996ee92577334881edafd88270bb967da795aa4fefb739e4367390ae\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 3488bf00f67b852592922fbae64fa56d2e4e7081678e789bbb3b4f48df62576d537da2e99c9bdd721c725b9a828194662bbd51ee20ba73d4ed5562482540880686d9fb1e8ae62d08e39fdbbab1d18e399ebf07b3a6559dda8b043fc25a8152858d39b10ff64776e00a839950e7a9ed5ea95b594b6e9e9d4348ceae08071ec5d9\nA = -1b135d8cec9969561be396323e2f8be0c60903ca59b6c418cb19876e9e3cdcb9ce4f5251eadea11fd6e785476c70822aebdc94617063d161ebe55584a8a774ab230b8228a2b65bd5a6c873bb6b261429eefdc7d0c64c7e78133e739efe57f835ad03ef8f84601e1a2310659db5e0ee706f23e3c5c38c9f8c36e5b15b654d1cc528f1dd392f1b08921af8be6fe4e4e6db774392441883ef867bc729338943b\nB = -34fb63435c90018e5843098e379c76ef3ba0615b6b500854b3dda3e77fc5646228fcf3a6e1cd87a506e4959ab05e24474990ad98ad0865942737734c03dc289307f1b1f424b9a8c2264350943449b3d2b0f71f989039131e23095d122ae98c0089a184dc530669e804140134e5b602861a5e61c030fc3d3b3eef0a59f8c0579fc9b0afceaf16698de3fa07c43231312254c04ab11ad7a29efc4597780c2cd1b64b43\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 8ea5fcf7fd41803606c95729d2d910941e43b222f9b0c93a1a803b197fababbd653a92ee34e805906fde29b307a962a294aa4dabebf0d181c046653ad0fe6da1295eef817f3289dcc6579cee8869198c39a9f79992cf6894162d35d812df327a64470c935994aca4985d0e6a783b853ad762338dabd575ca71034e29d768d014\nA = 6858d029a62b0f75e4c59f3ec067e3990b2304c90a097daccaf554abec49a9d297ca14648471dba08f22ebbf8e238c89ea06f188203599aba56611eb3d4df09ea795a7e28f91f4a9a582c6b949c6ffc584a076de653446aff9b24e87202037974aede37aa9a121b5b70a3e9b5ca376c9056c2c91f5d5484baebb64cccb6a09b4f40529afad1ed64b4cc4aca586892693fb5f92edb6b4d5f678f7a2441e51410\nB = 197d6deff7adc30b025e7e418cca0a641e1a1b35f78fb56b9d8847f0690313475e6fbc6f73c3a718b10bf37434dd9fb1eca33a99bbba674195b20d35e3b34ba9d7c8438eede24ebb48e6d39eecd93fcd7dac44235ad32f208919f57b261da70ca378f9b03ae5e5a733f97f0b3f4102d971272015bf50b6f3e50c7b36cdaa14a8a580366c9cb0118ceec6e627827b0b8f614656292675ddb66e1c55355d5a1d78e69ed31\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a25db977e7a8fa4578fc530995335411432ced67e131fee2cd7ff56970df64a6f0f4a7d225d2f4ccec8e98273ec9a0f1aef01dc0b866e425d64e09cafb9ebe3f80bc0ad71c769f1ecd5efdb4a990ebd3a94303f52f4a97e3a1d615918f8b2df5321c4aa9339b4453d7a710a803106dd0ab49c6cd9aea431f97fea9fcae0bbd90\nA = 13f97ba15ce46ae32147a0aa4c1639b6b555f4d8a1af15ede4f1103f7a0b06b4625bf456d667720adca0c4e26e858f008b012fae63cd89322b33fe51e87714519e7dc3cceea27d968b46ebc04024d063b17901a7ae978591ca6ca41afffd81769f04b714134cfaa6700cf23bfda6ce67313988bba5fd3782bc62f76cf551d140c978dc002a779ae37400d34cbea013a5d1338b203ff267861edd88ab8ee1e4c4d8\nB = -88d8a4c8c680fb01f493f73753c70ee753951d4734627da14962e36449db5490b8c575729fafbd203a125b500b96364e6799d9cfcf0efb4ec877e86865eea5e99e2fe5e7655c1ee0eac641e73b71c66d7a72c2934d1ccfefcf59781035b2c7b89e5de3f7d1e9128cac57947d22e7577832ba374492a2f53be37e17733d8bc625fa77fa5cf093975049a5c477f792fe75e85da26cceec820c8b255df0292824b4c3a8ed455\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c1f2165a402fe9becea284dae60453965ce327f540bb8969562485fd1bb60372b8689d9c9c97c91bcfd699dc370117ea8b704f06cae3d972dc6e5eaac971597c69d4dc24a68b256f97229e643706aa6d2d844078a5fee2d08270820055ea58155d7bc754f09d0c6f804e55ebe53e3ec418747d4130cec68533f6f0c2f8fd2409\nA = -626a1580e52ba52a877cdcd62b34cbc7f949148671d4a61201e03e98985d704b2975b9a2d9c4557deae065becd662ce8448171ac582894bfa2c59d4ed20c6d0471fcad1d0fed1291df5e4556aba72f3645486580c8bfd0e3c8f6cb34fe17ccdd75fad4d4a2db4e00bb8c2a23ed17a31e95631320590f40416c153efdaf897e3b278a1faf1917554d9292f90c4edd5992748b58492289eecde1af34976ea8ff507fb9\nB = 44c336d77391", + "18340048939d6c198f73f90e13030b69be286ef920902391d87a58df3632091d0ef25340eab395203e8dcf3389e95debb7432165147e145735d2e3226637b4b8cb7d85d68308be07f217f57fe439b31fddf3fd469869a20f1f852e1645b0d4903432ecd1fb6397db4c11f6b6b9c0fd25778b0ff00bab9ff576b16538a6b7da40f01fa7b987af8ead41ecb66b8940c0e8a1208d0026773e711153d99348e92303\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 98eaf476f11168bb63fddf7dbf3347e619f9b580ea6804ab893214e94ebc089cb652e307f1f37ea7ab9052a352e260ff7d1e8c17461bae68c52a8a8f1a57a84c79b2c8fcc2d504ac4f553d2534f2a776ca129ec1942d83c8ae24c772f6a8429bd61949ca1aa714cc3881ed731497b84415c88ad4b9be34197a549737edcfeac8\nA = -15897a5a986641fc2cda42d185d72aa1552eb92f788bb71cc74c0e424bd038e02c620d0686ff88ebdf0bc1632093c0d89e724e7d5b526b0ddc4c7e145aa90b36be0d8574901fdf286df84a6b52674a78cf21ae4865618b4347bd905461d878537b33cc41710ddb290964c48e44d4d2ce2ed82847de75938d23ed418bb9ff1caa03b5c1ac5d65692dd1defbc6013b3270c4314a45dc67883762fda5509b915e8277c1924\nB = -3a7141f54a0bcef68cbc3006166f7e15a5c2394892a428fa417a485981316a537cb3ec757d4a2473fdec2cd61010a9ff865852af8f43afc79a97d394bb6c58643858e2b4dc5cb958c33781b5c35aced7882e8b8d7b4e4249c2b82150adfb0c8f2bbb1cff3d2ea27ed24eae030ef468ae4d6b7462f0b072cd2a2f02426b3290b87b14d14b34e91a94c5bd69e9eda53335cdfa7df90a57f97f3d023ff85537fe0a8bc5d8fd7901722\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 34464b7a50713d17b01b5940b5acfaa7006aa6b9b083bc17e0535b08783761391eaca8703af2edbe13dd0fe9036d38aecfd9faae08c0861042ea1a25b41fa8a15b7721909783de3aca127e955e177987518dd010306a795bb66466fccd55bd9e2bde17470cbd36b1e8f8b63805229754387a5fb40f3ee9a8afb2e51e25c8bea\nA = 701ae8c5bafab7f41c999e492f04a7626b2b1054e6dce1b83002b2d3de46717225b018733b0fa8fe3f973202da8a090ae3fd14f48b27097513ecd4ceb1b9729e7783c17fee9be5221fce4ed3860275b3b36b7416594d2b65e198ff564e82301cae23756c878494e57b5ea8fd22ad800a582cae32fbc985d122cbc6e0eac77c1000d3ede45ae7aa087534adfdea8e9f924efa1b19c43dfd3b7bc83d7c40df7c6578a320a19\nB = 18e0256543619a750384d30b6a7afbbcbdcd9a2ce644dbfc97a8ff699e118032558f706502c9b956695cb25a46d7526596b3d0b67b69611009265838bec533a9488d24583e7d7f2284e23c3cc4ccc5920fc57e24f60da0d479d41f5b9c6ad9152903a4f37842176c6257fb1e3e0681d6d583e704c1d1b24cf616fe638106638fe9d79a0c74f0df67cb2df9d99185324ebb037d01ba0066ba947d5345cd3201b19769d438c43292f572\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = bc57cbb3e1051d3a3035f77c2e375c7e3221dd472edb1a5ccaa7521849fc0ccc7568238aea9335a733d839e89ace6f2b66ef238267e0050c065c3d9553cf50cc5cd93d34fb43c3ea1c31b8ebf0b751f595a7e5e3e860b366229de4286b9d3f0267f78c6888ab3f208c55d9292079116ea0eb9f4ec2934c97149aa132c03336ea\nA = 1ffb0aac11f6d1d257ef7aa997a030e2a12b0615fb11ff04f344f6ecd550e8e77e9883c246e009af33a51204e4066ed4249950e022a61337848dae17c88317e15ade5b5499c0d7597a69a02b6c18db0f975c19c16d2167c583571e947676ae9c15be60e69d76e78329aed5fa57dc5e616795b5487f3d52bfe74b54bbf93ceda093c2e14104a6d2f017f0d200a9fc89deaa283e04b0bd9015ec67598425312868eeefeae9c996\nB = -9de2d82e25b449b8ca4b02b2d2fc0a023fc5804ea553aa84674a815bd74193a2e549070e2cfa0b90a53070646875282fdf855940905f834f5a07f073093c658cd1813fc5cd7092af592092d789ab5481bfb14b6683139646cff8eb1c5dcdb6a33113d1c97d4b587f15f972c06046730b7e712a8e3dd5f4bfd07cfae289047de31776f222d11510ab6b70a200ceeb6802d6c33f913c509b31b96e2b8dba9e25b0d2250c3b102d814683f1\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9f7f4e010370ec1d76fa83f73c80825c3b71521855fca5db06d7ed830c910d0430375bf319671f6a83bf6b57d9d53cfaaed5bc5d615c5690df0067b18791c33cb9f0ac9fa5f0473e4f4eb7840b0b660962097606b3de5744089ffb37d9c0df1123a91a5896d4deeab8aebec469b099a3a9a4f6d822030ec2fc4d11636706fd0d\nA = -7f56093243ec2399548ed95df79363e6ff09de211dfffc314b7cee526535def0f9a8eb9aa6f1736528ee7aae8be55c06645708d576111766ea33e0564c12103edd61ede3128a7a642f968eefd0d7f3768b1325c2dd910d459b15e54145a234225fd29932234e59d3ff5099ec4d5b5c6075f56382ade1101115c7b94e1e2a7bf075dec210fdaf2357c735416dd5d616335002d1cde6056bf7c478f810b78c661a3dbe6e54084bc9\nB = 4df1a6296428d06f51f31a1b0f66d0b77a04db3bb8e1b80d64da649899a1a55d4041bf0bb47d3e3936ee0f3740e1e8c2b235e1b8944d28c7d617d1f968abcde9dce10d6e3c27b2e3607d8df815f5a39da9b5569e95eee1fe5532c0a80011e7415800d8a9ec175fb1d13dad959becf04964b70dabde6d37072dc9f6d914309b850cda33a565515dd6c0181fc48bc7033b314ae0bd5872480e02ffc08dac4e3030d83b33488cf149e19b0021b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 6da5fcea305cc6eb47fb17190889e6a39c339da1bea2d7c95e997fc538b4aeec8b0edf7c109faad7fb6c656420f4afa104ada7a0d3d14d3ef0fc6774b59aa2687c0b4efe7c3fc83194a89c832f7168346cadc2b1fa6fa9a23a67c91ad731b4cfb9943738c7f9951945b2eabb3743473d9c0444ade756291f53fc7641501597a2\nA = -19dfb98f9f7d20fd331ea749d2019d8367935fb75ecde45d6dabc815ab9e593e51178a72816f85aa678304e6ff3a2c24079a59aca253d76c4ac633fea1070753ce770765bce47428f8f5ae40c26a3ac91ddb551b3d575bad9a3b6fc7954acc93aad2131b78fd212fb0db7cca4195b41651a5311bbd4d8c64f1c93e6520eef8e6308e98caa1cd0d3c9b4041182cbfa131c4948257f1200b1c5351bee77ac8bc8e44680ce64ed0648f3\nB = -2736d5038c60553927f389c0650bb1355b0ce745a7dc5f52c9909039465344af910a5f6a9cc4ec130b9877c1cbb52fc08b20d672e42b853d26a02bc07eabb9e3f91399db8465b6a8b1c9f4a4b9eeeec6e9b6180f1a770c139c8f29ceced61cc7ba182884ae01d14dd85bc924391333e8ef039b586b6a0ae18db3570aa560c2b0226d5e23e7e753873637c25aeb19e74997da4f5d0755571785bebbc7dade57446e0df4cdb8df23c1003533f60a\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = c0265805aa8ab52da5aec06ef7cad2026fa0b18edb27b4903e3c068ca6464465e34d3f3bdb4bcc10a19441040deaf5569645f7e09b36c56631b3a6144d6206d39c9bcac53b54210db6d484cd6a2780bc68c07272de03a9bba7e51c9d86cc8883cd2e1864a2ed711d505930143c883c57545e9c40851c6df8b3314a8c9a0d201c\nA = 5622f906b077d243521325be82a43fce321412bdab1f15e4ff0c11a7066a288b7939afc01d30243c8a4150e74286611ac1ca4daf457aa23508a7af869d2d55f54f2746afaec477cd7df0d5711dd636802ae7f673b3f730236ac3899330f89cb71d48c2838322fe856d9d8b4053d9c1e66acdb5e43614ecff954dbe37c5269d7ffe00b34e682c0be3d7cf653ef212daa3d55dff92b329126636e440b0bab55f4810a2849f77c39ebb93e\nB = 1ebe0d1800b1fcfb67d7d54568e45dc604450c1dbe103ee21d48dda300c1d9b9415dcd9f5a56cf12c2ede3c862e895efb83621435377387b29b882b2acac78386895c7daa90810092bd3062a3a4867f92d54622d7f0b89b40fabc4709fd507d4002ca80de231596630c234fa418611ede0ae4a9616d570232c1b03329bad02220ef64e455c164aadc16190ce35b78060a6b117b4b0641fa64dd8e8cddb5914e7657573804e63dc7b216b1a9aa175c\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 606d2b6f756548568013bdaba6e811dbae88fb01f5f36d30d15dc1e099d86bdca9fc1eb3a785034ea14cb7f4776586327d57ca5a52ea1b30f26e2a76140bbb0e930c7780673770fe22c5ed443c349510e1494ebe402f2621b1e6bde39b8691edbe5c7242efaa6634553e6af146dd40666edf4a3db5d1e7f9347fa1189c1e5168\nA = 14ea5e6fd612945c71fdb17ec44d95015773edc908a85a6645a8eb823", + "d11226545d05b81791401cefc81ce9765eacea7a619cb482f29d38988d355ce731bc9009969b7487a3acca2d2065c1faadc5d6dd8ca1dcd3f3d4ff61d0a75ef75272e62193618f6b802f70795041de26d6ce367ba996dfb91167cb1fa16c8977f982e1718de7d60275a7f66e4ad72ee55ea06267cc4e8b08f488579825cc674b0bdfd34a01bed08b62004fda15b7c\nB = -8a542280f6c8bf4d9fbc96d5bfa6ee0d16a09dffdcbfeaa2dfa1097a760dec7bc540a0b5b2020bab1eaa594117a40a9bb99c3f16fc340c262b29909608740b8e77fe4706a88dc0fc3bcd47998e88fa02f617062393978ac1bfe14235d43f3d5edbdfb9f140412f4fc2dfc05a700f47b1f0f90da7ae07ae781d9ccdbb951f19a8b8a9a7dd8a65942842cf207f3baed3a0b2f08a06ad0d9ab7ad0110346293d51ec53ff8165b925c0e7906be8b7303252\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 512220042f151479a6a8b7c743ba83366cb7733caf37164e9c823422ccbf78b0b83f426a7230f559d50bb0ed3d9486c6a6e25f4cf96c4fdcb2c861566c6a73215b6d08995a14569710cf9e54abded1d77fc7722d06fda4557a3a99862e5ce963e1be25336fb42a4629391cde3aacd47ea5f5426e7185c5df27d9136a6df26f54\nA = -4d108217b778694931088bc255d1f69cf8f5a14252156163f948ae58d58f2ed54f518177d668e795474952c930052c1bcfcae11bcd15af168ec2e881e6ddc8de257d0cff90ff3ad409bb3a080d30fdfda99078cc3ad8302a4bdd77de66ac082b40fddb3cb36c75a86bacaf60984a74a0fd575d751ed2830650d85844aba9e3f781b2dc6b515bdb8d9459b083e1aa653ef177de76282e86c99e97dae9c0b050c9e6456a051e7d99adad7be4e4\nB = 7b9079504c635655a588ac360955fceb10cdea5f3de548ca2db681da38c17a70df5798f72cf18691d14a5f400ac69fbb47e64115cf071466c54bc7077a228249209542683ba57791352ef3409f6a947865d8f234ea9d39491b5c001685487b32130bce9aeade97d9537afe3f2f87e8f3315619ef7f215a73cb724f1adca99b90912aeecdc81485c0d00a74387ea99c965118fc6a9af1163e60d1ee6a1eeb12d7c2bb9a54f747a415beb5873d616fa0eafa\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = e36899d83a143c82e19e11494ba18478c0a9497fc89fd83df38adcb6b33918645a416626409a156899c6583ab9a4426438d9c32cac54b78df579cb7b6b1feb3f39ca4a6183743a4b823082896a89f9f1722be842cb2d2ceb605f84a9f9b61cdc7e184593fc2f9ff2994fe6cc4860d255809d04ab47e154eaec9ecc807ceb298\nA = -1422272d9e91a14b38b3e81cbd9411a0cafca23addf4f33c94a1bca70603db879dd8a9c0b95f5986bcb447731219c4f9b32a1e3253b027b7963ce40279dbf4008e526adc0bd7bcb2b533392a105c6e8e1bddfdd2bde7dfa0d2e3b1c6ffa07fea07ecdb9fc828283e93b0ce4861945562478b1a56de32251b7d31f9a2309488f7cbdcc38cd6b1c951570675ef0d61e1df69fed78979dc755f160d93ab5a3e65dc2944d3333cb85aaf87a153a90fa\nB = -2424fc1e71286ce3be684a10dd885e4891b52e9009c3021d90ebcaf68b6db81130bdbb74869cbf142e0f44ae72684fc12c85abb5157987428c7812889beecfd7bb43fcac2eb6298ebf1dbcd2e70e4274841c2703b8685df18f6e5bbaa1422004797defc6ba843e77f891bbb46699a863bc1d77c5e3cab809c247e2975e8170da00fd9c8b232abc3fc6b16951ac4e6c96f9503c1ff2d6832ff9c35b2c8aa408645849c577d2b8599ef520da57fe2a9eccfcba6\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 4e8a59476d47ee2cd0217bae2981cf25a2c38e5f5d5c30c2d8bf95856a6e8f42429e565f1836365e550d85207246514624e7ed932d6f5802a50ff9f15d500dd84b27729c1717a3df0f2d6dfd40f0094208445193ba6500ba03fa3f4bdeaf9251aace8729b32ec3215bcfa170575e26265fe523cf44a071470e3b1547901e9227\nA = 452cfc78cb9597e67aacd4ec83e5b473ab8b7a1dcb6097fab37e25d5a6e25c69c73a6c20de0e2a744375bbfe7f612036e69c7a503255d9e17c6ec1dc6cc6f634d4c79bed4764496e5c7c026fdf9408242d3b234195e67a5681e7d7b861f58eb631ddb9aeeb0e5b3ff7a7657a7fde5975b8a9e1f643893bac47debf7918c7ef8f6d7439320dccaf63b80ec9761559078baa8e35d98fb9dc242ba83536eef7ba9901395ef02b19990d8312203df7dc1\nB = 1dc222e7a737e6d97a703fa232defc6c0a4fb2bafd247c8e547b9c474421cacb7692ec98f94be19a5e40269e1f5713d06a6d081a943dbc667bc867e481b99c55e437061cd44c4482649faf870d9347e0252ba9dbe116fb4992dc2c2a0583c1351e9e01e71e9324f5fa942322485bca93c2d95cf304028e68224fed446966073ec7326c93ae326a7a533a36e053437910418bf1761abd9c4c5ab7e6f538e9bf963903e6c80f21a0a38a683e8166e4626a8d8b743f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = a4d5e9fb7f0d75ce41ffecacd2ee1e4d15f82dfd4decf5ab1bee75fb97792d0d574fee60a30b15af80bd38e6a25b1821e61628dbe456e39fea3f8a9ee6ef3d2332412be1500fada0c1728a1457656eb3e9d94c64fb2d0ac89f10f2b9ff57d73207274ae7e8c7538936cb7241615b830cc9011d4363ef88f51c7b3ed503c25179\nA = 13eeef030b3110451fcb1a258434aeb51d3dc805b38c72ef7c79d4b0e18d600e5dd28b552b59f3dda1898367ec7da5dc6d9089a585cf52002eaf8f9ec64b8d3ec50d0bef7dc3faf203c48583ec89757cfeaf888ec4a91470a6b8ec9f26a6b07f3311b4fe972cac2f2ffe47f5c11d2dca87c62680e2229120cba4de9cfce9f7f5c33af8398c07ffabac1675de1845e05a32536329647214e54e5d9216fc0cbf2730898eae19e425688bf184d16bd1d655\nB = -ea324da99252edb03f40100e528d9a5080c43be97fe4b7e03d9563ba48040d328e57d0defd4b7ffa9bef3ca0d2682aefd2a0ffca8566e755b11f2e3c6c1b707f1b9465592aba6181e583babd5c70588e7123361a8ae77d8c398e33f894ee288babea1d7eb63e2f3de469e502b5048417043c5a9a9a3eb921cea1533162e3ce9c79e6caf62bbe7e17b180b72c59b9ef5fe1a001b733d909a8278029fb4a63077ef9b3545f1159ad73dd75030aad599ea4884677e01f\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 2f096fb8fe2156c41ab695956f13f0fd9a084f87ea5f5b1acb6b60c62617b8d7079f4b072223ba18cde474af3942599fe070ddb0ac1a99f42b9506a2648e1b8f6106015aba0bf7a824842403bd3f4ac8b6fc4a9861bf0e8ac59be0322f0495e4b515fd579dfef273160ddf96e453f4ab663e703609c709fb1f016ca919fb26c\nA = -4212bf679cc00adb2ca502604b71dd5dab99cdfaf55ae92aee6bcf8b3b6354a384656c09eec6175a95c8cb4591ce118e783d6344525c25e5b356e45802ea3ce1fe764833132e6b7bec434e4481c9cc2986904988bd8da7dc2e31cdc481fd0e359674bbff524124bab1ba4379885a6cfc1b73d953e6d1aa1b938129d74fac9dc597c31383f2f7e02fd995f7065290a9812ba8e205316ad5bac6fc65c6c7310f1a6b033503ebfe85bf6d3851bea1b65b9c15\nB = 7ad83f97f40d5be508cb394c128764532f0aee9a108eb02840ca1c635860b6d751d5f676e8670e2f61466397e1bc68f97ea52d64b335d07aed22f20bb1ed19e3e42e4205d650e6d37714c2f80d39b111577725e3bc7ce75bd7ed5e44f8377d5fc2b97f05c3c1ed5ca1ec90ba3ff7935a25a8acbcb15fe1fc7aeaa1e444cc2f06c1e6711721d24b8969d465e4958cb87924b3e0fe99ccb371009b5b15747bf6dd5d0fb73b8fdf58d955c8773a55424a34c741406f6f904\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 909626a69c803e9acdca97c56781eb672d6fb31430a53b853f467ca26d4ae96c182d71c0212894b776c88e773acbe9602e3ca56584c39b5947724290def7dbf04c6853a108c1282def95dbd5bdc015b68daeea0ee959b35bc5af98a4ae4cc7486e627bc9432bd009b21ee9af3085f074a3ae1bca879e321018e991e7898f2897\nA = -14eb8e28dd04a159c576eb10578c24fad9eedd3d8b7560b681002a54a4bce2167de05cd061338f63c50b86327a79595a2dbfc1d3f4e76aabaf88cfedb69faf5148c61f8cfb2130511a3bf4a17d846ededd4c08f3b635182dff1854e8c4c48007af028e06f01235fc2becdb32adcb9e2058dcf8f8655624bed9915faa06be972282cfbf8530bc0cf2de5b2057df32e4a6cbc3c772feea0a511cfe3408a6dab0e2714fc4cf15602ba0da03bf0016f1f3f5ddfe1\nB = -388da160568aef9f82fc16f48a22e8d7aeac99121cfac9b748c815e5d3a823b673ddcd20c1168f98ba204df5e52535f61b224fc0374092f8c834321949fa0a812b5e65c492fd9fe8246b74143a943bcdbeba16024e311d673357a3dd3eaef9ae3a72bb06e03e34e091cbe5b6a9eb9fa3d7f36c03baa5c3e242f2c186b58db5dddbd73f6aa54aae027529b8f8f0a536b9b283ab08247b9977a2ac2d0d9f162ad03a2fe247d2c589b1a2d14b5f90d5b9c0a95918ea956e261b\nM = c462c7cdd79b7604246a0cd97c017700feb25908656", + "b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 128e8844a2f04704a4a60cd33e85cb7ad373ff683abd167894a35a1daf947f504c0abd7a614e293ce10797a5330147c88c4d5e1dad1bdbeaf74095e3f5a515f2af68b7bc11ee1f53b493133905b654318dcfe73118ef1931eac47deb6c4958406b704ce027d9b027803eb8e639b52d5983094b8ff4b54e86a7dc6ea169ff1af4\nA = 75e6b045aa44dd9b8f4b434dd4bb1346fcf558a5e96b00fef9b6cfaca72fe8b1672edc2a64beee8b959683b1861138b297629b44a0caec6bad2ac05665728379cffaf66a129f0ba40aab7c6b1c3fbdabaabc87ed3dd580ba80ec7ee765e9a8fbe845c0d207eee7a1a3a0c39650c75ccb6bcdae2e0d5149991dc3bf899ae9b7626a2baa17b168b260d82fba84a12f10e09234035e08b730cfc230f0d2651c03e34d4952fca6409b5c6ea5d8791c90466bdc4adf2\nB = 102fc193633b0e60a48dcc17aa76f3e52cbbd1012f179736a0ba7a102f8dfadaf434063b0ed1b1528a018b349eaf192fe62f868b538cddd7e8e6fd98b93147727d58561517b2836e4a373bb31fc8d5e42d16126ed80b880c1a37940c138fc1f7255ee0b7fd39b1b799c34e5178580cdc076ef3fbff65fdff7497398fb1cac75e5c09cc7df1168a20f88a16e7b3ac78091a90f1169bccd48c0d06b4707ab79b741a168deae5ced5d48bb5f5dd3f465e43c82b9db7edab24569b2\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9aa9699d1e5d2c6acb21e31890c1899f30a925b834adb5b8bc8cce83a1718944a2c90faa71b34379a21340457478c0c43121dbd65d62e290eda2ba6230bce4e6f18555a1380c7c95c1700793157f7c1cbabeb09460ca28dc596bb17851ab2ba6dc6bf311ea69bdb7fa8eb78df74adf171d4677a154b8536f8104d919bdd58648\nA = 157fb9e1b38f288db78a1a0e22fdd9f48a59779487a9ada2774a094d34536b85993e7b9ab6e24f081c4cdfb64a82271100a054169e4f1c24e3957ae9aa8300e85eb2a45a6d5987eed4f0fba6fe8557cbf6128e018c5f9df028131bbba6c544b2c6312aeddc71405f0e4ce648fbab9e5d51685949408e4ccbe06fe501a36fc13ee65c31f062313135054b7679eef45964c77f5a1556ac09b11c496d0ba8c6057e283bdaebb4e6d9e5c557d975745f9f98a288d5bbe4\nB = -82cb6334479bd997c771e894cac1ead87dcbaf8f5006be5c70ad48ef94303137bdc45f261af91a201b276a17d884a56ff27af7dc06cc5b7b9c94f7c4d4a36f68f8d309c477b4969a6e7cd1b2afab9deec06555cb753d8a0eb00965359ef865a84bfa87b815a42b2050e1635d5ae5e3743c007bd79e820aa37a968702a960fafbddecebe63f022553cadd7a4d4fb27b4dcb981e8b490e80bbbf13af8c4412d158775db71f5fbc9986e7b8a8f9299574abf7bdf9ce7544e8c4e85bc\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 46e401989fbcde9d830dc6e3c42768999f153d44d270d4805c5beefb470bc1e82706aa7173b359763c5e15d146eca91a32a36f0a80802871933cc7f2ed15a5472988849a2d2f57543345b531538db57ab9bcbfbe787efb0a82e61baa505aad628df5f9e881dababb35bc2decff267eaed3d3671757ae1764ec5163b792b4db3a\nA = -590c16ea2cf7fa7f63b5cf74804333f22fd2d0e1da7d226da8425abad2b39a4672fcebcf5cc15d220b0ecfeec09665e682fff0140f16889f7a6ade9ec11aae3fa3a369b3fc133babe52e42b7a8bb9a24777521f4d9e0efe7d7977dced9e40784c24d2c6056b3b668ada7856da71af73d2dd33d2e481ddf40999d86a6e236d0d73f31a67c52cc8b38203bb2840c0b92c2612ffe5fdb6be87f9a787d70b3dd506f9a63d144db3417495f0a48523c812d14a89710d95bc6\nB = 5a2865cf2254710a1a51ee3056b0c1f6c5f77d22d7aa8f939e6f48ecec529a169e630c554bbe682a8c4de9ce4daca77a278d7e752cb678141ddefa75ba42e661885a82ab55d699414ffeb75802cb8f4e7583bec8a7ab58803b378bb60fd46f476ea490c9aaba568ec17f3a6afdd6f20ec54a512f7aaf62d2f941e35b4b72dea77095e863dcb38bcaf8777707c1dd437ef2ac6b6a8b2b832f80ad2a6d6f279c053d02058b1a657a1cf5b6b269e15d29087b0cfc0c2d4c3fbf32a167a3\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 1c9649f4540556ae82ffd71b2c71ea8588aeb845c50dab595db9f8faa01a26c809d30d8433b6c0add465e164cda2b6723c942ee87241eb7baf9944cae08babd8e22a0eaf35c09e9efdfb9f8bfa65d53ee6eb23fcbe1d12a66ae05e7592ed788b231b000f895d098a24febcfa4372d249575926a5faf966072f29a62a401ec51c\nA = -1bc9ae5fc2f6a3f1274584bac1e145f02c5e8c4779f4df15e98dd34344c988c1437ee4428485a09090d81b18606a6ea5c1b9136872ab5b37373fbffbb5b3fa8fbeca1e112b9f1643658c2f38b9548cd8f0f271779ce0acad403177057ea0a2af2e7435109879941fbf463488a2522b831b95c1cff21d2d816d70c25156369dbcf04a0e28e1d746afb8a77713703fefa512816fe73e203bb4c3428efe09b946b750199bd7a03d30feb90230c219a103ad4528cbe0de1e5f6\nB = -39cae179d955049f830867d4115d3bae25127c945b1fa0c16fa850e8fd77c1b3b9b7916b9983c1659b7cee77b7dc72abfff1c56681b7931c5e58cfe4f1bf0168ae32df0df8f652223885717a98f858a497b1a4be62a2215c39316c34451b0d957791f49139921d9ac8041899b8fdd5d3d443547a26ddf5748147e4c3e93f5043ede42f38a9baa628df65d3d6148ac2ce182056700f0f94029be05d3ea3a218b40f65a87b4baf097fce107c080de24880259f1046175db1297016af76d94\nM = c462c7cdd79b7604246a0cd97c017700feb25908656b4733353af8119ecfa0212e4bd24304edd566adb5c1e9daa40894290a9e2e20d523bfdb5a2603409b312cba43d567a27118c15d4bb2f3867a7ba7594e02859850b77b929823049d43573a881948d674e95c7427e2d04d4ed81b5f4de21e0d5904c8e0359c99d4bdc901a4\n\nModMul = 9fcf6a47addfa336557749821a88ccd2573a5ce2c3094a17d9a29b33e043bea165499e89fd2c939f17a670694aff05e9af46836b62c96e597c83681092d63ab9d6e22751aa8fd4b9ea94a90a373876ef0f6514304a495edb5ca1795c9ade7965c70f9aa92f8ea460ccb670e9a62c81e9c\nA = 71b93fbad39b1c2755f2051ff7d532d59c985756410d58aed3947d6ae737ace5aadc35e7e0d29c684b9d4bec9c0fa277996bb30230f70431cb7b905\nB = 167be8381a3392dd4df62e150025e13b388bf366922ba8632614928922cc290772135857d1b5234d51c27862cb1a055c1b86260b6ec\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2ba940fc5165c6c5f7f4cb55cd89d1d5f59e90e78730bd66fb120a814514784879dc43ad4f355030ddb3486a59bc34b601474978a94ddbceafdc0ee23cb18708bdbd824d37cc32577802ac6057fef29a71f168e816309fc80cc46f251e7289c6a57fd222d5868263360af63dd73e7c8b1dd6b3f3b6939849580b9231940a4d\nA = 1220ac4bde4feca135268550ddc79d8b05ff72f483b39f77436f348c4f5360c22c598f7dfb76697bf6d2ae86c68e90748b8b729b25f932b2e5fd33f3b5\nB = -bfee56cd412318cd62e7b6cc49217345d3a94e7fbf6fa19053fa685efbc0f8b320b7e43883189396781c49371dffe7d126c032d1ae4b6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2ba940fc5165c6c5f7bcac0e449b64801e75134a390f120acc58cbee43888f50d07f7aa6dc2b33643c025cf745434d20eb1aeda8fcee5fa3fa5baf10d67c21390297857aa50bbcc4a29a6b10885f97fea60f1b88fc72512c111b938142ee8d67545efe386622162e8fd50418b09769b8c22efe54fdacd652580d609f0528bf\nA = -7bc53f6f2e78628678ebc8e35ae4905caeec61acca5c64fdf595689cf005bde2265cd43172802fc133dafd933d7b48def44256868d202727a4aa6c0cde66\nB = 74147c93e729707111d0d531b1c135453f3e59f63a7e082b43dceb8b16cc5debdb6d7c0ce0c00ec9b5ca51e7673e411c3cab34938124db6a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 43c47d7e319c32a758360dd726a1d91e2cf5c57f73cdf9ad2040e61a9c282a2962d96d300e04288461eb1ed37df19e6b88f104a250f9885898740f6487b081515314e0a217df2d4345d3cf81eabb2bfb346b634b9c251624748f6e9407cb677aff4c53fcf42cc027de267e6ec011e14bc7f3bc6666f693d21\nA = -1e6ce0b44105047d0da0eca7b936980267db41d41319dd5315889fe8fa2329023d7cf54f71ee179b5bfedf442cdad1920d311966f7175cbb953bb42ee105393\nB = -23a330c7e06cdef4b6b121d15a9c0bc774eb5e432e72d04c5f03a0c588e55e010b61f57c03c51edb1211685d8dfd2a35393091fd0e3ad2304fb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed45", + "2d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 768293c84c431b9c8dc6e538ca3f856c60ae5e1aaf42325865418b7bed16c7fc2589968319cf41cb370657c8edc7b969de10e0566b64ec796470b630e22477e7aafb38e99b6012f100c9d23d5517d486e3cab1fc60c1568c0228c9b55d2d77d23b1351fe37ad4fbf9c07f29330a539de4a32709d043dfc9e21aa1a\nA = 6bbaeec78b6a41818b7eec42fa3be7d639dfd86fbace2bc14e0369dba6dd3f04ede8b808743d809f43f70f1146dfdb1d649546441919e27f1f7a9760da4a3b152\nB = 1199dc2f52868a0cf440f6666b576541c7aec1e9cee14c1d22010ab0f53fe8bbf3029c639ff78d89dce82de85fd8eda4e67395d435df60158623c5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8e2b90afbdafa02ce68d537ae807b4e7f3e05a66b20b84cff309941fc3150f99d083841ddaf6f19f5a76886ad5d853c73051a0457e95eeb0fe3776a084a027ee77d14f3825713a59622ea163a679cff904db33bf6ab23b06eb4b31f4e34fb122c8c170321164439db783e7bec1c265eed33f33bd9cb6d1611c00aa18a9b4b90d\nA = 1c4821515167f7073d4b7cfa318ead1da1131499c12497447846caa84176a9d4af576fe549fd8b0f77bf8dbebf6c395f84dffd40400101bf28b1dda0bbdcc5da255e\nB = -de60cd639044e863c6a49c73213dbc2ca84e4225aefa5f880e829f2d9cb48ae92e3f2680c462ac697dc34da38f65fcdc1b4d8c3c99e8cbe29660b539\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 33e8e8e193b4b99d8bb382c29c1fc5403190d7654f43cd77e28d1bf77bc3a728dde9de9a89c6522ebc7222d25f46833fd1753a44275b04485c77b675d816090280b3541ca61bfa33921a79f7286830131d6eba13acc46cc2c449b3a359f1cb49d67a4d0cc1245f3f8b59b1684aa0c3ff1c928b8e880a3375ed811dffc991fd1d\nA = -50ff3e00feeb2efc6df6387d6409a622b7a8297a717b8d94d0dc41c6ec6f29a8455c3580019349660b31dea1e4f66b74147de93535e671c853b604ba06a9b62d34646c\nB = 49ff858c7081392defc3ba12ea8869fd61188ff15d9339be72657b00530b851de53b1fcbe16034816e73251fe1ec97bcecd8bccc470373974287ca328af\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2c88dc40414969e8b614bf8db05fbc38fb2b7ce144d7e707f9f8eca40ae2309c1fc67e713a8da5fbb20e808ad20aeb369cb72a77fd285e38a7895ec0fc795ade4ef1f1680f3a3b3cee4569cc9d5e699984daab3385815d2e515ba5d67d21dd1defc12ca81bc8ea645f8f8d103b4a0a9cdc92eb50690c07a037df274bbd5217e4\nA = -167ee0fa8e5d8b569d7848b068df06f6baed80f6fa6a442f9d11d9712622b512249b92c7ccb821ac751fe4ec0a7a47e04ea5571c7cb45a7985749ecdd87f0c0faea01d232\nB = -2207fd8dbf2b8e9a5e3cc515479cde241dd3671803f9fbf7859459ac66705be055fa759c85631ed2a61139657eee7eb08fd963b49e33666e60b7e75dd26b5d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 674885ca3ef617a53eaedb9564cf96bcde131760ac541a81f4b25c174a6fe1444c2c206f7171e343e1bb43f81610162994c497419e75aaa25b664c122ed2b27640b45bf646fc5da1703fbf1cc66e10a3c306eb69ae5f937081a1a18dfc8db376ea18f4c1c499109b0cf8806eb32cb1f28985da790047bd7b32c1f67bffb9761\nA = 413cbcbbb5851a4ae12555801f7f80ccd888bb82ef1b5c31b99e1901d7e0ab91ee489c84044bc21fa2010f11aac21d0531fac09feb482fda579cb9f224c3149dd6249b0225a\nB = 1b6bfea70f1d80350eeb45f9a5cebda954d72cf5cd27a299ef5a42e1ed0b50a541d1657b70e50b0cab69b22e31d0944fd735957b1ff764865d9385af302bb802b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8d74ba5fdc67733ced4d468f6eb6ec4c1ebd79c97682c1d4daa06105788ed9c5144992e555d903804d7ed0dd9b29ef2648568ab7ff462a03e0bceb5482485afc3b91448fcfeba435dc587db6f3a022428d37fa0e85392d0e48e7d4ed6b21253084e653da8175587b3b709e28426cddfec8d9dc582d4ac2f3d540305c0fe17327\nA = 17c0b7f0e2cdf316e4d32f040e26d41dbde1e6689d98f0652da1c380daf5dfeb6a511b72d82f1b32d3852e9aa2f594be10776a8fc89a8a35c160e8e41b42a06a342fa1c309fd82\nB = -d7b7701340c5a358455ca5fa314ad83860d9f765978ff652d7f542de2e123bb976930b8fe84b9608648324450d8ed2bac4e44f2fc71711ae813cd8793af8d3796e8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 57e60f79b4e156ccec4c253e70df8d86e4aef326150d612a5ac4dc285761e88ede412d28d9dfa5a6f5c073d3c91a65ba9c86067d81f296935f0d0ebd2af82e7f6b5b336422429cc3b8427fd8d3f5a6fe936f4208362632093bdd3cec1aa8f4b176d260f605caf4a12cc011f3d1b76135ac2507346674e41673eb16c0f55d8010\nA = -4f1568c207a9ec970b5c26f068f3cc8019e8cb483525d251cd2919b368d072ac8f40017a19fc7437cf88e927c9e7d6f539ee84865f0af24be0d6d98fb33d74e3e0d28020c00bcd61\nB = 723db98a78f42aa45496f31cf78695583526d25e167da48ec310e447ad3540be2636813a2c2f7b8c622795ac451992e91bb8e43e5737f0dd95623282e729d815b08ed8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 237eb5726e2c628a515104bafd44348dbf099569815784eca5d6a415d3c12421c8c70fee23d6d82f7b5b136b70ffed3b6d9e98cb47854e79239d96c26f2ec955e4ea8dabc29a1b0765c9b7af6ef09ca673d1ee21c680e4b8cfebf47bbc74c993d017ead6cb6f3319ce4de9e9765cdb3ed8fcc57a1b153327e1a6a965e5dfa89\nA = -1fd1f634685eb1470dd9080529a891253a28a0b31e15c662733e20d43fc4cd71f4cfe83c3774adf8293a0fc3bd806d0b31b61c6ed0b4414ccdb91e2994e22797e5771c63defcc0887f1\nB = -3ec0478afdf54c949a097ca411be41f931acb750ef4f0ce97d0f0fc77cf15970cfbe24b170aa332de04836b7a0e6c5d456814182d27c8310d5fb662a818bc421587d95fc5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f1d500443fc4f4b86e7ec93e4d0dfd3faabda35a6dd31445021928373be14c37fec369ce80ebcb77aff2151b7ea94d21592da1823ebfa0af196f286d7a69ea54799573bdcd4d09ca4f33b8a3a93b35de5ff7f65099d59367914f1c79440b471ced6773b0802bd8ca99cf531b62892eb1e78d67f8210592208859b0aa1754b14\nA = 572de2984fe2ed0d5ebb5bc3f62b197fd592795d91cb16b48a0c898991ee3e884e5870b92405f248036ef9b3898c5ee6100a09ede5a48bf7edf3a067e4fc77e7e6bf6a6e3d4f538e3d66f\nB = 12c379402b18a34dc8b80c0dcd25be16c99d6f76d5d64b6050b90910cce594bc022794640735710c7ded857ebd44fe5b2e51574a2296f7d7a61b59c0123051bf2ba4a168cf8f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4001c734e1391a88640007893f167eb79ef61e4717d5eb14b8d80c25ed59c753be63fc8e54bdaded22c9c7d3e49753eb49efa010439807dba0d90ec4f9b498aa97f109af542bb41922936223213ddedac4d0fad8f1446498f4228b758aafdf1d9692f59029c76ca2832125ba50e811cb95f2b982a7a4d87b4726e6dd8b1963fe\nA = 16792909716b581a936287d0a8550a1f3e840935f0f3ddca75aa32e3489269b078fd19a16f8d6b2326eebaf46da76e90890c0ead3b35689bfda8c1ead17a4f672588f982cfd3da2c2b9bdad9\nB = -95ab2c47f85001aa852d6999f29644a6a55f9e4e12bf905f911f90d29cd1e4fa4fc9d1a2aa6c215bcb5c5643561499aab8f2678fdc5fa9c6ec138aeb2d62f635c45f239e46b0fa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d1", + "5896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1bfad44b58d3f8bc987116d4cc7ac98f89f838a8712d81d726189e9e1469cf46fe04675dc0b82e6e556b02c350ef4e30ec6203c7f1df937ea80f435af7c10f48538fe7755ba78993f304e64ca0d783b0f46f61bd14fd3fd30768f233c59018ce911a94b495f58eb96438e416ca3c7eba5b1bca9dea5a770c1d2d9f2f62f821e5\nA = -78a6a6ef40e443c52036e75f0b35938d632bd45aebf45a1fff5c2e1b6f601a57382b9a82c3e8b2984e643eb1570cd83f3a6be6daac567ddf9f37bd96785662bc3cfee6f47503d239c77781a8df\nB = 4920f870cf9f371050e64a419ebe07ac92dd3525b41e8ecf6939a267e1ba853d54862dfc95dd21b3526eb0a0a7a7f8fb67df2e9472dbec81e15cb13266257177c5f2b92fced4cea5d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6b0b84505907a5ca37abeff9a5ba169975792c69b5751d9845c0f09dea833fb679c8dfbf3895bc470529e0cc736c9b4a0d08b75d709a1d04525ae583c5ba082d3bca1355055c7bb674aa1b92689cfdec4dbac84a96e81c855280e417f60e7e4931ef4f428420c0b85d2cd11c1030a47788d6ee6af0a76b5364fcf23b270e9d4f\nA = -143d843e3b12431fa0d873815a757a214cf731c298db61ab13cb87fe78b0a6184bd1fdcfec0c7661b10775b4ee2c815dede0ed497977c9ec5154f7b24a8a786501ddb8dd257bea51b9fd9401ff760\nB = -25d4da7b64f439987eacbde66abadf0da7c1653c1c1c6d9b2092351fbc714a20d2d7ad8093209da371150b69b3602480595533ecc1f3c5005a8ead10732272246d8cdfbab87c49e65223\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6bce40524278ce242b0b5292d27751a3dc414f962d9c1cacb45fa3ee693ac6890d2ff1647abe578c40ea8d4b326a2e0e2fa7cdec28fe2da089338b5fed91c4277cc5be37537eec2f17edbf48a45fbe38f15c58c3e733d408d001262dbd40c9d246c323e7978df4fb7207aa9270a12921743cee2a483e7e71b221b09a6b2c667a\nA = 402671b0cfe14655bc650bd35dd0c36ce7f65de274a0cc4b708c6f6c3e84c2125ab2430e702421904950b29aa8a03b049910305127890457cd0cc97a3e05df67f29d28b0452969986959df02f59d207\nB = 1648c29205f19fe4c646eb62e8ae9b65260c2cb8424a526423c6bc04ed55870cefef9b8ba808f8ed2e1ab170e2e411f68b934abb1a22776969f79f9420f8bcbef28417582942e26646af60a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 40db38dcdc201648da555f1062bbbb92c632c29b66902eabf90d98dec69ab3f3b28e60cad1571e7246f4c9e6aa62ad26a6d0bc08598c7a8571fa830cae4c2875c5c95a59f3295f998681edba7749b7e38cbece8887a7823b4752165e1a897e638836d408f439f009d0fb6c196e83e83ca3289d2bd0f0eb36b721331e4f9f80fd\nA = 14361ace8ec5223bf0165b78913b77ef921b7089bb5e28891d120bd3db6513ddc90404a4e6cd027f9b51fbc02e80d376d59e1f2b043954199ef8218bf26cacdc5e749f668ad3b4ab35cd796f94c06307e6\nB = -851a39d8b0101fdb22ea9e367286e572dd132b8a77a6a14dd0e995131467aee898230f37dc6224e35bed2eaf459aae579181a161450bd7ebe6b62ea7154a8a0ab590ca4a6c2f05531c4e24650\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4b085796665458b798f824d1c1a88c23ecca456fb88713b433228ca8735141a616633ccec4bc53ea4f6e0c74e4aab6fece2e4cc4c4efb479638cf54caf55d4addf75908076f5fb487ed00d540e5b984acb8f81cae3ef51db926a06382a288092b352793de721c23c371fd0ce7a789486b2e8b867d35f47b5daac2d339d22dbde\nA = -511565611538828ff7dbc45c273fe46f4f5105d41ccf5dd343b41e9dc579429e56a9cefc54657ef0422960d1375b72411a5cc93ffa323455e006e242580358d6cfb641f46b9c36fa777a613b17dd4a187454\nB = 4f22597947638b9a9e9b9b7c2a8d37f77259f1bb1c7db65003b6e1a1c807469c84c89a75b80bbe0324fc3aeefaedc6ad9c0d9e470dac9c30bc48f6abbbdce9547ad7624f0ce9ff3cb6be23e47bc7\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b90a57349ea94ea818207fe15c164f9d3530c7cdffcae178557274552f79c4ab56acd78033a570bd6c3e45789704ef0b0ef586594fe4cae3ccfbf9ceef46e769589b084adcee3ef8345375b7103232465b991273df724964248737d5eccbac558e35e4190112571d3e7c291baa7aa8b1800121bd573b8419f627c0091e1bba8\nA = -170cc62ad57094d307ce1b317ae5e825c2f2e317ad6060437afa105501caea00dc9a86af8729e2f3c3a854387dc3ba368c0a84aab1a527ab34fe27b0a69bc71c728cca87be728457c65eea7d7538ef3aa282615\nB = -3d9da1377a88f647de57ade46dc7caf71b4f42bbfaa5e77f16cfcc90f00b5d3e9e9d82355104c7cd0db4c1dac0496be3aa35706cfc0a30a1329755faa439694e8e9b41fba8f1ebb46140818c7008e27\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4cd4da762c7576d582572d3427abc4b4297f740705fc14a32b46347541b152d0d1e3a11f27213badcea1e2009e34a63350c7a59e4d43654b28298d2757d6b54c4d82f580e98de4230cd119ba350416452cd4b8adff29b9f35ae0c533f666cfed716838e2b91941dfbea8d6a978a369d5f27554ef411f15e5a89850655d7f3f5a\nA = 4f4a28af27b926d8ac347503d6ac0bfec388a6c0b38a577501c3ca4aa709c69601824ddeb5eba4d9e437a97f3e4477e1487d5ce7b4a35b90fb863657a5b2d901bb8c3c838db40b89b495ee9875e8eee607d7b8013\nB = 13ca192603bc8b2da29dae67159e4f8d32f351a503434ed9e4e24f74abb5908ef7da80781c71b1a5ce64fefd13a16cc1eab05a370bfba2a97e6cf90cfe98d3a487ba72dde0762c36c10e1da175f1c1b5fc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3812e9e835ae355fdf328b29ed8b86dc3f6895e379b8b5d65a5de41eab5fb20ad3e2290c8ca69f9500248ff883d9715f59d0db6257d13c5cd612211bb1fb99867161daffc77968bdffc1fe48bcde0fcce02ca93975b3cd9e93b56974ab4beb59582c3d0ef2a65957f701549f8bf858de0c5bc98af3e5722f1450de391876a2d9\nA = 14ca6101af00d67139b985ac9f149accc260336237dd2dee802b5cc6e506e217b74c1a007ec10c20012f071ddad34e7407012669109ec1f385566ff04cf1a1ab7562353c0af1ba1be0baaef920a188c60db27970f64d\nB = -94b683326e9de19e414f653aeb2cb4bd7b17e76a23de6a4d91c43d717a35e08f2155b444a9549dfd01a8aec4dc901ea9f629f16bafd2c84828b12d2f63dc154323eb2d54938895ec4c9efbcaaede274fd4ab\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5ad7411cef0581b2e675d03b0ecb9969102a283eba5e779bdcbb7646d94e843083a07269c932d18b973b57abe54eaaad0aa76cf7b61f30505a263bc95aa063efb264ae829eb1d1d5f7d380a0b4db59839de9ae6230ba51901e71b3e3d59e8c34a79678e751c8b7ab139123bdb2f04d90a18ed81d2046ae86da1a73c8dae4fc4f\nA = -469f61cbff01f0e4124ba69a860ec6dbc75cd758dd8ac7cbfed97645b16488a329adee62d1a66e90ee4212569d56d58b61676262f49dcb68296bbe5d8e23853e3fefe8a304710cea568ca65c183531a992ec5b4d82e226\nB = 4a0d48e31cb8c24a3b2c9c95fd19edbe46823032ef4c97fe65d0a30d5c2cad7a4fbbe89e0ebc9940ed9f9ccb8ab18bac269759a9740a7985809d0f38259e680f0703febe7fa012d1ded47f0cace4a133f59a721\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b2953981db406ebc544c39dfeb08a8b089064533221536c7fa2bf2a7a0d3a1192859b7dc0ea5036eeab5aa371e3e0070c3980433adb3e3a5202ff257bb546bcb9550423201a35501fd717ed4c0016eb3a675ed399340bac7f058a04e69c1774590fe747ffb9c27e78ba50fcee30ce533a1659fc49dc080a60f21357a6265d24\nA = -122621d97f42b65b060c84df3f0c0da097b5e240731b77a37bb9471e7e398b242db6f1b5e25062a9bed702860ccf6aaf386c1d6fcf60fc31b8c190d348", + "6949c5772b9e621b863a7cbf29449ddd68b7e0c21e669492e58e94a\nB = -33978406dd30ec2b192c416e422428683deac210017cac9e4355e8446d6969295b0fbaa8cabc92c1fc0068da70efa047f938a419bac160ed6f794a9f69f53a88648c9725610d5f309b652f5462bd3011cf68ea859b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2104dfef151526e072c09a4a277eb981a035379de3b1a55a88cb060681706f26131c388f5572c5646826b119c85ed450207f32733487e3c4e1e9d701a65058c4b4ef0cd1db090495643038229ed177b54695ac32110619038f1c1cece14faa693d88476e3d70329b0084d0ba5d547bbaa5b59ba1ce1fad5aa2f1c11a75bc7c0\nA = 7b79e6f1330fefffaf8521089c3348593e40ab7e8d4da3d4346571b43b12740958336580afd13619be3dc2d42eefd9e30599405da3e32e7f3a5655ece8b77a367059668021aa092460de75e627526da08e6206b0f8f539ef40e\nB = 156e234931907c0c0970c1fe6bd4b24225ed94d5f5b1be4693c8e141e9a6032425b4a47b6eac6265afbeb9d796eb230efa707d5ac4a73808225181cf814b319142e9d175ac461c75e6d479bb6bea53954bb981062eb16\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2a392c5fc96c29df2f5ae9eaf76e7d981dc1e2f3b47b43a98eaf556a9465ae8727c622188123c64658053ec50c25e54ac5c6c8bc279b134d326e911f14c873357647866eccb4f9038ed0cef5082c2058ebd71e1619f7c8f8f2fb80871ebbca3fbfb7845bd855d307d2efd853f1bfd467fbe030862f165e53a9cfa633d0d3fa23\nA = 1e0430e7cf15173d00592037e83e717c90d7dab4f54a5b2f0f5772762fb5f56bc0b2a53ec1bc3b960afc35e7b043f9d85d0af6c29288486af3e186e52bae6300b58917647231b40a12648cc8c020a797683a9bd7ff34eb6d41b928\nB = -e08372fc766eba6e0ef55a9149d700b503e2e3f978c8a397912e2735d5bcff69c461561ac0822c44160c7c1bbf722df421b74beada57462ac54a9bdcdb42d6a27b86413036ed2282abf62800fb2518a32a4a135bc948053\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2608f68632ef14dc3979725c8cf1a0db10a1651f17d91247edfae9935b53f6364d233b030eb99871a87b7bd876ab2cfd5a643387a7af9d337e81770db04a14f4f8dbda2cff604838c9af9a31e8dccf9277d453176589ba33abf77855b9501e63370b2e6cd22831e1e70ff1815302c0a026c70042957d08e74dfaff940a91a7b9\nA = -5d3568858c05a15bc9777af949eb01d33dfdba58439fb3f7af2ba792efe8e78b16d7fbc2a303a4c4c4be7c9d43f57405e88be54d6ab55268a4739945ef582921d2877019659dadbc76e0939f4b2cfbc91e5356ba2ed531526ed5b9b3\nB = 47f81f65ea1af04f702757c02a175a299b23cd8ad551fdb67020c50cbb4110b5371dc5790b12484e9ce647eeb24c0220a5e62aaec3461a9dcdaf1a22814b6f22d66372cc5ee31944bef33469f905458c172ec7871d9dc9c301\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5735109bd21d31b5f54e9221bbed78c54cf387e39c13d31557e8173e173f786b2d2f1acf3966c3bf4552fe9bc802d0868a5a7632404cb91609a7a45fe0fb83fea8d83b0319666c1b0ac520169c15be708343359447f2fd37960c1e96d32799ac9394e839b391f59dd347acfb79bcc4e34e76490880d163ac97ee69e3a0a6e68f\nA = -175011349a0a1ceba11756bd528f2bd631c106e709aab223032d08d52d7d6724e8c5b055b6f97b48261f4860eae297badc1214cdae9b2500a7a47b4b777dd7b8f1006757754ff1143b637d2a3adc555f38eafbd5478cde0b04e5f46d3f0\nB = -2aa7f75d6801b04ea9f690aa0c5448906595fd28b53775059c01efe54b463f1d87c9fb4b39cb038e770f99bb995a2118b86ff8d004bd964e958c2af82becf362fb0b927c671cc3bd7185990419d26a827a2d81bbc0126e1029556\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3b4ad19b75e1301d19b57ba9b68e0666c28c7c5c99df1d5fbbe0685dc1d3489ff39c919222719c5d8b7ce2d7ff967730d776a02b36a86064ed66a02011bab82eb575390f85f0104715f6e4954a1bb28518450182a8ef58af35d00e2fe417f07ba25dd9c85e00c3451082becd22e3aa0c9bcedaa96e6423c7df6c375b4c799c65\nA = 58e1ce4a9b512eb0632b02cf1207936d6707b802140540fbcbbdd712e5ac1426b4f36e74a9a9ddc812e572855d4fe4fca8a0de6644226f5698fb46a5f2a479dfc8b588aa8e02ddb15acdc79ed3d17143e290f1317274f425b869df54a4807\nB = 14e341cbb5f5a7f3b4dd864172b82ceed2887fcf20aae7d0598b3d8afafd2f10c27bc7456c1488abb570be3df04f43d892dc6a8dbe7621f55bccb0ee3acb1ade989a510b4e0cbe29b6b93968f323f0016d87944c908824d249769f8b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7fe0bbbccad6032069b1a335b3f2dac16089051cd9321f903181fad23be6853e2d209958e8c48e008be94a62c6206b34b4e994ca08b8f24a2df0e6394ea65b3b7aadb3bc43d04dc9d35a77e673c4476dedefd4568b4ade5d16f9d89486f3d5ed0566b1eb428cb0b688f10fe3901037744f278385754fca481f937cb630f60308\nA = 1cc0e3ed58090db55063c9ba11401636f89262d6ec096d361f448496e05181c5f7f2604333f26d511c13534618e90637adc807d622097f7eabfc03266135cb626e1bad20997e72da71bf2b3f65a4973dc27d2a594b1fd96b7bf7ec14b9e4b983\nB = -87871b2058d33cb67d83b6a56ab27839c6a6c771bd94e55f200a1257f2c737e39c4a0403fa410ea64e8f442d300df1c19c2f03d07fb74d94f86d26814fca23d4cd2cd3718252cf0cd8a0e36726f6e68827a1dab6bbb1d23b884381c702\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 35d7ac5cbc7e6c262ffa41be168b02a3bde9e112c512d1f68421d705ea34461ce3e0dafde67f44d44cf31d91b38d4d5f2fbf8c6c6a44ec3ed0298dd58f3d45c04346c11e57229dc3d2cdfea02c802732d9a811d7be5e81094d72172cd04caaa3c9d55a951c09f454f42add6e89e2d8a98e124aac86379df377606e7af9bc6baa\nA = -4ee01518f6581c560a186fa05c6f4bc26809c4822cc74a0bb74d5a6b0a368aa9bd0108f26113443422b8c589084ad49f919a9e7821d99127bb210670e732b7cdf610e464e300a39d3dfa7c82f90cf00ce329bc6763d7b1d4224a020095112fefa7\nB = 72dc8973f7af7122a05c90df190bbf1e39abca908c197590dc7ac41fd0712f48f838ca62a72a177a293ee6b2afa7a10c21e7993347c3df4f161a5641ff62ba123999bf1eabef29ec0d33ed0919818f4b7c35b5f41e654759fc9abdc0f80e7\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 5d83a9b34631dd6c63c05a0c012adf97b4d0f20f61907e1c2145330211e9a7e38128517b058e0a85e993c385068d1cec768deb814bea1323dbd333de091ad2cad72431f20c1e70ff7e1b119768ba44e14292c38b88dae7e55ac9e10ff98e9bcd5f0ac05af499196b4be0c6222d1a63227ee895fa6a8221a4a182a1323183cd7f\nA = -17b3e0c9288be15fda58c8fd228216bc466731d631218a7ddf1d2c9cc858c0219cb0757d3b680bca1b1964eb15031b5b9d761a8bcbd160db89be339067a2ea35e1ac3cfed701912a17ef9ea03999d92e3592e893183ddc05cbb98a656983b54590c72\nB = -269f96a4634eb37cf8a6608408128587ba45958405a29827d0d03d34816fcb1a2297f1319485439d3e8594532545086efbe4d21d31d30e2daf09b74fa8cb27df54e8f9f993630cd9a292c977eee70887158bd3fa3cfef321ef900a0598ac8cea\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7fc1c65eade94d9de7440eb8dfaecf1004905135efd4f98257c3295b1e76ccf1e2ab6808d158d360b7419c6210c50efe960610973d9ae855c72ec0e81d423e5863c80b542ad455700d2d0dee5fc403dc01eab460c24687401cf6a3179642e59f2a30268df95fa80dcdac230702352bbf6b60acb9ff5d45c5b09a3403b954d173\nA = 7906bd8d3bebb1303c1df1fea0b2503b0abe9c69b4f4f5bd01eec9e314788cb7d44b93428adbcef570477e8ecac2a64822e481bdf520fc381e1bb0b2cdae2fe94e484cef5236dd524e4dc364b72", + "f4c06d57f29dd3c5079e532b1ab1e71dd6a65b3362df\nB = 1479ef2807b9c23c094d0416f513894cc92e023b134f44a5333360dbbe98b8161ab899302f4fa11b470b97dca0c4e8ab7ae47e5fd0962834e6cc1763618193f4ee027f667368da580c623080de137b5869c3081128e6081b9d5e2dbafd791773242\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 730c04094b1ce944588e8291f7e6cf763c70b79cf362dc8a1bc63bb8790cd4cfe4eb51cf15a45a8464d69ddc3e1b9383cfbfd643f317108cd9ca6a6eaaea177c5c8b6747bbf40108cbc0437eb8f11bd2a0939da59b70c0c6129e2c249823897f2ee536b0427bc45035f121d2cbe7441c175899b97c490e6c3ca01539bcd05848\nA = 102cf23cc3b81785c73ac3613c816de47fd585c7d5f175185818dbb4bf0bd47d0dda9702bce97b29d66e48bfaae0fd07b47b40be2b48ed702ef21c54b10bb927f9d6b43604bec4f4b2796b44aa6b4e83f8bcd00f2fa3871dd901570e1a32888d8691454c40\nB = -cc5349a9c5280a933e87ca38ce458a711c71ffebb40bb1f7612b42b4684afc495e99c4a5f32eef1c9564c2b7612ea4cda7a0f5df6b3ec9026447dc565ca08563d46aec7ced9fc4cc5645960210d44cdc3944149051d569c9295dc50862f8f6d1f6cd1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1cfe1842a53d00e4619265e2fce7cb566ffbd912c9213925d01408a956af304eacb85e29fb6edb812a95e90769bf1c3d62b0cf6cd5bb8f8992391d2ad70f38a14fb9d1d1eb522aa7b7fd9f1b52790beebfc887193882377b7ce567d317d8432e1d9a908d6ccfe8d2de7de497d77b023b3959cc042ae30aefcc0229617fd2a146\nA = -5c3d24fdb193ed83f5f6a825c1716f98e3cde6b32e09659f253ca3fd2a39402b5bc3a6497ed7bc908838e93422559a13cf59156254bd3fe1e3b8600b2a777943cdb39b9d42c58043f1d587424425d3ef5f5538ea157112970ce3e09a87fbb5f7c96f1b5e65fa\nB = 675d9d2a05288b438ddcb330acbd59e4639375f3f14ac2d0e9e8b72de6ffc1d217ce62f997577f7eaddbe4603541b132cd41f2f2740363d9c331ef22df92029d143fc8495ed0152b918aed7ff22f564c7cd94fd3fe4178c90365ace43def8fe30ab05c0e\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 83ed1948276d689bb7fde814e67fcea72c4e3509c48873c3e7349a8fa1c08ae11ea4d814d8deb1021eb8b8ceec342cba5002a2ca45d5f340ae1aa500af4c7db120d0402c6cc8a840404be7221bbc46ffa10236043e5ce4415d3ef1355bde26d2d26eb7127326d4b8d671bb96a08e38a2c1dcc281830ac77202903a5e4777ff02\nA = -1be86e7c87827922d2e8a06e3cd6b64ac9a280c525749bcdbfac4856916321a964c9346d17465378251e6eada42dadf38bc9d7d87367bec94ebdc21af6b1302e520db08a64ba6b39920683725ef02b011a3e4ba46ef0eefadb98582cb911d0cbeae9c231b5e432c\nB = -352059faf97b433089a688c702b97adefd0c91d51a0395647f822c6762fee3287693e302fc5a5584a12c048dea1a320cb96fa70b5daff7c2ea21d249467d14c6bbee15a1e94c030e908342a939fbe8ae0de58cb6d6eae7758485e392ff6d5d64465b701692c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 402525e19b6b68942253d1a51fd9b2ca36fc84cf938d80b3d52fd4302de142b9d93d1663e89340fff10c2b5efc8cd47fc3b5cc5ccd49a6ea3038ead6454bf190b7f88f52c56bcf00c6ad5b0f5dfb7615915ee8af137dd99cd3d21172ab772f36d291a6856a8e7912750139c09aa024b930a0a6b9eccc83c2c5c0ee2473ea32c\nA = 65e5db532ecae639bd56dd63045bca39b33b4d70b2db82ca3d0ee8ca436e671828cde80217b48eae7487fe110830589ab1be889f1e1463f3b0757d529b2f0cdd2ac92c35e8ec141885bbefb6040a3b5e00e64a541913a38fe05824a929f8c5a2c46568c61989c3ca7\nB = 1d9c73eef8373cbb1e8393feb26d55c33a245c33d7031c234abffb2f06a1601f7f3a79ef1e8664c51ce5dba5f5aaf3b9a9e42470d381219b4616ae93c7f6e64792d23bae523b6a224c1f714ebc82a11f9be42618922b8d2eb7b55e4d45572e68a19fb0ba72228b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7a9cdb5dcdfb6e04351057d731fddb9e85f41eb432f01c0d980673d294d05ba9b0180133a89930e74cfce78ed54991b494a19e7f80f310b85904784cebc5639bbc631e80751807868e7fe16719e8ffcd1f2cbd1b9f303c3ed488b647670be3080668b5fa0e53b6342c33c87f0ca1efe1ddb1c877bfe2556aeb61805b06f41343\nA = 1e412c3d66aea2c503f3aa5dbad368a61d969a2951c0094f9da32d2794e47f3bf4c481ae23636baabdebdcf0753d431426b1865e62de8eae7238a9245d62820ad7f17b5380d701f5db776cd4e1ddbdfd542901731ffcea5bcdc247fa9c83f7e08a9389e5a76d38be21bd\nB = -afd61df72361260484fade8b432713eb740df83a401d73492883a5139c918d5c911ff5dc00140637da1c6acfbab4b0bc8fc1f337243d90beeb1c2a083ad8069494c73a99372bd38712a5b5393c779ec1915e878600e0b48157bea44ca8e97c6099c4ab07fbda57d1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 712580a1ffde78c8cf98ba71843c8130e835fee3afbb45e372d04c04cc388e403c9efac742611d7974bbae982c3aadfd1893f5da280afe0c1db1d81a9ed73b6ed9b7f05a20ce828316103259112d7754560d66733041e9470ae0d4dc95fd0484bfd56d66739f38ead7efa4051187ea41f7bea8fe5d958a29af41328246e2bc35\nA = -47c5755ca61ca8b7ea927f6fbe347f1362915548ab38c40f0418f4c9ba4ad520c3b2469d9ba3976669dec0b278461bae80eda53e9d11447512963e797f45460f74678acdd69fb9efe3897913b6568f8e03a6d90b4cb5bfb06af132bf118574b70e6bd2f6d6cb4d0089379d\nB = 5bda68c0a64218d3609d75eb4832d5468298f19498507d7d515f4c410f04dee535947571a5e75f1af7f94a5b3b05fb742fde23e7cf3f8b3dbee0a569e5a36d7a3d31a26c4a48a299044fd72339d2cee1a68966c851e76b93ae34130b75f4abe4f2260207d2254d23f56\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4a1a514aa4d1ada84fa841d0b668930c904783fac521377a7d622201867d773ad23dbb667e0d4181616358f3cb088cd157c8e72bcd03db64647b37aa1813f870cbb0318ae0a3667f8e6c19f6e0706217646ce633f0cc8bf4e8f0f4d7329a8647252ca6d376416d545e73cb9a3cba40f8f9465d85d57c2481b84b6d95dd42d50a\nA = -1d68bddd8c3e6b78daa0acfc63a6f39e97f19527a43f6cdec47568d57b47f4e4b7ee88e4a28d683b569e406ecd2510351dba25f10b9f7c82d6da16d848bb970cedf7675e67937921bd334eec4bc8fde83d67aca57eec804ce22bb342167602fbff452d5f0f2a7f38b576e1e50\nB = -34d219765916a4c8ec843ebee9a7aa1162974d41cb4d6b60532513608452da9993749455d9701af6b7b6c7454d7f2fd5c344cc938baa5259301d4b56ae8d25b6f6510ae6bca114cae6791fa5a9551e8a405f5b1c0bbfc27138563b2d64f9a4d7a8f42a23bfacc3f1ec9393\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3fe24e66e381eca525b24cf767215837019f44ed4fac6ab118d02cdbd658066505ee5b0feb7af51859992ecb97d727121e38873f748a61d70201cc43228a7732156a80dbe399e05764be19e37dc1b93222bcdcbc45b1a4817460f7021dcf1d70e632bc6a306628790201222bb522f4cc80adcc907463a539b02f74004d42adff\nA = 773454a43f495959dd55b8a064d70b1b1ffe45c084f5f9553582e24fb402b564de68e5379a8d9d02af101594e717a6c6db2e7173e557a64d2f28fd45c4e06041deda040705d99acacf8086830af19c7ab5e27f91738ffbd937dc27e5b7869bb6caa12c2d7930366ff75eadc570a\nB = 13d884a2396268f1a8186748a15722156a172a56dd3d8c77b9cb7001b6ee06720653507eba9bb9918f2f699cb37f3b5ae514f5180108a704647f19b0fc075826153edda66dc1105c1008ea8ec6f8c10057f8e8e479e1a1274edfed9ef719b30827a30f26da78820c3696d01aa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 715bab8708e53f76d2ef2afbb", + "845bdaaf978b54ce25f84dbbf9074f16d30a18733a02a4ba5d7b092fa6c25d3b9b0d8243c743910f1b7b785d9cb02343fc6d59eb0817bcff05646030ce4fbb2b9ff76781cb1af66b46553d365d02c61e677ae97defe92d057d4378dadf8cba9824b0022c086e0d78b5442bf3d3263ba22c643f7\nA = 168186208c734383d472374fbedc2d5d430e85690a4881b740008623120a4f7f83b2cdf85dc28bfaae5870abcd7ff1bc782ef11c78a75c99d41f8aacb52fceeb5f10266dc65eb00b0868937340146d8850887686d54218badb97647a6d82c0c6650ca1f9078d73fc6222aab95c2967\nB = -9711e5b3965654bd9427f79c89a0b3f3cdec1c857f4451eec236c1f221bb6773e5dcc30e7381a18a813ac2b03ff4a4ba679aad41e0e5d7181d4627f682ca2dc8af9a8b4f878771446fb225a979ef9c7e641cac819c307c8dc50d9c1ebadf912ec7c844e416f95b546cf09391f9f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2714b99dcde70d6c3be8b671d78abc155793f13105fd4b7c5d760a4c68ae89987311dabf2a9238d18299f983b8aca69a9ce398fdf2c9775d90b11b3dba17bcd8edf661efb6e9c50b4e37553cbecb54eb214fed1d0847287732810e550a4c86b51d4e5da1cb7722ce4317e69644620ad806d6d1c94e1e3fb4d87de6178a997453\nA = -75231ed37f1dfa4487c9fc79a6f7b36929fdca086e42ed41f79430b2dff521919236fe415ccce590e1d3b986e16dda866f3f0d29ac1adcf55d87fa5cb67dbf4693293188516e360bac513303769c42181483fbef7abcbc4fea1310c916396d29f37d9058a62aead94511aded7c4b8de8\nB = 5aadfe65df0e5b877fe45d42d7ca02882cb6c686d486374da5ece6f87771675153c84d74b6f40df1db567b7e1e3c60c41d21816f958f5576fd2ce2f84a8c3be4749dfc7e5561266b7c9698c7581292d0d813cb77955458d63bf94ce87472924c4ca79504d1ae9d5f025c7a2504156f\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6613b1c8ccac0cb8fe2f59e76fef4dd05acf1f1b2bfc20aa3f193622ce3e9d4c7824ad544477553bc68f05f0b546e7c1ee87301e111af7929d1f40525291b88e211db7175f4e5c0953141914fcb4fb951dbf77442e7cb28fde495704f1b5141de1e50fbd0e359d0d86ad709c8f564c84dac81c7602717c269219ab1cf12e809c\nA = -1bc03897b02d1edb633e2c019e40c20c1d89a210b0733412aab675563fae8bd75dd7e65988cd8df4d9b343586e27f548becdde274f62dd421679554ed9eb127e527a69d69fa8b17aac0424dfa2a7692d1e63617ea45564b55f01a70325bca050862d583cdad96c4a2e123d0ed827348a745\nB = -3d5239dbe7bb3dcfd8027204eccf5e9444e68d322a0b0c535a203a1d0c054e7dc1e588bacb891388241462a5d2b43e6cce34ce46a23e6ef29670603d31001374dfa347dfcc794988e58945d0d2d17da6565cfea559203dec119fc357d396f65b296deb07686b0ad2d25a13fd4fad88d2c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3a7fc5680aae875b9241200b9f4112a82cd624ffd9044138ae3cd65200631ee9d7b918fbffadcad7e598791a9f0bef3e23005d6bc0048ba92461283492df3bce74c66e417b082ee052fd8f808d71f3ab18f9ffc40f8fb51ebbb936d09c26a3514bf868141f7cf238c1abb3d88e5d50dfc188902254f07d63fb8cb611ef8e4149\nA = 4a30f32d467b29dc83b40bca2fc4ccee5f08a64069cb87f20e63387b2219b12aa312400c4ca59608f50a71d2535cde40a6d248290793fe01693ca40b93a5cded2dcfbc9aeb36e187c9d650782d12bea917daadbc6525f266e074037803e4b2f300778ca8dcb304658cdb502c93c94a16c6261\nB = 1ca5e5218dade077fecb81d579e1c9290431b34df5ec84aefaaf233d68f17dcf60ee010db26320685af13a821b6daa9d73d8f3a30826c3ae7b2bc5e219cadcff826283cd7dddd04cea7a5e0585d6e7c9f23b27f14ff815fe53bcd75fe700b1b91671bddaba737fb43bfecd2a77e5b752a206\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 768d312175ce7d2601f30bb38339f046e4c2ba5c19ae5f7ca5a562cc2462c579fce9985e9e8afe2578db542c8d9e7693e0c74ba161334b249ce720d568e9c18f09c87cd701e6f2080b752362f2fe6252a1d0caaaf1fa18199776e4c6078d89d520b9c63db159d5fba7e0838811e68794b1413c248f3f7173ef29eff28f15b656\nA = 149353e91bdb70cdca8f06648388508511a64d05221305cad7187ea40d9ccef91fe17ceb1e79667bf66e8e6b7a57faa90a83bad119c02984a8f860bc1f23ffd33d4ad84896610301cd2e8e80a5ca7e8d3ee63e7dfa459793c9dbaef3569eb4f8a021c6a3d032a9c94d3f6b8278274d0088a98228\nB = -a7cbbb6a434e4b022d312ecd4a45fc7fc4d3aaca038cca0fc56e529fe7119ccdddc8e76d51a2fb862ad3d27a16ec8a51e5f66b9c7fdfbddcd05a0ddea14172339cee340c8c651eb653c6aab6551c99ae94f26116e15dc62f2c2e63305bbf84590fba1327ee721150d46464d7e22d45d53ffd44\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 763912f4b16549e6ccd60eaf7a0a1f64d9c3bc83e4a9b87e209a3959ba3cf609cf47183bc543f08e346b6e12b8bdd5d1c07c603f74b286ad432d58d7001299ec7a4dcdb56ca875dfc7ee5c75bcfe2aaba14959bf3facaebf8df92bc12937cfd4a4865b3dd74b243ff62ba256d110b01b4089730cf48efdc66fe272f9241014e\nA = -4df3899b40d51c83dacb442fb143835bcdb550136921df78800f0515a6cee77fe3236dadd2a0800b79ebdaaf8cf4aba5ebb60cdff3e4b4531ecd0903c1674a4559339123e9f09158080fc53c4c6ae72c961c8da2f357b7c05368157b4956e592c41b25642457651abfecb4fed5d9fc1fc3825b772d\nB = 450eff382e73f2f38bc3a4abecd5f8de478f80a6b99fb6252173c90d7099629afe859442bb1f796855ee9a2940f21d1f9dc44f462edd74b479e1f2926ff6faefeb55adbc6152b5c97967b1dc8c44dfb85b5e02e870d2920b75422c8a427e99e35e2a4be92cb0ddc04cb7f4044f716be97b36f045a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 56ef57d56c6d1b94cf0fcdedd3611a8ee444c2e25522b9ad175587619598da341916b183be03b1e73be300f9969120d8f3a23750cd8c4ffdb87124a2139e8ff2c15d8dc944bc3c3a066aa16dbe6dba4a74925e16acdb2b2e83cd7fd5cedade6a7f7409a509c00dadc182b2860609cc9a375cb8bbdcc350bcb2c0df9b3bff882e\nA = -143caf995b7783b1316b5551978727f06512fe114b419c735b3381ec351275fb7fbd6ca88b848c3e8c9faedebd6d084cb8a231636f68f6803d14bafd90534609d4a4ac0fb953417be7fee4e4cfefa452c5ee5d1e1b97ee75f83cca8691a0efeaa8bcc1f1e0f18c0c5d6c7684c9da6c9495d31a32f40a5\nB = -3025fa05c55826c40089b12741b7d406f748cabf692bb0227519a124653160142633700e3c0676000943556f97551171d231c1a35f7b7d8f96b0366eb74942466ceb4660f09aecb2fb2ac050ef699eb05bd8834a2ba959ac71550b5c026b9093c8cbbb7c5fb9390a7818db682b7c11e58996c9d0add5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 42f363c34c20c443c1ea7a1c54f98c6977b6671164a80308000533b2404a7f280adb1f3b98101cba25249131288f7ac68b0ae2572c7777e7381c1f4d05fd82188c4b1ed5636652e0bfca4d096bbf4189a9358b79f6b6333b99e5c4b7a940c2f7d1413bf9f47a2ef66b620b5e220b2c3dd7267452eb1b9d8d9cfb17bbfcdb6abb\nA = 499d05de867bda3118a8cb82b80ac91fc505e0fbc6c7dac5fb61713cb6e715f56a31ae8af4b400461d7ad1687a2631faecd90d7829f67d1b9e36ed7d55704b3f2aea65eac061172d698384daea710ed92cf1140cd4da427174bebd173c2ff1675b2407a84649b0a318602f33105006fe4d5ed8d0e015b99\nB = 17a426a12a0175bb46bf7a7e727eb5238af383cee6f4d5e2bd82b0d29b9fed35f3d8ec95cfdfcac49bee47b25d3b5f375a3340fa83f8dd9330a593a974d208debb7e567e59dbb7251b54e42dab2cd50fc63aab050a41bd88282373f8195c94c35f61bb48aa921f574cb4ff0984ccedc070efea8c46e5cf8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f03374e9596cb56cbbd89794090ca7a4b437f4c05fa38a09db60e5ca900b208fb85b52f71c29fd35e62c9f9529d7ffe46fcc54607ccb07f6f8e13fdd4ff1185033ba4fcefb1ed4bfc42c3ea9f05276767d8dc9b7b4aea4c8bc0ce84951d1f590cec0751f73667db19060e2bff64da30fc048a", + "1f5700fe3f489920675cc3540a\nA = 1073531f678877ba854fd1e7f857659614c526847ffbe8ed131dc9f2ccf69e1f1e917bb44a7b905f7ff758f61c06dd59ee09567d9f0df2550fcb98b776ed1381ce052988aa08fc5153e31c621c6a51ca61b386e3a9163a5cd69608b3e200476a8ada35d906c41d044bafe71ef5c6f732935f15b53bf36f7ef8\nB = -de3563925474e5408e245184b57f328e265b6cb62eedcaba809d8f257eccc0a457eeb82c451f93af93ce9f36dd1aab386e7c02b356f31c2d170169dbe15e70cf5bb9073b35fe0e7c7fd7faa91c5b2b0740734f12eb741a9d9ac6dcf7cff59f6e16324ea39e1e07dc5b9daea27ac674dfe5d0a5790abaebde9\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 1aa22f9013bc1cdebbdfecedf710c1bcaa41c696a3d7dfc1c8c601fcfcc1c85c8cc24be7df2cf3c7311b3b17a4ef2dbce545dc467d2a92d371e02a196a9977cb9042b236acf99d8c0d34a1c4dd8792d3497cffbc87c397ccee5d01fc2c89ef051324a7061e423720d0a3821a36739797393bdf7a45b5fc600824a17043312bc\nA = -4fb2e3fde2a0c653104c077cc6459c9234f86cc2d7b317329b68289826d3e2b975f1a69bed1a53418a0dd86e1b2723f4c4c5a29d003161e667c2315ec24a36f8bb5f2eb0a94f261e791bb829db685cd0ec9e1e301dc140ea57cac1da228124ae029e2b8ab1fa3ab99c55a9ca94dc7b767162c0a24af851fbb984\nB = 63702537a07971e399aa9a1a0795db052d6c8185c79107216babe11d6d8d472b61e604cecf9eaa6d44a2fcdd1ef0b6b52226ea0c6902d929b09e16576e6d1a6921765b2134c5d23c69ed61f36ea9a5552e5819350366240693558fac7a9d09ecd3702076c8c758a4bf6843fa843dfd688bef3f73515db31bfc26\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6acb23ea695d4b60cce53079390da3cb3a4bc3a6486c238c421f3bf6c93c027a0475f656c3e5435f0211e90458ae81772aa956ef284093020f7b58ccd9373f3fdd39fdf4adb8dd64590f4a7fc05238ba20017bdad07f5f9a6f076b71554a7741bdd8c98ec68f8fee88396cb1f47c64d6da4c228caa3dfc7a9a1c032a9ba4fedc\nA = -1b2496ef929bc673042996ae80f27c6bbd33fa7c20580240ef8fba985d1a6117d6e746989924e34f281e7d2509175d0773dd999bde16662e88fcef52978d19cc45fbae3997fa580a66171d398f4f0e7605d9f4aa4f728902cb886e6b6dc9f0161e7cf1ebac05a09c5a1bd69a92273280758173fd2c14550ec221275\nB = -28399206ae2820d26a5aa0bddc4903776611d08fc4cb34a22a8bdc2a19e9f8cdab94217f346a8070a4145f989e1dfb49cfd100267635af0e062872cc879c534ff138fca603b5d45a6860ea85b6de37cfca000c81fcda3d14ffe81da919b2a25214209b085bab9cb511889665fc845acbcd038711533da171d8308aa\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = c012c4d17ea4c95a360218adfc3363f6d89f5aa524aec70049ef94c2c05e59a66ce01e25588e164bf2412f9517b7740de53d037e71ec3a1d426f05b18b128c41a878da75421e8c8ef3ebd5effd40735c00818eeb1ec63182b44e817403c9f1f6c1a0155334be63a3a15109be6d45ac0d1b1ef5cc99e9b284b00c487d91e5472\nA = 796fba6276fb7129eef2d1572b305f63d7b8c49371cfb3b2c67b141071e66ccdb5e321fa2c1bcf624c77317e2aa135e1137dfa46a34c3ffefa2fa3e316be81f45614d422bf86fe4518c2fdb7e416bec199de033cb5fef7f193a80c0f0e6ee924a12c8f705f5ed3793ab770914924b45cf2578bdd09c701169f0a881e6\nB = 12cf934763127284e642ddc232b1c889cd86617307b6ad72a9fe0d48befd7c5c5370a0062dfbde2add256dc0af850813b22320ceeaeed347eb9319bf22320b2fcadeb51c4bb26a160f7459fc172c27a91d367d5a232d00cf7bb778fba83afb744177bf1ddf45446baa035fcd0065f9b493d92eda37e9138f4fecf3ec55\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3de123bbd50c35805b943e76e97b7e664eb9feb99860750bf97e275029e836217375cc1910c13269ffbd0bd72bb82ca445ccc4b693742a96d19d3dc23f78e5ccbba46d9ff5975f239551c36403ad5fe86997536456c4a5ce54807c24e3b5317b1c7b2a1661aad85b63859d427f0703b460cf72b9acd3f87e2e69d7f8f15e972d\nA = 1d0433d84f1de082d2058475e0168ceb369013a67aa9417f066c29c28272a0b3f8be5ac7190ab78591ae72a1dc8ce628c683281a9ad563e134387b9258b9c96d2df288fc118a8cff068ee49d635343772c2fcc252facdfc93112358414e1734d6948b909b53e46263e9a0cbffa141ef77bc98e7fae8ae2bd85bd875aa7c1\nB = -a31a574d105305e47f4fc00ccea0cdf854556886b524901c22e6f3b59a42915932ab209a8d5da29ab70d1472dd5378d9c79a7447d17665f9d1f1edc1e545e417cb65415cb8a368075c16264f42555d26e83adc704b5c126c6129318a8f394af8bdbb32c8114470d11b2acfe806acdc7b96e1e348a32ff96a988de76d4623\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 770f0c3104c0f3395fabeb75ddfa2c21a111d23438463941239f7c63e4b6e6832b84508ebf3cde1d90cff0a2801beee05cd5118f9a726a987eb58def6780be899b473ea71c697557ff63a4c6db894e9438595acdd98abfb529d75bdf3c1d619d6165a9edb6aaab8ada50b61a3a84de654706a9aedb7321b0523558e8f18116fd\nA = -5fafbd498d610e9f29c38a5c6c262b71672fe9e9c84f0f071b549390353e4fd0101a059b7c547007e27df97761767302458f1936395142ce5776b0959fc5ea039429d64ac5d50c2ae0ee45d60c0c50b7ceb4ff9853d57c6e883f588017ffcaddf5a1aa3e23ab068877a114d9a2cf742f01f5f5d611424c8ec0d082f5c165b1\nB = 552155ef110c126afcb87dd20251220c7a43bd0215ecd22249a21c93583e120ba6f046c6fe03086ef3c97311c4d520110a450470a473d8633e3560d2cb44c25559af07516aff50d6d176e8782c06cd9aadd3354cc695c4ea8dbf85e01dad479c8e8438154351fd5fcc6fc7e9d2162ce2f0179247f756f0b9b34b54be74821c5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2e9ed66317734668c4c354d720a011fc65bb67439b2ac9203dca65a8f567682be40cbad4f55a83e836f1fc135596b624e4327acb085a61b6398237fef5a6e6560b488d4a673b5ae7d734b896d9647d71087621cc81e94d58e01fc2cc2dc775f9ab1b6031840a672fb715b77bd636e3d87b4949ec7bd60721bec8f9907b7c072f\nA = -1a6b046d691830d33eecf2c53953676ed3f6fdd20c2252f6e915052ec28ad1fbf7a5f264acf87ef8ecd515ed921ce6b85017f3d8a8f1d14f269f31e3307c6f935ad468cf012a912b0650a15106fb949cbae7b36c9cd496538bb0646a7a28989dfadc719424519bfa43cd8833d3a748c758f813881d83c98f7cb2a63c2a4d06b8e\nB = -34f87db0f839af6e4c4bf146789db36b3d0bcebb9bad81db690ccc3a35070d8830c9745b2fe730a1f3a252612e7026bf9889169b57b8984a5479cc4cdd6844ee3e150a2e7bf7680eebbef30e0591c895cc8b2ca488d489554f2339e2f55598717ddd8ce444a060cc95cad9eb478491ee8d3b8358c3762a970224abdc1068af0bde\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6455ff7c12bf3bc37120fe3f1302a9916a6ffdae6ee6a37fc23ca2f3a7ad910dc0e1027d4dc304a8eb4eccbcf3c87cf52a13dde472c07e2df2420c1d36bdd5e88c3d76e774ccd2ecaf6a0ef55b8c60231b1348a738f812a4fd9d0c158fd5a9fb19cc7cf9f000860d4cb6509271c8e43ae4193843324db02a029beb58ec2955ad\nA = 54ec203e2ababdb0348135c0679eca2a8e778ed46e53f195331a48d3828e5e40da804ecf95eed819ecefaeb9c5377cc1afb1fb220175990d347981353e7d90637adf8cbb16812af8a3783dd312d967a490f8efe3f23746929cf2a5a8df58e0b878367f6c5e4d3c086f947fc2bf70bfc3a0008a8bb1d7d83f002930640b6ed94c334\nB = 1311b88a05224e15f1465c8da26784dbaeae84f818e029301ea39a982f714c64312f9f02d094c401abb6a89e8537d64c178637364bd261f4a27beeaaa901cc7b3d4e36ebcd9453cda33d47a53c6dd1d121dfb83a222cfd16158eac23482c8abbfaca59e765f6c1fe871d884d281793eb19f6409dd6bbe4083bf762ef24c24f0127613\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 64104f6c06e563ec66de4442d35d88117f2535edf9e012897f44daab5a1b8a8696f84db7a68d64ae24a394debb993bf6734c", + "9df542c7e473b2e497396ce39a064789d5d7b339b65766b002a18096e7fb9f312ea5997c2a85463fbd6fc18f25769ac2a2123ccb0e72f14b0608c4c22add72bda138b83f986e78d5c9da31b15b9d\nA = 145f580c2ebc6c0354ebdfdbb1d3d7fa17f0b55493b0b9a11b71001c840a967dc77f0206c3dde161b5a773a6b5fd9471fa08b205cb6f728e3afba440b55268d6a9542e234ec313d53583c580a391d8da5943f4a900b279ec9d8933f2cfbb260b74ab714a8b9a1af3190d914b6e42212df84f933a237728a5fd5473ce2e272eb82bc83e\nB = -c67f9b9295dd5844307b8fe3cb9c1875257258e4be6229ab097e148c0175ecd0de4d84fe03c8da6e27153c709c2526092b1abc73b5fb40f1d4da9e0f3d8d2fd5f8a4e6f3c30befd80e189b73fbd77e8547b34010d2aa57072db0f00537cf3ced95eb517b23e0c854b4becce128a575a31037c3a9e106a476d8b0277d26dcee435cebedc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 11913c40d577f70a5346ff1cfdca492ff52b640eaf257510d311872c8df7ba9756973da5b9206c6e5254bcbbb4bcfdad5fc4594e41ee44e77f168e2d20a4b228480a9908b102dafddd039ba7f7619eed7057e8af3a72ee491a61dd049bd947e5b09a94ef94d5f336945f47104fddb8493ef22fb648ff5376b68e96c0555d74ca\nA = -5537630b7cfb8daf76d14e617f7b69f7b75b472801a9a818179d83ef2984d0abc8ea4214ed3d3d2bd785060e9c2819e861d0df760fc1daca8340e8a2c997c9ad201d6d2f12a82ae3883cf9f5c51ff1c25277c28175859a7b8e5b6cdec7cb3875071cbe415bb698b85cb19f617162587516f93c728ba8b2cfc19f238e2cfda115b8ec0431\nB = 597296cb27080f33a24241c1e98fdec32f7a4013a7340d367e4cf2a521cd462a2803109c27fcec353a30dd20053a1f744394fed75829e8396f8de434399bafd6cdb6e0ee81343f0cb99ef3087a7c69bd43bd722745a46cdff0c2c837fd87543c3c63df3896ac101a145b478dc224644996fc72460a89beb5741b91a42f2fbaf0d62c099b32\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 42f420adf5c6b32ce53fe23af4e392517e37013b8c3a7d035a93f6ff45142b0b0bd5525cde85f9b7bd9ce219bd3514617e89ef4d9279cb9a3e89e44f1994d72febd23ffbdb0a4f19cb76448199b31c5cc6d7ec1e46fdb67be1211c0ccd93c123d56ac0d9cd2ad11f0c58c713165003495b75b60665047ef80f6a393474cb727f\nA = -1c6ac9565d1950ae6c55025f76e0a040eed0462218e97aea87208ba879acedf413ffd5e63a92dd8658cf5f49d633ce7b126091a55701168ee4932db004dfe8c35c939887fae3a892b0b04d8eb74191bf8fdcf5566b4d3796a5d2596b1e750f64201057ae60aa705edd58aba4b48f6a2e511bf5007a6c44a27e3efd5bf2708f7046c1fff7864\nB = -244f2a90a57e5d066fe22f4d52f91b44882b8ef76d1dafc3387abcb224eda4a2100239e729bbc745237f8129d457e98eafb2ede2f3afb81e63520493da2a5730f1170b31fcac21259e90c894f8bc488c5e5dab2c2635bc7b1ff56c3685607f6fead73a09f83a7a168c4245729ce5b06e482d7d3d72eff33d14cfe2f32f72175484ffa292a9af6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2239459025b257fd0b6659f54b8874f93f07f4d6240f8ad761c9da288cf1537d8bd001eced284bddf78edd611c7f28f1393c6fb879aab6e7df8eefd347d63628b1ae086148f488b01272f67ca19db71a2b284eb17e17aaf1e3e8f23ea253595de474d5cf47c16aecfae360eab7855868b8af361491f6ad96f893f9d3eb66d07d\nA = 558613de283911aea1ee21d6b926f531f778c5226e978ce329860682b5375fe5e5328ae27b00f504f2a2d24470d16c1edcb8e76b4d1a740e55538e79ac7da4b45c5299993513ec3bba7e7395dc829a00d4e228618dd348fbf838eaf0bd50f6c70253fb1c1c734a07d0813915be25d3163df13511f3675022cb85af7646c14ba5d13f615ded8e5\nB = 1f3c3c468146c29408d9207e15b25186d3b06b3fbf9556eff7ed7ef7788032d87ae1a4d2a0983902d4c70936c615d8c9ee26c89af8b58d60231ede54e859763237d5ac59af686300a3e92f456484ce77700557ddc0f93bb40e5d2e5117f2356ac7ffca26dcafb3ce7a5573e07ee97515b6b082fe75fcc9dccd76b4fd416e69a247fab2b30965d9be\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7650985e7c6e5461268867dfa9782cd8154bd6a4bb5857d6555e9d9746ee79b37e44638940bf8d5e974911327f0e53bbcfda0739056bae2248015c35839f35e7e359e93d3a339e7af38c0cb43eac5b41e1406e34cdd4afd458a5d126f70b5d683415b490e0ad61269ffe7ea8972eda6addd447d97e60891e5099ee920e18f233\nA = 184845d3762ad1a9c925c51fabc7b9e15570a84a06ecef994910845d56869264273d75fbb84a31c97c27eb9779e8b39f6829638a78b266326b60546507f65128caaaf36d4e7f85939b75cfb3145e2b1bd8372531cda579f59efa0da9c95a8efc72faf326d35c660b4444627d328bedf50a919029dd164de051a4c0c924103e365cd640b9637d8244\nB = -977390f52af784b52c1d54e82131b072a1c308406e9b82587102e67c6f7145f0020952231a5f0ce9d130677bb5a7a37d5a06dc570a13a29673c8a9068f06242ac438806c37ec46136e7c1c1487ca2d330fc1f3c1f42ea51ba2805b74c44a61fb2fac109710dc3dae78a07057a753898d4e849b910f035bfd807178f0108812778345b256c7b59f8883\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 35d48c3e43070a10dac0e256afb83b219aacc0036f554bd998b9092ce3bf87bb5d3b00947f2c86fd4e7ab830502d15fb2d4e47ead087f5c779a9ba56e272ea86116e2c81345d379dda6b581e9c8f4df8ea56c78f04d4f7412d245e00ac645847af6ae97d5d2ab27e48cc878d8b510c2dc753f6ceb1b9e7bdd923e0e065a6c11e\nA = -76e575cc79d7f0c313a489b255e85d114f3933383cdfe75cfef649f639921eefb9b3b3184351fd0ad252c6e477e153ee586a0ff6da1e1b2bfd7e953e6dd778c849843fa5cc355b31f5529ca45aec81ba67a1e364d5a74a4656d266f7decdd47b2fc2d81d6c298afa2d1c39b5e8eed519a9997a14513537cdcddde0b5b41314476264d59b7d3f0e9a65\nB = 6b7faa437b4e8db8fba56c62eddb8a81e9090d1b6655a2185d656b2db0e85225992297381d653e707aa15f3017880b0f07abf3dc455cb09c4e551b3df3516c6db4ead79b88339fc33dda96bba76ff7c388363c36b67fd5dd0ee63f92f67549dd77e37e9902ae51cb58057579f03286fc48e3b7fba763fc5844c222e6a1eed9e1634d0bd034cff222bf147\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 445039f359b55eec647296fbff4f22beac09cad32cae79c13d591e314fafc2b77839816aa4f641250938865b0a2c30a10e23da71a6dff5985ebf3df4429fe64c327557b12d987ad9e9971f7c7b1e4ad01c94e1e5322dbcbc4707a959a401624619029558fd6f5b14564469b13146f9a2555916491e4d77caa70f51716b299135\nA = -18ddf976fec2090f7d1f4d41b8f875e56c813c04338f595d6e591b3eabf9e105be792f45354ee9beff997e6c0e8ec3fdc714c07b3466ad1a949b9d30da0115f5484c3b9e00c7cf0c117db57c3c6cd7434371c6d9ac7a5da1a0e2d705bacfc22f62785222d59bb5bcd3e3bf2df8e845953c6ddf1b546cb75b1698dc8e20bc611294ff288056723f1e46ec9\nB = -2cbaff39103570df7d85a5673b50fb8818434bbc19ab4e33bcc8289a4047d85de1b7029a5cda3976ab12e1d891b7efe3d5576bcb3713c597771f93532853290068761bea04200fcaf9b05d8553b960ef5e28064de89d9e5097d12b26af0b64beb40b33ff82a55af7c5838b44282917fd4342e2065942c724f3cca515d9142fb8e46652242e8f0ee5ae07b6cb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6727c0d0ecb4a375d0fd1bc52146da1242099d445ed9e87b1fad4daf8369fbeeec49027d88bd98efb425c1e3f73e412fb327680068ae57d4a53992f3759af0ac1b96a92f56c2cf552e6682d1fa90c3910bbc5c0b1754862ee13c5ebd62d5b98bfe8dbbf9bf53bf9ed0b967f3c9da24d4334b9f3f75314b429b05b8e27142623c\nA = 5cb6c49efc6767cf956885690ef740337aa71b90c1d4b9b0a9e4734de0c0c50f2358fd45aeedaca6e1dd0fb510bf097bf46513ee09f3343bbd1c11f507eb61d51ada40c5d6b730561756480063f60caf05141bec9a769c241d367cb92fa8e229ba2e471fc73f48812a25bfc7553c395ca77b80443ccaa82fbb7198f8c35c3b5a2fff977d8b2a29cf9358ee1\nB = 16ff229a0e67a410555dbd4b687f1470ec854ef67db73a902f2d19953c55071c4a26dc320baa8571586f1fd54fa490b0d87dc83e5bf20b78956084275518b307ce69aa4ca1079e3aa753d97fa1cff62e0b5f3b99d96a24e411fc3a3e375", + "ea21b7b35a578a72df68d28286fd9a324c06930905f696424780083715f77961532bad061f3901ed276a9eb6e81ad4b4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6e9947beae4d934253e481d27e854a59c4047eeee4fdc7df7e174a8f045776109c148ba3721685195b8fb59263def88891c5953b5a0ae85fcdbf02abc76f4d3c0f5d9496327d063ce8b3ba875b4f119dcd8beefb3ac884c25955af61c35a69d0670c3c349564e5b84f7df4252d6d3b29d9a75f09e9ef79f0fa9f797bf75b8ccb\nA = 188785951a3befcab56128cb6fb9576bee2412e6cdd7dd1bf5643babae83c8011af99aada405e119c3be33653862440005be994bf37d3802cb6c73cc312824c56841004c8e871ffb560e93a1d222c93d63684e90a91394b9c8ba8cac27b414bf818ee0de7217bc2faf099783800485ce2e93612ce39fc7e2f1db708bf9bb032d92b66159073fecdb2e0257058f\nB = -8dddf094f30284c213577ceb7f1b2efb1e4213a548e6aa840f801cd6382fb6d4995908b7827078dc3f46fccdb9e071bb8531ea8971de0ddbb714d678bb71ba9d961e58cdd5f41b8472146ff9b814a5d1d6368bd94812f8d38f235f39aeb2421a57499fe7102c1ab167df7d33b32a6dc7c8eb8f4babdd6b6c929d1ebd9bf4774aa40cefbf136feda7b6e10ba4dbef1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3f4a8d90017dbe8e77205e65fa7a0875a1ace6f3f215c2974e47dbac779804143da3dbce92db391c2614c078997c7d1a15439ffb51a5787f5bbaf98a4dcef576a6317b9b92dd8141a8fadc05d3be7c150630668e620a4e07b4b00519f34e422610a160de112f1ab8adf09a9169ba95b60242c89196ac6e155021dd84b3054511\nA = -65ff4322f8e46e03aa6c1fd10a207a5e51db6991bdca232c0dbc9d73ba77fc485d881868be7b14c25b05bb59b7f5bb6c4b2a7d53f35d2d7af282a0423285c5de656429ab7d3af7d92837e41ca701f527845e98c2bfcb51647512e6abc6675cec2a7d34ce55ea4dcfe9e7a8397d45a7a3e73bdff06e303a8f04ab6285eeb1bb78b1455931cae203078eaae826a6e5\nB = 4d936b603eba3aeec3d3f1f9acff02a0ecc28a8ec64b6bfd9b153b1bbacf4f1e186d3deda8c1c81e759237921cec53251250e3e838f5063c4a1eb6cc93637f35aca10b965533d18b713617a312e74c446d63eccee93cc97e3723ab27357ae9b3cbfcb3e2bfc589a1bd582480e776198df047c3ad85f611ca6fa480c70aeb98af02f57d56dc9659b2a6bee222dc3e0566\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8a7f3cde3230af1f1fc25e0c0e9ebeb69161d3864fa5a03e5d7f8c82d9940ded285df35c008f61cc151b4578e2677b2f2cff3236935de5bb1d113597eee448496fe29bb18343687f6e9f1c783863e949a0954de2993d47a03607423b458bfd18c844ab57e9e2a43930df159ce8564edb5a2a37a06425626502e3ff9363b73c79\nA = -100f2984dc1451fd7b71e5d290e4b7de2d26175a47b9bed524fae02bd5abf96faba06e955107329559bff3805689633a4a57275732bc42183acdc792cbf7b6b24dbdc8921b73c0308d0c0ce5d8aad75f7eb16352e67116e859b323deccfe5d9ffdd1f0265297bc9eede073146a06acc3c330458b07b8fd0bb652c7325cafdcfa165f69cd0de8b145d49ddd576fdde15\nB = -21ac4953e54347a56800d75f6feb6ad660b0442174cf3c5dcbcf6528e2b5da95a614d3a8399da14507df4b8eacaddcddd627b10ec2dc5fb8c43d96a38e6dff37189ba275afb9484df800587f4953e327af71dbd58780bd5885b4cdab15ea0f2864f961bbfa9bba6b2d9448443af87c0cf178990254c1ae6e19003b1621f3240a6e5d0a3be2deb5dd253f5e1f88dbb60b522\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 76f8b44df8d8547f8b3d8537393d2805c699eb37d19bd115bd5539adb6b6a00d004def3b7793d5c71e0ccd2b7e9fb87103c1a5f56a8f18ede1bfe1607a346297166596aa78dc584c7c32832e11b72fb4f2d40ae1591f341919bc0157080ee8febb7fee5461a918d2178fa407c37a8243e24206ce2c19c3addcc2b7c3c1912b6e\nA = 56f4d397530f5c90203df1ec799f82a0096888fd370d543e33b5a2c8042108bb75a86265204c40fa5a9a44965ad2fb41896b134ea56c79699a230f38c0e3fa4e5d346cda70e0253b9993c9da5642f4e645a0d96cb732f8f04c99a83d1f1360a385c6e1a972b89915489245ce58830788ce23b9e62d6b48a7ff9a486614d6979033f7914a0735d201c6f29e512374088db\nB = 10fe818f6af7a95cfefb0ea0726f9a3e0e7c30dc9785b1fdf6e2b810515448386c7efc656479794d389e109ef3efe37fa6124c5a7db3164268da0d98538606c57bd2f7df9482860e81f272a27c727d7d81a66fc1a9bc8c385cf02b7ca6bc7ec2d8d6ba1dc992caa216d02c9bf0fba8ee754af77567c6e275ac1b6b1b36b065760761300d156e40da8445712b8fb206c0df346a\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = f580f9d2438b22700c3ebb23d1dc296f3d33deae2d32dea51c7ed3a0ce7b06af11046bc1cc279bb744bc31e7f822c17ffcc5dcbbdabe213bf97bb85c7e19ee71a513bf59b25b3b5787e42e9f3ef6aa1acb8705d69924a107b4f88e0cf9276c2c7c47fa4bf56c4900b557aa5587418f0ddd899630ad3ff678b5b907c07247b2b\nA = 1017a4fdce8bf41ce804b7c9c836d85ff6ee899807e1736bf0357b015b701b9675297e5ebf588ac6c295feed3c6a367987e192be0d89523ac7d64b0b9576f311b5b2705c5398276a52f06085027480c2ca72884ad7be34967bcc6c8cb4ec4fb761e88c16866a2e284b40180eb14536810eeeb180ab701ec47ece62af65a0753f95ca657e7d04ebf3c3a7db02993da9089840\nB = -aeb03379fcd4e87cfd18957a72fce42e016951a72b673a9e81f666b3cb20d2bba81400ecc2b38601bc3270eac46a633a1a6b55c50f00e9d7fc8a20176b93e971cfaa4f41573b17b8ccc498f8a3230825afd0d7f102daee347a9d59cc0914ac8689c1d8b39ccef1f3def44054307a7cb7706535f0cf4007231ba21696424c3d5b42c8e85c278f7c2e8b7d1787effa601ad357eeff\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = be05efeee19cc91e30a9277a6551aaea63aa3861b63f6061efbb0b92296e09f4709529eb849d9f40406fc59c526a4697144cef9661b556040458940ffd6a87ed56cb073d2ee0e6d1f05936fddd1b9a8974a3088577847ddde6bbdfb3d69158d5b3899c13ec78fb5cb6aa7204efe308bbe0b52f18381fe838536707a8a27ba0d\nA = -669660e75eae9930dcbdb99c477c980869417ec9c0e8c4053f0bd8ae62d496daf7539f37af96fd1cfcf3149bc02b8182a46b413e3397b49d4b4d204491440eea65505cf5d33a8e797af08f3da41f5a0804214846bd95d730260c6545d51126278181719ddd396c55f119e84da71f0683eb6db8393b098b3a0c5999862644e073b4918b5c8aff17efe860744d85bc94b582d45c\nB = 6045f903a750b69b709cfd6a1c8ec9fc0d7da9c53a9d26fdb0ce9a17c6a0ed5ba633d6fc01f004f4a48cf247d61f7df609008ca5bdc8eafe06dcfa06bb67efa6a584b5a2f02768718a908978edd475a2d2926af2a6e523549a5cbecedc78323c5c295bc0b8d3e14053078492e82e339ea2c6301412a5dd7efc20da0aad0577a37d853eed820776e672bc6d23dc821b5855eabcceb18\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 705bf20b7d92e68a69019cfd721b27373c7ff22f911066907f556321371fba70dbcb9774d3a26ca43e44ab20c586a3c1546fc3152ce011be66e04a59c6631bc8bde18efb7bf1743b9ed75a7a6c5bf5a4117368b81b112a3cd4e1c44a621f534a11c426451ea5fde880939ee5bb28d9843730e284520a976cd9f60c94751050ec\nA = -17c1dbc1ad1d2d33dfe1af7b4cdc7b69fefec5a92656957e111aac292e44719c7c752ace33dc74a6568be38b576a5ba174bcba77a034af5fe101699c99ca39f8a3b0a20679e6d0180868a232fd8fc775089e185e5eb81585403f32619a2f4d857bb091a824a89de2e84529e5b0702b45771a5816c5a823d81ddc89f8a70cc3d3a0c6bd6d85e9d72b69d2713b61c46161f7f4700bf\nB = -2252b54c602456c5deb86a0f249f3982c3836b70a946f636b22fe00c6e3b91b94e19200a33087fe734ce9a3f92a6099ad03a95ca523b7edb9e1ed3464d38fb96c470464e1c54790cd48769677efc5e1d22f5be4c15288bc5ea1dc184a05fddd5e576b3b4962f37437b4f9709dcec374377db44c8ba1d8611c0c3ec35f9bba213eac59a047e78195ebbbeff941c7f862e8c80eafb72b1e8\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee", + "199bc20d30af280fb\n\nModMul = 7306e3172929c00c29ca1db360eb4ce82066f237e9cf6aae368d1f531620e9b61eb64f5b3e2b735a3b565587d7e955d052df94a20e4aaabe493dba2c18e85fcfb65df166cc48733632d165129b112598bf5e4c58dff662e558e5f71b25f36708d3ab6536b1cbdb5aa2ee56d9e019a9c3629185b188af909831629ffceab634fc\nA = 6b31ef80767a7693e7d0a9ecce54beaf5848120f036923d80b7a0245aa6a46135e32314f3b227268e0bfa1f45b4dce83bea890526c7ac3efdc8e485189ce2c51597c2864c2d3664584be23559c03670622a53edc2c17b3f1a92640078ec35189dd7953e55e4da0290ff1e2996d164d69f1bbe6f5285ae89209d611a7d760e413e23285066eab8e126c320bb6130a91d67ef26d4dabd\nB = 183f06828033287497322b05ac08f62dcc5fa67b7a10c6c5a319c9a1e642754230c6d9809dcfd2de4bb9e360d6e6e1180f6ec6e0d4c6185e34ed299b6171e653521d0f7b8975ed5e7d2c51d27f9784a4b6f9b5e97379fcdb42e4df981462cd5bb9d0501f93f217d954f6baf70343ec710065eacbd2b778430ddc36a7ef0515f29d5fe78d8708d8ffb6c3391c6f632cb1bacb4ec52972ce0a5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 361ce44d153f4d251952c0b90681a19b7d2d8df7a6c5d459691a80c06107b2e818f93f30f8dad352d2dd87b01530d51fd1c67cede9b1a6167697098e41bdc5dc5e7a3c310116aed0c7b5fd99dfcdb3517c13daaba6ad10879f600eab846cdc110d392d9bdc0e8ab34b317840a725a7a12ceb48c75e8dfeffe2947aa85b2a5158\nA = 1e1f2e44bc7c79a00afc3b2570d5cd27ad5ec9f45aa94f63f2ec3fa6b69077480212a1cbde25ded7ab1c6cb1ec26d5905948e5c1d6d109bd5047b1e038666054606b42e880b609f6f00a219dcfb504d481d6fe709f4362940f6c4b6f2e05d243722cb32bee5508ec94eeebb53b5befa551d3ab5dff9cba3daebdbc97179e56cb778aefdda6a0c24265728ff9e59ca3c2d615398d97e66d\nB = -e018708df037aa2918850fabcad82731487fb812213b1c067d0688462a4d518e5ec7c4c84f2cb2017aa6bc960e2faabbe361ad8f66355366cae869d366f06d7cc32ea08dc51631e7f36a4c775611095d8aed06a0086d0a471749246d7157947a1eb5d5503f207723a7062382b3e45bb84c6f555e48f6d63aaa1c04fe13c0108507c0ced669a5296bcc16debf18e03c32eefd177bbc1dd2f19cd\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3aeb3ff6e797d271fd2271499a740a91569f300d7392a7b5898084012a3c5ad379a57d5169e43089cd58fc7210314758d5368dabca2f0ec5cf6786801bc99b45cd60403c732d9f98936aed76da724bd3e7d4b622dc690778f11fb0310fd4cd980b220627f7a864e107f93a6259081c6581e5dddba4890508af8057c1af29a745\nA = -75e06b47f60edd23148c3736c9c125a617beea7c8fd47e662c9d9be883ae925b7801a0030df3f4bdd3c9fc386f18c4e002e5daf4a6f7fa27b2f71252c83d5f1695e50d62a10b99e1900987b342290decf681a064f789e11bc3fd75d64e2e78ace56e7491fbe0eddd6f9958a5f95775c920ad6c051ebe7750fa76891ab00f42c910550a42bbc1c1e5aea0ae13b7e6f916a5d228bd57e854f7\nB = 434c8e4767d0d7df2125def75a978bb1509a26bf8305cd03df748c6c12b6dc580a2c1ca9a4526eaf3936fbc4ec797d0733217a54ffc9e1d7c6ca04fb39679859d5bd3fa64cd0a09cf1a056094b9c20ddf1f00e134533ba9892c2ca7346ac8d0655250eb45df9f0b7983bbf71102c6f1a2d9497e7a45eea7b3095cac037b7aa755beeea8a6191da268780179a652d94a732a2a5c7b626c0de3145f4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 734a429c91f5b0f06fd47725ded06222c0193dd407e9daf136696f203e153c9bf6df59016849284cef93fbd35edef2cd31c9b956fbe562d2a22100f177254144718ac7d22c99783fd523b642984794bd7beb0d0b363e28d3f3469ee332ee364faaafef25c1d4a11b5e517e44a412ba717a113ea9e1e8f2d6db8fad6f10d06950\nA = -18dcd213e9938fe4b6a64abee3b9867f65e47e5b0365d45a8dee14ddf787f34072ce32f38d4d48ccad236005a23c5fcdc02b72cf27001495663fc56f428072d3f1bf5e33ab2c5f9dd9facf122f7225ea03c2f67321530a642803f65a2e9428f32d0d974e68a25f705e4f8140568f7e4b132942b49f9ff53f04f241feaa29aa353925fcade33a0cc192fee2628c2111da1e652cace9d304d0f1d\nB = -2e5397658a5e6db9d30f09e93e67a30dc84b1e17c25786e041fca48ab710e1d0497ce615264f1abcb23d5aae8412b58430bd801775acdce06cd362438898697940712062b611c92ae6ad10da31784207c5e7b9362b20d7254da0df8caafe0736002dd466d76b1a03e91a8dbe8a71107abd5f07b00fcdca2017391c7c3263881a3d02a89b0e16a2a765a32d24ae6584cf44a88975c539402db9a301dca\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 427609751f28edb62c717bd98ddf999cfcf65128b652be1b5aac0dfe1bc0f7687c580ec70c8290455a9448c69dcb550c0cfdd109af561ece2ec8707c1d02e8097e780f32ddd932e706f81f68711acda0e7610f4dd0fd55f6ac7ca3a3184f655b0b29d2d62974739b43ded96b413b9e3f0033ca1edace24b6bb610bf06b5d940a\nA = 6576c31d48daaf7d6bc3658952c4ba18095f1a0d73726f6fe59381af45a2a6b592adc79fbc3b597e1eea711ab295cd991441fb5fc4ce5f047e571a7d949c709e0d31156184be4b8a6a49691ef93d7d3b120193f6ee82246aeb896b8b7b4c74c27c02cb39fe0335883a3f088a71ab42b947a0cd59dd2155c65a0274ec0836bb8c2fe394500724ef84d869bee40291363389e7012d672b1eab6696b\nB = 1ba2888f30be283b588cddf00eb3ae3c641e35fc0bb3a9fc85d7fac1e81052129f499afd3e8458d4cf893d51fe4a2bcddf70f28c8edef16c7bbfb791daedf1a8248faebe36953560498af652d1f1c7aa0e9a5a667d9c94f7d9525cbd5a82147d58b738dfbba5aa162858c2c66d0dd7d8db38d41a2261e6efc7d0c8b2dd2d6962be0fc796705cec8e87a13092e4a3febdda3d4dbed9d11a1d5f92d7dafcd6\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 533d6d8d7384e6e65569ba0daae0a8cffbec1d20e417a6edb42d401a59de0a91a7e6854db081ce33b76faa63f6d866993c245e69ddbe6c86d339f7107a4807856cbca23cee2bf5496388ae8fd8d7c78767d0775acd7bd6202dd75451b424034e2766185969b5663b638d539f718e50a9f752f406c224c000bf1ae1fdd60a2a82\nA = 111940235b144a42a13201a41a3f9e4ff02948f8e9127d9a3007906988a50b36d7622d1221155f2516812074a7888b1d8334a01c02ee33b3164d761d02b36729c299ce2455a462bf18471fca42e5b01615d53723c3fefa5aaf4a039a6caad35c348a0a4dd3f0204f084f35c0b93ab233c4066dc50c5fd3897a769a7c5bf309f7a9c30e905466c8394d509b79d62a69b58c73d8d3f1665ecd9a8a4dd5\nB = -e2633e43c38c0b4b8713c20bf4e2b8ccba680ecfc1139954fc42724277beadea438596942fea1094091671c2060dfccd0351b2fba8cbed35dc963cc18f8e8835052da884799d88ec1887712000a0726b17cbc4302421011d5be8d234440eecc363f09e2c04bc9cded3cbbac9a5bdf0b6d418822fdd90dead20e5bbbb3566ca94ab85f3a00d32842eee6521edd18b9aa6872340b2f47deb961f58bf231e01f9\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 33960d7ceac73f342d46275e04fed56563decf2fa4c0e9307c90288e911ac8782f8e1354fb051a9da8e2db83d7c710b5d2b611495e72ed42259ce783a7e7a8f601c07061ec749481d39a082f29dda1f9c7f444a33ae1c1055d37a677b848af371cd3bd41c851d31a07e144d7add66df39576b8200a8b918201630b3da8e664c3\nA = -402034484e499a8efd610200790d443c5d3be35d19d8808da85954d42dca3f24177de48f55fa2efd7e4f7f624d806a8d461c3bbe0b626fa1f3cad2145746464108b367b13f3537ff395262256bfccce5f0414e1f98b59ed29940171d46ebc4bfa1a27802cc30d9221cfbceeb92abdfa6e84ab4a54965568aa10ea631e82067ae358a1a93a3a3fe3a5ed5636a0c4cb373b4d49f46f8fbbaa665a19200b7\nB = 78ec7dbfa2b28e268619ba6db34a23adab25e7f8690aa9464a7d8fb7c6b87d5dd9d33d4c023bb665f2d96febf2638fc087ed30796fe7517fd58e4120c0d319688e67a32bbeaf62a987a9764be75384bd499b0e00a850f27e303f615031299c631844d10abc571f9f2a0f742cc0e8df2fe3c244bd825bf1d9134b2f1059e2a1b61985ae8daf9bfbd9eb24ba268ca58553891945ff1a314a78fdebb5444677ac081\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3a1ea3fccd6f336e6d444d68af1753b83145131954c20f1e3c433a8", + "9eeb7e267425a34d91f67fd65191dce85769ece2fc7ab12d032f3e30f8509095ecc05148e47a85391b21a18257c338a6a3ca9816987abc8143fe443342b34afd8a52fff00dda2e42b1b39322bd38c6a1f711051f791d6cad2a47ebd423a9b933485fd5861\nA = -1869c53f86755aa350115a9f49d6248cedd42a339506b8ff59cb878b7745956f142fc4387322c41f369773ed375b72665026771d4ed1b9ece08f84e4782d4c3b0177853cf9ac3a55f7e52f39c1b82aa42b30628a4fa6a838754ec6ff9809308f675e455bca6f44e298394888d85fee29d8a0c8e9cdb9aa08d68cd70e13a243b5804a3ec199f52ccd462ba6594d856602cf1d5efa509047633923d31f78da3\nB = -2023c544b6cdd8d971bbb345300f7a101f6dd44dede6bfb5f4e6b4eafb7a40728a3063f6d4bdd0f606ddecf062828cf889b2f632d0c9254c28f36dd974aef116b73cabeb2bba98635841c2b4d2aea833e35eb1db9fa9a9d33bf7b51c49a14907dbc6036b027a039192b47406bcc56bccf375fbdf40b82ac4b3c660a43d5a6eb656868d383cebd099d2a73506f675cf29649617fe06097a46de93c13d1e590ef2cc71\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 4331f18a94c169cf0253136bc4eb7480c9fa4401c18db1194371dd53e5f7b75f07ec2e1e1c4116a5d2a8b2cded4b22925b67a88af9b8479c6e821d58cec7ed9f780a4c41e729982cb33f69b87d01c11cb9a8f7952db1920b6eb2124fd5d820555a99327117d7e8e26d18e748fea3ebc17e1d07161fda57a21a70c7f4e251612c\nA = 5e7d4ef7d6ace6cb106e38d96085d3f3505983fd952498af3c1d9b2af61e4ba10e14961b339c6e64e11ac758d5fa18c3222138290866970d67d0a4f4e19f453503eb8dfb85b44d1050c86943e7c5d6faf7851bedf7d0cb6b13d2acee25372243591d37dd230907457fb440f83b62395f80f59a2d02b87134887406a78efd77614f3193e517f234434ab3be084f1484d3f2c1f68c67c0d6e863585a8a5ddd0be\nB = 114b6e6726433ea88a2ba965f0881beb3ff4d377526e4e099741f069abfaf29e129a1f5fd243c6599f725a389728f755f9cad767ca1d6ae5c8b3a32102e47af211e86d67574bddfa42b2cb466d968f38b47333b1b55211fd9a315acd5ef62cfd3e83c13ee9d3fa20a06b2292177961dddc7dc39abad9ea31ead1fedd3d699f651b656edceebb0bace11bebd0cfa581dad577b8b42f0a844bcd8c8227880876dd7b0aad1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2468cdb1a26eaee34db3d2724e37f023c8a1788526b3dca99321b574685cc8303c609c85401a58fe6da181daf4111fe8c6d4b7428b1cd301cdb9bf8cb6f33140756c8b490d3b2e538ff294fd6471c4d17b9d9e4adeae0df088cb9daee18e825a368be57af4a096056b9e76b94c8d3b911b6a074ed41082926773a585007752ce\nA = 1e6a59efe0b14fa017c32ffd0962700fa9752242b06ffd0b604b9bfd125114d4e0909534ede704cdf1c9e88a6567f4a2989df752510d087d7b7afb515ad594627ece54b8a8e539074386121c9a3e1c12eb2641ded8719e56d42ef50e2f3b5d7d59f8a6f897174cc00a7449d2b91f33e9df07902a95479731a44fc4ebe8048c449bd515ef6cffed70ae78c832cd43491203a247fcfe0a403862266777947fc2542a\nB = -8a9d3646831dcc852fecc8e2335549e8baa2e2d82fcb90846ee82bcc715c716d4a9f62be29d5e1531db73c2186a4d2f118266de33d966b78f989600d772ffc55b1364117d6750cef67f4bae851e7e3f8fbdae7b79de7eab54cc1fee56e25d0632b2929e352c882ce78fd64dd0a1473e80b6572f0d4eb67f6bd6e45c7617314219d6f7de5e505a9b395096cd36650d23e8d57d6abfa9faaf0ddbff90d32865bf5ddddcaf28\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2909d3aef7a21244efc9b5b16626e260907ac11f3d00647f2170ba37197e47b9767030195c2f6d5eda717a83a152141bffed2e26777417ecd8e27aed8666698c2e85a414dddd52b07b52b0da7e08b3217fa6a331f84820d21086a4424974e1e8cfed3501eb054242a9f8bf0803a94981b7b81776eca6d07cd50c050dddf81d68\nA = -73ecc8a6a1507fb5dad40677dc6ec75f0d130ea704d1e87b00d2bd56a6be21714bb30202739170b8dd3605f0553ff57439051efea2a97def70a6d2cc3fa2b9ec27a00c1338bbd588513f0f320272b8933fdf6635e585d1e79203efb5c95a454fcd7f33aa2aeac08902107e9bfb29587ce8610d50cdb7f2033c5b726742fa9f7f20b4780cf9244e6abf6b812171a64b870c3ca4c9e898d4c15e9f5b0194ae736c3783\nB = 4049ae926bb52e862606842bbcb4a5148bd1063b6a56f331cf10000c524b4aaa80b3bd914cd697ebc98d68bd3c2bd5c87fac4ec68606c264c56e25b19d118dc9f2eca19bebca07269714f2955e107b3fbf85530b1fe99c42d33031958280b8e8abea5a918a41cc7e6980149ad68fbf1c0041798d2046d7f88a395348b295858c61c2f33d8512b6fe75aa8fbad62e2f9b0b7876ef95af8a7b7338a2d6b25ec6355c276fc6ce23\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 22407e4fe280ff5a10eaf46d8e1f5a1e77a07410cba4106466d703b11764c60124fa355733b47327e952a12869476306926cabbd797fc80b4a6dedfbec0b7718ee754d447825cc405a98b85f1e09ebb9294c4a4636aebfc61af4545b921cbe759d3f389beece3f29c2c7c07691a4c46a1a72ce418a239fdec80df48732627866\nA = -1e165ca7e1eabd2ad1264d5ed9c3d2b687f2db5b507a0e4d21d9e042cd46e93c2444c6aea8491b5caba2d8146bac656b7754b7b1ae0f6216029c7167fd3b1c3ba2e20469d386d8566ebbc05cb51bf1f1eb2cad9dc4fa454b07cc1bcdb9b8f5a43e354c4e0f4e62d52798f667080a0e0a15414391269fe8c92f06da74f6209a3b215adafa1eb6866f8b3e419468e2e5b4db0d0ada80514249320cecf034477977bcceb91\nB = -3f314681eaa4cb41a3feae8467f7d76b8b05939731fdfc943235aa4d67bdca30e64de541d17a8971e829bc0159384643672bdffbc93b3eaded7844d824604f46aa58b1f1b9d788106aff53438954af015a0387268266a6ba262e2fe7a4c51b5af6ff7f918674b7407ce8282f66e84fd2582edd809b465e4401c67e5faaa9e5748c06e3bb8ddb23fa649ccaf9657dbf79b937eb8959aae8d5bd9513c1e601c0e536cf60c4fc3802d\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 385ba217033463cd9cb882fe30373c2d8e8475dee54aba1ca9713a709f40844905c2544ad792784cc8eafbb412dd68de6f98522dfca1c3de8e3bf4cbd09bee4656c4341153b17c98f9ac09411d16ec9880835cae772bdd8eee51eaba7c02ca6a1034c2c5d2d48e7ae3eb0e22f59bf69537ab6f1e49e58a71c64b8934113eb069\nA = 5137226623f4ce4dc9b80a783777ef4e53ad3c2ec648264db472c517a96383ba1173e52c2659a97ce36341a11e832f4ad293b89696f91a051c35bb1db6182260d4a276d1a9b4be848c206899f87a361d318d38b4073a7470c5743b816cbbc3bc1b20dfd7971b11ad4e20d947e352d42760104a5a3cc590b985ee3b5e98c779e38d2581413a2208d31873f9644ec979602671c9da72fa6f66c603c1bb6d8e690dba8bf4933\nB = 13b45d4105e3f5e8e0ba36c812faeafccea2f1a30e2ce8ffad57ffe0dadeae3a23e813758f270423ecda3da083b42432eead7f04842db8865f9f1e2226a3d298ec1895ae69adc55d1d338c3fb787f0676664564eefe46ca95206e81678cf1a2f173c52d809b1e06641a9b467f191ea09fcdc597271eb43da1a9a856784972ce0eeedd49ad363dee882438f09863ba5af063925871c525c6c0ffdca428054e039e149a424c6d1b5b2b4\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 7865f718cb30026837ca006f5cd997c5b917726ac6d9bd8c3fb9eabda0854d528d6cfc10e4cd3f93f6848582690c6a83955072daefc6959d33192fcf42a111650e50776ba9ae43d3d26e0ef2c6b60c3871aec33eda8c56353903e7ae96592fbf350b88d2f56e03f7f327022a2aa9b7c484a000135b85bbaba6f8836cbfc81901\nA = 16978c06a03276fa2e0bea45740a98d55fccc9d27321fd0a5b8522298a2a90d391c06c5c59e7eca85efeb9b4c91d4a1e9178adf816d597311f004ef98d209b59a2d4b901fa14c57b7297861ee58b89c9b2e931e4ce5818dd4006f3c40168bb4d3dbbd059c1f1cc24ecdc64d37df16b8e8d0529247c06f905ca88a5d283ca1b9e6856fbe8115a326061905b369791772a47900974339722d19b3aac16a0bedd93e1e4e4289bb8\nB = -de6dad276dcc0a9e271ad523620ec570fe6e3b350b934932ebbe36dd571edcde968b6590be14326e0f6394c0a2172052ff8dbc3ff15d94fb6e36a098286333768a84fd0404dfa354173d01f98484fb20897c439c48952b7f1791209fed94e9e72bfb3df5f368d420d587ae8bf036db6700f77b130459e9de2a541ed885c69c5641defa9436a4f7a69d2848d0e5d1074f77fa688b6dcc4d4c7de25a3b1b040546ef7f418112127cff173b\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd99", + "13b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2d3dfd14e7ec60f842d1db83e29a0f6b052990fe8900887dc44476ed3948870c57e72e91e1941c476baa6aa86f76dd8ab6e6ea41707242c46d39b54215bebdb1f28e59d719fde18bea9994610214ea68ad9f2da24e1ad8a06f8bc698f8e76379ff332a2745af472d52a4b8e57d60280e19f93d5be669e0832824321e9ad8e76b\nA = -5144d5ca834f7bbb35d3fb95818c1f89ebe08efdffd35993a7691c05aa1b67f6a28e219b27fdcb66e516097c9ef5f00e4257c561b1f94c52c577471cfcd7a55314d3b0fa308b59449a36adc884c48ef5f34753bea746bd6fab2f20b86814c9fe50e8abaab742916313a50e3c390c67fda8e3729ee3329dc5e4b7d3107083aa3a07daf7952ebbcfea15fae7338cd0b114e9ab2f81dc2e80f90abff7a7ac59e3aecf76fab87633ec\nB = 48b927a46dbc4e23d714b256084fdc7cb9d4c96a988a71c956e0bf98785ebc9bf22b9d5c6ba0c419e60afbef7b96cc0c4a13e397aa2d2dd7995875d2ccb127169423455d138131199a263151f28d232ff4ae24e316907ace1fedd02a02cb5ff9c831de33e6702010fee2232bbe3c1c193ce792eadcad0c81e7d7c17e49168377b68690bc61f22dfddb17d82a3b993804726037cfac8aabe8548befc52a3c6c6baaec89a392133cd9c45b1b5\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 3f66970f600a9d09d73fd1ff813e977f539d69fe1784b8a2f99506d868418e4b47338ee0cbceed555f88824f98ffed39befb69e8907a5822ef7cd2a9950a070aec8fe4db9d68e1c0620f9eab4ab529c7e69466e325fe1c6c011bf7ab62bfd1a136597d7d5c47e8eb161ea048477bedc88fa30e4f7ddab2cfeec3fd0bb3fb61a3\nA = -1343c391be3f2b72c4b79d8d6091389c9602e97774b18eabeaae81fc0539336cd8c899341cf75fa758421c7f32eba9df474c934642003408b32db66cfa92e6e414b42b1d49c7e655ffb4c80f5bbff8d2774ee4f7198839680175e1ffec0428939653c6697eb3681d0f92634cab1cabc63f423d5a71d65fc7150aaeea74f9e0153923a1c65dee4a165e6a01a88655fbecd2db7697f4d2b49fca2508e2b8f84129785d36d88bcf59f4e\nB = -225a0a4afdde6f6450f28736c3ef6e67d67ec6206a63b11763bc6e69b03f1494b275ac504868caa6d56d684a12dc1098ab0d030583e73a2f45a42b8607c0f19031b9c5f07fb71919868911806d210d43aaaced5894e844881e89bab85a203af9ec3adb105e50b4250343ca50c26df14c46d73a22c2e4804d26d44ff0bbcc13d0dc7e326c9e4eb441f493c9743ae0eea0de045e05d19ac32d2379196a165e63ba640ca42e4861caa24c29cbfabc\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 54e95e86e87bc220c8f53f8485402327885be34e34063a1b81e52a23fc3056758cea1c039ac4e513f70ed9d394f5806fb771dca8e342368184e674e6296b9a705c6380bdaf11550cffc73f9f55b9385c85fb648f105f11138a3e1f9dc0a39a0f9755f8328701484d45784e3e4b2ebddb32c9d9132867c6513201116428b791cf\nA = 5f1239e0b5dbfefaba906bfd9003336489ffdf634333cec2484c582dbc19b66782ba40942d047c3749597ec4d89ef61b7803d33a9842f0c903461be37c679ca213aea894d36c1e12bbcaa1c679599d2adda9bd23e712dd0d0bd3f91d146e7a04f3e7ddec8b0db7e12377ab32ba241ed1e01da070c1f3ec85efd8387a7b9421453969ecba8cbdeeeaae6ddb098084bcd250601af780960c32f0a1ad7d7e61fb19f40dff1060c5f332830\nB = 1113f145de014bb6dd6ca05de159b97e9736c45bd3bbd8477f739daf79615fe329ce948cab9787838d7daf797218af5ba7925685ea341b802690bc9588ba3e916145cd3ae9d0c4a149637b890cf50fdfa8f89a62e508eec68f9332787733aacdd57ec1f359ff7fde76138d5b33d32e64cf7d252f2bcff14be3adb1afd8da9dc930f5261e6d715ac75752b29f083bb1de7b0b89ddba633b8137f3fd299a7f77abf79781a10d897e7bf2c958a097227\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 6e0160eaac8e1c31cd3cb6c5fb91ba086d033b4b69e41dfffce7569e61770f6629f23e12f0074c47c46653bbba94701ca798e1a242f7c4e25708d3acb5af6ea307b95cfa220f8879cb4cfff96b843d6eeed2b15c8f1bb21bb2b511cefbad0618d49d9ba33cade6da6ab3b846a6a24e35fb36d41201d3b85be831522b9bf509e0\nA = 14f4e24627c773527ed2243c0d1947395aba5c9cf95ae62a48827ffc1477614ad9c7aaea4b4fdd97e3272d3e220601565aebf87928c301656e9edb08d6e680de845615bb3a81c61ed043adb9d708ec1447f057087211673fa6ad8977166a2b4a8079a4f29d48e7fdd6875ccad05d2c219922b814589996cd9642ea2b798197407acd274da30d3ca008fefb40a25b38cb6042a581393283d6448cc69df9a5dc2b0777052566a8608a1010d7\nB = -b4188ebc5bf3ba31cf7c5e100e79806e92ff6f863c3d68a66aeb3ae8385f596dabe6f627f3812d0f2baea319d93ae00de41ab65e42eae7d396cc8fd0a2dfd35f303117fde4db5e8438df0c2b3b680dca538b42a7c844a9bf0d3697fc89ad0a73594627578dabdc214e0f4aa06b40987aed473e7f42d318bebf7392d9c898b4b8d73a94726aef65807b2ff746d4a9aa76303ed7b4fefbab34f5c87c2df82d20457f68289f7b96dbeab581294974e322c\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 8dd91f390c1f85f153f332de17e5de82979755d835398cdf3dbda1ee73c68f8e7565a964ae33fd5b1f1060572bb3af67eec79c4c3e2eb4de118d471f74351b80a5dcafc682bc3cfde642e611ac1d5bc2c49b308c30985b1161c4d78cf7621b503e2dfaceed886befc004f3a729b4a9bcbb8f13791d973bf38fb8101d6b7a4d4d\nA = -70e99398673324ee83495aa0aadfffd7bb9c94ee5251fff365124fabc50175d794fa84509f034c2b86d83607789338b0eebdbbf709a129a0ed0afd21c130d94b279c56f1c7c1eacfc6cd13f724a9352b2b37412242a47b23ec61ef0040a8855371aaf238003c45ab9d18a66cc7dab9653b93c323815e5404762d3f964d4654a6995af507bb2db2149eea59acd72af4d034217eaec0be5ba1d23890081a6a234e125572e3bcf68a6ea52d9437\nB = 661d8832671a4974b493e5d71e547cd46b36730f4017e50c5d1a7520fbb75f0314cbc2ac948744dd494d566ba580a2108106b120a797cfeb1fbfdefdab6bd6b2e073f90c77e814cafd0b7f79afeecd59778b1dfee3446fb32139b2311011576674f96f151f896b477c631237995e11e61e715dd8dd38e802af93124c66eee735c472972000cb4788b26752a630ba63b45e8ebbd979f0a4da5b359abd2905f0b7f3a21b1d381cd02ac08e284218ce41c907\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2b591d2c57f6a5484b43cd7ca247c48a1b38319e843257331c8807d499c7763de4eefed529e70d4c144e5e843ac00ee8d106d0d82163cfb7afe528a7daad8e7ed105942d1128a67e38d59325cffc0c3dab9185247e0082e3ccca82a900d917c9bd0f892d4b518a752f8e9d38eab2acaf3b3b59f15b0fe4cb9a3dabe6e0191493\nA = -1896f67485a740720e23e1642ef02742ce5f10a92e51af19e112cc99c0fbddb60d7190086c942d293d076b474d056e74ec9f0c42055d745a57ba370c51ab2b761d889b766cec909811e2b2fd11d6916b753ae00622f038a4bc55b813a5d06e6ac136e81689407de721ee852cd21ea989ea7c8cbd00b64614caf0974a62097b2eb865f46fdb0c1a2e4f2d839066b797e51392e5ebd14dd92630c070acb546dc7438631fef01594878643a4cf77f6\nB = -3a8e2f3b8378a2605f5affa21c4fadcc655f2f8357a3427d2cec0118e55fc2bbc25931259e294d91bde8dcbacd39e6cbc125683da7d0dcbbc67d7c5866f08e7c4732cd4384d9366868370ea40a75beb23b81306303da4a3e26ad357c5c743d0a4ae775a472afddf8f21cb4a1a3350bb6aa71037607c334a0c79468668d3e727cf1d0610e49f27780901c68aecf1d145953e45f5b090855be714cb39aba2efb0f7db2786b331dd9bb8843de8c73c95ab13b6b1\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2f53bdd643b5b22445e2af3667a93de52f8bc7bc151e196c0ab0bf3b4e4dc0e5dae9e507508711a9e3de52e2aeece6aff7fc8a1db65588de3272839390a35a847e29204d3b9b70e10352c88a10c86cd33e067fb530d20a3a5ffe67938c5a7a9218f1164f36a73324adef64da64d5fa5540d29a76a87ce010fb7d73a59b109280\nA = 75e31ab221c08b3bd73bed03f878bf7742f9b36a89bbfa7e90f9b05ec11edeb0140dcff6e9ad1d62cd7af34bb4284b3a52bf1b48a40f744b561d9ece056a9405ab15f508700b14914e4f427ea1df3093497410a0108066e9b259c1a26ea72082b3cf0e3a99ad054804da7bfa0200d93d65354b75e605b47a4e1e17ef851a37c59a95e1b5172801e6ecabf70f1e6e382740998fcfd8a297aaaba7d04b668e3d6eed40358247767323a8393ec359628\nB = 107aca18938a9cb244ad646a37a212859b3dda7518a5827aa2146b47bfb3bd08d772eb7a866e1f", + "674aab7a1c74cfdc2bc6e9ad1a365686213655b2c7b1977855bcd42ccecb804bc01d92bd7d2667069d853f18a0f0661f028955e39f71ee82b9ce6a81dfb2951b33b123e71264e819bba4d0a8c53a1d99964ad9ffb58b7cb5cfcd3e30b1baf5aa5b3cbd20a0df7ec37563e2b32b4cba91bbf3bb6fd1cbfb2fe0f84d720efdf36e9645c7e9ec70442ea5174528bb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 32d16f7ae2632b5cec2e90c34d191599acd9a1b5f97845595988c1d0d4ceb9acfafbc4aeee9924ce55e109ec88c57610fddc664316e0f9a5e3ed56ea447111c0383ecdf117ab42351b80e72720a4b1d98d4c73f5235507c5b4f7849d5e9b527d054858c0436ac3d2de2704c4bc25de4cc702f5880d5ae34094766938bee555c8\nA = 133a439cf006c753c132a8559ea13c64f598c5f8bd5043b89d04d7ecbf0ec58b225551c8df8dcb341198fb0b487774867e5b68f9058f58b3cc98168fbed0d0ffa86bf74b4fb0d4235976fa86d52b8dc7e82df176d70892954223cc484ae58b6a60459a9a0803ab856ff9699789172b163615e322e193bd758016f634c83cf50403e416ae241d9b1e44add17c2a663771ac88cf8b9dd94622d80d879ae41f0f4e7a1a32a1ab164f981900fc159aa85d82\nB = -fef33e21c07dc26a47d692c3094205bf4efae6af32f1c0f46ee579c1a22746a3663d66f2919f46f973fe558c61264157d531e66bb9ea10b4b49d9f6ad3ad8762a6ea8169a9cfe01d3dd65518c2e6e58e8c88d1b2f42d207399d7326752560cd45d0ff571309301683770793fe3765c1337d14021d39ea6980934c5fefadb93047ef07c807d0ea5625ae0cefd098988d6eb7af993c062ba313e23176e7abdebcc6e566304a5f9e03da05bc1cc58dfbbc898a67a5941\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 20877c7f53fca97f8e484ba31f23dcf51ac0f4fe4c5121eec576e043c6ec5492725f1b9f9ecfa64195f71909500a69fab2e591377cc2120bd5f60d3fb3812f9e80b2f6c787e0081c1439dbea76b819ab44bf6bffe87dffd771a870e4f5502609249c5260f91175fb217a9eece4166540be877d564049389306e0d6b313706297\nA = -534042b0811c9afca04d20d83898e7653f91a73de1e4b516f3228c6d6d9b963c7f8f4c36e05383da90f4edd072a7eda382c47b84b46b4dfa16f269c2d9ad0fc53ed2ce51cd31e4e32d0c1ee21604d3c7eed2deb35cf8df6fe1c0740a1515e4c702a2074ad6c0fcd403603b4a4e2195d19b265958ae854ccb0b41cf22480389a053f71544cf594f6833f3e4d91fd3d9091df0978d04d3922ed72a4fa3579c5fff50eee812dfb2a334148227a0f5739f8ac6\nB = 6935a3444434b0b03d27545721e253e4281884da027246e46ddefb01fa7cf7a9a030581dfe618431a68ef6d79b03b34f3ed598e7c8ac030e2b4cc887dd31664604fb8afe4e71fbc3135d6d3b4e596044d6b615de7184ebf8dae8fd58506286ae4d3b797aea911eb59ada39dac756d0e9eb6a6c767ab77b9348929a00f8e311f639d19ed88c86eb91f0d4cfddd34e98130eb520fcd2b77507c24b6804d3d65d1b21e6f6d55d1f6e92bba0544829687a096be79eaad7d88\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 24823628d4fe9540103ce5f611f8a6ccf18788120280179a40c2636f30a13e5076503e8a4b6b6ffca21da5b0f9f0d85feb2ce10b51292ed069f35289ebf5130972d720d20dfb8e6ee80c3ac598570d38e57ba33dbd75f1b03eab7847d865c3e8e471ccaf302461a6136dd13b8d31c9f163799a3c24c7284b8826608a9543816d\nA = -1d476cc98529efe5b926aba3160b261723b009e9b880bdea04e9b5b03f173040ffafd1627b38be8e00840e85d7acd3abbae2f7a60b305256b920c2b25a8a4373ebbf1a0c69f6e74792cb0d849872500519b6d1c190da30c572e26b44590b7ffdb464a900fc38db013feecf909b43bea549e05f1b7e70d6ad879c613293cf61f0cecdba1a6565eff1bfcdf740bf553ffd5bb7d74f7e9537897184c527b990dea20387bab0dec3e32727786bb14975b23ff09f8\nB = -2b6e12c87ad91a2fa878b9245875209cbfef400e637b557c868ccbd6e94dae65f1ef8caab61f292d739b139e384137a747210c09ee6f3b2ceb6dd212e14525852b8c54215191e116b7097f6729f6426a8bebdff86cdc16effa08d932ab512d7265cc0f57303aa5e6fd2afe0a45180557935c230558d02c3030b38ca88de5fc75c1240d25a22fe32c4e5096aad0078d50989812d7dd0cbb02c736fa563efd32d14109c44297cdb3d4fa3b93a2e15bbb6eb678e93e943979c2\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 2c4bc23d0b4b1f79141be9149ee20cc9f1b58ee0a76d5f4205e0862492c18daa20171285d6ff0b600c358be487e78cb5450d151efcff8d53004eece94c5a37f49a15fb2b5f62a79568382cf0a4232407b139e1ec5a9595bee8435b4f138dd72fdc2946b03817e49864812b7b61f179bdd8389791178a95bb6311df0a5c60db2\nA = 5b0a181f07068af6e1e4b715d92c1b8391949a1e3cf0fe0aa49f3333c826f5582615d39ec28b1367804c1ef54f15fb83b3c578ef3ae957fc89ef22a343175df3ef2fd425f724ec1c3363aa000ef624d64c6d678a4cbd90b41cf7d69a7e03dd60c5d3470dbb75228b34d35469847772ff3d74b1a89a2c492c082d3ddb45ba4df6e3f228de6c64913b79679cbbbc36a2924e722c2c640d0c5a0e90ae86b5364dfbfae80df3d75823aa58ac6c1da78e988a11831bf\nB = 19567bbcf615b777b35fa7030db7da18126cd695ca7dda67f5146c97beeb20df24ba0fda4a4f03523a0d9b9f85d9acbdb5793ecf9c1f4ceac81299a1aa34417779175a4bddc0e95ac68309da51e4f115dad6fec33a75d0c5520692a38df64e8d684c9304f9e2e6ac6a66d2e16a03c19a30efcac712aed2b9ee774ea28af4f37c45609464289de3f9be379c733d711875216bc223f2f468a0c9b4a8277bfe49c590ebce2e027102537bddbf2856c3b6e9389c4d1f5390cb0f346\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 36e1e0b44e5afc35d1e19e88e75f030569eb99d326721ced9bd7416ea7367a98305354eeafd204f1f8a652a8442eb0823d2e6644e6320933ac481a3709777381dce8a7c165b23aebf31b2ea2745ce5b352acdf0707234c824da9e1af98bbedf80e940fba00c229539f310838bd625f1fc103f267265ac1243855622c5df72c17\nA = 1dba8bd9d1e6cdc117a5a01b5046353084946fdddf2696f831a942d9db4637a5ee76b84d4ba63156b8cbc72e40559a2fe9b8e2682d8ba1db0cea042bb86f8ed71f6609df52526c42e7494f6114bb62263d36784dd55d396018b8fa47fa49ca6e5c76ebb0b00e6c764e36cb3ec75e3af6a2c14dee01fab78070239638521743d04f184dae79d49a2bf209ddeb4cc72e0c94a93a47c107f5369070ad95ffce034c554fe2a8391e67f817c6cab5b88ae9748072da5c9c\nB = -849602ea3b79b33af2bd3ef9d1250c507d332e759d428902dbee054fdbcdcdc0a357a51d00aaafdacd696a15a64cbbdb7e1fdb347be5ddb1f609a4390a6f29f79ccdb51bd1f0547d0d9a2780517f8753a906428fd236f8ee1b433e57f2810d0ad51846304a5729f53a871d8b0e14355d24d3f092e50de4f044e2b8aa14cd8a51fbb2ff36b0b37defa7be768c56fbd4f5169d9d4698fb9072cbb0a037c219552728587d7c35f27456c02020f5f9374b6c53bcf8eeaa14be51899d3\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 77eb3cb5277ced02b72368e41f04a35796c2c6cc1273f109336fdfa745aba7c755b6ff3833e9b124d9c78584f6bfda1c94273522f020371107870c288592b7c23964320729d2308bac8813586e72078119852e1d7706d8e15c195486b8d94358736869b15d59c037ba4dc8032ceaa31eac3a9e3dc51ee17706a6956cff8537b8\nA = -6a0753edddef8b74f762bf802d7fe9b38638923ee2d81bfdda354d40df4422e6ac43724de1715c4088da2e68b63c10c90b236d7dcab39b9a0ecbce57628f4c2950c79cc88a89daa20d7a8679232c8ce5fa30525c56011570107697222e0eaee6871adced52ba01a3aea0ccc9901cb3a09eb4db2f93aba0083180bb41f3f9eaae00fb458381213dad01997e9b88f21b0a79ada1ec3837ac2b63611455fab6839363b796b105c3be6106ff284544bda2a32352bbce6ef8\nB = 542c5fde65111ec8a38d76d8c5735cee17329dc41cfd0f13bf47e6d0e0093a129f3449db380ee9a70ec1e44640839ff18b950c8fd89346cb4701ef753e6ef49dfd9bd27d9987e572bf8e68df399cf945813582fa1d33e07be938a7729efd9a5e7d730bf61c537770a0727f6bb9ea6add5aac9267bf910eac1b7d92ab4184734ef8b1d184c292b2b4295ec1bfd17b8a2a2e4d315a8b37b8ff9bf6a1e94a4772267195c5a7ea6f0a0c267337fb97a023f1b50ad697ea31451192cebcbb\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = 660a1f378a23fc3b47f693a347d90640fef43add97", + "29d74546933f4b78a26968cc9a70ad6fe8d85bf28164881bf7a99e8b96683c6f4fb54162c144f99a27e3feb736f0d382d7e5b934cfa835c723191e5692b7672cf6918c4a7a93b24af00b1beaf1b80320b14cf2d1539e3376779872542406a5df961f765e59f3480e1cd40b\nA = -1cd74c052e62ee8156ba5d97f28aada75211979b1c5925ed015ea75f693a04c4dd0a705f6a723ae7b79958884c96fc07f81fca064ce2affc70768923bfbca6049952eea3ae048425b7c6ad1611ed4b8b77f7605629b9d198a77a27f25eff2f82867845cc868edee4ae31afc5d022b2ffbf43c14fa01bef8d7cd9d0e58362a0ff9abbf250e43ea5065512cd707791ea4868e95d8fd2357b3b3aec1a06888ae940751ceab01cf9e49015d42371fac30d48ef5853b6894ca83\nB = -2ac904d3632e25a4d536097d80a157791a6aca6eb10246ea21f4cae07aafe907c6e4c726694e14ce12e376c02d326f4bfc02ed539a5b4615a3cf5c838ffa52124f9b843598a3821cf9f1fe94e7206d6a525fad1ef77e7e77162e8c6d3d860d4f568e8f81153dc47f167860cd52c1ca59b15f1eaac6b9023c8b375bb63b6adf6972af8ca62b39f044378b11c4a969f3939d9fed5cbe18c06749956c7acbf963f640a1e1ceab73fc4c77463ee8d1575d018f49bf0f08161ce4f88aaab5a70\nM = 8e2ba940fc5165c6c5f7f4cb56a6fde2fab687651099c880d38f6eff2889f6a3b2a3a186d1fee05ed452d11ac712cfd30340d22da763af7b2ff65a3f6e202e8b4f42cf5652c625fd9913b4a032ea9448591b9a839b8c25d2323cc2d0d3bf7a6d15896aa85237b6ac4c9c9a854a23449e30e6a2b7c4a2aee199bc20d30af280fb\n\nModMul = cbbeda9c467ca801ec66fce801c6765a20148787dc6becb199a15c58fae8d20c1d391a1d9d57e1c74bb412e1b8f271dc2cc53c3355c83f3e2f00f15eaf0df735160a48e2273fd1bd75533cf94c5175ce67e79fa6c1422996fae36ba288a658a7a5422a59d39dd81ddea50979e933efc02\nA = 7ea551efeccda23622a1a5029e5525f46d5ccb83c28ec9adb7a3e97c2b7d936238c483a4a9bc92fe0e21208d5703611e2795b91fd5019272d255eeb\nB = 19bd92c534f56dc4235dfb7efff6d941112d66acf81b079382c86fb10dc5473bb8adebfa53ea3fe6e4df8412e7807aed029694ca786\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a9cd6a0a89578ea773fbfbf642e05935a995a38bbd54480ea3ecea1751370ef95ff5ad0e3203613f0ef6833237d549676a95b720848c5e9897cda82642a2f373951d5746b559bae2d98ac00fae26e5957c61ac1de95318b1b1aa6d5c64a6ceb6575f1b807060f9e2a241e378e6ebd72ade7d2df18d5353db7737caf52f888\nA = 13c68e450e9e091ae45863f6c1faed25906dcd90a43620b1a40e7a506e7a954256bab0225f3678e7ce6c4ba6e3a83c8f04a3491d9bf097adbd98fa6e78\nB = -ddef76382342178fa6636e62887fce6e19590065c766b047073329ea15fbba96f2cf088fa5a989f6ee3f6a513fbf66f621c6ea6ef2fe8\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a9cd6a0a89578ea772021f58ce74cbdd8c44a09b3937b198adbd8e95e8e35541eca26438351bfdcd8600b4f9b71616e1f16cee707c712d40da9a440681f8c8647bc90ba4c68b08ce4cbca458bebd5110222f06b2ca980a2e9419e71064324e8c36289eff9c67f6d5d011e6db8538a54aeff8c20800b0949fa42c38fbabfa1\nA = -6d7e88715e9854b435876fc9bb2d25218a1451efb73ad9cc5f52b2bee929530e6618a858000b3f24fa5f47b5f461c84eca971e38cda6e1f475f6612ec32f\nB = 49eb76e4614ac7b0ed3f534811a4ea6da5ea24be925ffeaa38bb228fa117ed56ae976b590d6c9d9a7a8546d8a6ebe4bba771d6587ac44f09\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 44f8596fc06afdb72a6e4f876b70b8d5d734589f41089c510b0da60ade642fd79cf8e705f09910912624fa1f646da596c137f124ec1a327beccba62a44f228f3c0977fda2af631e249b2a4de17d170df07bd812c233a96d17e1e93910267682d24c5c485f99aeeddceb658a7db258a2fdf73eb0266d26b92e\nA = -122231b14c249820f0dae625342415f0c6e7f93787b4206b79e9ecaeb09623636730810c7936e17a1eece68edc7c97218efb17c069bc59bdb9681a79c910c4a\nB = -3cdaed858523fd55553ef85d018c1097d7b88f6c30060d1e77b84821ca20b5625723c7d4331ccad1a70371eacc7f7aa11220f83f1bf3595650b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6de7efcfbc1e8d2cb14cbe4465c4ef71f0d1d7e80a1d80d9ac2d0b161d45fc9d915c54e33131591e8daeaa11ce02404c9b8494added1bd83e344ad4de7c04f626315caa56fcc5ca2ddd4e1ff064a2957afeb5d280477bf1f1195c7294d89049024fe821dceb53c7d270a8b4653e2fc0a4d8a3863a854bc3794753a\nA = 47423c4fec1eb6779fd23e3d4070d0a7bf9a946f5610eb469876797a39c58577242daef8c34926f6974089fc595508d9c573d0a275cbeaf37172f10b8c849a493\nB = 18ad789cf09e9ea182eaf43b28b4f2540e533f0fccad325430b73101c00e440bb64b70ce0f2680184aa8caea2f6f6517e9b80285fea8b61887a41e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b18a906994d3247bf8a00f20e4b349a500159d086aa863772e71a68f91af9d19e4c021843f8bb6eeed1df708d55047dc8faf219e00d559517632dbd1cbf4bda61651b9644481d052903be1970f04bb4ee8faab9adbbf858324e6cf5aa9384ceba655a1a107210a9497552ba8a56d5e0e70b0c757baa71d1613683707357827f0\nA = 122773509ee608cd9ab3ff6763629a18eae41be64bcfb05122e0b3e112db48c64d2a5a515d96a042850c1c848ae5fd5f0ccc57b273d25bd8d68568cb00bb17b1589c\nB = -af398208c01ec9700e332f3e694894c7cc412a73bde8a79e08764ded92f0d58db8056883972c79a0c9e0ce810786cdaa3629baeb9e5c370a5a59d3ba\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 64ef5e7063a1d95226569a27218e35e93d870a19a43fba9889a2ca98ca5c573fa56ebd77f1403b3bcad17c1351803a809c245a97bbe32b45e21768f28c5b11ad542f5e687a17f7811df6c8735e1778e94d9313c19fa32a6703af7ccbd88b489c96632d10eebb580cde3b905f6345a2a2b86a871b4fab36fa4b0dab9a6c1c5096\nA = -7dbdc37a51b601417efdda2516aba15827a40ffc304c523a47c544d5c0bba6c1367a20d8a6268a5c3f723b1b68de57eceabbb00d44185ec4ba7ecdce5d80456f8cfe7e\nB = 641cf85fcb5fbacd6214be4b7b06fda1b80f4683c21c1d08311f6e23a15434b42d30a51912898a1c46b46c00aef7ab7663ecba683897825a4b07d2b7dd7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 370f20360ac844bf4275f78b7fe71ba5db6f0bbabfbac3384c04b256eddaf04725d2d57b31afa48f047aade156c34441b4a41c0b2146790a2e15d13b584021ad55965588c6e55ed3b5cf5c36b780a27c5dfb72678d57528ab17ca2ac696aed3d9abb0ca448d9d5789fe37e632fa9709f3bb924c4ce34244d239a940dcddd9c77\nA = -1a0cc5b07271098a23f01b3c0d47cab8b294794b74a8b162ff3b313fcf85ea81fc99433cdf4450970311e1d5ff81e9ba27eb867073ed250aaa7795e44ba8d4000e879bf31\nB = -308f93984acb78c5dac2426d9bccc2e3ac361143807c7d34c24ef8f8db5e68a904ac8bfed1edf3cc90d21c87ae4d224b8c46fa42eea77797f94aa848160fef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4c8f466d1d9829aaca1a22fb6ca5bdba885606b9264933ac2b4c18e3afc0c406aa71ee7ff490fcaa804f457096e44576ff8096fb1d2b3c68450a8bc36d1a2797ab8b621ddc91d75e7d6ba01d86e959171fa428a5bb1f26766f94a553c94f6dcc2e0af90d7776ed3d9fb67e842e88f7d7342afd86e2f5d159db7304ae4d204a3f\nA = 57e894e37159cf3c161be9c97a946454e43bf09a7ae8e1437570a86c6b06f84005c1463d27d726afd2e25aebb1657eb78957a9a12c8749049d12007a81d766dbe008aad6d83\nB = 16dba5cf077403ff4af47438f5840f65fa4e058c5cab3cb730154ae0fcc982ea097c6d0e75bbd635e97314f33ec7e31f0e41cf285ecfafaf36382b33d5e83cd55\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee6", + "5c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 29d13ec304f26247a45ab6869720720fe019d6cf370b9e2df9a65828214aeb4f8b17969b8dd54339d08eb99bbc66720ed78ef79033fdce6da33501fa8588af86ec18be4c4ecfe01781f9d1379865100dbbc020b892e77027d1f04f8171ca51fb73129dd9a96568904eb44e19f56f842b223724a9ffe28826803185e4208f0ff0\nA = 135ebb133a0beb909101da896e3aad7e26ea72b23e60802e54cc6c58a07b1205e2ba1fef6eb86c420f011b70e3f725aaf9fd1873b6e1c1cc7005c7c09e55550414875cfe846357\nB = -e8cbf3feb7be7fd12b01d5bd024e47538f434b496613320ad71f48a8972f687992f97e4b69b5842d2d6a4176a5701327c40325e98b27e4c0f8fee5a457d92181e40\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4309b728306535bafa6787dd79e58324b3f86eb5409d772018cce2159f75832b87909a672b8b4b14342b352e76ec5a6dd66737cb0a20b81c5ce222133bfddfea878b132b6f9fd557133973a0b44aa41a01d54ab565d6b9c62da67378a4058255047a95923daf5f0f7adff2a3f06074ab1facd986d7d26cb475ee818199a390b6\nA = -7a63e108bc9790ab687e0fb8a1cbe1e9ff876e7b5eccfbc136ba05fed93412dbc2ffb1ec49518e9fb867429cea1d7f82e2b159b75bd40eb8370e8a54bf0e0ac0ff24aa3662774bae\nB = 51ee025b2ee8abf9dc5ebf1a4600131c00ae4b6bff966dae5c49ab5b9017e6b1abd6434736df6daabb2bde254022783764c94e66743dc752c9040563df7016a1581fe7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = b9ddcb9ab858d2229cbfab87d87236e8206cf5e1a042eb5ddde201d56e2695a3d0b2a42bda6a284fbd2a5b2c2b80446ce88c024137780c277ec80bfa6e9d15397cc5bac98e58c9130756ed0fde58d475a033fd94b1fe0ecc6fd91a8b42177abf3f77e87c0847a4244b9fd4980f3b42c7c955836bc994f2babfdf9c5b43315ca\nA = -1f971ee9a7c966d1e82166503681afc280fab255665b850645321f67da8934baba1226e9efb59e0ac4483c8724f63556a213f2224b993e4e082eefff0056f7aa8a3cf5b655e0f72ddd6\nB = -39309313b04bda1103ca6f56514026538b4a29ae258a2a66424abe2c652b959f5c1dc4755ea37ebbfe404839505c2807ebe069c9abb9150205fe35bc286ca12b64ac46133\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 47555924c31f040619681d4a12064790e981db2c7853efa17e4d20f741f33c56d80862caf86bfe0730870b6c0afa9caf66e15047e60256fec29469d1760d5e9b77d79a84fcf7a1dcd0168a59f870f1635eb033e0ae0ac17bdb73da803206d48cfc1da48507cb812bea540daa2393321ccb0d88b57abdbf3a3bb765692a2c2ebe\nA = 754d78d5608fe8c7ed8e26a174fa27833a24c48d23f0e702454b7eb578cb107da537dda11027dd6b41daad329e036794de562d7623bed8d9b0e909cb3fa38d4d21a95c5f4246e0b030a32\nB = 1839baa8b8fb6575832136f1d4632f72f36cdbbdcbd00f197fff3cdb88b851cbd74910ef6d43cfae9d3248e9c85662d7fb596ae45a460feaf308823f06345bc5fae8823230af\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9b2f026b11d0674e9ec060fdb24b45fceade3070db4405b363d53df1219a02a664882819fe602f430636fc0bda935b14c55c8a0bbcc9b6683417e3ffe7f5d58fae229122ac6e42e76899254295dc5a08ed43c79120a5e5e4124b8fa6048ee90836bd2de51bbd2c6b9b53212e913cde871f11bf32f91b3a78575a006da36627f0\nA = 11402b3b1a45d67cde9730062e38aafe1d04fb1f8bb1975f25cd9098813efa2727cb229adf9490267bd437220d9ffa05bb993e45d2f889f140faed3ac3c7b53216455a830d6edceb02e8db92\nB = -d8e011f18bde068badedce8106f6602429fbcac4766334a0101b57fe94603203a4a8975fa499d8a68198aefd9e68f28e68914f920eea1083e37c67d59476bca9819a8bd628b89c\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3a74066e7eebd9b63a1dd28548be60573c95f29816f3b3ceef68a5f6bb797d7eb0b0f4ee612dca794ff82f5d7461d995b9dcc09649e2587639ea017865328bb5deef17b5283691724e8aa331d75c635d5e19ebfd268fe5471714aaca8b48aeb846f241c1675e18d35f029b132f81128f19028b0a471b3f75a530321135e35fbc\nA = -6c5dca3fb7b85573d1c8899868940794e428171e207b5f9f89fce4b7159236c0755e2959d870754e902e9c40dc1fddeeff6364f898ec0dd669283e6d26a612d9af3c3ab04468707bb8a7827756\nB = 5446269bbeb613e69286f1012ff62ea767965533624542f3b5c866cfb569d6193aa603061701992cb4873ea8b766606da1b57d7b37cf52f52bf85b58309387200b0ed36164f30d52e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2a4e727ac67451ca9dcba648050a085196460e4aa4836c5652de863c3e2a76213e0f590de3aee8639304c54a9dcd5f7d5d3592f647e3d07d322708e1e26329f4a31d66c7f2e9d482f22cd9823074dd57d14040a4f00ac2af9677a2c98d58ee1e094b1a8c40092e77eae454638bc3655e77441d4f218c637f95c147776f5bdac1\nA = -19fa688008a12cae228c6ac4982ecbc88da248d7ec785bf2289dc9103bfa3a91eb1e5fd6afe9e0cc035d3312e9ba64028fa6a229db6d0eaf8af43d8c410be7c689c3e557137ebd60d3fa04edb60cf\nB = -3e8c87fba4a41c3a84874c987acee9f560b9f027338b584a775c1fcabb766700f758c4d451077a9427257334a569037b0bd006375f71223add62eca19b1e26b86dde0cc251e48d3b60ef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 52e4a3f6892b425b935c6f9d1396d2034eb0331cbc5241e1d745a9619fa0cf0fc521585cb9d6b1034c5fbbbbecdc81c757f768c7a82f6ca291cf5afc98500c579f82ccf0be233066730f738c205c3c188f94b878c11268871ba42a5d950dc8a399887997cef2b6b68badec1ca641b88d1455e6d97a2841da49df7eeb766b7be6\nA = 67df01e34a26e8239c8edc7ddfccc3850f39864ed237d4dd67588efbeaaed1f884105508f69e20ff6a5cfae1516f6179ae6fb515a66ef0a7d633ba4218c30875287ecd0cfeb5bafafc492619942f97a\nB = 19f5076405b3c81519c0863d0c963d545b2834343e42bb3c779788cbb46d89be3f775b62f4114268a0ca0e6af6c0dd659607d40071dfe7f1ad0df9a5c53b741c04612158de396e9c96f7523\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8ac1d96abd2cbcaa8f7e3267b716f675aebd23694d24c112d202653979636d4d47e27cc36f850355cfc5ca16b78cd1848944f8759fbf6b03fbb7eb347536a9328a5cbb778a6bcd983081374a3f543b1380add14a9468358009ec2baa7ecdf13e7260968eea74083459406e8889936b2fb98c8b9a3597e5f9ca10b76e1dd0337f\nA = 1c9ab23ea37f324544280d176cc02762db7a39935f1ede9695b53a3ee2db49d0485c6a3742a3b5cfb51f3c21711bf89ed05afd0886bbf61cbd57b23439a8a165484ee8e4c0e1c0ca2b6478776aa2897d87\nB = -e30d28dd01655b7a419d939e3e7530258a667420fc759bad585802c63fe5efbb309cb502babdad0afb208aff5ce5830071c5a974604c69ee47f76fd87e2460a5b03a57ef0185881502625886f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5df0700adbd880a5730d8c0637a362a9d42c64503c3b9784046b946c2459a619b5bf804a41c92ed6370bba730c7d39fb2e01558f7ec38511b0449d6e9db8df2cece4ed348782ff1582396ca8b3196474e7e5817f8c197c44d771923b6e286e41e7e23c33fcd8765e06793169999544a310f2e080ffe13640b85f21a18fa11928\nA = -5c01fc52e86f3a344180bac284d2376d1bd693f20a46479c77fa57077df62f83b1e81c94e577d1d6733d276f9cf70555b20e3afcb97534e4e0108a6cce87e9292d78b2d7367ff15fb33d2c3289d2a2913b58\nB = 6bbc39283be06382ea91ad6b1630b38f32385ec90019d2ded7ca6fdaa39defbe22585be0df9c0cf613f6f146c71f901adf525336f6573f7f43e661c44b7097f110d4551e8c75449da8fd39201ca0\nM = b18a9cd6a0a89578ea773fbfc0767c8ab81", + "7cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2a01005f1f387c4d8d24a365708e2506b044f86dfc011262d3577f7313a8f51ab943037361bed1858e021f8a46491a5c73284c666eb65cea1392a780219f13d7188721d7d4b975272293a5eef63480f30cc9618aa74bc51f4175246301a46fdbd34a6ec72d5974aa920be5f321a97b8f19c0ec56ba10eaf2e61f2b45f134b304\nA = -108bbd8824e8c16b81dfdd4dfee691e012e578cb9cc80cf050c0ec4cebf71a968732da36552979ffaccce6667e46c29144dab75132cb087681d5549dc5508f3719e129553fdc97f545d7ddb7d3a4fc575ea67c5\nB = -2ad4d4078c47a3c8f5f9b48e10d52d72349ecf0f54abc60bad63bbbf4d8efb185de90e5e1a686859e1c429e30977fca492aedbf084019e9ceb4490aa471776ed2e8a09151b37c5caed9ede66922b7ec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a1b1b2d33cb610f1b398e03f274ef39a583d13af14b79e6766859b9ca748237b481a3cfd5d490a073e82e3c53d3ff5cb6219b2b2f71927f27ab6f567547a22dd35fb5919e1ed2b6dfae4d536d6d44fa6216d94d26b33f52db06c4ecb29702588b73ebce87569639f786df4fcf569bb07d5379bf8b83743327248c2d71b5dec6a\nA = 5bc53b3895cff2bf7bf10e24fbdc43d17d277a982d5d92f17b9b5a2b9ed8b6104229292ef3997591e2e6a116fca21ad5d061ce438f33b7f7110293770f8313077152c7546cd522ef4054147edbe1878072b1043e6\nB = 1599b541c9809779df3ef40971e7a83f21564bd5d6596d51a3d96defa4dff41e83ca6247969a3dd9a746ab72ce21137f2d7ea015ac6b2ffa8a32997e8b821064d35afde3435b23e47cccafa74d5192535b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4fe8897417446c493725521c0ea5b2110f91a1b5ba236cbb6ff3f52b0036a49fc82274ca949ac2b592fa4bcc792114bf2f2a78a2cb44cb22c6fe7e4bee7981604de47f6da2ed1fc6a8eb32cd9b8aaca0f2feec76a2438126ae6f409645d897769a6d340308f82dbc6a98ac059fca6f903c5aecd668fa838b67300c654d4013e3\nA = 1717c6503d069103f10bb4b36427fbdd2371b30793e492e4161fe185b2e27469fef6a25566d6b46f6a7f97446315a22d1f1f662f912b17e71feb2c82411ed7eebb84d4f594deffee14934b75a845d83761f36141ecb7\nB = -8808f540521c20eefaa037fc5da782c891fdfc668b955eaa2e4edb592e027a964b4cfbc94c548d785d92992abe282d90dd137c4d76419926740ce138d567da7350d89f2e56772d8f5bcc9ca8d7076540fab3\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8b9311808bef497d8a5d14f7d851567a196a051610246964917a1f9d4f4449357d2411ba9fd93983f6edd76b8a8e1501146b08b6e1fcdd97b6a41cf637b6ff0cff7a2d6351aa1ded93f8fc1cedc81879eef751bebfbd1559d5d0320595c79e3eb1db0951d7c67c663bc57a672faed9e14c7da6be6b0c6bcab3d4d515e51a0b5d\nA = -511312fce1849c3d177d42088e55d534f9f7096282916e16b041f66ea90e2cccddab5cec0ba8ebf0b047ccce72da349f420cc28ab19bc156c1cccdcf5216f19ea922698127f090e97444751dd58fe7a2c90197a9ab3d35\nB = 6a5cab5e322d5f651f798aebf43a62af772fa2cc379905e72d253c49be8193a07ae6164f21cf08baff906ef800e361e1cdf1604f454483e10c8b2bfdcce77c12b0320dea63f9ac0afbb86115b656d0198aa883f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 665e16ba6cba87c646637a233ae04805a302ef4a10d79c5b65b146cbab8c9ccd491faa32937d0ee955dff7dd0ea3f79fa43c133021c8680490b91d9c1d8a8102ab709ada7508bd59042940b2bd3a4f8c195f781313e45fa8d3abda1f8e13b35811b638b2ab101d1caaa92188d2b75b2b10d596ab159583135b0d4d15fcd3d882\nA = -1375af024e9974cf8170801f4a709b4e5862ab7d18464077727bfc2581e557cada991e9484a1acf80182458158c44871e67e783f7573f214ee4ea1f1821a65068f2bbbed7575f03a4bba36b0fa8cb6dc58c73b100a6c4a6ce\nB = -2d64b6bd987d496a3c121e89f4b0c88b6ebc6e30fa9d47981b52862551f3b7251a3fc376db0f2d6daab6e6fc5ea8fa10b040d0dce334ee91d8cfa6db9648df907b199bb11b2b5c41c67d72b760c404b0451f70fccf\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 49e9709810d9f3fef159e5cb45211453e7a94878dfdece19af839b89c0e43b226d7cfd46859963c7ccc753350e74c2501131474e3b8e0edcda18583b0392ee15f1dedcb7144000fc7fa7eabcbc83d12983d2ade477b4687d75b723c1a98a951d21b2e8ed95735aaec77e00de288d16422fd259c665a08a34331cb99299ac11e2\nA = 4e550ba2fc2a44452f068860ce2a59230738a7a15f5de0aeb4d15bda8c61ee3003568dc5971e48343d402112d7a86860a7f08f5cdc0de21fb1aa064ee5df26fa23839b5ff6adaf64a4a18c07efb3582c2fc9612d2208fe99f8a\nB = 16f31365545772f276d8ac952506bf4033a884edf1ce583a63d8d9f6809e29d9cce3b3d227f839e6c09b459951465ab4570d2d36127c0f677fc0a63975801896f2fd17887ca16ff7f265e2e7adab1516ce56ee1ee9de1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 89ca20a3fa109a65b9449edcc729fe97ed45a9bd69eeb31d4a566ec1787b24cb7a2c25b3f89b36fef1cb3645b17c69ac8ae243cdba35e17f5738b35278478bcc391add0b5ec42db9ec1eeffa63a3ecd2ac0338db57cde9d2eb9ca4bb1df84f1a62245c4e585c4f20f26c98fa1957df34409a99a18bb442ac14f0bd309266a35a\nA = 1fd8a096be30e4435ce8cc604ded337a3d9d2fbc9666d1893c38546c4e155315b536d1bc323c1e7be162bb0fcd58440915b053ca0d0896e99265241f2afd46605a2a7486e1394a07b23f3382cd190e943e596c747b6529b04bdb13\nB = -a3960a51af5ecaaa70146ce55d639005e9b6b9b58592441d5876fa71470ade6d1e2cdde17bb80532551bee0dbbb71a0cb24dc8a129c1f6e28920055d87e9c66be27fc4b425737f36add7d72e39bc83aabee5534637e2e22\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 654d9c55d4a62976670a5ecac3a6165734a65f1edcc1ca81a8c444dbc98c3409ac8c4f6fbb92f122045fef8b7971a276c7dc4eaba21f7be7495394053d4f9bb14b63fc02c8a55ad8fa9bb9aa26aca5c47968ea1b7646ec606f53606d5529ded83639984683b8a020e8ded4b2d9f668ceadeaa8160245b36a819db14e58cf2bf1\nA = -67abdbc70db183b8c25b0664805ada269922556bf15aa80a47d31f215e216673b8d59edfa10a74f3f09d066055c3b9abd5434ce95eba91dd51576adcfbc7e2556df95fd6642a3b7e0486a635ed5699eb7fb285589c887c8659a2b7db\nB = 6ad3e854ea57aafb8980f1e99ab9cda24f183dbbc513e1fc92d4e239077816843f47927bac28e41d3f31c9ef134b72c09dcf14e2e9677a430d43002ae70c577d9958341243030fe58a800a068d6b01fd377e61844f0d434dfd\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 74bb23f7b0cde7924ee52e58bc0680f151e6898cc1bd4a2eaaa05faf218b419a19ebf85b0219f924a26002f9251b83506684af659e5b680e05138432ba227977f38a479ad9d1f3cf68a86ea214645fc4bd1a032f995307e9c9ee432e816fd852655ef20214e24522c17799ef41d1eebc6e097b9792757f7fc43124c609ef9696\nA = -19d3e6fd6de9092cbea55d65154208a0c93ae409c3ee35569cf774b8c8b7b1c9dfdd52e9f408e14ea3153073ed8d92746474e524a903a45a882fe46af92b033f2c41eacdd7e3c1ff661dcc5349ed6bd1aa845eb1762f27593708aa185c7\nB = -3d466d29e8c0008ee6f402551e3d62fe044787bc9f243db9252ea97da9bb75f5be416def97f13cbb008fee77f2eeda672bccce1f36fbcd26e1f1299619535da0a3fa3ffa0c6fee82a494efd7407cc770cf46ed1b8b143f42790a2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d4", + "8\n\nModMul = 197eaeb8221b431d5fed3d701a175abc146a9fedf8060e8e611a54f8da2fb27d2fee4539ddce1f3481e6a64435f09a2d5012540d6069900a332461471b22192fb87b63221c7822d3f2fcc35cc38feb6b3e49b5b0fceb52b0ccbdb4e1fd7b0f3eef3d582a6ae194c249ebc52f215b568712b3e50bb8e01c64b114955ebac2da48\nA = 7bd216d0acd4ee392258a7341cd56bfb0968492fe75da0c9d935713a6ac883525a4a520b5b7940b05e3f5e0c40372cb11b7ca193e93f0d3883fe5840e66346aff0f38829322bbc1f0a0e63ce5e528ba5b13596ad7ca19d20b2a7c9bea4214\nB = 1ed4805e53630b886cd733e5281f6d2699b3c79da615f4056120165cc63858ed2ddfcfd0af0c5fc54662aad90f26c55dcf70a30d04ce05bdf61028730b900587716e690dc0c6e02419622ab8c115078b92315e7c7a5ffe38c4a404a2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 40f69f2d1660eeb6e1840164817621dc95eb930875333bc3f62a644ca5910c1080505de0d54fc9fb6404a61bb2c03b3981e558abf9e86f2047c3928599b529ef3d91c7ccd13c1d69431fb9ea3f02b001427cf519d9fd8182219ad904f47b3785fa05ed24cb0ceafd537311633a2e26c27e61be92eefb28a49d7f583cb6e072c2\nA = 155fb75044fc54a6ba6c46972e2f97531861b8d6afbc358db456bac33a44bb0545deea2fc83023c08b7be473eb68accf5b65b3c5d6af88bc6d8ce722c80d5d1527e475905226b01ab9d7b5a6557250cf8be935339db330df2dff92f2e88e80da\nB = -8c6016966a2cdea4b2d8625aa367e1d079638870f1b61e6b3c3a1e6281ece41018d2ce93684d1f0088d021107fb595390664c11435c6c0a7b93c2c6895217a89c469a37d3250dfa457b928ba6119b5c9ca5f2d47b36e60e4325bcb4383\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9b9e6e1727326fea099eeb008a36539f3d47e3882b77d6089032b99c6cd36ad79fa75b7c19d1509b3ff022ef781b6a8c16fa6881f9ee2c4e00a4dbc93a49829622f4ce6ba9c55639656102d81167ab8a5e1fcf14d71caa60be732f1fbc71250256520c7c5a4579c3fdafc39356a2bbf2c7ecc526dacc0293c7578424c939ab6e\nA = -54cc11ea9806ef27911ba721f19e2ccb111045711d301863792f0cfac798758f0a29111e3a0f84d294a79721067f50858767abf507cc10ec9ea3eb27a91f06e7f6b7b4be7001b548cb7fb734166bad6739935081bdf6d35d58ef56180d377e5fda\nB = 7263e8b9a6f5387f44c55af64b64160efe97ec8a8159e723ca8977bc17c861e22041ea227c9c9bb467faaacfe352b03cc620eceecabb6db2db108b49c69752bd0cc61a5e998ac2f404ad052a51286ccbcfaa214ea8ec14cd9a2a6db56c3d9\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a18a7498ac9194f600cea3d66615595c27a3efa7ea196ba12a80b5f608f85fa72afc366d23f5ca98452dd190b8f86031a9dc097f94a217b29fa676a6042a3aed2355cc8e767d464a8adb888491c8cb82dbec8f117f57c4a07b41e7e6f6cbd7dc25418603b1d1d865dd2140a649c9d52019ef39dbb6809d1b28b3c1ae64fc6813\nA = -1b663403c73e4a9003467ed12766f16354f79073ce89b66066857d19f3b42791eb360004d23e02874254bc6db54662717739eced153944c4776f334576746c5c4145b21a23caa2b2a137498554c7b749efcaf3393c5457b2bb87ee2ca3bef5f191107\nB = -21d12aad97a5c6e639a2ea0a82b1292aebd418567718014465a22b9ac5c8c927963a2a4530c41d5a7a6c14805e56a7092c8716e4767b54a393d8552c5d3c366b39fb3b8667c60e6075e9293bc938e407c53afdd1174843b76aed187f56bb4be5\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1983576ed73d4d87d8b94cd3f70c149c0273e966176b85fbbbb7b3202e2c843bf1f8f4546ad7a4916ea4c731a22bd337b6177fcd2da8bd301f3af9bdcad800449b57986e7cbcbc7eb313d6512b2894c0cbb6cd753a870860a49d6a682c20b5e883b8c4839b3321aede51bfc42bca163a924191feaf05e196d8dcb7fdd9941a60\nA = 576759af0f02406e8dafa330babe9473d9d970bf371ceab30d2f98f4470f669e042e1708e2677d52cb9f99deb9b53f30727d16c389bb63e71e923475314b615762c7612269b5ad7bcb5108068bb5159cb8dbb8d08de2bd4fa4d9db6cf6e3f5997b9b416\nB = 1a4e34794747cf4aa626e964b839ac497b1357090ff63088f9fd4399312df894e41b395d17b8ca1806baec6115b1476912ca9c4309f00a46d5f7a52c8f640075422af06d6d6d796359132f4955072ce90e61b40c992a155b2bc31c262e753aa7d00\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3448648ff9f7425937b6faa54551ce14dd15566e5d41b2bdb1a8db62037459235a5b9546d289cc2295b0ed584fab2e1a798bc25a0c114238f61ad3381a5b441cb67f92cbf66007c980db3351adb9cfd2cfc769b5b9b0bd1701425ce1ee8d4b9f438ce1207fa850aaa1d3d1f970aef874c2b2499a150d29c2ceb7bac375009b77\nA = 1fb54cec882c274b98913e76342a9b8e631bf1d381fd8a4f7e0eaef475642ab3f5da70ca2e38741bd0182a959e5e985f1e0e7d737beb8c725c9b5ea22f7ec25b6e564809601e8405a5b1362e7792791f55ab64a57c03a99a8518d7f65feb0e21be619a6a95\nB = -8180d172d3afe00e0423245f47591d5f750f20d2cedd8ba6ab6f9aa24f74498a96c9001a0124c4f98dbd402b63e71eaa3a7af8b0d2fa417fb1d45f64e10030232b9155169153496aa202745a432e547002954eedda7cc9c1ca76811bd902b192f1a1d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ae0fd585408a99643271eef575285a6261a4c4a92c1956b1ab436d3cacc8d4cffc07044e57b357ffa43bfa9aaea57824319579c5c3e2fe4dd48bc818178beb5fc1ed60afa08828657d00bb88894c975378b1dfb452a5b88fc3c1d81099644a998a47a497c8a2b12c444fd2a088f47576b7f4fa40f34a208fbc3348ce33e59150\nA = -7dc7dfb753c0bc3ab4d07d5aa78664a7f57d64be4d4780ea81e3efc967fbf1bd1390248bbe259da32108ad96bd8b39f2c9f118bfdc96bd06147f812af831288bb687e4e1742dcd1dbf2b7adc41afa28d07dfb8df8bb2da5359e66330f5c65964096a96b31dd8\nB = 756f3e407a3ae698f103fa37759e90554f38378a9b8eb38581e0970ec8f9c00f8392612c61aca5fd37d1063b78c19e3109f35c0684ce523c634190b3164ef06959cc42e2b77e1bb2fd50eb59c3dccdb6090beb809ecb0ca30457a5c5948328eb218e219d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a2aa4550e855623a8ed488bb63db8fa4ac374c1ae953781aac590f78a364fc33380ca2806445fca5bb9ca2fc7ec4db5819dcd5769e3b746286c49a7c80149e7fe276d095929e2cac6ae57e8102f7d4c96261ca44cb6f1601f429528495b6c3169e15f9babc5be696074d45559d5abdac42393094c450d6a4a45bbf60ed7847da\nA = -16d0aea9c752b2e6e4e13f7ab1f0a2c1776874967b0dfeeef7e00f8d9edd1e11d2aa702be45fffc284c47811c51dcee184a134b8f6d1874026eb51e2ec80c94837af4602cac3efde556ebfff578fcc56c00de99a43638ab68387ec087ee269ca64233eb5b1762ae\nB = -3c6b60b0ce4b13a5d6d9ccd67c76ec6b71b94ea7205e408eea099c7ced2f3a462954741d353d0af850b10ffede8ce0bf80b6893288413674504829793d7ae0cba53b163e3f26cd99beb0a9ad540f6d2cd5097beac604b1694a9a2f4c48b28338f9d6a63e75b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a1a8fcb68c53846b3edae33ec070ef5cdcc1346ab3a98a116344e6d2810e2e3f60f0fe435fe7ff257c7ef4c122b3c34c776f4912a9621b6949308e2cfe2e0827536c7464371ce804bd7cac1d76c5bf8b4a6fd4ed56b65434c3fcf0ac7be543fe2d09ac01c564d7b9b463740dcdfa9068d4d8e33f29297ab452e6ec55c263de\nA = 7c4878334ccd9e20cb11a643b206626ea5d0b20973f18535cd8f0fc2f0325a67d3558e4cc9cceed0d88c6d2215c220b8d0ce230fd701502b02081e3f6548e58e02bc2e79e4991f8ef188a84b0a367758b4e534b72cd87de7f82a26de14fafd162a50b359574812cda\nB = 117d8b1d2a3e2049e6edbb9494c68a97145ac3e658aeaa05e8ecec4b090d5f467cde34e05fa7f5fbfa32f1d9dad70955f22130c358468eb371555fdf57a40e1df398c166a22a9df2e1f4e18590b00856b4f880f6629f1a4296056dc66a29b6f0f25490c6a8209b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600", + "540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2cd3de06953acb87b773b8bb28172b24adb283d6adada676f5f4548990827635c51506c85670767828dc5b4b91b45a7ab89a700d70bdba4e0355da32b52c173305767721d18dd2cb6c55f890611e7abc854277a453c7500efc4cd4fb8e6c9bb7a73fe5c77045e715fd35d415b3496f7463ec902cbdc18f9f6f67c33fd78c3210\nA = 1a20ad042f46330df937b879c72ef00dcf39fb85b59186b8e7a9d40723288677ff6ab2b9bce95f34f2de37887c8a9cdcaf231254bd00c7e25b6042695d7dfc05a11765120d1dbce29dc74f35aa1492ba0c5ee65114d9a246b57dcc2eb2ea4a310be98383fb934121db20\nB = -f8ec67323cff9d53499ceb3afd44b28f0538c39dae8c965ea27d645b430c2f8a4965eadc8ed864f2549eb636ec558419be71f986f4c5783d0dd5253738b876d9034735bd13b18fc670438387f84848308d9357ec2aa4f6a453bdd36ff08d54a6800bb41df416b17d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1aebe2bc35eb2e449bda63513b1bfb55988cc8e6ec8b3c8fed5ce4dcf53b95f1b438c41e3b2348412b35e1f734edba30273935b03d16efaede429960442a01849c352349e23b4af88de4d01e9ddb53ae900418d49a84b7fadd2669261a574557c4fbd782f8e8f400895f6a6c9679b72983ce01bcfdb641f5067c94694e9eb80\nA = -5f97994c39265b5389526e3847876a10aa3699e3c3762a127d1a9f892180cce68ca6139a6f71b235da26c287bd3e1aaa1436746d983c23c3105c33ed2e06baa1e880f1744d81a80b98ee1f16220940d721a92118a9b949d4da7d1477db8f5b357b3ceb7df34eb5f62078cf\nB = 4bb4f8f4f4c8e63238e8774ed61a7eeafb3fe9a6e19cffa648defe82f4846e3378c892d223957564fcce79596151658a726031a6921cdca0adf0f5325d858c048a6b94312ebfd19b803eefcb93bbfaaddef120ec3b8c366b6d978524d5c74218da77e4c3b5ebbc66cf8\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5d64678a32c163874d1c81824d628a1051bce3b55c37055acc47a8630d3fee648df5d319e50b4c56f465bbf696433409b89c07e442425d3018a059ec757d77b3a40d516ca3148010036b003721ec9c999665915a3c442d95ec3c01c232feb201be08c88fa3c6b0769e3da30f1d73b66f98e31f4306bf4e23de78e74743b224ab\nA = -178d81e419f0473c426e24428caf25d61b648bbf963f7fb753ae15e5ea3706b53b00bfc8fe917ac9fd6c7096518584566ff71e6d35197f9aa25107a235678cf9ff8ae1501c1d5a15d2a27d39d066e169745e1e8c808209bcede0d732423d0c9cfbea322ba3201ebefc5315c0d\nB = -27ed464895b65d9518923fde5caaac0c72aad0d1b38fcb7827d6ad4e0c8dc09e119b8b98183f0ef8d5d1133f3f108e951caee035bed0d48bbeee6d1ddbff5864bc192b84eb8a500cefd223972ed51c7f720d1736646825f95f2f10ce6ad47a267bdd8c80f65d644df158d7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 52dfb6bcbbc5cff46942d76ba45301cbff76e9b894703a6a7fd1af29d615336372d147c3932589affe5c6533f28d3e6a57ce2d3cd7448bbd81e09a13266ea31630cf044f654b87ec3fa3294eb65873964110fd42d86e78d128bead5f117cac98145051552cc3a86c193d738b973f866d068a8994a49df3fc7c7314fbd9805e80\nA = 797c67ebdc083f3c8b3ddf9847b7f3c2a39e35ce2119f746ec87fd5d86671d8fcf2b4f6d440c43e93f45019032e629879799eb58adea729d43d2e40ede6485143bd35979609a12faae7e4393879c40c0511c886c66a24454e4f9912bea944eaa417c9942f09ddfb227feb14e4b4\nB = 1a599d1cd0ab3614f50b71b93c999942bd3d4cbfe7900122d5083151c71d9e0c299bd927095c5c3291418424a7c12947389bd4e0a3c2fdf67b3f512094ec0ce5b52695e527de2b3804dca2edaeb1ea4b487911053272ea926cf2fb3386dc4b1dc268b808bbcf4eaedd21168ca\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 99bb9082e4537426c61f3b813f8c97675c44ba9ca418960ca6e2464cf61ad4eabb01ba00798463567ed3d829d3f14201c740f19fca623b1e9b57b534a65df0f070a2130489afae89b91003cee432fab11426c4d13b7721e6f9db1bbaf0adc0064b33e4b9f4b795511a0744b52f93e3db7bc9c0a991e4e122c463ff344fe14cba\nA = 187a8144a0045a92dcad94f0bae7285309ec8fac7dc864b08914e5a4dc3b1a6bb9212161a18c22682ace16a4bf3c03dbaef088b09844902a3255fd6adc0b7c6397dda86d6ab67204d8061c36ca20fd4bb348202037b249f6c110c31580148db46dc5b1bfffa38a683a27054c35326b\nB = -e93ff16817b725016279a32dac247961ae9bb00af890fb49c4fd8cf5e815cf98b58cfa1e3735095e6034c9a2f2b5d8030ab30e2271abb45b347d755cd9ab5ab5ce37950380cb306bbec42b6b8056793a0955bcaeb23e2d6a9548684030566eca2d34c458f224c8e337cb8e3c252\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 631f53d02c031f592b3dfaeed106160488c08e0672083ff195b22a2c0b006f11165a245acad6f35dfb15a871a9a2b45c544111f71f86c920b42fdb6551e56c55199e6173c00e27c9f47256349a80236bcfd3acd1730f823031ff9ef594725cb9429ea183a7fb2e03124ebdd98d435313e43819d995c4fe81fdd4ba718aeade94\nA = -72e20f1aa2b5f2c4218fb9e11ced3f45a218f4c83a2017d97d0cfbbf227c9082cd43f939c8909e52c8795cfaa75d80392d3649dd85ddc35bf1cc54ba389bed9e9dcf867da1c05eda080274beb6b868b54fc85e12ae127dcbfffeb043f9d59333d0ab3374c24971e1bc7269450b418c8b\nB = 61cb021a3a957703d14061c21d3b0fc19598e19a17df9d6f2418c76d4d37b3f62bd4037aeeb1eda37f83df44c440f5e49924cc72ec5b153856c6b621350ec89d98859d9d1ec7ac4f0c418c6599674322e7d618c5ca588d5a873d5af356d4771c6cd375f5dbbbc69f50b982b8c4d1ec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4654a62d9491f28599a976288cd2068d8e3228da12f645413a92f482efc66d1737495cd4a4c733f147eb5414a2ef6266a116ce264491a3463c9df1b030d83b315f76f3bef8cbccb5c538478a65092547b91e991e6be91ce4549c3a6e34aa7b466e63eb3b88054f6714083695c616a078ed54e1ae46e00f3593af845fcd0ff51a\nA = -1a342c154aad619e567fd32e7053aef8d98335a4fa0e35bf06acd7998c43d821de1076dc1fb67dfa1156d7ff30203ec736384a9aa7f5f08cfb302eb3a2a7179b2664094c2cc0df73fa05bf2af24a62b8e394fc76014dd83b434df26f8a67a624884a0b9b4f08f33e9828ae64f5d0c8cdc2b\nB = -2c57e15889c3dc9c94361c17585d506933a72fa954ce44dda9f5e33408552ebf49cae87bd0be35197f887fc6c7deca1452a4345eb67d19bd2e7d3dcf651667a8900388e4d5ec71e9433e3b01d2b3d91bb94d0fc3c51c70793f978e4b5ef93a9c6356c0b2f7accb9e4eb457a2174b50dc6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6124d9ce4de2880ae3811836235d6d89a1a4b710f1d5a517153ed7729dfb5b56b0ac10a4bbc811db9b26465f03cda355701f9f28c5257fe288743cc0789cc54a8661f46e36eec357580b00a84f1d4c8e3d689bbc18242f1cac30a87cb7a47ea06f80d7c5633cde4c8cd8a1a7e27acdc3a2aacd608cce9e2efe7864d41a56ceb8\nA = 7b48a9663d914e0225d7275e965d866ee6649d7267474d5336d28d54027ffe8572f4aa26230dc7abe9957d211e6c2c8f3185cae962b878cfdfaaf6cfe32058c299247f372ae170a1f7cf71380787f6e90995da9ca5a4be8ab1ddfa8e6e5dc65b6f168b9b8e29e0257e0eec853a6e1911b1afa\nB = 1fc4dc77f4a18d4406a4ba536e500aff68d133c6e7725717ae6537b527c6f40f93202a2292522fe7d04e0ef804d1a7013b04cd3d88462fba31534770b56d2e5672e8a6ec7a723186024c40b4717defd1433b9967bd692ef81d5d4e39ba10a3223d250ab6e71d5d253dd0a732ed386ad57e54\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6443de73e1c826c90aa36fd7ec5d0c3324c42058b1c35d3adeda1685470d363732d23cceb08c3f973034c24fe65506bd33dc45d7d617a53048dcc103d3d1b4fd0534586c2fb7489ff5ffb98303bb068", + "fc14b1bb6bb43f763dca2c891095e613bb7b6920163aa6cbce8cd93d9d39f4512b6e0b28d361ae11cf76037eab4cbc819\nA = 13f739846ed2c3aa0a1923168cbb46f4f0a2f3942ba57bfa5c426cb4d4b3d80d9530405a31bda329a1814c560d54defa3e03fc4f808606a598607783d539dbb1338d5bc0c2e272a7ff6ee6f93e1665d6f5a0ade30308fa047db086646c763106cb875e014e2c18ff8837e4d4d86861b85a5b7197\nB = -ba019333046f76325fa9f258006a7c10d27e89f6d482b95c79296c07a65b8e3bff4a9c9fa7e5d0038da129390ac851f8c0651dcf655a3d4164a731cd20a701895c12a906c732906038a8e459aaeb293fda21346964a6d53fa3e370ebf43c7ec8f66229405095c6a509d0fa15dcf45de8d0e901\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = d3a6fdf4a26993edd175de9a0f012e1eb15a5a1c4dd2741dfc6d0f9177cd5645508b8ab09c7fb34066ba893c38144c7f2ecadfc2b0d15728b407e5db4fcbbaf1871580426400433f14dceac43d28f03376e791b7ad01a112981f29ff4b66102305f0ecc4fd134c2cdc79a5e9d9f085bfcb7e6c187980e68b6c7639c12e8d200\nA = -464cb16fdd395e32fdc613c63ab4768f8cf72a5b74a0a5b0cc581ee4aad1972cd97db7966d3124e30c9a1c80d85c46da2d36eecd7c3bba5866f9eab4d0fa55b2d440a311654466432c681372a80a7896c9163c12314ac51f652aad68fd9012dc63fae6c7673c5da8faafcfa1b4ed5550f2baede5cc\nB = 40389ba4d2f5fc152308c9e8a8c36258c770fb2d03e6189b96c4f8dee97ccbe426cc14595c8482e9e22486b61fc570f0e7aeddad2f4e3a480d4b75d14294a3b912928da5692043bd98ab88ece87a9bbd973ec82f990c0ae6091245318c2810187d69c38fa80e835300ed06c0723fe475f3fb22de6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a0f9eff3a210912828fd7b5f2d72479cc9ccdcfd3e8d21739e301de02dd5c257c7ce4bee2def06c9d0c90d5a86bc45fa9f31e456d353775916b3d5684759e4500f99ca1f91f6767a5e2f4b735ae4b756d56c358a06447fa2c2ccf0ce667be4ed143e9e1dc627a561d92ae53a62477270a7944482cbf671138bd2a85fce92b08\nA = -1da555639228fc6ead68049d836d60a4927ee77472fa0ffd3c787d55b6067012560f5b1c2ef8bbf6119345dc6419444c675c1c9cd50602a93ba3718a5b3e1a30bc108d796998b24474cdad19bc2960b295fee97e03f2ca7589a3daf35bd28eb37a67b5d2cb35a30998d5f8622bd7e6b7d3fddd1ae9670\nB = -291fea1ae6dd1c66c62ae3a3d22904f4b4adb2a48cb795d50074095345d661a033f67b20c5d7231236dab871892deaa9458c235c342bc81457cca3f014a75f5124ff4da005dcc1108e75527528e5cc9c051a97fc6cd202bb9166f9e72e366bdd77c965a70592e5684fcaaf2e03421a2025ca190fe158\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 50f4d25875150bab63e4162265a632109d6b4743f9d6b55306858034732a4895ffb3720286acceff287c38320ee9945dcd0a1bbe5ae1456b7f36337cb7d22b679a6821a450765471257d52b6ab7d59a763e75e9e64581a93aa54761f6a760866d6baf186cdf4ad2b1a6af26a3e76cdc261d1f07b0a7122c8ffdef595812e7208\nA = 78a1609a7f08c93c9bf9090ca7c93459aef815719b5dde5f217567a9f68ceca05594f6ab17a4666ce1c0c4434e0f4f38ca1f33e501d6958a10da47211cc011da219d4373d2bec4b7c6477b1ab3b00b6c45279212db39bcc11d1e7ba49916c4271adca7eea531adad509ae119348f374ef1203c5af8bc019\nB = 152b46095d3f8db5e6e1a9e3f35c085da00e52764b261c3aa775ecfcd38572d2e86bab2f4bf29c2de4fd2fb6f35f66e8685714634e1be980773526bdbf9c43b1335c5d59f4dffe1a1fe2495ff9b7a3fae3e53e7c3208968e1ad1dd1dc8cf2e2415cc76dfe5df9e2e1eb63f7c7687d539706502d56247728\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5a3ad8d6f1b0763b77f5d40169ff0013de638b459e401f50f4cfb505565c8a4465e28ca1bf988071701dbf52ac456e01e170788ebd2b7cccb50dbfe1a65a89a8aee18b3c11986c9d6e6571f964f376f322e10a1ddd9310bbb40f14b0680385c40975aba43153970237c535c6b0e2cbf6bec918a8fa26cb2f69e98d77215c23a6\nA = 1d5c14b0b51cf31e9d97b7c49cd26097d40454978663f8a74095fcbf9c63e533708befb1a467f94cf599a41220ce13493a273fc30c49275412c5205db712d5e1832b39e65c150c3a4b251e2aab853e4ecb4f00ee5ce6982ef9215775a33565bde3ddbd932665aae506941d3ee31b3f9e4ffc0651f1fb4a5c6d\nB = -93cae5dd84584a2a3d88028d6d4cec4146cc5e350b4d92c52ba2393ab69fc1dba96e244f98e2f93f31230904169641aff30dfbdd3dc5fb1f3489d63aae1efd29335345a79ded546e42f2ee4a70ed932699fad17a771ba65fe6e689664bdd1135219aaa905c962d39531eba3e82c3425c24041e17858cbbcf2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 61211c706730a1b98c628b3c8cb070a42e2ccf9fc6302bb1c2960fb165087f210e9d93416ad9fa21634a05dd0723cc23b8d2a846ab7c3bc402999138433725e737102094db5792249b4b5b1514a416b80c804ecfb04653c5ab18b0a34d8777f6c2955ac66fef62c9ec2819f0e3c075920f951f86b32e02bc43239d9218580067\nA = -46c8c68f492d8f7ac7834f89bc76098146432c59b3301d4eb70d9861a6e24c7c9073f910108c7b35538a79de10640291b54e5755359baf47482b97af56475211573576e9412ee017dcf961a090a6ffb5cd995992ab68e3fe60b6186f7595bd9b8acf8695c4f7359cb2ac709f032fb993d16a74822b4935536453\nB = 46953f424d988fd20700ea08880e7e09ac22d60cfc294bd4aefe637408a3cacfcd0ea6822a679b68b665d6bebed3506d25edc83cc7154b83e22953f9d91157cebd219cd5177fede28c63a15710d0f92bd9e542a7586855bbe57a94c520408fc920b3f8d65b194af2b2a580c90db1cdb27ec26ba929de4573c6eb\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 50a063fff02f2cdc68edccc23976f4b3db99641073c85709626292b9475b9a988fb8509a6223f0a517dbae0cf7cd39dcf1e8ae75196d9f5008c661d8b5153cbdb9520c71068e4719820bffda4c393032edabacf99339e0cbafddb6042ef887b8c498e87e16b62417934015172e63e7457242b864a47aa10e203f47320f03c0e5\nA = -1740e8be7b4775725516d37ba643fc64203f3a61e6b0164d112af56666ad97afb0059c2c4981fa81d72264f8669db4e50e11865907655b1f669c88f5935cacf1b12c1db63cc84507af12cf0210f990994055d04d93f148f213e3d4fdcfe9dc42117c059897697914e3e3fa8fdbf0eebbbb9c3b9fdaa7efa0c9d5c93\nB = -226308f8fbb35b5f9d129c0f6a2bd3e5c272a408bf32020905acc6d02d7e506191e76a3a2ac47cf7a63e6306b256f489ca5cdf76c7c3eede175ee4a7acedf922955e92599647b69d463cc14f2b178b88cd471b8a1c1512caa66b6d5fd8840b98b8d070e6593136e98cce9643e006b714388768920a79944be36624f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 747cba0d1cde75dfcc0b2af9072c5027986b3e3917845870c73c452858ba21d6d1615eb71ae1b5a03ca44e22845d5432b368541b52a4bb02498668e8b99dfa2eb90ec1948d90564e6ebc388ee9816e329e1d8da0d3e2b12d901d47e22e8a1fabc37408be0f89e7a4ab0f30a03f7e2ed817006809e69c21104d0efe548165f64c\nA = 5fa76e37aaf0eb3d34d4f4c590e02b6c63fc62b1d4c9e172cb0dd82409df87ecb43a1680a2764f62d13a5e919db2db08feaf98d5cb92a859dd42bca1047ff57b8fe5974fb3ac11ba2c0d8e2203750f30650db4b2cbd31d07fe18c4df84a0dfdb30f9e528932c097e89d8f8be6ff029dd970a7d2c2551529455b9131e7\nB = 111199f91b3749f8cecfe90e9b9b6951472cb701beb39d63068c064cbb2a1e1d30736026f781836a52ad0d828be6c20303c6c0bd03ad664dbf6044a5bfb67fc20a049fd37c62ab0795d836487b883768ef7c8f427eb98e5ab6621fece77b4955822f8efd190c417ced398c221215b50e9532a869eceeb605fa1c936554\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 646cdb3ed472a7b4599f02329054846a8da173000eee7533240ade4dba82ee3d7a6a92baa3783c19dbd3f76fce6b5bdd83f1f229b1c71a6faa18602e368f1b0b9f8c62bd8c854844af85c2081924c9a153e27853b2a48147950fb614028e090e2198", + "e613631c95e565c2b9b64a43237fd4052089f9d1dd2c00525dd35fa946ca\nA = 1c8438247c0ca376f508ccef7933724df512f9e0877596f7f4ea73dcd824809bbc472749833b537eec01ab23656e9758da22ab8a4aaca1aab3fe8d2cffa6672ca0c44ac029c2ca6c3e71780c28c31b5f154c8dee782f6ba009a69d83b1a3a03a2d6275bb8bc3932a1170470fb7e405ae081f4770b535edf49f73a12ba589\nB = -e365c8edbca8dcc4cc11986a5a901e4ed0adbe89b0ab70a53aaf5821862432a1320cf1850b515177b630e12692cb025e3aa43e9acee0d8ad5e48bb15e9a3f34cbfd39d285127b52dde58751f572ae68ad98692899ab12d35e33652c4426ec60c5029e51f7e32ec3d2031032aa7b6b2b63f84fb0023c81d031773f3652cd6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7a3e22f4a3f7ae7512ed73a07abb5ce291bc90bad507a5ccc0c17185804b9d231b0ae2e72bf270dbd60170f34b240f716529a449abea0b3d98ea2890a4ce3d9e2214819aefd070e00201e9f271de925c4ba59651e55174c97a13a30197e46997c6c2b152548111aa98df120a617c54b71f8eb8b0c8b4dbd5251f5509fdb8a1a8\nA = -78a99d206b4f095847e9a21de273aa6c47034c9afd4c081a8e93c2d75f4ae5b090921ff5108c863785c413e2f7b4a361506fb66b7561b8b1c5cd537e90274bddaa4e91ce74ad81c6dfbfe1a34a631dbe455d74ed9d041a9183da3bc469bdb214d2ffe893f89c3ae30f8ab99c3aac4d2fe864b891fbf4f537745fddcc60504e\nB = 5c41274e9590c1ea44c113ce505931758f2cef80ba3b10440941ec9aa2ac984b29868bece2922eaa225555dde84a8334f1caede99091165151a39538e5b7390e81df757f521236314239c213e9b874e396a022f04629c09bfaf929a0e9fe0b0c7386b0541446f6a2570491067f64e662d8611c4fd6d1c78a9f3ae69f34d14fc\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7fd27b6549494c9bc860146a3e8ceee785ca03faa94b0ce0a964844e7871e813414cf3f111da49fed1ede5e71e5539f34173d41f9a17ed129016bb9b04c86487f5def9fe350fd4dffc67b6e181e3cb26378ea15ff9b9ebdf1fc86c072c82ecd8bcdc241301daf1b774af5f90f37e45e6126c5da7dd3753a1e5b366038af6ae31\nA = -1930548d105661dc25a5ee303b61b559c4bc1f2e28b2c40cf3e25f98dfe01a7dcca0f3dead6463b55a5b2e0440a651cc9e08e125535e081c742bb3b2f8955ae897909cfca683a4822896d8a4a7073c29a80571445c6a0d53d2efe4a30a79d2fb5d08c0f95b735a1cab17ba40d71b054c9270ba6bc870e58591fb1bf9dc9b7ee8f\nB = -3e2a4c1509494f94406e3843c9446edaf0a6060144637234c6d9ce84d70fac54ed163d77d210bf557bbea0404922c8aebec67a0475a3c7b74bfa2f226403ce987c705c712bb8eb0934c2b390a173c3836378fe71a6939e48d187b27cc7236ac115309fbeabd9ffd0396fb7fcd6d46a1dc683606c757ddc3212f5d2ff3f2e450fc7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2078bb5c82a394c30a287aedcfdc5271eb3246be05954181ae4f86ad2880ce674640ecd55c2ee3f4e89e2762139586516a28558481303e3071cc9ccb9a538f887553bf5726f3849fc41ab027fb1c680ce7dee3982587ec71b3760e5da6956d6894ad8c4526d8de953c0e681ecd44883a21f0abef1544fe601743efd3e5eadb8e\nA = 40b4ba1e977825b7accb941fe0c0a49936a8a47429dfff53502fc0680d705b9fa0efe003eea3ff0b649998fdbae8d0831bea7f34159aa4c7add6bc7cd56fea97d25fb9a6a10f4572c26d792b76c18ada19b0ba06b6142c420dbb40d66be669b7c51d8cd2a5022fe1a8aef7b60965c0176eee69c32ca5023782c5410adc1b15dbdc7\nB = 1bb2f18d7c8d306bf80ae1901115c8dc3d286baf537b812ce06d6872b61e5bd44f3c53d7f31ca8461b3628b255f85338cc325856fda5a6248b7c476532c1bcdf9713dff9932a50e52a9441aff96092d3fb0fd76046a8d88288d0cd55741083a1bdb20fc6e9c20e82490273354bd826bfe001322dde9a15763f2c0e6ffd2cf60019aea\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ef21dcee9eadceaeab13287d6e3c9741811f6ea9d5bd111799ae05260b1de2ffbc192818fa45dd7befc3baf6840e3b9d24cecbcb2cb1c3d653c4aec6531b941d926fb6692f548cf81526acd0b6b0289d70dd11ba50ca8de6e174f502eddf47e57440142c7f74f594a9abcb48ce1873df057b132ccce8b364de3edf411089d28\nA = 19d0109e0c47ad45f57b8bb8519265a4390534d2ea07f969d84ad33556518b6234d40d1631be3c3cce6d59b7be14750aed114008458f50a6a84ff75b4ee7e4b826ddcb2d2293842ed29e4e484260a92199c5c66367c402bdff0f1a8057127c6ffe452498bb352802e0005e6cb084663bcfa82783a3d72f3a2a341b8075983892e86756\nB = -81fce71491eda139ed996f6a289dde8635a3a257ad6756e844c768e66746011fd797658184fb44b0e3f3c5600c56238ac7687b5be42529d5c9b97c3ce10f3219e1e451bb2dfbbb44cae0828ef894eff3b52b8dba4c115c3b471984441045f2c2db426cf5f86949d5bb7662cd40bb3b3172a19ca3fb6858315d688f13c17550e700cd5dc\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8a5f90344071790373044193cc4fd92116248aacf05ce639b6aac4461ec3ccb0805ff9876ef44fa71088c295db14fc820f7ae2c0aeeffca055f8f7238c6c90db706d02f2cc43b4960abe3ca4b6dec8bba55327b958e75c60c5d1f43fcf9136f12481c267481a725eecc403a16aa6221346df680560ff316a63ec8b51dc37aad6\nA = -7a54e7ca04b9a22e2b986e72e634317ffa20f6f4ee90353d559db3f3c1bc6b3b92ac6b364f6c5929090373962b49b59cb5d87554387761164982955470cb45dd00c4a8982dbaae3a1ffe700e8903a4a8e4a21eff9d00fa496d475e0e1a205be267499dacecd31551f8a9d437f37dacfdf5a2754f0876a3e02509b78674e7ea2169c43f29\nB = 652001f073d63ddd526abc957bbb48ca74154c8f9698b988178b3313dcde9acbb19ea11a935184fcbcc31e0117d8d2ec695ac56b5a71614a12cf90f21c8882187428755b6a5f11c314ac8b952ced0f65db0987f0f87e20b82a811599f4160e65c7418af7f33604e7b8952b70581e3e02dafa025cecda970d04383ee552abc620dfb9c5df9a\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 67f903e0e5623258826b681506f3e94cc0b086e262bafaa1395294aefc9f6b6323410a44427010d5e8d8288993973ad9939199b85cf02ae0a09dfb69801536a3fa6af5ac373add7efd25ba5fee6d8f040e97056f9f6fbb45795c0bac94c51ffeaf496710b00bc9ddd8e445261d976168771060c9bd9d83838a84ee9428f59d6f\nA = -19c695ee3a4ada840a7e3626e61047c5081867b15843ee9a6506ce45540d23ad25ff23b72f988bf26ab8b98363d9a2997773604f43fa732f59a4b16ddf3a45acdbc7976a1fce01b3dd55559c20acfbb7501730f794bc45fc09b1f035d60413bbcf32a83fd3c41599049a674f165ac5283c42aef213d777ae47eea960f7727f5758146efe5bf\nB = -210697d47beb73f45207340a183a729a1e78d84bdde1c7d8f80bc84559c4aa4572ab0e6927ea175acc7a268d05616201cb235e610d1012500c8ba9351a37bd68b4ec42227bea55cef5ba7d12ffb180873ab9d33d09e6e969df99fca728dc12dda6903169acbad38388fa9b001edb09056a2ee2aecfab0468822bca14a4bcdd3a4122290ec5ce1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5fbaff0ffcfb2330283fe59611ef51cf045bc2690e31f2ad3265046fedaa990b5d5060b3c38f17bbe8b2696e527fd77ead8650d329c2e0c1f3b2f5bec4dd85641022f3e0ae6f66ce98cde1a785bb52eca796ae45c33142e8264621ab447cafe988de926544e1a7036710128c42fe8b574f7ad69d830894237d95a55d1bc7f5ec\nA = 482db04e35f9fc1d87b42bc5efe25a049ed924f816e1b0f9c8ebe34bc771e67e26d6057563fd5d5320681e1207c0b0f4b7df547cd6d5be6a2e0f2bfb088f990b0303d0ef263cf45681e0e9a1147c29f2ca5251faa633ca53f6e0b109ba69bbe20c58a76a22789243d1acf128dcc936602e832a20a2bfbfedf963bc1027650f483814d7f5e6905\nB = 105aaf563d4c1d436c6a4552770a527776f40bbb844b7701313c5ada95180160e7cd4b7175ddb943e5a22c910585dfc184b52935f06b12c84b6431395f28af2eb9ccfa66b2ee8f40fd44d753c6a83d67a6f3fe3658fecc7fb2f4a8f357c5d244422e48a33d0e2971059695a59d0d39b235d5194e919facbae7623ffc92d771532b6b0cf771912c24\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0", + "a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a9d204c1a497f350fa1300cbaf682c947eaeba8b3aa0450c1db9120852a2edd2a0249dedef3b3746298ee42834d869e9f765ce987a2aa4712a1f35ed10d0f7ba9cdef938b073c3a526e5bf45f3510c94ff1fb84bc77b08e2aa50f5cc75e2f4da37a8a711f8aed5e92f7e486877229cb4ff2a4d0755029972323c0b51a14fd1e5\nA = 13fd3d7cc9d6d6821d2f2b1c40c8e070bfa85b994ee8f3e0baab544dc71328a1a57b7ee57392ab6d24bd85f9ea0f2a312148fc4f4b22c589e9a265d97e73c7a5b420bee180409ec179c438a67abf37eba61ac76197f3c9ea5edf2d4b8aab91e9bb1a432ef1f214c043664a51ceed1f2854880dd458ca253f09d6f6acafafec310774a672d07147b1\nB = -8c90ecd56d6c7cb129d1c9c26e94cf919c5747450542cab52281d11d8fbfcf9ea797b29588340d146cc40e77dce007b68c0c24356d4b75513b75eccbef6e22a5b88417cb6c516578d17d871e7d0957c09795f9a0f19b811db75d61c27e1827fa2773846857fec020f98444e307d3e52af501114b962ea705cb0cdf815109054abd00810dcc270d7bd3\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 57aef35a3f5388c810f576dbc30d4e4e5a39248b319b7766311157179d8bc1d7ef019cdd8c2c0175a8424abe7b33565afc0128724fa38f0900140b6f96bda2e78d7c803124cec8c2f2d6649afde4030c76cd33394fb386342d1ce97a4ecd180872134fd4e22667a687915bb4fda21f7e0bc9100ed8cd3a6668ed3a235d7b15a8\nA = -673bb11795d9d20a1e4ce8ae71d041705990463964505befce5949f895fa31c92d53f91fbc110df4e789b3f3f01f184c55df92927b8b680cc92864466ce5590ed2e98901cfb78b32ea79bf68b57a14cddb53209e08a7f430fee23f4a1475fd2640a515f8b609e98c760b4301747ecb61f1e6209b07455f1c8a7bb4e20c269e17937f39c6a2fb7b2990\nB = 46beea6005cf96a2acb16f37e357bc8975f4dad502fc3aefb4666344dde456c0ee7ea43ec493b6aecbc7aecc7d4cd107aa09e874ff564f5d59d7e12047b048c1da1faea36a7e2d02d0567bc4db41b54a75110626d13597db698fffd577a5810286ea8bf50625296ee8070419345fa269a354ca2eb47fa3108387f6a4b2c0ea3e779908a14469106eefc14\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5cdb7c451b2950c9d87638857407276959142958b06241b2010a9f93625f9106f065798f79ce5c534b9e5a31fbcbfc63cd200fc1cf10217096aa0194acb9043ccf7ced30d9f0bf66e0dfe27ee2ecc40bcd8de66fe2ed6f8cb0d874ff7b5fe71951412731fe4e19c34bee64c9312577b9e7b2ac08ed15aea753a6cd3e286192ec\nA = -1eee9d5d3854db52f9b43698e05d6a0f1d1f8df5f32884a775b25110309c46ec5c7e112eb64b2d7f948868bb9670068779b0a78bfc7e17860ee02692ec6790222b4384b9bd7db5abf29c46261c10d95f503b821a4694c45553e0dbaaa977892b916cb8990ac9ec29ab5c3d63ed77138fa1e95f395b3b233d039ab5daecb0296203166e9386d1071c61cb1\nB = -34587c2bf3473a2c5d7f3399d5ba2bb09be8105a0b9f3d8737d67b03d8b91b1c869f4e223d6246abd36d99d84052ae5894e58288a614a0da8d69f1aa57428632c2b059ba99315ea2f68ee210e65a741e94125ee4a723a7828bcc410aa2dae06ea8ed6cd23f66ccca7e85d2e071055787f230ee405e50d1519377cfe0cab4e5f97b6cb893b01134813a7c2c6c\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 95d0b209654de56bd7d6f74afaabed2cbb3247f449d80511d2d3c689f84c9b79587d78abdf0eb37f1b89f1f8dc8a83f7f9fac2c8cda1fd3fd64e16f5597b7f0a1df6da6db9e828ce7be0e876012bd52f5a74ca73ff8ca4611dd9f342bf77b485305ac28a1f8ac7538169f2bf3e4ff4dc5fdb9dedb97fa743fd8ac8791b8e288a\nA = 7821d4b65d529c30b8747e184e450cefb11b5ac5dc77905e6fcd3df64336661c82ea68d588ba616d23df485ff0658fb3376d5276027a40b392f47219edc5ecbf510cf0c5b431b02c65e5f432092f941d32ac5f71ce3496e403c7637f63a23b91e3326d01d2d32e99e0ab265108dc5e7919d3983839b3c7541848dbcd420a594e850e587f1846951852ed76d\nB = 1adf5c428f2a95c27a943637758d5dcd7ca36592fcb9d52ac0b7d27adddad5804e3edef257aa51c716801ad0c731e13c5dd000f11b5ff1b69c198f236695c1b2f99c0afffb5d084f80fdc534de3b0df4597404b50c7e784c3c55dfc9753c414d145eb0ca4d07e2f65b63f3eef8d391250a5500ef64d9bf963d7250d6906694e7670f92e3d5a7930f0f85964a21a\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 46914b197b84fa99addeaf55dd803182083a7ae34d6d4d3a55d6272af40a600563cc8d9f6b48110d0521b8b99751235bd5a340b1743497ef1cc459dccf5d6da970c4c3103c978ad2d513298f1fb3e68b24a9c7b0795f47d8f7f6ca9caaab9a9d80f15982599d764f8738217f9158517806fded5f3552fef8b7dcd2e725ee04d5\nA = 1c9f5f2a0d72806dcca92dac1450a50cba05b5dd571c2b3b988d33528d90ecc83444e3ea8df80802c30fbd5a6ec2ad9969be73aba6dd27e0dd2c842b95371d7547768916c0cb036964d041284cd323c8073095b2a8cb8797add5cd80f03595de9d18af8df7dee0d250ea7048faa47ae0131ba3f350d82864dc95e5829b88eeaf2681433dd4d58b2c6f70426af3\nB = -aa1e1b3cfd5ca0facc75e46d872584d55144620f849ab05931210b4e1526f12679bbd9cf00efdbd8863970e2abe8fc9fa7bbd21afa9e364e3c9e32f51fe66844fea4bab7f3b1bd278fd803f6bdbd0d296321e67751a0b894da338ab431871adf1514269ba05e0cea5558cd5691920fbc18237914f3dbe4b253f774e5dc1dc57023c080a3b90a004b809d237658ca1\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = ada55d93c533716ebd8c16e23603071950aa714deb942ebbf77206753d2676a7aaf61673c03a4db69d67faf6273828594d85e3c8cbf38460fa2af603fe9c1b6ce104854e7281757b26589f079da80685aec153fc5fd1a223004cdf30247f8398b8e92899857dd199d5d5c32412bedbf9d55f20e52895fc1dbd04c84cabfe1264\nA = -7d22392a8da1966e6cc5ef50d7409c614f8c8f8e5791778f68a00b4a056d0002707933043d05e48347bbd4d0dc1b6ca32a1aa4bab9992e7e620263283eb68d97af13b90a29c1b7dce39ec0b8a63878e8d65aebfb3bff4e67129e3b3725f999f1ec9ae92007911f2cdf738499661c5b6c9bf27712d0f29e871b17318e95c3d14b2e472cf9e466bea91fb71a493b2d\nB = 40279eefe59f954aa8c51c9c214fa07707b1d095f697ca40edb820401a45c472d1d7bb413eeddb64c14ce6144b4863fe9337ae4ae8698db92facacd6a56f3b33129c5b608eafa29e9d92dea620113051b926b80b75f320d7ca3d2ab597168c68774e68c47670458f5ef2ffd4604f20bffcc7817eb09c9057fd9989a6786a7e067ebe6724a89e7d1580f94ee4ed502cd4\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4dcae9def5467526b0ff071003e56f5537852cc0bde9d86eaed2c15e36e6429c68c061e12d321bad12e29626b5013c28f118ee59624ae2f35d2c53bfd89e6afdb6db79f0321ad5c55cab03e6a1a97ff7bd58c760d0e9fd7507de987ed2f94f9c79569fe7f03652cd53c67ebc6bd3c9e6c5672891a9d2ee11b300ed3b19753c0f\nA = -127f5ca6924851faa2340c4c8f425b1dcf41b313c5c2910e5eff8ef2faaeaa43305de2b3a65a75fe54c00fb30c0ce3e8007db1ea222521190ff1de6d0cf2e777ed61ce8211dc167bf115a77890d0bd1ca786e967a04f077c89939ce484bbb1c560f669aacf7756a4338d97cbd7f09a376d2dfd4d632bb451f52c03c05762f050ebbf112f8dc5acdd9b631292fd7073b\nB = -3bc5e9c352c46449a9155b7ce5478c771293599cd2dda58a962010f1f21d094aa6bee03f9311545e8dc6213f6aa73c08b55bcdf4d1d84fecb9eda35c83eae5fedee75b2d15a003f8a82b2b788ea19f7460fdd8f447d973c950b3b250a3022c19ff312ccdc86b6ab50c4ba627b15968c8a66d306bbdae8e88fe28c1853fdfb3fde92353f46b5bc448ae42306a4c91202f03d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 62a812e35f46e04b3afa7d26c8fd4eb168b6b64cdc839ebd0a46bf2a3a712af8e97380cdf0bfa8a274f7b73e887bb4cc73c6104a176d425aaf5352f14ee51ba549a6926bd8d059b8e3826b174385d4635b0c36df75a4e7da44c34e51eb82322b34ae00e8c712eb75b3882822bce5a2f2f5fd74355319ebe1973284c690bed2af\nA = 71c57b08127a956f0c17fd3c639bd1923ba19bfdb83c0cb9dd78e62b8fe4b7e0019cd0a6b73a334c622118f96fd6d91c1e06d4dcef8a3d0d6bf8f5beb", + "6389226c50d14d3947ce9f24f7e0e6a7befad2e4e92dc9ed8fbb9811d908c03ac074b2a5c67b67831a350c4d548ac70810bb5617d261a045e53cdc48117b9fe86d35950d0a181b73c8cfd35edd31af031178523b\nB = 1cda2a51a707f8c4d2cbff6337c3f63519705614c26a489b545b1faf366b705af1d953701b568a684856fd3186c035f878788f7e5dbea16b5e7b6e767cf611452a4272abf2a9c5e72b7251a1ebea5098c60cc5bf649cb70980b97d48580967ffe2913309b6b78cc12d91025ae403928851902dcdaaa60f5b323a1302a5ce114cbe174e3eb3c2fb5eafc44076396c23d53b028d\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a9213cd809d41b6bbfc2123bb84860788ce22d5b91f8e24fb616efc286a218ae9652b42912a58bf8ce596a1b48e4c72f27e52c36be1940f7d2138eb895ee36bbb917a59f73e0b6c3266bf4759ffe2ffaee3f6179492658e0778bb43c4df4bfa1a46300c9da496033142ae2c1e33333fd7e82c5a14686b255e224c51aecc2a590\nA = 1cf4e2d5924510a5fd06ff4eeb94a740e430613277149993004b8de1a2b96ada54b05365f305e896df5fdffd3d7bcb54f9a9dba9689e5ad498012f7a684d083c31d7017aaaee720bbd42382e526a35d2add21d9369f7faa41dbcfe3dae426948a402635771a977e19d5c353ec7c1abd279975f2effc0b7bc19990154b723f2f8c29e606581ab9d3966702f68d8bb8065e9d8\nB = -cdab60f9b8e1add4c54427b638ec5f76b30654d3649b500f833b2943bf6cd5d8647549657a8ff999eaffe413ed87e06267b97bfc1b77637b57f29039235548a7569fe6d4bb16ae9c6cfd38c0b8c73aa60797d0d69b03d5a98314f7f7ee25df8b896ecdfc782cf8057f038b6c3e79c99df52f839fd4eff302ddd1256e51eb31cee24585782a0439da3db2eee79a58f889d8847fe2\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4dde3d63aeeee47441a7e733bcccbd4f2e495ca3c746468e9855177f7672d5d82e51da8e268ac24e8971d802e25d842a16a6b8d76b8e46a7724108c02d38a4830453408ca5ced7093676a1db4bf4c94b9b7a9531ab7c26f8de520bafe4431a55a5f5d8c7576427a0f5bf2081b998b82da2e8e959f2ec4d5141b55e40bf6ddeef\nA = -5770ea0a75ff451fc2c86d428f2569884b2c88cb6d9d407cc22b191849d389f57a5765b83adcea21c350b37bc6d750d4859f547da22ea8a3698a5cb6154b946331ae2ca18e7eaace951dcd49405bf8d8a716f7762eb242b8bf5e4c53a662c906c3be89e53ddf7a706ee2406c7d0ac17b54ff259c1bd5a092325938832763ac4caf0232e80a016cd1994441808d8db7e546de3f\nB = 7e4246ad4af268695a51912053ab6628969af4fcaf7f1e97dd977984a1604e8c9fe6b920f39a764c27d89f75986a4bbc122f92ccd1860f24677cf346474fd9441f572f769daf834e6a00cbc027e15d6aa7ec2030becad41e1068740cde82abed768de7e2cfd325848f6063e2186faa76982b9ca73ef22434a28bd2e3a5ac477af50f258140bff938d3fa02fb904a8ee0ef3c1f6fed7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 3d8bde8d0625fc46dec46fc657c49c8ab12a988cec4ec1c24e6f4d8ff94514c8d8fee4a08399c6bd23fb6464a38bb5f249591456c283325e343cc289c85df0ff2c1707a6e407ff7a24383b66ab603b75e2dc3835ffe9274eafea148f20764b8ca30cbe483c1cefd51f82dfb93d7793b3ec19a57f2ba03d884f345bcc3188fe28\nA = -1680dd51d8be6069c86ae157922d55df3b58ee6f53738677bcf7332d6e7ef304ecc7ff7c5a5e1f525459d77202f3e815c68f17f9a6bf358654a92f9f9acb252ed8e9e6a849da7491f26d0e33900541ab67ce966d042607258b4382b8108729a703b429babc34496528f198a7e0f814db80fad4900fbccdfb64908febf5e09805d3a3049c0f164f0bcdaaa9bbb06df8f05309be83c\nB = -2c6c6b3c89f6e1d1cdd9abd1a9706e4f642a25738aebbc97cbd60e1f4ad79b419dd54bd14f2bd147b1d8e9bfcf92faccee61a43dbd1a2c084bf06a2ca476b3d169fa2c99794fc827b7f4dd010c0534e7cdd03d00456033ae0203b78a7ed229afcec2d1cb96892eb18898bf53584dde56b4316b3bc5186d97e3a9edcd059d7fe14561eefe4881beb8519c1cb7c3ba22cd2e13d874aab77e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5b4fbf0445807c8feec7efa3c2bf8dd86b1070638f3c87f1e173ee980412a28847b263a266506e70381aae919ae05d306d3a67a6c1e72c8ccf1c27d6296526e87f0f436c98fd1391f83440b58fadd4fb1905a484bfe8f516661e7176a268660387fe6a7266ef02e5fad91ffa69247bb11cfc1b5c3a88c76b7923a26f8a31ece4\nA = 65fe4d55bfcbba2bbfbdae831aef3dc8c8746e1d04cea174c1d336974d81d026f562225b4a297b1c3b044ccc5dc9c830a805a399bf26c0369b52ab0dd2c0ad19e723fcf9f5de2990ebe5a1266653195a2aefd9a392fd3da8c22c523a362f195babbbf5329018e3b454221b3e77cd0dee79f612f86332b1d104aeae7d8d84ad06b107715bb76bce20220d1340ecfc666b2bfce812814\nB = 12f775dbabf1c112523feab443f6e95d773e8220d66fd87bb7fc702588136a048e17ab6845a9c784dca275cfa445d007e8d8383740b156df7048650f89c5ef1a84148488fc405898f9e326cb8052f626c8881abeb70f3a0f52dd83e3ae0cb82d178cbfe8c393449caa2a87e7c8e2901a87e276b49b6d012f3cbb65641add3694fed3e3177777e78fe375f3a3b378091bb8d2998286562faef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 4f0af7cb0c4e82d0e6589b24b55528818bf2164d41f58505a2b302a8f677df146f8077945dad3790c323e19b37e3379eb95de8abdadfbe4417f8bf8da643768a622ad4898513fdbc72d3b1d2791ec9ff40634678faf0e17d6e0851f08c39405907db85b74937ac403a9a3a1004013c7bd95a585728010689fcaf63b2031bc8c0\nA = 156dcadeca94985ea8bc0d1378daf1e85ecc4c7f8b6d6c7a5cb9f9ac368a97c07e381004023bc575691c082b5e9e13a02fe813a55e76196e4ad4b0f9b1e089bb71a0d5c94254b66e3e645fea25d69bbc5af266e730482a60105306d664f0ddecbd76d54e7235979aa2d806b809b3468078b5d90aa22cbd2c441198d4a52f6259972cf3d02003dc39dafdf3581638e56d08c5181d36e9e4\nB = -9a54586072d093939ad86df11fcd3337ad7e9e478dcbefb2b89d7555883fe8565abcd5b0a9c88ab135ce5327b2a326db645bc7c0e3ce24f902544675ff9d946abf30302f123aeed0f4e28edc72758ffa760277caaf4817a3ae8615784c81896d2404e2cf47c06b09085cd0ad1ec46cfc1f04d0272eac29e774b30f19939d08c036b185983c93ba15d1d27aebe4a357b9f6a298acca3940d2730\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7c3ac09486a6fb518b98a9bc8a8b382bf2293e2c1154470ff7961212430fe2dd28697e49256b1ad8add082ee27b6ecc016b120e971665be801b720069d30c0a8c6ea4795613017e8883e5c0d0e68f982c328379d7a0afb7825c553e087b33e9d78f90e0b95a6597076b8ec2c1d375e2143bb778c318ca0680a64072cf9a4fc08\nA = -71d8e7ef13d63b4f417c01ec1241020a8ff4c9b2db531500984fd3e45d22b2bd581894c8a248ed7cc345e70a5698407df8f0e4ac71ed2c0d42122a4f92279346f463aed899253206786928a0eb7c37f2e51e1cde7f97cf9288d85c3ed7f49e62af0bf9abf062d2c6544d83b9d3438b3881e0d07b1fa0f2a4446fd43ab3b4f81fa2cdaff199c87965e298943c68cc15f2f3f3225efad68b73\nB = 64d52de221f102af62ab1e9526935b005c81658f8fefa019bc58e641023fa785798ed0dff8f7f999dbcc2ecfa47d5314ac6676c82170d6f2b18122c17c1e1ec1b9b54e333a184a46ad35b2150c8165f0de19a24b98327715e5a641c1b6d3ff9d247c89c8749e775e6fcf5f967c6eb5e73523d4f1ec12db7321b14398f26201a364e1371f0ac922781ee252c6d2b3c657ef259ab73cb7992a370598\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = cd08b388ffd41d0aa29a3dbde74106c57b18d325be8f446a2d9ae95fa4144037dbd41eccd50fa34096984cb11bce555c117c5568d76a8f79d308ce11043fe2413d37d6aa60c366af6c1da93d525e4b2d79fc82c0a53ed62fbf72c919db8a3ae11f5ff8057d7501f5f6dfc9ae461c308d21919d0de9e31b759d1d8e3526fee58\nA = -12e58708c30c93383cfe6e99ee3c5caf1900a7e610605706e77d8f428fd59db2884f5021d7a382cb18b75ed22528961cf43be1c700c581ceac3877e83eabd860583e6e94f3f2989c179ee5047c82b53d37054c9cb7ae08be60a91b10d49510e9f0b90ddf89f93790c3e18cccad5a9d223c605a6c567550e2b4950e184fd97dd68bf30681d3f9c585365de2cadf36a43f5a5305dae555396dd50\nB = -26ea5079ba7ed137a14d00d413d6f818e911cc", + "183c88764de4d91d7a9b4cc7af3fad703142dc7905992eb8bf489f6d8231bdb25603ddf3c31fda8bd9bc4d78835f9ddc1e6445037f05125cb1ccd92eea2e927297e5eb915d5d965a25e5d58feb8d79a890e6036c80ee91e7469d9eb672d7a8db68905d06f5981fc40bf486575a067d35cf14ceee3ccb79b72871bf8f52b92e4910ab17e5e59ab3ae6f9\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 34714506322dccb91308c403c267f1ec75f80faf3cc4272dff4a84c13eb1e6133af6681387006c61e7e087046b64e7ae74eea8a3c0564a7c1f381e1c940d92b2c766fffdaa7318d07dbeb877943a73b50517b49e5117778b8a60212284fb92f29a9f5304f8f537e88acf8afaf01fdf64773f988cfa9551d6884baa70587ab76a\nA = 638b7c549ed14256956bad532945ef9e11a50313172965386635a2fc7db79deb0cb5c157e9854117c17f1509d505d01a0e138d2e510dfcca45b4f7ec968b5214a6699b61b8ac68adf64d5394f50d577a154c013612090e2045462160d1f552592197d7da78e03491ae284dc9faf643805f2674af8652bae93ff230fc3eaa833dc62781e5f74d0f0b90290d51d481b0a94ae6e972197c6e84ad7ae\nB = 141f62297ee88ad527fd1e0e09d9ab5dd80e17b32f34a674a27b00d719839701664ccca1b00da2613396cf633b0bdc4482ad3a0c3e209eaea7c22f33706ae44155f527c9ca4e341e651760d1c39f65d5e99e649d013730d2502b6b65adb8a73e6bc734b7d879b430798dcd53fa6c0badd57896cb566d9f1e0a7b3a9161e9808e762ca819330ce9319dbe7f49bd663a9f57ac53d65c6851dc7bc4ee66e08f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 7adf54c77eaea2a1743bc5011ace45b7651846e77f90402297f117d8b1c0377f93f49e92a2457f3d3debec3022a96c74c166d01b2279553ef518ec0e612bd7b382529184640c55b89255b2679da9cf370913351592de39f804f1724de36db90c045fa644e8ff20627f67d6afd4546f00d7af093f668629f9a06c07fab5654ac8\nA = 19c491d5b55aa25f2e18cfb7fda18ed4b020e3f63244eb9f6c4dfa86eb8a70875cc898e305a7acdd3eee081300edb3e4c837940bbc1927f5ed9f651e46581639e133515457464e9c451390828e5e7e00a688daaea74620363706cb69e02717489ba9ad05774c424c18e295278caf4df4ced80b4cbd20cd631df43f2e16ec0334564d9dc03dfbc7111e4252504fb449d5a25cb13630b7c0c565a82ea9\nB = -c3f765349639beb80f888d9c8b7b335ab46b55064ce2a88180c80ad280c6b7314df52b7e73095dfd82896e24604854a48121353aa1de663eff07882771803010005905896357cd5a56a59f0db0045f1aa2c0b5626e132c169abc64b9893f95932f54c1d8cc25f215a9ef6e4cfdd6dba85f6faefeca81793b2258ae1d1427e81e458482aab87f6563abf435be69a05b195d1eda90146a8cc92748ca6f798b10\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 32ba5fc81a7747c3d812cf036bc0edc49f08824d53b91a65a6d41edfb1651d99c11ccb4c074d7f04e652276ae3fdc8d6eedb72c6e46cbb1f7f4070dc9d179ce3e21a3826f7dd2c27943a8d26b192d7f5c4aee9ba0647e406133e3e89c262d37cf468aa3ab8c5dd1b8900dd06cd600abc6d372d9408497d9e20c86a9a6a4ad9d1\nA = -73958019a5a52357b9c1d954c9b14f51ddaced32a4d7b7c95730697cf90029564118ea168d23a54381f7bbd6718a6b662e4c87410e48ac53b7767148582b0bd6a3d35f488e7fcf2b128e0a58b5d468dedabde4d624f4a82e808dd7b175af0d3658c6df1ac0da6495bc9a8dc012f8de55c2003da9b2d478e1a089fab776d99026684026968fc309dae46a6ef2412039a8207c3084f96b4e38e4fa01d131\nB = 4330fdf00bc6d13ffc267073b68aea7419ebef257d63f8f244accb9ee46edd04fe5481292de69d377ba6b6304804ba7ec0a063b42339e6e37867261b9945ec705d3a0029c6f499420e02a773476546993b3c5e1efc2417f51afcec7145a9c2625496865c11636e285d4c8b053ffe66887333c51a712fe9c8ea57606103fd689dc88f1fe37dbc33ae4e92067c5bf51b53e2f8205164c800e5abd677c73949b00ef\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 69b850a99b471003a56931f7856da357a2254ac50ed81dfae019c9b722b95af16047a0d5422cf7ab66ccd898e85caf0e03e74cc8a5a413661e5da483b3f0363e63a7031bb30626c8f73d6e99e290071094b7fe5bbaf4d303192e59acb5e53fc7cdee78576b51595d9f7a25ccf3c7f8889de68b9deec167778ca27ac9d4c71c3e\nA = -1976b3bbbf92acbfddbc05b5d9e7b62a7666b239c1e6270db7ec6dc2929bad1024e745b897840853d14cd815aabb01aed580e1cc66ce37f9d1cc4c9bef8ddd35d28285faa29f2003d2a4623ead7d73302ea9f380f16b3fc06b7c2b8bb4ce4c8b03bfb6056a61c620e4decc6048cdda5e2d3ed8a13b779b8829e2bbab91e9f6b0304b1c08bf8fd85e0f3cd7ee72255e5342e077ababdbb545d7f809bdf8145\nB = -2cab554f7a5d21c499a1025f61e6c81ab0fc68a874bf60470cfac57425a451365be62c380ddd31f6e202f29769e2b6106868da7c81522e03fa6f0704522a5f8bfadbd007bac65595e149f6c585d7fc022db016bab32819049e7547bf85d4232a7fe19084907c528e7eb0434f2e5a375ad9b7d463821bef2f6a721a635252576c176ba42519bfa5d97d0e47facb4426aea0d755507dac81ccf1537b1003ddbb0727f6\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 2ce33adf34f2249f8a2d2e073976cb4c78b71414e027657fcefd56fceb022a06c1969dfafd519eb9e2542662c7647102f5c528734dd005fca666be57b46234123bc3db286cfce07bcbb399eb6764daf2b9aafbc2898a5ff43ddfae849c7549289640edc4ab7c4b9fcf5e159623e5497f509ad6f0270a41fd864c9437302ce380\nA = 509f5d5b160e923b4fdd72f4d522a713d780daa4bfd10ddbd62b26497a2e7925c495afc2abf0ecfcb7980e588f96c4078bde51c7b2c19d86d15bbdad5de72fec2e0a284dd693ce0902b40e54af87ac5a5df38ae6d1d882ea6299fbe6910121ebfebd06b454ec5f855bf3e7cd544a4b0d9a764428662e824e2a6185723534f5e6ad829734347d240c48c2c0f8bd6be6ae8a495a9e383fbc7402a4096b8c2c214\nB = 1a3b7f55307031609afc974857a6cc75821e73a1a9535bd6b8e141437c3fd4a6871c904e22c5d9289df7525ac69a0341d3620bcfc5f04b38ae540e26beadbce0002a8a8bfd0f6a270007e4c52aec2fab11fb2a831b9886997256e4b7e7ad3b0ec64c0f31fb0d637869143712291f5073a5756466d7c82c31e08e09683478229bccdedc2cabb7e426af9025185d8dd5124e08afa4e981236180e0a390004adb7918de6ba\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a81fcf9a18ce476a839c896cc5d9b639fb1d74610e2f618c25310147b57cd77806c2aab90be7be4ed10f0122baf9b862b141ee8e4be5e0c23ea776267f14c31e50b119bdd33f2b41f6a4c43d35bf6f095864593e0d8c0f1fd4656d8371af844d197308bbff14e5a28b7181eb6e6a2b31ead7361e287f3b4550ab0484bf7baaac\nA = 19f1ce60ca50bfdf8e02313f1c9a45496720a2ce467f1e8bdedbb32525d762878b61476989c7f6ae8dd29c983ea596e521bd4cbf74dba4d505dd9ea5df423474fa9725d5b65f1575d26ead95725e2a59a6c8a5397ebd6b54123e42bca44781b84c014b8e5d2c1a86cf34d764b242baaad5be285cec72ba8ace808058a0226c04f95eb2b53a828d0ac41e6b40e5a4c4092788d9f7e988752f175f075d545f421205\nB = -b115a1101d97664759538d22154de4b000c008e551e2ab10ad05f12274b10a4cbfee762d232df5188fa1161f37ba61d146e8b95fa715d98e016da8beb0600de65216cecf8b8816f6e7e73e2a2bfa7d0bac74b517b906bbc43357fca69de9cb5507bd95205515b97b3a4d6842f3d7b09606cce1c7436c462f49dd05e915d04ab6fe2748ccaf025bd5d19749cc468d228ba43452ccc479c146ac6d781717bb9966bf3835dec\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 1473f092540ae30de595666beb33e430cbec42d7a28d4f7982e62f58025cdb617cfc33f1e5ab93d2ebefd7345561b81271bdc50bfbb0db6381dc0ea023ff7c72605da26dc7da2b5664d2ad7967426ca97b3745f82528964bb68e70087e14dcf2d71d30fa0d1f7b3f10b19b357e7053fdf22bccc5188c6919eff1e5c402b750a4\nA = -68f280cecc512d51ae534f30aa198cf7b170c346c1159fa9cf158d0127d43e50a8d4704ec54b8b4295dd7f51c6771cb5767fe0c975414cbe6d2bb58ae66a095e8832d5f443498b1ade1f5bf249da58595ebd878677b34e3b4c99ba6124e2b71d86a8d99727a16746469de51b0a61d9d981459a6cebe206cd36a09f00ffce7f532e2c31999847ba000b9e01a4b84f454544b6362a5c093b9abe9d583716f4534f2d", + "e4\nB = 5b79684387f18d7de6eec3a63d737490dc2a46c0616ec16388dca2be60adcda11ae13063ede3fec177171a51dbef430f8c4b3f6d297b9d6c020fc44e3ffab891d0d751d033fda813861bc067c181118dc613335ce89c5960f952e5fd28bc72c41b7b6e374ec29b837f1e00271cab646c794579d315260921dbc3b984b86d98b8f8816aca4f16de50657e4102f34d9e29ec3a03e0da06e70f69952339bf2ec4a7e74daca82239\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5e4b3f4aea7115d592bde9bf7c6594fee77372ffb19f7745b4de878a4024f81e8290c77d2915424df20004a7abb64c214104a3123e7c8f230c159ccb99bd937521b433dcfb065b186a685fc40f9166bad9380a02e297ffd6a307ce8d2c8f2f1330447a9c06c327b74f3cfc2e98f3351a8b385bae855941228969d1c29e9da3e4\nA = -11c1d396693139df5bd91825c119d1241c3f57b7ce95b46472dd82081738cdeb0868d18eb7c8ee7808016b3311f982adebd5a2e5f4e201ec4a34f3037d260fe580e771222de5a1a67947a4552cc03c5c59f9e60e25063a702ad3c3aa43f061a22567f938a91f1dd697c3e3978fa11ab1d65030bf327f8049bda745658bdd4ba8f3e34b060c6a2c6c5a8be54c7cb5f6b106f54a37d2be9f674f7747744d4350b3acdf373\nB = -25a65b6acda692ba3330d70dbc3ea4dfe208c0df358c50b7872245a909c5ac19ec568b1a1340e1a094f5b8e7d1e3b7e04bb4df002558aefd4540135d62d75bd5ce959128c1300b9d98429d7369610866d98b22c345e531f2beb80b042b6ad48da077043401a82e223e9e529e7407bfa466dd2680973006d047d837c26a60cabc36a7ef538f603ba19f8e923f168ebfc3834df8f77a559c9e0342e33df245f551bb242e5a66e5904\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 47872b544fa0425981ae17bb450ea346135e6ed7a9de0572ae14a6e85e8319f27cfab778cdd8cb5f93b417d9c66ae0fb7bcc6652620f7f3f74acc2bc9f2c090129fa8315aeec9ca7adc5356484474ee803883ba4695d7bc47c87eec508d16a15150cf3f757c4713de71366e958d6af045b2d282b6ce96976692c80b1e0b6f846\nA = 7e8f55c040862f12d8cc6e506608eeca65ce38e9e8ab18ef7007e3cf0f1c9a0696795bd10f8e1e1f55bb4f4f3a35c2e0ad18289e250571ccc26a961f730346efb1e29fb143ed97cf72deaab19834fa2e98e9c12ae4cd23b9c5ecef4a04c439f7d42e110b30caedc4334372ca24cfe4171ef1430528f7b57bbc823fd606fbd30915c5817e6c57c967c4c404a0847b1455da17effeebbec3f9357358e00001239aae209228f\nB = 1cc00b95f6bd3abfa697400c98110725a7e109aa9b8cbbe9ae16327c4fc8e5bc93afc7a94da32e98e85e4fd5eb545192c73007d97a4e84ba64fe187ef61d17f0941e165c9fe64c7b8054e24dad30f92b50d1f526b4bb031e6b1b9058be24884b170a145212273c51692b71bc57ee53176d8702b975bb6ba96284b462da2ce38e12d86b342c7f4d3cd489fbce88a309c7df1121d7bbbaab6814cd1e54953e5cc46813ead98f02360372\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5d193b085e57c3f1e825cf3b36c8bdc936c603136bb782a244b04a79fa713dc7b08436b85ca3b483d2e100a012d6430679b30c8e4101c8f08ca0f9010dc0f27fb37be842054dfdd99362e03a7f55ae58db7b47f694bd35d91a58975ae1f255c41617e773f91c2640f768bc702a213f073682dc761e056b34c57edd85585fe04\nA = 1bb1c759ea94b61a1721ef5680f42af30fa31444b27591a03b7c9bf5b90845ab965339f463a78bddedcd62fa21197c32d6850c61bae195f86e1c7a23e7a20dc618c59ce3a1c6ea6306c0b01b11a36d0fadf8214c36a133d689438021ce7c78b20c85256ec607360cce14f139513d9f3ea6eab067b1ffd0935d7c43419b93ecfadf2c5a902b7c39a69bdc023173bdad574adc77706c1a666d66f69578a5bffdc7cd6eee28ad8a\nB = -e8072c49cea603d48f20276df188fd2fb28f8721d578220cef7db1e56379c04a6b372e56a047cbe59ea84ad026adc5d0aa930011db63bf4959f15781e060e0240dfac0e2a2c26be12a21e5650d12140bb49a2a8e0f6a86e4b1eb79d9b8aab3202bfd339096529170cfe3e0c18263128686bd9305e92a3c43e1523f97d8a6a2707773e3d441da162a79089c9ea1e094cd5a23474121188013c8c287965a5e77599f6a7d64174b06cc165e\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = aa79c899c2b9518857c9e4f96523a44607c3f6a97d1f40d6474ec79deb2feadd955fe92d789df4d362c828084559fab56b5e33a971abc5449208d31671c7e220c5945886e33ed1d804c059a8e439a92524a785076f9730732bc5a152aeffb5b9ecf3a7e4b55983016355c4c29827496fd4d7e6532c270cb9ef263573e4c63074\nA = -41b326c2b86e7ac14a2050bff67bb5bf9697f02594789c4a2b3e8455df4522546278d0620f28a680f6a88ab545de5829305485422f4e70a5ebf0ad15508dfe3f16ac556436d8fe8a8cde83ead549d88e0bb24dee52ebbb49159ae71589d918d3fac8011cfc3afad613ea09173856b7b79b55a2e43e0f7cd21eb9122d5f6a1fc5408414f5aafcff863b870c67b740256d317a0c58af9a81d8025a086a1f3d79f7408d4bfa06b9dc\nB = 4730f03c389f9bdd92fd864177e06140c9dcc02d01fe7d37b51d44de140696f116d11bb67adf7db797edeb7c304386a7f5e37bfac46a5462a6d4c49b1bc034c2e0dfa56f14bbd2a4bfaf86bbad4f6d0dfa13c782fe680847d4b43373d7137f5c2ebe4ad58c695a7d4c407bfd888ce04abaaec60a3fd33db10eaba6b6acf0e16cb61d1beb9212c2b07921bfb5595ef1eb389200b356eafe8b5288d8f0e2cf252b38301de65190d56bfadf57f\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 23f9850dccd2af799f18268c3a2918a69019513c55268faf2477c50677fce277d8ce58a0cc06dfe389170faf5f0ae13ffc4954c746eebae66efc14eaef2c2ac9001f3c7ef7e32fdc31dd725b6a8093e33daa6d19808908e0c2d3e7c1c58e0fe9ed92f4d7cf3cc222393ca4f95feab5d34fe29116410a1882dff7cd92acb87590\nA = -10a75953e5fb9903411869a2949f8f04144d6e2d61f95704ff55a02f40c4f283add405353a68bf7d6acc1b8cce738f0c6f9271a538b4c688dbeface58eef0a0a1d491a9e66958750db97bd01466edfd245cef03bb6a3acb81acc63c38538e7f15deefd15afc422a8641c357c31a069258dc0ebb63f06094ed8fe7d4d420246b40302361967c81f0a9ca542fd1de01967514ff2565de7ae3b4a200d63feaa22fb99a251cad66624df4\nB = -351242b6e6d0122f7120deb8357c3bcf25d221a15f83579883bfb4dc2e6099e6b7b95fd08f6e573d93354b0676f7bc9fad563d6eb0f3567ef43efe3d874b9c7733e4fe1ef491043e1f80aab6094cc9b9c236570972233ea74e8779a6eecda23a65d08d878850cab6005159265893dc0f66920a12c26dfb421ec326a1ac09e9ab8085825c31aba488af02cd51f96b205c50e692dbf2d844ff0a989c3ba9f1c2bc7f2e7dd9458a72d310eb28d490\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 69c7fa326630d7de69249807cd8bc55c9315acac26fed3caa3c8a9c6b51ee96a7dd0b3bacd5cc13c15f199e268c5eb91d1ec36c085f83b437b9906caa6e39ed7bf09778610b621426cc8d36d96f541d0bfcc7693525d33e0c2ecd77ccfe80289a11155b37c7ea7791b5c2be3f9b954e230c19d746575afe9a1a3a9677d23c5bb\nA = 7cb78ca8e5d903096630744c85975719c16333e2e44931956d8c45b001d35ed4e184dec88c9e2167d2f338fe6f25540a144cc419590a4ac7caedea3bbbc565365d3357baa62fdccef2c5ea616614e0bff60e81916eb4abde0c9725b1bf6869e8b1e11f6d0d08fd712bc68003e55ed462ad4946f7f982e663f65d45c07c659d9620d5139d2b3332a68d33aec36e21716a3b75f44272a19f860e6ab3864f06def9a5ddeed340ac0733353\nB = 16d5b074e008fdd30e73ea95cb5fb87de806319388b3a44f33c94d38be0e6f1a92103dbdfb3d23b6e1d19bdb29ac14833003e9482cb7524d0d7b4c377f4911e3372f2cea6f84c938d84e3994e80f0d68e7e385ca29e02f70294c921dce7cd3829c5854ce51d1f4fcf7dba910b51b48a3f53cb1f187182435f21f6981cf8440f9c8287a9749c92c0304cc2bc91eef32d8e6526be802de8aa16684e8854cb0b67d9f7ea00f6f0145d14e3c251f70881\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 72192604b2f3f68b9ed3e261120ea52b06a05869f6abd21828ce8abadb3a71c360a14947bc738e5d1d530b9636d796f785bb44508477eefa80c4b77d4e8e35463e15ea2a48c682d3288c5abeb66181e4bed7d5b4e0db20fdf5ed68513aa5ae7e0978ec1c4646368f206636ec90e808817bd1d03acf9adb9ba57dc153873fec11\nA = 1112d291463b28ef45e879412e6607a3e20d50", + "dba5044e71883bb3cdfe9bc694a577fd7d896dfb836a171f3a4d8fd025d3a979b43e41baafaf7b535d9050e47f4880828640e952435648960bbb74a3c25dd90bccb3fedd254dfc0f031d0e8a468e93bb69f771ed35f1653cffea1a763491fdf6efa21aefc287cb611f5ea0085f64cc3705c784f87ce00846901833d01a3c45ce047d822ba390b538f0a24720155409f60ca0d90e13991aa1\nB = -d553fa2dff0265cd9d083ad097af87a99af3d8d93a9f4c07440a28a427082004ae5c81d22bda1dd2429f540de8df175c1b4d0d50f0227489ba570b28baa35055df951d05b584ae6b051a135d7eb2a501b2441f82c135a8ec0eb81d379b96ef8f2fd526ee62293bcb934c76ef8083727a4b28bbfc9f515ebcc2bb7ed9594a106e137ce94e9105b2e2f4776aa9c6abdf426a181181fece3251c3ef4f8eecb634e6bd47c5878663fd51c74a66b92713fb7\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 459e19faf105ab17ff794927aff86196b3cc3461e69cada53ab8c8c81e2b1820408421ea1af6ae10257e8cd9dc16386906410761fed62cf9ddcf0da2a92800d99563fbb9cb1ab0ba46a17cb9dee3f2b68992c2b832a5932e4533fbd5c4487d870f3fb5d7a1c358f4aef02993360915a9e9cfde234df5f51c761d84568400b618\nA = -7a964c62e38e4124cd2bad727138dd12a086a2bf01c095b078ce2f81288d3c8435ccce0c8e00229184091130989434bcd107a3a0787a2f5f4b0e8c23b1cee9a8f39ea279fb6081efb6c3df1704fae9e87d63ac6eac4c6687b3551ab7ddac5ca0541e12047d04c2fc760fda0916cd2b585a90d25880fcc1bde8f0a1a413969938d42e8b3b5f73118798e85b901c2e15860e29e2ee8b1c95336b97dc10a21f5300e0352adb60b40a8a99333380\nB = 743ff4d91ea3e0f9c4f72e5daecb4fb00b15b86e30bacebbe4384324523d14e22abe29b00573733f594d652a88d98c987f8db08b27b4dc68577784fde02dd410ebdbfaad9e9afc6a22a8cbb13a780222bd212fc61e38faf409e940fba35ed909e6938e83b0fdf5b5e3ce138604823e788efc3aa0df924554fb70fd2faf8249e17a827c5d85942005b328bed97e5ea1f1810219d77f2fe121ce66518e37c84d64aebda3c397684212384deebd520a776b95\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 183950095d9424b0ed09985aafbbd2e5d64bf541a56b68b42ea8cf9b2c051615ee7bb6c0687ca6fb0036888fbc927cb7aeb303750871442ff2c0087a95f4efad568f48b03bd2b9a9ac26af8c259a3fa97cd2af7e3d8f36148c26785489cda6c00a21e7eca219d1f41b2e82ba8e2c1cd752eb08a2fd50c6f9077f3096e2eba05e\nA = -1d2fc778cf44c6992d1f3a056860eeb12f969358cadb087dcaebf5f96bec42bc0aa98672260adf1732da057e9e0d22081e33f5fa71f248cf89dd361036ad58692637cdfff584a191279f178242ec0ad397efc52e99462f496caa0f3133c4238aaa877fa7094662f080eb284c4cbeb992a368c2d157ac5c8c9160c167716406190fa39ce0abcdac52c8020969b87a4f84bc09a51f7b2ca288c93b1aac64e19623a7d9e69976a31074f637e4c82aa\nB = -2f188f1245b75cd21d052ec76edeb5881944a143fee31c67370fab0420a748f3f1957bb8332ffefdeabd0ca806169629f130c86c99bab490a9668fd8200f4a9b1704c589e75b5c8c855f133d50b2ce06191875e2872b36c78438d6032d53004c047f49e4cb81e19fa84da16d053e6cbc7c8eec0b9129a8831eba690e0542ca3fefd204258624e92844c8b7bcdccab986475a47c8b22e89079ea6580ef8f496099cc24dc2911dcb1921d1451e2163b55bbb7db\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a02c38d5df9ff7055ff84122342ccdf6ed7f7d54fe8227af091371f5ae62844645586adaae99c11f4ccd828103a81471bac72dc20625962e41d603e760591bb3569a21f45bf062b86b5fd1c617a4769a4d767a0ee14d104084c12ae875316a8f2be7adec0104381dc02c20b5851efdf7d4bef0d68076975e0ada3e58e101e8b4\nA = 5daf37d616da184acb278a75fda4e4fa49e544eadcf373c054b203a309ba198233f2285a1b55dc92e05d0213b26c82e261d8383a845813077b2e1b5f4553400f09410987c8dd21d4383e0f05747d0482d1a89f160a5220b22c78393873564fc5b1e4d5627ef3d4a05612709f301381df35606e99560fba07a917d7ea7413110fb5a8290e114d5200cfecb00b6c53b2ee29911bcb2fb2930eadba0ab9dfaf46443370307d9c3b61a329f0b8b8cbe7d\nB = 1d9539fdb1afabeb9be6e774dc7c7cc4bb4fd63af7abb557a5fc80a3fd23a4600de3c7fae89b91f3d441b61d3e24b2fd3d7803cd71620e7313917b4afb89ef5171a3d8a68c3c74aa3dfc8058d555eac429dfb6db40a9e0c25aacd2050418d6f32bf21cbb76981269dcd5883178d4b69a931a0338b93022a2ed0f78f3d8877989cc406f19d6d082ea344309318c56be7946412ea0867c78418ec32b9fa3a61017c10939c9345021133116933a3d1eb86a3ef16424\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 5fca287abf1f487e0ec18c230860eed4a2e550228b1500b1e33bcd6675646b5afe505b55073129f22352dc2b113c584ea1b98808214b6916933e90e036b129b61657cdea9026e1fa087ee300e055ae8f94ffca933a2d70453ed220468a5a3cf1a65d81eca11cf570d7d038722397f487af60531f24a5f069671354882c8bd2c1\nA = 1d9fe15171dce97475f4ad329fc8fb5469fb2b8086e4b01eddb6ceffe5324cfbd28d791705848569739b6758ca7e7d7d49adf0c11d891b0a5879ca870d1ca5ff475513322ff218cd26024f97623bb8a53084594e1fd64154e1db702522883fcf4c0d677a7fe90096fc76dc3800816996308d8f0be2dbf3b879f8a000c0ac534511437e2ce2d7ebcf42fd1698a829eb846b3afa581c24d5bf97abc6e247f110f4e872a2474e3acca6c8c0d518104c3375\nB = -dc0da8f7adb8e9f7b0e3f293cf623528dc8e9668317910417e52301c50c62e7d30e77ec7e38d6817d1f5a93e851f8560f642f23a0b9f836812d27b1b41c0867088a3108332b8711047560052ea30c8840f03a25c65b227a175d8f340095823788adb5bdf2b7ebb801e20f6b6435e154f78d17b8fc4373aecee56ec7b8f5686a7d22c8571797fde85cec884d45ddc4b1f2cc47ebf56a879bf286f349a0edfb531168b733d43de3b86b49eacb10b06a432c96c63440b\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 6222c1a14c6390d73944cead58eae5e7a6c19d19e4563c36cf624f5b61d99991bed7dbf6a0723abc56469eedfb1f7982987c2c7af6191178cf0933ed5f191b8117c9d726cdfa8b82a2fb25ca5436023f5860aff5fd482c611f134569ae87395dd99e5e9d400b5ab1e3064210ded096411654518110ea45899f4be2516e35a229\nA = -7f6766be6c6ca9bd1fd7ea1f80bfe68693f7ee4b5ba2946846839060d6028eabbb9079a165c1a07eb6a01239f3f14095225b8617753a1cc3d9c1e69b516d8705cfda396f4f0d05b0944a0f08b478d261e968c06918914ba87c8e7b7adef5cc2a875917d00585571542af219bd726e502b7f3f0bdf0cb1dfc6796be2e22e8ffb5b8bfac7e15e991022974e75d3a5eba214ab8a1aab2fcfcdbc6ded2abf834d1899d2e3ff94bad9c696aece045212531773f\nB = 49c6f869745983cae44d33cb7ba141234905441ca53172abd1a2dd8bfeeac4b236605cd2dc5b04ff9aa13de84872145b935b85479136065d2d57fd15fbd97480c25c6354636c17ffbca33c9319d65e82523e39fab49321380a130fc160857a451a69b1d0509d5718a9cff8b49c2d677c1f66bf77333d2511f58d3eb2fb47b3c162cc9be8b012d8df70278f0e21123a69724a1f126369a236d54da026ebe222c513f24b577707b5ab4b90ab0e22b4e38ceb4181d4ca101\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9e9cc8c5342dc6d6daf55fc9aa9f79ec18592e8b9724a66881c379245c91f06a7df50a6ba0964603a6dac97e77a55d06efff17c93d5faf107fe65788d0f56483915f6ea0f1ccbda7656eb58fc032b5771600beafdc12c2076110a9b9670bd0754ff6a72c5d6e1a9e4e42c688e1cc96d7aecd815bdf5dcb16fcd1be1275ce7282\nA = -11635fe16dafce21efb1c599305e9a16eb5651187cbf054cd9d911c13e8eafbb738013e212f9c2b3662ea15ac9bd82b5751d43a38e4475d2310945a812262309094ae9cf59e0e9f3d02c92d8ab01f5733a20f051054a240bcbe3a7b6bb3f7c434229f631c4af239d33bd3ce30a372a480fdb49b2716091d26071aef372b8bd8ee8eb7f2965a372a836000b3737d2a833a39230e721e4844e16031ad69cd45ced60a64510c1248fd776611934d8d2a913d965e\nB = -3bb2cde9d3fda96fd7e6b24645f8e00b43affb223f2b5c3f4b7cfee905ddd6703a9d6c01f1f099ad1174da215a645ca4707d8156e762e2a253d7cfddd05ca19823ada9d33924013f677cfe4d86bde025391e0aaf91c6b776a9cf8a09dcad7cea59ee7aea1cf5f5bfe67c9d4456332d1f98e5310db9a0230381e1867a8f75b8757283f911f1a5e0d4afe5d544afa8d86637f9c9d87428fdcf8b4eb8f477e617960948253b24565b2f23081c47e211cd3c788a92732a49077f\nM = b18a9cd6a0a89578ea773f", + "bfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 30dc89bad4b449d1df9ea9b8f9d40b323c71d7e1133bc44d33bdb87c38cddedf83bb849e83436e4c92a06546fcf3e24ce6cc89d2e97a48aff2c7e3703da1b167a112f662a89742355e11e131e41052f1b379753cfa32cb0efa3a07465a258c585cd68c86bc9a473f5262c86c50992aeccbb9725b69ea8b3a7ebd2b6a24db52dc\nA = 60463fae1e9354559160d55a453c12d75775a53d1606d1fd16bef7e4ad1c78f9568954112f9280c46781180951534c5372dd5aaff3f33ac9c2e0ce4934d7009aad2ab5d6a5e5a141a36846e8925c7a28d116c68fb78aa9a687ec9bef173c1b69e0d7261f96eacacf237e1fe5874e5d553985b0fe7692ce8f2a5feab9ad9a2ad9c4bbf050b73b8030ebc36b94af8c6ecb67f8c94607d80cf600efd4ce4aa006f9b1832da8a1fdf8a564be0b4369149e8639e1714\nB = 15bfc50290b771ad147695a4c6701c47f2e8aec0657a4ef999eb45685200981b0ab5f8abc143d64878b85e9548651a1afd0913e3b14d11d3a26ab9793596801662a67b0062fdc8888feb029266f71d170518b6a4a040f59996bd4f257f221e830d0faaa9688aaa6afbc1f9b40d25097eab9d71d80aabc085f3a07e48bcfb37119aa00de60be55fd07d5b1281adf7b98bb589cdf2026252edf2f075ee176e23afa6b1f924c9fcf3c34c76752e833278a2e6b62017b88b77eece5\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 8b506c9bfb75ab7ab420ae6c9b371ef035fab512188d9df76f0b31831573b44cb08266186a04d20cc761d61b6df3e33ecb86c269205c2c79ae6aa4d3ebacac8ec71d9bce1d7ab146530b131c9038041c6ce8152a6f1c09b9bec8eea4462dda0f08d75edf296eacbcefd62a0c197ed30f799343268bf6edfee4995958db7e0420\nA = 11c16713fbf8bc9696782cb5a88174cddbe68a04e8fe93dd074aab33dcd85f92baa178b2f3b8817be0cecb802cfd3ebb06734c9d399a1f090e3a8a2110aebbba0e920427bcda74bf11700b945985bd532286d44a1a615cf7c501412e454edd647f8371cb8149474557a0d47cbb782f460de7a3cc28991491ea0fc510286711b882987b09341c079565414f2c930e7c3c3a3e3e0f1d786260a7f45c70e0fa20dfc63849906af61707cfdf5a9b7a4291a1c1586d16b8\nB = -cf5638af39c6da3757a09a92e0bd54f852742682dc91c71dcdc6e72f7825a0979a1ead2e158479ce5565d22472dc3853e6bf7ba43296a5e0e0a355f0703cecc02ec79da83e3e9de10a6eccb858dedf7d4c400c27486a5b8cb34d787cde6a5fd271e83a6cf66057838fe30db1f30663cdfc22ef5d002b0b5a05831228ea200f95382a58d0d8aba36523d9b5cb7506f193131916f3ab66ac9552c26cd0c2ab1c449eaeb8fde752f4f3c3f9b060cc1f8a1e37c4fe5ec306674b66158\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 347706abeb168205cef9b0b8c6b9d6449ac501af7dfbdfbd41a20a6a47872cbd7d4cd32f7b0805ecf1573d534418b7cce98181e079d5061b02639fdf0161cea5314dbbb2ef39ec841f695281f3c7de45f33664e0dd1658f645adc1dd225f781a3fb1634517c556403587b2aecd56dceca9ec19b930cead2b1d303aa056d28bc7\nA = -5e1c869e5dbcc684c245d5c69093bfeaadf388cbf928d33a8ae2148a2b5145937e4f654c5f6a36de1124bad1de8bcc9067fe1f9a44fc6ffe55ce7ed5cd0dbb6337b0e1e96bac1eb2a3606dd97b0bdb975ea59448be50191cc7ea36481ca9fc85c1c3e1c97378dbcd6b355622046888df2ab3d18d805f4d31d464f62a8e630e955beeeb5e00c70242b8f8df708705abbeb95dea3561756298b5f3f7fe16e965294eeeea4546f5e8bacf9d6b4f2136d2e206a87dad1f47\nB = 70225f0cadd328be36ece2172c836405db3fe80ef99ec74fca25406b73a537adf5073f2b550abfc4c0fcc2c2850dace0da9a266768cb4d5ff7fc6c1c248ad74f47592101b61ef96c1302924381abbd96cf49f50c44bf7e0551721a8ae85abdf9925548d13b8c5d1a27be8a40d0f43eec3136bc3035057b75aea779b4262cc66e6bc68da93c218f1920979291105d4b02117d66deb92c3e511aa588b27130202acc9f69521957f79c7e731bbd5461552b9b6b24240dd71ac449be9777\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = a2cb238f326d47f95869e2dcb295eba819a443dcc7c2785461389b58327742702f4c86e47af129f1fd4611cda93631f9333c358a29121d58286333083d13e66f30a9533b77ba3e26089e7eff7baf19bef8054af4e24735525908864ea9c4756b42a69c897003cab7b63cfd9a5927ed562e29845308eb2a55e7f8f03c87a5b7ce\nA = -1aa7ae6f56c38b654b281525b9da953ef366c2b9cffd3042105ed428dc7e5f2f2d53ef90b468bb471753606cc7a3775d86bcd2f4d5119cdde3c487cd39bf31752c5ba297e529c1b8121487e0e1de702156d0166ccaf51888a24fe7b48624eefaec855e2200929c21858676ec9bf4ceed0a832b69efd5065af544e49a3d209b85a77b0953652cbf0aa897527c52c9a98de9ae4c827f762e251478c88d410123625ea52b3478b52f6b9987d42009ae427763357ab53195772\nB = -226630b6fcdb5e274a25066ae2ca2c803549dbb935a97c0d7f6ab2c971d74cf6acd265c9d6815a6b2dd23dcb3c23b390fe8b1bed92b8c64c76c0ce62d5e7ddd7ce445bab0ca905dcfd0f128e5f4ffe966f3903d7ff1c61fe174e373cfe35a6d83249ec40b4a354d46fa1c90682efe468e895ea3da710838c262e8a47752dc6e7a79fe20051f51180173b58e0aa37b22eb8efee5b6dc264459ce4d135f430cb15afbf8c53f0de894bd2aca1f7ea32b4209a22a075f7b3b18e86f778a9e47\nM = b18a9cd6a0a89578ea773fbfc0767c8ab817cdd585c16afad3600540d056c323d83e7756b1b74e4c2a84df7edd562e011412a0e2eb6b64178a9e04f67550f3081797873f444dbd03d776835d696e464443a0a6f48d509228fe976c54cb82925e8a80ee65c01ad9a5f56784c54192112fbca30a76ce579d3eb6e783643e971d48\n\nModMul = 9ea62ef634\nA = 55cc58c9d8\nB = 6b49179821\nM = f753311ac9\n\nModMul = e9ab3a2aa60edd30108\nA = 5134a36c2bad180dd5bf\nB = 2ba6485656d041690666\nM = 9b9cc4409e86c8b0fbbf\n\nModMul = 621f9b797e866028b7bd1ff828bf29\nA = a202338dffe171c99434d84f3\nB = fb71eee7045b3e3ab5dd809dd\nM = b3e6e8d53b7249df670e3c59c55d33\n\nModMul = 808d463d06b7b7f98e3cb2783e2196c349d62672\nA = c669426a92d3cb5b316e2b5b9\nB = ccaea3874008dcc92450d8b2f\nM = b04dd2bb325baed1940cd000e8cb2d786009ccd5\n\nModMul = 872164b92b9426b237858c4cdafe1694f96b0e0e4c19e894a0\nA = c3255cb24a813e27c3dc410f0\nB = b144f39e7c2d33605ba7bee16\nM = f3639f4dfb782f3107eb402fabb5fc878903acb5e02e129077\n\nModMul = 6124d7d171\nA = 235b938139\nB = 3a56a22a28\nM = 83eb4af4e5\n\nModMul = 9c006f56095d442ba98c\nA = 207e14237c42e3764e5e\nB = 8a495a26872432fa8e33\nM = d0cf2b8ae5c67d6736b9\n\nModMul = 97387cfaef652932a230c82de59cac\nA = 82ae0fc5e943af5bb8c4adebb\nB = db1279be12d59ba3a9c036a61\nM = aa36dc1d13390169cd54d711eb511b\n\nModMul = 32ee73c98da657464c6fed4274df20b099689e00\nA = 9baf08248ee24bcb17714e420\nB = a7f0428147bfe098666180749\nM = ce0bc198331c9ed1d21f0d498326e8185d3d602d\n\nModMul = a8b3fc0b53df3b92753edecd6fbcc5f4840dad3a44da704e34\nA = b36249e259b303e453757721c\nB = f0c1db50670d92abd93bdc84b\nM = b05cf978bf2dc7e093d7d164e46d547219c480382df32b33d9\n\nModMul = 2663b741ff\nA = 58c8e7f7f6\nB = c84681fc87\nM = e0a50dcb45\n\nModMul = 21af3c0b42328f41b81e\nA = 1f79f5b5bf78c9700d\nB = 5bd1734ba0f0e59c2a25\nM = 9ff3fdfb5c089244f327\n\nModMul = cbc280b5106c2c36cb31ad7e7c986c\nA = cadf6482b769e83ce7f7277dd\nB = f9862a06da1a9c89547b76c61\nM = cc36144c88139ce921d2fd1740bc4b\n\nModMul = 3813f2fabe016e19fd8e70687ff473651a5fbb4b\nA = 9c51a5bacb5d9f055a9ac2962\nB = bfed5625b21b4e82d1f105a0b\nM = a47977acad7c5deeb683ccd265cb30cb193f22a9\n\nModMul = 76ff291a02715fc87ebfb3e99153c04e53358dbd7beae43478\nA = 997c4a7b537d9500d73a205a4\nB = c679ce666af284a459ae5a26e\nM = d0d0fd4922953941acad8beb65c00603b19eb44fb8ca51e3c9\n\nModMul = 1a90c92fdb\nA = 94fa7bb475\nB = 564b0a3339\nM = a1501bdc75\n\nModMul = 5e7ae5470686bad7996a\nA = c725797912c6c5f30d94\nB = 3a7f4c99ee3f5fa9582c\nM = cc50c8b7408f09a74973\n\nModMul = 72a15b13bcd1b63747342a6be8f0f2\nA = c33357af48a2df569e3c11ce6\nB = a4b4c5c14d7796adab54b6cae\nM = e22a0fdca62a37f4c8a61c96a429b9\n\nModMul = 31e179bfbf65b0695dde36a4fb72d131830dcdd6\nA = ce8d3adab8cbf15c332c0b289\nB = 9333f94eeb7d7a86b82becc51\nM = a532a76bd5cff409b580d54d12ef75ad8179b381\n\nModMul = 8f4b8a585415adff3a7bc35fa88891ba31e4a82672c664fb14\nA = 9a2b56a54bd0727ab4be57ff2\nB = edf1781b4296567990773005a\nM = c5a7c3b97ba00d6f174a019c6d37eda52036c528f351bef0f1\n\nModMul = 917bcdb402\nA = 55c7dbd314\nB = 997b29ef79\nM = af5b4cbd0f\n\nModMul = 660c4bb2b771f523a4fd\nA = 43fe52461d5139620a11\nB = 1f8ec4b67de1db54ddda\nM = d0458e215b7e6903d96f\n\nModMul = 7aeff02c143e4426fcbcf32bd1277b\nA = a2671586369a990dde7829f36\nB = c7ff67937c900daccc0ab1d8c\nM = 8ad9c1d4d3cce681", + "d1ae27c27982df\n\nModMul = 4b153d57433f0f7276674d3484e9bd0d25227d07\nA = aea36cf51dd2ce06c66b7a407\nB = 80c9fe5bb0afd2bf8b3644f96\nM = 8cc22a67ed7e5a7a2322aaa09ec2be94998494f9\n\nModMul = 7f8447dd983b113f04c6288f9539e53a2e9cddbca8b2fefcc0\nA = f67636b03821c8f13f21217a5\nB = 8473a29f4ae33f36a0d2c6dc0\nM = b829af37b557c3ddbb5257c8b19144b90708a45a274d6655f5\n\nModMul = 17fe4644a2\nA = 912611576f\nB = 7a10d36b80\nM = c5fa605133\n\nModMul = 8159b23d4fd697b4fd35\nA = be2d646e76494439e60\nB = 60fa770d05ebc69772b2\nM = a6e7c940cd749925a85b\n\nModMul = 7c412dad5c9fff91357bf181caf2bf\nA = 80f476ed5acae75b34ed54c52\nB = fb818e2bdab3b5f4bd84db3d0\nM = d0339f7ee41337d8462d1a9c207d1d\n\nModMul = 70432c749da4ade2c38237545ebfe6c4c6a92f6b\nA = ee9c92de52210e61adaa6eb4a\nB = 8ab55a85b1abab62d33e75fe3\nM = cd3faa6de4cb62fece4c3f94492d457834a6a041\n\nModMul = 9fef1c18778a8691c5e71c0b5208e82778e9bfb632da0b7e28\nA = bd162c90bed25e84dd5b6b77c\nB = d887ee03020c5df356f091db6\nM = a2c2d45fe9decd93a0ca3edab8fee46d27ba23fad9b5294d5f\n\nModMul = 958951bd0f\nA = 12bd0d3375\nB = 668bb65b4e\nM = 9c617dfaad\n\nModMul = 8a109ebc9cbf86613e43\nA = a3e7019f1bbc35689a77\nB = 3189ecd3fd4ffd0229ef\nM = ddadc50600dff2abc1af\n\nModMul = 2b4d9f85a398c852b3a0cc82524619\nA = c244fd157267f707319ba6c6d\nB = 8a07018a748992429bbdbf326\nM = bf3813fb54f749ea5627f59ce30e07\n\nModMul = 28cab7d574e6dc56a6a622f8a7523cbb8dcc5e0f\nA = c9909dcfd3a59a3cfa538b267\nB = 8bbf89cd5a4e24adc2d8c646b\nM = c8f02682b9d480ea98faaca53b747ced33ed0419\n\nModMul = 69b2dfb3f1d8dbb13e9e479f38edcc427d5968acb7751a226a\nA = 8019266c548982a520ab48eff\nB = d33c3e3b13576dcdb3ffaa796\nM = e6255103732475604df7c6f7ef7e6b49a8ef9e2b0c717925a1\n\nModMul = 3eaa4c99fd\nA = 6fc42faa85\nB = dd0b4e318e\nM = fd7f22301b\n\nModMul = 56b6b811ced3433755cb\nA = 145573d17cb0c996c69\nB = 9d3297d5ccc184896822\nM = dcfb3b383506239e83e1\n\nModMul = 34315b6bc6d3690c28060485ae331f\nA = b963a26973894cfb42fcb2d22\nB = e8523304bbcdff1a0ed4141bb\nM = d7a379aeac7d8cf94f19e7924d35d1\n\nModMul = 2ec9466e8b3357496f07e37ba24d36a237883846\nA = a75f3904e564997695b6707eb\nB = f9f47bd779834dc1f5fba0654\nM = b3ae5abed45d09c4dc5abcadc3ac9abebe1949ed\n\nModMul = 88b4d86b2c1e1bd780e8d2499c2221e05fab4f9b7047c2a044\nA = a38eceb9c551f0e69a544072c\nB = d5f8e7c2d534b2b8985bfd213\nM = ff81809b84fb8eed3508ad891d3d8208249d8a902a12d6acf7\n\nModMul = 172f2e2e22\nA = 1584ff1055\nB = 2e0aee014d\nM = b904cb0bc9\n\nModMul = 122c10d3200270b9eaa1\nA = 86fd189e62a6dc1e4ba0\nB = 5235635f7b0336f5f235\nM = c93da97d0e95fb63dc4d\n\nModMul = 3e461e10ac4eb749512097fbf76616\nA = cf4ce10cbca07164f3812f89c\nB = b7e4639c233fbb0f923fb5104\nM = 949647857e1406871593fad5c30101\n\nModMul = 88117b59d9fed79dd6aaf083ee938215a995a221\nA = 94c888795567d434123d441a7\nB = c60ca79e61a352e34e0f78bee\nM = d2553a7c5dccd639a3927697a2e1af03845f2f25\n\nModMul = bc5f0076a8c2f6cc8f4e61540d2d6f6d6b13b775b363dcd71c\nA = c170eaddca5295d6ec6272dc2\nB = f94a5685ced7661df2efbd34e\nM = fa6bc46aa05033af72aa42793e9174af2e3ba38992f33572fd\n\nModMul = 1110cdbe5b\nA = 5db02b38f3\nB = 3369537903\nM = a8863f7979\n\nModMul = 90fcc5f3a346d3d4ea4c\nA = b93373680ea0feeb31d8\nB = 37f9dfaf0e180be64bd5\nM = d595cc29237d1c19e2db\n\nModMul = 8623a9997e514cf3c1d06c33c14053\nA = b396f5ede6212f1fdfc7e7b77\nB = 81a1ddc18306f2d2e84030148\nM = a6be32a91b34857842255ef8b1aafd\n\nModMul = 63f8f0254df06356f5cab8941b77619ad58025ed\nA = 806b2627b08d987438f920bae\nB = 83297039f4aa8efc1a185fea3\nM = bb8a7e7c19be02c25cf5682a0eee655fcd5b69a5\n\nModMul = 697238dbe3d395e81f20c9fcc8db30c234a1f75f3b2bc27438\nA = 930b04224bc097ac1d8bae8be\nB = b79496a80e45212c4663e5b64\nM = 8ff7e19d967d317c255380411898d73e3786269f09079f19f1\n\nModMul = cd93b5b8b1\nA = 47a51b2d5a\nB = 86d6ba5155\nM = efb0ad3643\n\nModMul = 2037821ea789118bde0a\nA = a92215dcae19be637ff\nB = 93b9a3664a406737958f\nM = 9df360b69ed26f610253\n\nModMul = 3bf11785d28ceb668dc55b870faf7b\nA = bc8758854dc48e057cb6210de\nB = f03ca689620a77ecd8a6f0de3\nM = f3ff0747d6e5f34a0ba4200f579259\n\nModMul = 7b30b44f75ed12f54136858ce4fe77d00e0952cf\nA = 993cd09f3e46423a8ba2053df\nB = feabee384158032dd013dc08d\nM = cd0b21388cb2033b1e792ec4078334df70b6c8f9\n\nModMul = 8ce1e17972f1a9d2e9437d0c5219354728a5f31337808d7650\nA = 90e5d18b017118177ffb080da\nB = f8e7e09032574f6c66e623ec8\nM = da795e6ef63ff7dc4baef5c327022ccf65d44e3c4e24823f11\n\nModMul = 8fcd412054\nA = 2e7f9b1a\nB = 6283de2c9a\nM = 9bff560ae7\n\nModMul = 57d0d3b79f1e2f3632fc\nA = 2f8cc403de5af54cfa39\nB = 3b798c3ead52878dfb2f\nM = 805e6cbde400d4b4bc9b\n\nModMul = 23331614e88633af879201f568c359\nA = f21f19da4b20980979a645dac\nB = ea752050b79883dcd69222536\nM = aed3faf4c88f7c4afe257c5ed90599\n\nModMul = 56dcf9ae1c787e773774df3c8762babb4675a212\nA = 9accf901fa599da05fa6ab5ff\nB = f7f6b9b1d7bae06237532e39f\nM = b5bcd776bb2eb0805ade3c8b47e883962d3cbdf5\n\nModMul = 61d0ee0786963906a028a1df01f836841ab6d39d88ca2717c0\nA = 8e57680f213d088ff1a1e7db3\nB = afebecc9943b0093f87022940\nM = b6201f68a45265d7e9183c3255feb4c110c05dadbcb13881bb\n\nModMul = 143ae78a29\nA = 334abb952a\nB = 74203e7a50\nM = c9535a9505\n\nModMul = 897a2b57e69f5a1469ea\nA = 1ec8ca0ea4fed52bdbbf\nB = 3a6273cab05e478a57b8\nM = dcb33163a8ea42c1ae6d\n\nModMul = 4a2c10e90e2d37111db79a44d3e31b\nA = a90e7bbd63fc4af6de83029ee\nB = cf09c3dd50b41afc7045e057b\nM = 8ab85d47e4270116a64f97dc4f0f15\n\nModMul = 70f94276c9d85fd3f71edfaad6051456f754da85\nA = fa3e9ff6e1aa1fb78e51711cb\nB = b115ed197c50b7ec4040ca255\nM = ad63f69ef1346e7549ba71c13b24b279f53bc9bd\n\nModMul = 861e7ef401866f815b983ba18a612913ecc20a67016d79cfac\nA = fc41a9ce06e882942f751be7a\nB = 881c05a51d1ba8134d126a48e\nM = b12200b39526c33b70e8aa23ebc400dea0d4d8fe42be103d5f\n\nModMul = 4e0051898a\nA = 2a06523f70\nB = 651b5044f0\nM = 9da4eb09b5\n\nModMul = cc8274c88d6affc3742f\nA = 9ccf0133f9628532f4f6\nB = c1d80907057be7a67b01\nM = d6e76e362da831f32685\n\nModMul = 568f15bed5c4405be9dd04673a9c46\nA = dd6029c3196feb6da7f0f4a48\nB = a5f6745f2cb64913d1d3236d8\nM = f62f02c9b9ca8993e3be9a02b444bf\n\nModMul = a629452d5ed19df040eca26eaca37d82c0fb1d8f\nA = 963c51a9415b03e85ccb09f25\nB = b1cffe333afe44311cb968ffe\nM = ab2128698d498e8d75455033cfbbf4487535773f\n\nModMul = 814030123025d287aaa8b826792999d72f2d589e0c7f7f3dbf\nA = c3b33f391e78bee97ceddf313\nB = a9136f3af450fdeb245eff425\nM = b6aa9c517eaecb70781e597b907583bbb569e970d229235a35\n\nModMul = 8735bd486d\nA = 563e15c52a\nB = 31293264e1\nM = 92f4b193df\n\nModMul = a541f69ca163b288dd0e\nA = a608b48c1dcaa18424b2\nB = 891b0b296e911068b00c\nM = d4140921f4b2c84f1eb1\n\nModMul = adc1b7cf65967b013d046866b4ed9d\nA = e97941448f65060cf63ecd486\nB = ca68936f76cb87a8fbdd37311\nM = ebbca2482fb82eeca2866057cf1179\n\nModMul = 44aa9f0dd58d4510a7364e130698b34eda23a632\nA = c11f83f01bb964ffac93a2e30\nB = e05ee40eea39f4538d735193d\nM = b5e8b511738979dc740a6a1f7291cf4561787be7\n\nModMul = 8b16b82f064f471983c7154abc9f9ba355111bacb90400372a\nA = acff8da571e1c96810bf95707\nB = cdd23e5504cc26d0c34a62b06\nM = f38902a99190ae0b5ef26849a6e943d651925666fea271fee7\n\nModMul = 193f453197\nA = 8cb3078675\nB = a8fb003a87\nM = b60ff22f4b\n\nModMul = 849c26c8cf5cae426a80\nA = 5d1e3d2b4d038a0a34be\nB = 34f70325565bf0523314\nM = cbc189f9a732cad8f425\n\nModMul = 9a4e64ff530c53a4c6c5b6b5021920\nA = f53b81723cf74f520a61e614e\nB = 9d8ac2e6b839143fdd079a2ff\nM = a115375435151798f3644bede9d863\n\nModMul = aac303a4623e80158af1cb3331965cc8e3184edd\nA = cce0a88606ff962fdc37e72c9\nB = 9840a500a2051625c517104db\nM = b99dafdbd91ec3c05791031df5e193c03d6a441d\n\nModMul = a31401dfa761bbe82b66b5f094151865b18a4ba75bb9b3dedf\nA = e6f48c027284856aaf3b96425\nB = b4c326f72a6a22fd4b93ba5b3\nM = e57d9608ac6e5b129b2c014958bfc59137f63838b1ba88a4ab\n\nModMul = 8b0929adbf\nA = 61fdf77ac0\nB = 8892f05400\nM = f12b3766eb\n\nModMul = 91b57f353307b173679d\nA = 33f8e73752072b4b5cfa\nB = b4c730f79f4f2c07945d\nM = d41be1d8d2e5753e3ae9\n\nModMul = af04c564adfeb120bc4770bc8c650c\nA = af151333b3d4cd1d29fd801db\nB = 9ccaac44ff91be11b30bdcdd0\nM = e0bd6e70d5f5ce08fbbfd48d43101f\n\nModMul = 1b8d623796a5065d9e993a53a9587a0fdbea1bbd\nA = a2fd08df2d4eab0cd6d29e213\nB = 92c9d26ae7c215b52199ee28b\nM = cd529f4cfa46f3bd3e7fadf167fdc02f6f881da3\n\nModMul = 4a8573dd8dc50a4fa39f3579d3869745eb8c1153ca508deefd\nA = 855f941d085305725da617f5d\nB = 8f09b7d2c36e0340523da5421\nM = fd8caa05edeaa81beefa01957eed97a981ab34bdeb6d8c704b\n\nModMul = 2d278e089\nA = 59d20a1716\nB = 8e2a58bc75\nM = b3d61ef699\n\nModMul = 2f937ce359d0f6cedd1\nA = 1019d11d26040ffd5b1d\nB = 7cdb6252087423d43e08\nM = e8f537323004447e669f\n", + "\nModMul = 6567332e25af83089f7458786ab0ca\nA = bf9565e9f8a098894447b58fb\nB = fc867626f268c24cc0ab7bf8b\nM = 930f39183353363dcd822933a438ef\n\nModMul = 3692e73ad1d91ddc19cad3808eba2c5fc88e2bf9\nA = d0a42ce512629f0ffd233a9aa\nB = 97f6d3c4c655c7353a62d6ac4\nM = eac2ea84851f880214b8f40f881a2e56a6ba6f2d\n\nModMul = 81df390c9e51b30bd639db15adb464c7cb1d011cb5e260be58\nA = c237eb242c40960861c938c08\nB = ab2f481f0d768eebd90d2574b\nM = 8697d7a28a5f42c9a7b31949b8b568f861142f44fe66c6cd3f\n\nModMul = c952f9aef\nA = 81973bbcb3\nB = 28ddee3bf7\nM = c4a40993c9\n\nModMul = 241dd53d93f7bdbbb2ee\nA = 2136eda4495c45c9f96c\nB = e74c4baa8ca3f6b7cd5b\nM = fff4594e7a5f0a1d3e15\n\nModMul = 5f861ed8b0aa835761613e6c869cfd\nA = bfc5c1572086079f5f5d18d1b\nB = 95902e14923c8010b7e905178\nM = a819c6c109d623f9b845aa23712c9b\n\nModMul = 5b8ab089c4e4c6804e48a2bc1d218718b3a32598\nA = fbe65d3852224a812c432672a\nB = d57a3f38da966d2471d70a048\nM = b9e6a626d3ad026d14248fc90c882bedd64a1f13\n\nModMul = 761438baf5b02dc095b7040e082da7b167c2b9ace956284ed\nA = fd91701ed2151f8e994bf4ee1\nB = 88b66e735b76972bccd9db182\nM = 8008b2d1274456aa68dc627b1ec3e1762c6ed2d660c64a1a55\n\nModMul = cb743c97a1\nA = 9c69ca9b60\nB = 7488f48f5\nM = d67040ed0d\n\nModMul = 931b2bee1bc30725a31\nA = 650f567b544ce02303d4\nB = 5858da30dd1fae88a675\nM = 91ce30234bb29fb9e833\n\nModMul = 5b4f262cec958a20390b5e568ccdaf\nA = f7e240e8a077e8e87506db2f1\nB = f8653fe64e3bd414782f51634\nM = fdb8225eefc1620648737d31dfe1f7\n\nModMul = 4c011d1ddfa30c901793cc6ce74db47584cebbd1\nA = eda8e9a9ea3cdae17bd50b1b4\nB = 992e8ef4a45593e4ceff67876\nM = 95e2f120cfcefbada1058af6c8853cbebedd5763\n\nModMul = 6e99aa5b8107399848cf24fbd88ed6350efb68d737e505b466\nA = ca6c51ba2f410d09bf71d60fe\nB = 8bdfa8fe5ef3b2ad02bc63c4d\nM = 84daecf412b8c50ad6dfdb546c3eb783dcc6f32003eda914bb\n\nModMul = 536175913582e73c1002083cb0cfce5471d4193a5b717881e60abfe927c829d\nA = 9b474b6f7d7f11dfbeb7a0724694f2daf9ccbaf2ec13269b5ae3329e8df95f7833baa68324509dcddfb5afa1d14f2dafc55e2c225475f16fb396beecc7a66dee\nB = d74a5081f00af2361c3537642c06cd47aae7e366741c9b4785e185af8b328acf3e2ed71e3b9a4b6fd49d956eef76740b3c6ec5850a90e7e444dfeaa7214c5eca\nM = 5efaeebe212752b28b5441a5d0b2600190504467c6359e9ab26320ee72cffcb\n\nModMul = 6161cceee2b74e7965a926fdf5344ddf8cc41994d72154a8b6014c18cf71634\nA = e7d6b74a1af0834aaf93e09a6488340b661449ba2bbc73d775e7d828163813ddbcd82719351879a6d67ab6b518011e1db43a3d620d1f24403917691d15ed6f90\nB = 3ecc8fd3103fe52a7e73ec4be4e60b69584bd886a030f017b482bde9d4b0b964ba8471cb32b3e9bd49864d9028a22d6b6b46be0451bb4222c3987b74a509f8fc\nM = 7c3e3b8b1a6110da82674aaf88c288cef4cfddf22e7c9b75640fd67fa5fad59\n\nModMul = 2acd55bdcccd55882eff0bb262bb62f78bff8e932aefc9d32f54d5d4e9b8bd76\nA = c221d1f0d1b7efe7e078dd01bed773f8876fa324b3fe91985d47d343e7f3878b457dae2f9ae68971245278a1d23cb541c56b94dd9ac43a9fbe28a46efc627651\nB = 49f94c19ff7ce990637c3d2019ed66f7e6dbb1442b04a4593cc480521b991cb1b878f8c31903240f89e34336d9e6785433617e729b71adcbef622a683357e035\nM = 43760c71742e9cf22cae6fc262c008b7f1b97a78c8063957b74aa4cd370c1eeb\n\nModMul = 504c11e38284a30e3647c1ddfaed94503d833bcecdff05e749422ad1d9442540\nA = 3fbabe2d65f443e7db0a6f332330ecc4d1d40e14fcb510499552020405cafcf10a50a5ee47cf60fd8c22a22b3f753b4167c213851f32109babe4b5c298d6c4cf\nB = 62e5b0f887dcb1f1794bae7dad46a066f810cf5f82a1eea99207b5f0fb0ae9084c5e62cc97b2672b1cf4cc1400a19bdcb093c97404876b584a6482931e7ba9b7\nM = d79fab3eb31189268b2a0689cafdaa0826f07d432591e8aa8bd3c7cdce1470a7\n\nModMul = 13a6431c57ddf0ed3979412ba8454a0dd9a2694a0dd76453aae63366c46e41db\nA = 7e1fd0bd9ab0aa75b264475604aea09f24239f94847ce2549d43b71890c0549938d167adebc7890d3c492b5874da7bf18d895ccaf1803b9776820598928b407c\nB = 5e54e5185bc86f16177f1354a57d36ac2980def141b389e4bfda134fae7c158009ccc61ef66281905128b6297f876662104ead2315024f129c56eaa387f80b4d\nM = 182572149b860615dd853f37f7d51a35e85f5e4a4249a60fde58dc68e0dd7401\n\nModMul = 145a44566bd75103083b7556a822ea6008ed3a6a1bf135b68fcf87a294c09b4\nA = a195e4315caa8cc0707063c7359c28139d4dfffb57eb726156336e13227ad9766ea1fc99152893ebb194fecfc153d47cb927a633217328f05e4d8782aeb89d04\nB = a97ae97dc7e9a224cab94ecedc08d0cbf7a012dc5209b1e1e8b5b843fcf61e65db3457d6085545a633be47b742e8237cc716357ff5bce9b00e23671ec1d049a8\nM = 29b060ee2aef7e43e02163d279ce49259127198adf462d13aa195c7dccf573a1\n\nModMul = b00740cef7791692d45f5a7110f3eeb260638f19f87c9245436fc0422de90658\nA = e6b97c11ad44fd451d168d65d1691d2220db8c3b6c8436d59f4c1366aac52558d0d6b61f5d6966460a4a31085fac711e5a09af5563d938963555d4730982eb0\nB = 6805eab5a4da534f07def6d2c320a6cbdfe4831fc2163dfcef740607b3181d8647bfae8f8c16237c1c1c5d14b9e3417132f81b3a7db4b7fc11927aab30dca590\nM = f975a94fa62b4c0e68df5c3ac5917d18927c0a6d9cf39c26f6ed97a81cedf227\n\nModMul = dc04b6ba2eb1e34ea8942a50d1d0c5479dd22109895796ffdc9cd32b53d4764\nA = 7fd3310af09a67e0684dcd8e3b4b651c7c13c2f6a0a47b59a7f5cd8bd80854d1d4fe02eaa61843d6bb2b87f99d8ec4842864681eaf056538ffff610c231e1d\nB = 15f1661c59ee9f93400073e18a91503a93d47537d2da5cf5e4bc69ccc87b07bed171a95f1c5eaa9c7d7ab207ab3f1f7634c5d16e706969e869364207f61d84bf\nM = 22e2856f4c2b6c01448d4aef74aaaee3a14e9660b5b277200f2e67464ecadfab\n\nModMul = 19299c9e960ce15087e9fbd66f95cafe82546431b92d70db1de87c3425c1bef2\nA = 8e3abb1f24e1f91496db99be9409f57f67cfb6e0e33d603a2a31e1309f1d0bbdc413c3e4fbb5e3d923f683afa9942b9b9fad6a6e558b2297889fff47ccef7d23\nB = dbdf5940dcd68127d476badbd5a2f3018aa4d8db79f81337ddfcb108637110b934e946d3284ec09d5255605ad72424f1894238ee4f7964dffc27fad838532321\nM = ab6b4e3d3909512f5d1d62a30c1ab8dd5e584cadbce9dffd12fe203f8936ee93\n\nModMul = 4f88ad4e30e6e8e38cba0452d98d4a3547c680f16308692e33e5577772658764\nA = 5137697bf48982edd869e4a42f3cb858bf65ad5b25d1c0e8b75d054460d0944ecb5a6924721c5728964d84231c7ae808f556837aefb23fe3ad36aec9f5f60f20\nB = c79554304620f8116b9a8bb56f6a23620e9fd504f7163f732e1e6367d25c6ff98cb01d16faf3e018dec6a067d1204a6aa95470598ce757bcfbc3ab4f5d8ec88\nM = 9ba20dd78923d8ef82897ac46a509cf22c9b7986a4facf42e5416bfe3576a735\n\nModMul = 985a4d2a7431e09fcad03e6a3f926582dbc0aedc588f17aa5db40c2d3566233\nA = 908bff40440aaeee6c90b6312dc017c3bdae884a9074e02b26f01be1f018390e01f0d111f99a06c16e20538df8000d4066cd4bb3628da88a3a5cc240cfac719f\nB = 6ebfe9fe53909876784f9d6e5dcca4cfa9463fbd8426c5bb8890ae84c2fad119615fe1e1f2ee5fa544a5ac713ed1da8c1e04f282f1f1b9fba4b4c4bd9db20538\nM = c66842e0a11ed6ad1e8f192ea97f5f244536cfc5234c7fdae1ff905123c72793\n\nModMul = 133d7b31537b627da2c042217cd28625437c28c3e06258427d9a4384046a1f4\nA = afb695e3e40347f60a500e01fba4df1c1f2fd4ed79e3f65913d82369f79d80db6b3978e6351c70c148f572b9c0c2b1efeefa605251b3156d9b66d240467e550f\nB = 8855046dcf50f80f278227d5260b9be53ca2e4a1cfe1afce4d35b11d0fa17a36a8bee8126e13bbb318d476becad5a935e9d160fa481e1437b292bdc169dc7d45\nM = 3eae4f0d6c7e1fb9de1a4c160404a8767783c7f839fe27a543f5c389c679d47\n\nModMul = 7f4576a315bad5c7fbb1616e8b26c5b34ca6f701b9b1adf0485fec181c41dee9\nA = bc2baf0153a4598f6b5f488c43b2546cadfaca2c1931b919f98ba71835a8fe78886da1fea25b194e60ed6f9e0ad23c988b64af9278155c1722dcf4983a1566c2\nB = d8374d91fd3c523ecdd6bdd265c9a8958dd222f9f0e25454fd683bd86d7900a273b56f1f47e033c46527e32c721094ce6bc927d25fac05d7fa6db4d7a6773c94\nM = 9975d8e7f2a4d9d1ff8d442b93ff269a83fee43a18bbfa8c2ccd7ca5fac3a8d3\n\nModMul = 57ebfb39605d4fa6ef5fd03bd8e4fd685664297c29b7ad75a40b133e15fc5ae9\nA = efed8e442154b1eb6c75775cc23e01fa65c9c361e222da123d07daad3039f305e7102edff23b65c333f0caae4f7929857c3169f4ae47c9f0fd920c38eb42bf2f\nB = db05415ea90269a74b0919ff772c148c0eeb2ff9dea76a6e73e82eb86bc76fb42308b55ef83a769a91d23b7840d5d2f5129f15279dfab7cd8d63778acf202f26\nM = 7704390c4b1da86d51ff817003e5451d601a5352296e339e5da219ec5a330479\n\nModMul = 40b6b0d44cf8a5ca7f4fd03dd6e1e2a11f74f3911dcd8727e57db8d65cd490d\nA = 6500f3cf686eec4e1f243616ac0ea8e8d11ddbade490b86baf231e7b2fd55968ee14b6bb7badf8c898874099831976af46bcbfbfaea10d49aa803c6e51238e2\nB = 1fac744fa1e26e789639e049679d0e2eb57336279f09555e10210e7143199a3df5fbf5294edc386ac762fa3a3b0b4bc28945adf21a8af747a29018bf76d3710a\nM = 5c0781a87b84ecb4362b09c623d511de53c085671dd4f08e9a551685b55ddfd1\n\nModMul = 6b778ae9822221e6a8376379e0032d7edb14d7b5e32a7310897b54d1d5626113\nA = c4a5737a9496129a136753f8c2e52bbd2660f2d3fafe4ed702900b01c14e506d13e3bbeab19b357e5ba9fce8a4fc3dcc469406a16248d6fb53862781fd9d55e4\nB = 444e5a673eeb37fd3b4f6b6f5133b0f46c2ea532e1953da4a0e144407a8e2534c5ff40cc9af7756e5aff9df57d938fcedaffb", + "868dcf4e458b36f506ed7fe0ce5\nM = 7f5978c0c066132a9bdcb00727bb802b72777b9e8e4265f76b80cfdc3a788817\n\nModMul = 5c717e5dd25abe60f761d6f9326ed056416add4c1384682d87b7ff12e112f855\nA = 4351965a421c75c5b4c251861e53316a300ed7983e27e17f9308420f0d2cb11e9c476294fcd9042a525bc1a044bb442d1d9f853c9e07245170e0e2711010cd1c\nB = 4e1046647c362c8f9c414be54075b4e9d151c6fa0c3da40d90e6042625947ca2c9f20cfbcfdab8666dac5a15f6cda9d47b09f654131fc5addc07e382c9639323\nM = a6c789884c66c7f028099e0367b3ed86871277bf070c541ee12fc02fcb6181d7\n\nModMul = 4452688244f542125168853f1d444f96ab0f82903bb12a97e59f0db633edfd6\nA = 9fd1cc81981bff977244c044146918057ad06d3cc26edfb8fb4118ee02b959d45555f9507ffeb23c3688e29ccdfe5f583fa3761f6727573542bee8ab5f5b600d\nB = 856e6a03b5c93fc19deea51b3bfe42c810c5bcf9ffbd08e2625eb209baf6a4e24943a3c090d89c1f70aea9f0128e511fe92e03715d917168c1e1ca77a3a8731f\nM = 2c245d407a78903ef2b279ddbe32106e6333b6f44cabf87b8641b047c79ea06b\n\nModMul = 375f8474ee47df6b9a038512002e56cddd374d69c69719d8d369232c64a839e2\nA = add40f1dd6d4a2414b17f0c628eed9a8f082f3ad1f34ec41935fa86b34d4505b22ea80c062386a9ed63f95c67e55c686f837bddf8f4da791f98b08c02f32d4b2\nB = dab1caaa11d5a208b7a6b7a1d6482a4859daaba5e3a77b1b1020e8ae62a664953dfddd0b47d40526e7a3c6a5363c6d41dd9f529fd8b58d5d31bb67e745cb71b3\nM = 4f506313a4f49873a405f2e5a6e9cfae9cd5e9f67b5ef900153366570e28a955\n\nModMul = 36fb0733a26902f0f8f11625305a3c94fcdfffe294eb6ccba110aa628a314df\nA = 52ee1498bd6a1677db801ae2eab4951345a1fcf8fe7d38e3f28dbc27fae508d87c9958e02a375ff4891b88ee916b96331e7cc082615faa028f6d541b5ce37876\nB = 9343cfa074f50c20e8472f8f7c4a7d330aa30ee417ed8027a4c956e84cc5cb31d5411c14796d9325fceef79a51b5d8a4c89182ca273ab633e6a7b22a27352300\nM = 9d7c334aa33634f9f313b71b42476a3b627a6c5bb8ac1d07a8d732d5c087bd9\n\nModMul = 4a377267508eb045e00cea66a417112dac07545304bbeac6315625275b7cbfad\nA = 19616a82b75b08499d4b1f869df2db8f71398672f3f97ffc6177a4a5aa913605ce8a6ab5f778cac508f0b3f2aa680b01ccdc57c0fdd6cd678a2ff2dcd7f01f3c\nB = a5643a9a9fe3be4134082daae4ee7dfd85d9452beee856fd939d3be9788b6bebcf3571c67ec481ff9b20f70d23e82e2171b1d0ddf0a9435b40115d32aedb6811\nM = ea0477e7f1a02cb6c21171066f3dab69d4e24429043b0f049de660fc80e51937\n\nModMul = 7952dfdb91252658430e365adeefd9093740de92cfc9dd3d92294f2dab6ca0b6\nA = 8e6cd7639b7c134b53e6ae6ac5f51268da83ed09e8e96d65e4bb130dcdbbab9e48226ddba6efe93faa510bde8ee92f2a641774c4272b5a2f88024b77a2cfa110\nB = fe4e8109a49b16b96871e384564cc096277dad4e1bbca8e5feb33f140a4fb800c8f3096b1bc7042bccf249aede88e6055c0db609f94e214b1251eda494be724b\nM = aa46853682af960824140c35d145a6dcff6283b2c59994b30ecf9b8def41a025\n\nModMul = 1aacec7f7e66b0cf4eb2dfda9d8d3fbf4eb8e928cbbc967d13e3e38612f0346d\nA = b0fd7a936b0908ba6fa797e4b855d673ff85d665ef3a345e560e2c0049becf5c25b6c0068dd617ab47a8fd151939ea0631f86806ddd40e557933c0e880fcdd0b\nB = 105c87fe2b1bf0be5405ca0d530beda1780f0045e892d7810f8a8abbe890f0a19de66497cba55bf38e190c52992467c22a320c38a4bd167f774ed812f1271d5a\nM = ac4f0a2b22df691331ded955a5d0e7d1910d7920a59d4a87636b2635397b7335\n\nModMul = 2c25d180156fa7d2fc20c9bd6d9ff0b111c9ad76ada0784e2f8fa0bd06413f66\nA = 2aa4a0a73df11f4e60956619d0b35eaef45730d619f9b920298e6d369b9861f6411de28a34af038f288d7a3d6a35b10c8082b8ad0fb275a8f67c6832ac46ba9\nB = fae1d50b72feb25da2581829409391bf289cd9f730c99d265b5b2d63889381cde4adbf85c3998c2478f2866526b8f64605d75765edd09b78ea45337207d173\nM = 65c9d79a09a820adbc9beb152bef387c1439147ed50cef872d36a69f1c7d5fe1\n\nModMul = 56ec8624fc199e7b4e68358f88f1a99f1d4d02577b8c6f7e28e4ccfdd981f995\nA = b0a0f9d05d144d2ef257c1e63a7127a3b8e0d8b64ff8f6447618560593574b5c5da6258b274efc28da0defd988bef1efca0f481f809665a78954b36741d668bd\nB = 10901b9dbf0016cbcc671da75a75b7a6ec6a66dd17b53a97344864b08f037098537380bfb0137b6becfc36a75206686d16bc4eb8fd54299494374e3f383d9b10\nM = 73882376ca850c125ce9f20c291e550ee48f0eb0d571109ab08c22d6719496e9\n\nModMul = acceebe131aa34ff21b3235f045bccc8a8f762dca20c1dd1ef6eb461ea971c6c\nA = a7714b249eb0f0cbe3e6fa0b04e895fcf14c404876197defafc6b57026ae7e5e993fc47c1819581adc03860ce07f2b7877a3f6d0912c0cbc659f5f6170a1cb2b\nB = b7278ecd154ef5243ad973ead291ea186acb63e09977e644a6a9fde195d1a33993fc47c1819581adc03860ce07f2b7877a3f6d0912c0cbc659f5f6170a1cb2b\nM = c52ae49e1a4b21ec392b76844ad559653b7b9f67a58b3bba6c2ce250017eab09\n\nModMul = 62b5b04dc84bb4ee04934c03ef361bc6e59b42144dc117b9f7771525c67c3688\nA = 2b65f491caf0b5cd9c66c859fbcadaec7213e6b848884638791b1620d6e4bc9dde087af0e7329d3b15a45df2d43ebde61b053ad7f63917aa922d58b4f3222620\nB = c1bfcdb34b0766be980540dc3256b9ee4158310fad2c43cf24bfafca08ee185647043f5842a9d9eda224449259341b7c50998086434528d47661bf5762a7ab5f\nM = f73398c32191b436d14a0b76c6069b1d61395568753c832dd0c707780a232dc9\n\nModMul = 5613c8fb0721bd3f605089def48fb2c38a4862bb387886c1edc1bc37d10f0e15\nA = a3d8b12a2c8f4021ca045a4e4903687dea63ee7e88893b1911aea77efbff00f8f5c7884cbafc71f59fa2636195c2ebee61edbf642923f34d87ba5eb49b06a7ee\nB = 3231829c81b26dcac432b502ce22e126ab564922b1e9818cd3da46edc5ce7df026d0e515809c97bcfdb9666581efbfd364437ba9959dfad099f90472f97c69ec\nM = df8344fa848d1066afe4f8d985cff65441751677dcf3a4e99b40365fc3c978e9\n\nModMul = 30325f7ccbc2c69e11d739ad7132a947c53377aa902ec70b152f3a75e050c244\nA = e4ba620125f58a63fe12fbd3eccdea477d56b120c76d5d1421bebd74e8686b4093f8169070453ccc04b63b173568385313a1d9c841a4aa82a61cb84d4286a941\nB = e87aaa990307855f8e5f2e5509d2ce31dd4b13bb7199cf5fa0593e350326e222efc33a26c69245565d6ebb5a484cfef7d2558f22dea8054d07831d536803d0dd\nM = 43d57108eb0ab9bebaa8ce137628ea825951c6accb9acb7f1e991c93b8563897\n\nModMul = 1975db7b72434ad32c9aee412645f6670b7f4af1f8a424a5031c559d3e18dce6\nA = bd64b1db27fa7da4c92a4ee092f58a2a53ed0f12d009fe13b36d5fd585defe778fafea4a60e8fe567d03e9ba3b72b189e22504ae8ca6aad7c2ac0f44abca2f6\nB = b487d8116198560d6c5b08c7ce63b0acc0c98e6f2a8d709cf4e3a409edd55f64d72fc27a70dc341e280ff5a1b09fe131773d466cb31991d2db23a2a86d225c80\nM = 39d57af763eabe569dac1a103e169e6e3b4375168e41e5c3b961b6e743915923\n\nModMul = 3bbb5bde9e3e240694326571360090e1fc0a4ea7b2311c1e0bd3961f6c159385\nA = 4181ee3bf9a98bcd49eaea243a179cddbf160981efc720685c7be1dfeb5aa552685a2cd46f340e1e1da893b3b460692fa2eaf6c100f24a14f239e45123242d53\nB = 77cd04d86dd5da322af78be54246dd6b7af490d903db1db03cbccde535570b81c6053a84110c07f097540ffe7510320024b7bafb77e9e239761def76092e1d59\nM = f3b9833a303eb540cf8b6cbc3cf16394b1634ef517be57684e42d364d8bec3e5\n\nModMul = 2d8174211f0367233b3a8df7c5bf0066d6aa792be7cdc5e850a477454d5c829f\nA = 1c08cec52d96136fbd9078b7b8db36ab63b86e19dd3dba7b2e3190ff566180e89dfee9423fa4e99be2187eda6aedfa86b9a45eb1e4655257315ae6a280f0a6ee\nB = a8b4bc9647d8df9b7c76cc6d0f2248cdbc41f5da9c061f9864aa8415c9557582cada456cf23cc32d47d1fc1caf19d36b398019aac4734e10f55ce3cad419e5e7\nM = 7eacffe21f88413af94155a2a8e37f70a431a59653738afda04a1bec72d0d9ed\n\n# Regression tests for CVE-2016-7055.\n\nModMul = ccd6f75b5f24b7c5ce2ce755fa89c2450c6a7d96ce8c8791e659eab84577a7695e3b2caa7c980fb23f60634233e9798499c28b0338c1f1a326d0ca89fd41f2fd88b759f317889832966b551a950043ec7a4b6152d3e2cbfb40e88458e70ab783b96f12d271f828d5b39e198ccaf8665411d85026282dbead5d24cd01b6c8a8e9\nA = 7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878\nB = 095d72c08c097ba488c5e439c655a192eafb6380073d8c2664668eddb4060744e16e57fb4edb9ae10a0cefcdc28a894f689a128379db279d48a2e20849d685939b7803bcf46cebf5c533fb0dd35b080593de5472e3fe5db951b8bff9b4cb8f039cc638a5ee8cdd703719f8000e6a9f63beed5f2fcd52ff293ea05a251bb4ab81\nM = d78af684e71db0c39cff4e64fb9db567132cb9c50cc98009feb820b26f2ded9b91b9b5e2b83ae0ae4eb4e0523ca726bfbe969b89fd754f674ce99118c3f2d1c5d81fdc7c54e02b60262b241d53c040e99e45826eca37a804668e690e1afc1ca42c9a15d84d4954425f0b7642fc0bd9d7b24e2618d2dcc9b729d944badacfddaf\n\nModMul = ccd6f75b5f24b7c5ce2ce755fa89c2450c6a7d96ce8c8791e659eab84577a7695e3b2caa7c980fb23f60634233e9798499c28b0338c1f1a326d0ca89fd41f2fd88b759f317889832966b551a950043ec7a4b6152d3e2cbfb40e88458e70ab783b96f12d271f828d5b39e198ccaf8665411d85026282dbead5d24cd01b6c8a8e9\nA = 095d72c08c097ba488c5e439c655a192eafb6380073d8c2664668eddb4060744e16e57fb4edb9ae10a0cefcdc28a894f689a128379db279d48a2e20849d685939b7803bcf46cebf5c533fb0dd35b080593de5472e3fe5db951b8bff", + "9b4cb8f039cc638a5ee8cdd703719f8000e6a9f63beed5f2fcd52ff293ea05a251bb4ab81\nB = 7878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878787878\nM = d78af684e71db0c39cff4e64fb9db567132cb9c50cc98009feb820b26f2ded9b91b9b5e2b83ae0ae4eb4e0523ca726bfbe969b89fd754f674ce99118c3f2d1c5d81fdc7c54e02b60262b241d53c040e99e45826eca37a804668e690e1afc1ca42c9a15d84d4954425f0b7642fc0bd9d7b24e2618d2dcc9b729d944badacfddaf\n\n\n# ModSquare tests.\n#\n# These test vectors satisfy A * A = ModSquare (mod M) and 0 <= ModSquare < M.\n\n# Regression test for CVE-2017-3732.\nModSquare = fffffffdfffffd01000009000002f6fffdf403000312000402f3fff5f602fe080a0005fdfafffa00010001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000002000002fefffff7fffffd07000109fdfffef3fffdfd06000405ff00fdfbfffe00010001\nA = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff00000000\nM = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff\n\n# Regression test for CVE-2017-3736.\nModSquare = fe06fe0b06160c09\nA = fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8f800000000000010000000006c000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffff8f8f8f800000000000010000000006c000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff00fcfdfc\n# A in Montgomery form is fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8ffeadbcfc4dae7fff908e92820306b9544d954000000006c000000000000000000000000000000000000000000000000000000000000000000ff030202fffff8ffebdbcfc4dae7fff908e92820306b9544d954000000006c000000ff0302030000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01fc00ff02ffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00fcfdfcffffffffff000000000000000000ff0302030000000000ffffffffffffffffff00fcfdfdff030202ff00000000ffffffffffffffffff00fcfdfcffffffffff\nM = fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8f8f8f800000000000010000000006c000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffff8f8f8f800000000000010000000006c000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffff\n\n\n# ModExp tests.\n#\n# These test vectors satisfy A ^ E = ModExp (mod M) and 0 <= ModExp < M.\n\nModExp = 00\nA = -01\nE = 01\nM = 01\n\nModExp = 01\nA = -02\nE = 01\nM = 03\n\nModExp = 01\nA = -01\nE = 02\nM = 03\n\nModExp = 01\nA = -02\nE = 02\nM = 03\n\nModExp = 00\nA = -03\nE = 02\nM = 03\n\nModExp = 02\nA = -04\nE = 01\nM = 03\n\nModExp = 01\nA = -04\nE = 02\nM = 03\n\n# Regression test for carry propagation bug in sqr8x_reduction.\nModExp = 19324b647d967d644b3219\nA = 050505050505\nE = 02\nM = 414141414141414141414127414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\n\nModExp = 208f8aa0\nA = 86b49\nE = 2\nM = 30d26ecb\n\nModExp = 27308229\nA = 17591bb\nE = 6\nM = 30d26ecb\n\nModExp = 2bdf498f\nA = 21292626\nE = d\nM = 30d26ecb\n\nModExp = 11317167\nA = 4a655df24\nE = 10\nM = 30d26ecb\n\nModExp = 2e1b88e\nA = da6b761a86\nE = 35\nM = 30d26ecb\n\nModExp = 20a12ec3\nA = ea811\nE = 2\nM = 23bc042f\n\nModExp = c42ced\nA = 1011a6a\nE = 4\nM = 23bc042f\n\nModExp = 4637d79\nA = 28d9a601\nE = 8\nM = 23bc042f\n\nModExp = 20e5669b\nA = 72fe6bc20\nE = 11\nM = 23bc042f\n\nModExp = 142ab9e3\nA = 9a07b9363c\nE = 29\nM = 23bc042f\n\nModExp = 14c64646\nA = 822df\nE = 3\nM = 30915765\n\nModExp = 160e35a2\nA = 15ea542\nE = 5\nM = 30915765\n\nModExp = 2f23a488\nA = 34d2e02e\nE = e\nM = 30915765\n\nModExp = 28e67f93\nA = 636a32703\nE = 14\nM = 30915765\n\nModExp = 29bfeaa5\nA = c8646998e6\nE = 2c\nM = 30915765\n\nModExp = 30959e22\nA = 81dad\nE = 3\nM = 326dd68d\n\nModExp = 1a1da4fa\nA = 116adb9\nE = 5\nM = 326dd68d\n\nModExp = 272bf0d8\nA = 2d21ef08\nE = 8\nM = 326dd68d\n\nModExp = 29f5054b\nA = 76989850a\nE = 16\nM = 326dd68d\n\nModExp = e6c7b77\nA = b88ee70d2a\nE = 3e\nM = 326dd68d\n\nModExp = 369605e1\nA = cf26f\nE = 2\nM = 3ce082eb\n\nModExp = 168a3c5d\nA = 1f82caf\nE = 5\nM = 3ce082eb\n\nModExp = 125c4bb8\nA = 2e9c4c07\nE = 9\nM = 3ce082eb\n\nModExp = 1c5fe761\nA = 523ab37f1\nE = 14\nM = 3ce082eb\n\nModExp = 21703009\nA = dc832165e8\nE = 20\nM = 3ce082eb\n\nModExp = 1228d1e\nA = a5555\nE = 3\nM = 24665b27\n\nModExp = 5226af4\nA = 1077bd6\nE = 4\nM = 24665b27\n\nModExp = 1b14eac1\nA = 2db3a834\nE = f\nM = 24665b27\n\nModExp = 161727bc\nA = 6bd962cb6\nE = 19\nM = 24665b27\n\nModExp = 10d61d0d\nA = c10caed407\nE = 28\nM = 24665b27\n\nModExp = 233da406\nA = b125f\nE = 3\nM", + " = 33509981\n\nModExp = 24032799\nA = 1656b7c\nE = 6\nM = 33509981\n\nModExp = 129ecebe\nA = 2e671504\nE = a\nM = 33509981\n\nModExp = 20c20bac\nA = 4d7a2de44\nE = 1f\nM = 33509981\n\nModExp = 2e3ce9d3\nA = c53b3def4d\nE = 31\nM = 33509981\n\nModExp = 12fadfd6\nA = b4cf8\nE = 2\nM = 36e9d4ae\n\nModExp = 457ac85\nA = 1b1c7e9\nE = 7\nM = 36e9d4ae\n\nModExp = 31debef4\nA = 3a973028\nE = d\nM = 36e9d4ae\n\nModExp = 2333ad93\nA = 552b97c45\nE = 11\nM = 36e9d4ae\n\nModExp = 99ba1fb\nA = 8bfb949cbb\nE = 28\nM = 36e9d4ae\n\nModExp = 27b691de\nA = 93492\nE = 3\nM = 298fdb16\n\nModExp = 3c2b70f\nA = 14e7b0d\nE = 4\nM = 298fdb16\n\nModExp = 1486cda7\nA = 29acff81\nE = c\nM = 298fdb16\n\nModExp = 11725275\nA = 507489205\nE = 13\nM = 298fdb16\n\nModExp = 24d14627\nA = e71c55606d\nE = 35\nM = 298fdb16\n\nModExp = 222b8d14\nA = 9b1a0\nE = 3\nM = 3db59d12\n\nModExp = 3b8bd47d\nA = 13f4e8d\nE = 7\nM = 3db59d12\n\nModExp = 17e72356\nA = 334774ce\nE = a\nM = 3db59d12\n\nModExp = 306447ca\nA = 47079ddd2\nE = 12\nM = 3db59d12\n\nModExp = 90bef3b\nA = a75d62616d\nE = 37\nM = 3db59d12\n\nModExp = 1\nA = cddd44f47e84b3276cc36a5c0d742cc703e61c4756168601fbb1b6eb598c161019562344dd56ab6f603d920a12c360b285e6496a3605a2f8d691c3598233ee9366b5f2692554893bdeb67b7bdaf35ab7273ac593145e26bed82c70ba5793bf4bc5cac4c80b01785d1496beede493806e4f4aa89fd8d41de80dd6d0a3e2742678\nE = 0\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 0\nA = 0\nE = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 5150fb769d5c5d341aaf56639a7bcc77c415fe46439938a2190283409692f29cd080bfe3433005d98d24718a03a3553c8560c5e9c8ed0f53b8945eb18290e1c1a83d919302510f66dd89b58acc2de79ad54b8a30d3e1019d4d222556beefca0821b094ecf104b5e4cfce69d2d520d2abf54f3e393d25ed3d27e8c2e3ca2e5ff9\nA = ead8c5a451541c50cab74de530c89376d9a55c723e0cac3c84b25f0093c08a2961e49ab48966361c42c9f99111587252d98395b76788400d75c66ef208ea2767a28d6f8dc3a859f39c95765d57f139e7fc14f47c908c62df051e7216d379f52028843b4d82ef49133cce8fe671ae179423ac8da5be43b01caaf425cd969300cd\nE = 8de689aef79eba6b20d7debb8d146541348df2f259dff6c3bfabf5517c8caf0473866a03ddbd03fc354bb00beda35e67f342d684896bf8dbb79238a6929692b1a87f58a2dcba596fe1a0514e3019baffe1b580fc810bd9774c00ab0f37af78619b30f273e3bfb95daac34e74566f84bb8809be7650dec75a20be61b4f904ed4e\nM = c95943186c7567fe8cd1bb4f07e7c659475fd9f38217571af20dfe7e4666d86286bc5b2bb013197f9b1c452c69a95bb7e450cf6e45d46e452282d5d2826978e06c52c7ca204869e8d1b1fac4911e3aef92c7b2d7551ebd8c6fe0365fad49e275cc2949a124385cadc4ace24671c4fe86a849de07c6fafacb312f55e9f3c79dcb\n\nModExp = 1\nA = 935561297d1d90255aef891e2e30aa09935409de3d4a5abc340ac9a9b7dce33e9f5ce407f3a67ec30e0dc30481070823f8542463e46828d9cafb672a506d6753688cbad3d2761079f770c726c0b957071a30876c4d448e884b647833befbcd6b582787bf769d63cf55e68c7b869a0b86374f8920516cf5d528f348b6057450a1\nE = 0\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = 0\nA = 0\nE = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = bbad67352704a6321809f742826bf3d1c31c0ad057bf81432abeb30dc9913c896c03e69eb1cde6b78ffcb320c4625bd38ef23a08d6c64dc86aec951b72d74b097e209ce63092959894614e3865a6153ec0ff6fda639e44071a33763f6b18edc1c22094c3f844f04a86d414c4cb618e9812991c61289360c7ba60f190f75038d0\nA = 855144760f2be2f2038d8ff628f03a902ae2e07736f2695ec980f84a1781665ab65e2b4e53d31856f431a32fd58d8a7727acee54cc54a62161b035c0293714ca294e2161ea4a48660bf084b885f504ad23ea338030460310bd19186be9030ab5136f09fe6a9223962bce385aaaf9c39fe6ed6d005fa96163fe15cdfa08fc914d\nE = bb552be12c02ae8b9e90c8beb5689ffefe3378d2c30f12a6d14496250ecce30317c642857535a741642c3df689a8d71a276d247ed482b07b50135357da6143ac2f5c74f6c739c5ff6ada21e1ab35439f6445a1019d6b607950bffb0357c6009a2bfc88cd7f4f883dc591d4eb45b1d787e85aba5c10ee4fe05ea47bf556aec94d\nM = dcc24236a1bb94c71d9ec162a6aa4697b932717e82b667cad08b6bd1bbcbddf7cd167b7458de2b0b780486b39574e749d6405f9ede774a021d6b547271523e9e84a6fdd3a98315607ccf93356f54daa9c75e1e311e1672d0dc163be13f9ed6762f7dd301f5b0a1bb2398b608f40ac357ae34fc8a87d4fef3b961cbdb806d9061\n\nModExp = 1\nA = 9d92629c1ab181c50c31619e8acd0d235a1f5fc7a0bef4d4fd54b4f1968d45921f8522efe88e69c6c14c576c564592b9feb00d1554b88b038934eaf4a8ce81a2582732387490181ef158360c8b2d9ccb326ffe043f776a50cb8202837f08ca743b562eefa007150ab7012c341b16248478d4775c02ad71ea13d5e82b71e2d600\nE = 0\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 0\nA = 0\nE = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 24eaead5b57883c2f454928f8edd470a344bfe07a953194f7d635d705ef13ddfc64140c8ad6f363d4c828e7c7891a6b6d4df37335de4552c319dafd1c06d1f743240082a3535df4da1475d3eea3fead20e40815fd5a0876c881c162ab65a1eda494280c258901ca953d1d039a998bf0e9aa09273bbef4865f3054663b72d75ff\nA = a31618b4532f53729ba22efb2221432fab1dbb70853d6a1159b42fd19fc949965c709b209de106a652aa422d88922ce51dae47f7f6deaf0055202e13db79ee84fc3d3c6f4c003ef96597c49d6895fa53c22ac9e4819f7048146b5272f6279424fdb389819a0b251c823c76f4bebf4f1246de455aafe82a0d34454f5039e90839\nE = 9f43dcb641f3ecf4dbc97450f2bdf3b7ec6a2f3e8e96bb1df2bf34b8d2d78e1a9018d04d960ffd0e932cfc60d3b9b923e3f9f29b3f3d61cae3a9f7245078143475c7fcb896ff200f7d94c4f2708bb42750e37c185a31c876814e4f06a00771707654e1da2fb69c16b6500b16385e3b933e2276ad3569977473f699b1c7926c3b\nM = cd607549668469b792f495c141e500871880b0611c8004293a561ec7f9ab6561f8a9b90872742386adafb5cd1890e8204ae12aec529cca0a9e382c96439137f09de9973b12c8492c62847e107deabb7dd946ffbb9d0ac73b462c481092bd65326a17f21d8d6527c47a5dba50aaa20c7048b8788a49eb3ea5f29bd5cfce24eb3b\n\nModExp = 1\nA = a8558e7f455b27c0c46d7d0862eb409cdefbeca945e0284b5bf425b7ac0f3d316bc365594cc1639decffc621214d61479bc75135120d4ac09ea8b742ad7ec1822091b62b1c6f564fe5e2f4f5b7def92cbaaa9a898549207ab01b91c2324fbd306a87f7d6379b6fb6493c5fca76729767f136120da9c90bdc7d364f7d242d5acc\nE = 0\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 0\nA = 0\nE = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f", + "\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 292f0b39ca0f1c850b1a00cffd2d54924fcd5fc7e7504c9d593e6c0ff74760b1f4bdd81679fe06c50248336f3108c593fa111072ee87d0fcc89a63243a1dc89044503663eee9bc18f51c3e0193d9108303e12ac90ff78f6ec752a4386af09c42db524a7cbe9a3d4fcccd56c34d283bcc9debc17158b5fe8df0c1888a9841bf8f\nA = b4fde2908745ff92cc5826a27dcfdda09e8fffee681844fa4c7f1354d946d5d84e0e0c7a4a4cb20943d9c73dd707ca47d796945d6f6b55933b615e2c522f5dfc33e0652917b4809bab86f4fa56b32b746c177764895492d0a6a699812b2827fe701d40ef7effd78ea8efe1cac15ff74a295a09614bf04cae1a5017872ba22efe\nE = a5524b41dfc6b570df1d8f6633ac7777c1131abe3a99c6166b0d29d3b8883c41b00a0c53cdd6f42820bf05c810b6ec53e77a8c1b9344ea0c91d4f410a2f204c369f3db33bf8c88217fc2cf802a9d9bce8119242d8e781875b85431be170076498c0963574ee423551aec9557e2fc672ab1ab5d0cbb1c400535df9481e7934d8f\nM = 88f3c87ac5e3272a21b8a858da640d6939fb8113a95412c38663a0f352686d69a5d7927e60b484b9fcb8ef12978fe25ff2ebc9b61c5450e04222ef20ba3cbbdc5ec45581ce0f58e10be7bb9de7fa08752303a7a1db23b2ac9c6692ec63bf09ecd6639e06c5491ba568ea886620d71da32d329615f0e1443a75d09ae35b8a2d7f\n\nModExp = 1\nA = e2845c572b46496ac158a731f612fd40ef626fa7134755c25b1b7614f4d7b29164e6142ddb7985e4c7ebc575855ff901e95927fe98a5aea2ad3a4720c75782323bea1518b2c57790f44efd9411be4e95b3896bad1e73c59658290b309e5a7eb5ef8be08125063e57336b80f17eacee88966d12bbaaa15a25929c82e027cf696f\nE = 0\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = 0\nA = 0\nE = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = c90e4c69df92e26549b016950b59080947f5403430698e128477782480dd70be96bed2b9042dd8c708eb432e02710555b97af11ce6fa9b53395022851c32d1f53f04237fb0763563b440ca6e81a50d909d907d9c26b7d3c420dbf88f7dadd488666848135f8cdc608dcfb0691989289fb54379c2e84c262f9765f68c012ca1b9\nA = 882ea1b9b6c79a3b1bdfd284658cb6227ad825e0178cab713c7413c2ec34f03cfaec470c4f5c521f5e9899a2123878ff0f5b36a4196c08ad1b04d03746c4bfb5d126f5eefbfe172627d6732710a8ac8890cedbd4fdef69a19f2b3253a5aa0e5dd5484f72d59b17bdd1dad3db209a3ab839368ed3975069685911d7b35e41a9e6\nE = a55703a72ca3f6074b939ed3d748196a684a3c8e411c2b39a9beb98993b6eb7ea3fa16f41bc5b5c3710b91c0fc74a8072793052f872f61695db3a2df872eaa427a110f1a8d568c85d58bd350d0df8eced7a10be80f7567360c1a8047b9c44aa2967cd0d9dd2caea2c1492358c2db4f0214da343fdf2e34272865dc5c63be2ae4\nM = cf0dee80177869a532f0c6c3a0bda3aad79bdb6b70b6c227b32d75c26e394a90c1f2a6c2bb841ba9f6556b15654a79d8b1dd0c90709a093497bf40be0807cdbb378a74de5893c25067224d3ea8d37387ed6c4a981138853cb89caa9ce6cd0f6a1e95de24d558e90960f93844db4d01e372650350d45a9d34a36042b4d4b9e78d\n\nModExp = 1\nA = d7a99e65b8af86b1c51d851f0447e43cd4f343cb0ada7236283e69aa7ebd383826acc9809e5dbc4002d0f2430022cb026458189db3805ce2de1142a31ba71a6c064ab51f0059eb4b931b8bcbaef023c38d57aa5f3e14f5df77e547fc028702071b58bd57338be1e1e4f98d3553484e4de359cefa29c5f58d3fa5d823f389dbef\nE = 0\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 0\nA = 0\nE = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 186c50ae259aa0fd31859cbcfea534e626a254de33956d5d719334bb32e7cf37cf199a21f079a5b90497228994d05efe19ccd8c769cd81f896286e8ae557cacd1630a928c629ecdfece29ab3697794aa707734e007318fa7029b050bb09ebbe6986187c6ca843f55266d275620b3f0fec0ad5f847ce8b314d929d128b33a249e\nA = 9d5e345793faddca9867f23eeddf6816c1e837f7a2cf96fa077212514acb6be87ac01a237d8f2f1d07d27a8ddd1b0ae0d97e1bda4f205a89435017284cdedea3e407b1b940d6f52112b6359b3e86e4c83074b17c210ae2c8856b42b169b4a7a6dfa65b368a7959496cf9bb1ee93d019dbd79101830e3f5ed08604ab90890b914\nE = 95793fe33696f53e37498b2b65aaf27079e27acf1da97dda2c3e0803e8a02139f574e04ee03f7d1ddd029f528e3f3644515ad6f10f0beac2767f23d9cd8a8b9b6c6e376e36b64a0ae2711d7d31a5a75011641935b503110edbefe9f0ff2da27b5c5f6bb8cc151fdc86f67191bb99160c6cacc86ca368d5bdfafd3f3ff5161b1e\nM = 8315dacf124bd473c578946347e83d1b20c750a7d9533d6215591be40bc78bcca77821f8c8f95375bbd6372515ada63d22bed2fa49bd6fabb0040c538d08db25b09d2fda02a93ab086cd1c27df93c37ee9c6a0527d089179b8f92b5dc3acf5ef1c75906fb80b03f5c2442a7a4088640f66376575ecfa4c697c1a571397ee5a0d\n\nModExp = 1\nA = e6a079bdf7b0638d50b183475e9ddfd5cbdebfb29f5fae8e9be402a0bd36085737b556492ea7fb4b1000ae9ce59db66098129b757cfb29224275fdaa46b8b7eb18a93ca7d3e446dc38c734b683d7ba7927b008d993aab01f44239d3c76be76d1503908e9b5e73b36c43ae0771368b01f39c042693bd92c4fc50810f059e1b332\nE = 0\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 0\nA = 0\nE = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 60719701a2dc0bcde281a93ce0b8421d1a718adee43c1b5d9fe9e697a48ab3db4f9f33c73cff305ab6b6c300c149b05c6b289dce4580860dc56bc59de81ac074ecebdc65aa3ca040b44e5b3c80ddba1658d78b9abbc4c77e5f171f5582e70ab4438a8e1e2f062d618c4ad09c70c73b5b5fbc9f8f0bbdf1d530a933b705f85af8\nA = e1b400cd3b1f2f1c6b437adfdb970d2c8108f1b39bdbb13582179552011c6c97cba6bff2c463212b7f62776aa3e3aff9f175990e79395e819c144350b0a23d61638d500ecc97726b098e1af334aece23a851c718612442c04eb7b3805a24cc8f5b90042145eb5e5d6a408092832b6bbeb8a621419a9282fb5c075f41c7f1fdc1\nE = f0460c5ca9b3a5c2d1b93c201d020dc43e1c81d1daba432e2cd310902da23eb81a5172b0b357484eb8fa2c04c270893b8198c8ad35453405dadaf05195b3aeb5ec0ccacecb4b6227ca43b27b97e240a4148a472670ed60f304302f757495fd4a91af0fe09800db0c3043a6ae213bee6703ad80523ca433d99ca0eab1e0b7c929\nM = 81dd561d5d5327fc5ed7c9236b5fb21ef713c6d5e36264ba65ccc801b8eb107b714aad65bb503bb1f4721c0a6f97e5ab89300f049f42a4616ae43d29c089c286687484d18629c1be1b5befbdd0b3cfc86b1d28add89df4cc5e68dac3f56f2490a9068ca9c634ec258c030ec5023baa9133fd2af32fd1112895f9da549d410247\n\nModExp = 1\nA = 9dd1e6f2d3ff24096b54e0ebf0f10e283e484a1cbafc0431adda1296ed97692f3ba99440fd4f67c96dd8bab850e1123361c99362df9ea205ff8e90d1b329459f54730992d5a360e46fcc5f5a909e691abb9a06613d6991bd7c2aa609f0d7b441d7ded0c07b8c394327672d38a905efb2d76aa3be5bb14d0c002aa37e287aee79\nE = 0\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb", + "917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 0\nA = 0\nE = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 86fb0b8dc161c41de2adb0f3ddcc8ad49c1efd729a52793a3ac987d4011c9c1dadb18657dca718df75c8ddcc49d60f152c46ab85ae9076ee7bfd405679a7da3a5195a1bbfd7d2b998c7b135ea91f8c445cbafe1276fa502c2a85477716829a2e0d24ba02623405a3654bed8f355bc7ccdb67c3f9a01e249e358b60d7699498a9\nA = 816610e6018ca47074d55750dd16a281019dbf95dc752605794cbb8ea8d75775317ce685737859728320b529fb3b4414b40bf3a93d08d8994a21ae54682cc1c357eb529837a7b0129a0843eebd9341c9bee3a8ae30475bdbff517e885a0c9f2b6a680643bd981efb53bf9dd49f3dc3cb757e117895fb34b1b4336d9bf8384558\nE = 8622c37631e428402343dccf8ed09d47b3f4201e95058910289a62707c3ce0b7113c390056cc4796cc9893e471b12cb3f63f900f3356ffd25c8b2fed6f6a7fba2c684eb241ca706c76cecbf72473d8a58c02338e40714b5610465cc319f0a529a7aa3898d9e638b247abd1380c6e8f7fa210c9f1a1a2164db6db83a6bba79436\nM = fda6f9d8588e3614f5a68ce867a5619f6ddbb8d64450ff402e1c4f1a08b518f79dca21e5983c207c5b7324c16895a1e9f1282fc6cf60b0645f6b02b652ed5b129e67c939e854ab492dec30ea878c3edde10a4b7d1d14c57100c6cbcc5fc085a0d7308715ed132fb917251919c727487fedb66500d5610b0014a43419acfbb92f\n\nModExp = 1\nA = 9edfce4691f46eadaa2043c7b1092b831ed50f3429f0bca02f985c0b77c686d951be84d772ae4b55f08935bed6e3206c8441574f215736b5c1c1b7595b3b789b55cf56db83741b10144d6767ba2b97b23a5e83504c60e06ab22834b0145655aa0463108317a379cbfc8a93de8a66925a999b8b02bf88dd85fb9898cefe9c95c8\nE = 0\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 0\nA = 0\nE = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 442866609915aa6f1bae9dfb59e721e1b63f42c0f75fbf0a88344120fbbd7aacf15208fb7c9d8bb8477d553cbd826d7e685ad764a8423e81c2131c040ee83a03cab8d5ce50866a941b48c78e9f1330794d908562d4141cfbf26e8c80c69551339eec41e37e2b37b54330f7bd75748f8d26d56ab9eb3b0c127540484c6445a7fa\nA = 8ff65e2cbcbcd8697cc3ce9a26855d6422ac7eb4e66500648c08be697e005cc3c854a54cfab91d43489cd60be8b516a9b3c9688e5e009a1689c6b164a133859a5464ef422c86344fef42cc477c9df27768377c126a066d1b62f593b7f6d6e906feaee16addb7cfbfc043d741b7dc81a87c17f167b7b8ef1b1fb3dfd1eb14102d\nE = a3be10ef04535fca6784e5dbf3733d677dedd50fabbc3a860496628950b4747a328c2ce0d903cbe1e700f0af30f59fb917202257815097a2b516df5d0a82642faeffdfc3b7883766c78fc4be5901ebef891a9ca27f3bcf00960729e659bb3fddd54a19ce628e95ab86e4c7a168588bc9f67b05dd21a583acd8dc36e615945648\nM = dcb68f6aa530ae9b31d078e2e82670adcc98228e7cf1aa59f81e66426ef14b1591b833d889463564c75b5fd5551ea295a0da581dd80f62c7008ff0f26a1c9f4f756431d48198af157149be8698336b306b0a8b8635d3fc2c4c2194ecc4d2af31ca1892917cc2e621d702eaaeed0d9a0c3dca575451eb8bc5487e313988cae745\n\nModExp = 1\nA = fe9f77f7d0475e00ec964c0effb9b8e079c32e376ce77a9c40ce4018c3df44a77b4f294d9565502b2b79accb30cb58dda6d15e1543b6d4a53296543ed11c7f51baab60283ef03fae37dfeacb431392487ec2839551a933895c4dbf18844f7b375d3e6f558d3c39993cea1bbf7fb743a6a07bd3753c03eb7298811476d7f3ff1d\nE = 0\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\nModExp = 0\nA = 0\nE = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\nModExp = 91fd879d02f95a9f40fcd1037726f73892caf84e9b43b4aa4126d9062a0d22c464e7af2fbd91aa849612d99d9519b724a7fb1cb018fffdcff321d883ab2519953c9f174f09dd8f13ac87339887385966eb4a94842276637b2c36c0a5036b1d3bbea438bc6efd4b4851c7ec06879d60694df894717569bcd31c4b13d80df6cbca\nA = cdec5edc1cb3ea974342b85aabc0f9385cf877ca328747d40dd4d297623ad69ab6582653faeed5aef225208305135cfbee32e066cb43e18afacea3a32acc8aabbc49617ac33e741651924ae56dd6aa044a12a1ea50fef573b5befb2f4b21b9cf83ab2aaa6fd153580a0761666ade8fb94f202a3c3dc4f33297eabb4564374168\nE = a0bc148ed50a9b54036bb8fa1f214979052ebd47db8b347af3bb03b806bb457b468ba34781f8a25f289a7a90af4903dc14809a166df2f4c3527de2ea6911cb1afb9071a4afbb522a7d50634d66fd584c73f32d05217dc9f7f16394c68a692a953492ca85f89cc11da95fd8cac6231647923ced48a1b3b0ee68c010286d452836\nM = e7a96cf6fa930f73c8bdc2726bbba246001a9d27f39cc2b978c99dc6f15af0e8aaf26b565302f1112e607e2df4066948baba931b89cd9bbdea2072e05b9a4968fdf282c43d997987c3a3a0434e925a679ac81f316b7a7b724b79be3d6888b66f4512759bf66cfaaa88b9513dd27a44aaea75437268a014c4eb50ba2e50093511\n\n# Craft inputs whose Montgomery representation is 1, i.e., shorter than M, in\n# order to test the const time precomputation scattering/gathering.\n\nModExp = 9442d2eca2905ad796383947b14ddfcc341f5be8fec079135c36f6f0d9b8b2212f43e08bf29c46167ff0fe16b247cd365df4417d96cc31c94db1cf44b73b0ee3ebcc4920d9b0d003b68e49c1df91e61bc7758a8a1d2d6192ff4e1590b1a792f8be3a1b83db3ad9667d14398d873faf5d885ec3a2bef955026fae6dbf64daea2b\nA = 3a4b4c57e62c5e9d1a9065191f8268fed9d5f6f424d071acef66f0662b8210f4c029ed991512e40c9c912043c816d2c4c5b53fa0e5c253e16808aad4225130dafbbb89fd4f30cdfc1c2f2179b636a7ddc4be579795820b4b9377637bd8a21a0ef5a90d0e0f865321eee23d9be2a3b7320b4012d02941b892df2c40bdc85c1898\nE = a2c56ea1362511cac0301918e15a9afe7d37edd438a5c3538d258ea01f0a6df758de07111e868b3ad8fc89b629b4955d78a1b3af902be1806410ddde25ccc6a196ba5949395c1ad5d8725b18815dc1cd5ac1c7dd17773f571e3f2e628255af14476e0494be23a4a4dfd18e23142f33d7a59c236fec61660e360d9676a747c69f\nM = ede35a3a7afac817d413373a2032abbc067b1493f709ae6e1282ee5469743391d891b904938857168802b7872d3cd7ac18ab249a9e540a86f970b1d0f310a4cc29df1cc9d4063d98c554f1a32f4ca5eba3523cdfb142e0fc609907c7a92bb0187009d97ec471db3545f42dd5fd29c07b7816085d09477ba31fcf90084660116d\n\nModExp = a7f5844fa9e7202d4b70ee252c9846e63d3d091b0387768ded872cec53458e19df0d9b4960226e269b8ca5dd4c4eda423a67b6dbb48235c08c12c6c7c78db47287756d3ed9cecb9232f7d18d5d80b9676cb68ba4a290c97e220beb1a069976b5e6022a4c1e5ddbeec86b62dda24ffea1deda37695c9f61a8817218e6370c0679\nA = 7d6d0cc947ceb949cdc4e9e1044f5deca5bb05a491041e0d85bc4b92a0944a57c72845fad91e59010c61ad1712bd2f612d53a846a044632262a9f2e3373b062fde2484e0c165ff947f2469f743ab6e2e5e13c640fc4029b1c9213eb8473c674e7f9e95a4a5c5636d4656c1e696962340d77b322daba47d6fc894f2a2cd9e0afc\nE = b78012afe806e2344d004c739c97324256850980ac97d88c4ed9a838517639ca112e235978d21a176c33f5a68703aba0f2a05501bbe3fc8d49a000fbf530cdb431581dfaf8683cb15a2aee5e239cbc542827100da3b47babf4a16ca7c588aff9912e674abb449e0b767a15e415f4e7f2bbd6380d7131da3df8d49b13bfd35ce3\nM = b72d5c55bd2998472f1965e75a51be6155c1ba04656da8f66bcb34db36a7b1db66a89d1d05b1bde10206acf85be7b474ab689220faf1bb52ab39d8dc00512dd4e26df1179c11b973e1274db85a88c7cc2a17113abdffe58cb930ddc5f3ccc4d68b4e65c913730509f7ce5656e8bbaba9b1be177ab9f766678f018fea05da9cdf\n\nModExp = 465ff295786a88496828fdc763e9292d557", + "957544e9322b7996807b87fdbfa7a11614bffeec557ca831c4824c8e4ca3b1a1c7f3f4f95ec3fd6a86b73bb13d78b73af2b3c7e76954d0cc03bcb0cd606867ebb3765a8b3d0108cbe4f343a14016be9c33f6d200f0dc547e7d6b02bfab1e79dcdf9c9835a814cc6c855a12ebeb66d\nA = 89ad02bea3e9ab839a6e23f20122409daba52c68e1e893034b30d321c0305434a6af940015e3fa5ca9c35230da34beeb1ed4fbce6c1da3a8bfe3f3ae172276c1d1723b47ee61e6f8fcfdafad102d6f7ee2a79f510c7edb93096205a40a6c9e665b88b18f39a979e2e61286d939952a6f02fe8148b7515bb25f4252337cb6e60d\nE = cbd6ac628cc7afa3c61bee9c22a06a395087ec1811fe9681b55216700c435996c815e7cec8aaa90016dd2382d0306a5414630124e14f3d396a4ba02ee17851bf720f1607ff813e4bbddf01338983db12f59bd6371a738eee3eeb716f21051d6174d2d6c77602942b9edaac18d4b3a723096c0d00dd23a8a605c585022f311560\nM = fa7a3e40364c8a8d0f14f0213a3f3e035222ca0ea19d46d10ba41580e5dd2805c8a133f3856d7d5d97f922ea540e5eb0d10ad04dfdbb74f518f58da0099a6fc2b3f3def92985176e07fc78aff2faebccca10a429794e5f15ff92f75fe90f527c60ddea8093a9078c703c372ca09f7aeb27ade02f3595308c61dd9c44e62fd101\n\nModExp = cf08bf00261402102e9fe03f3074471dcf0e9b3c96d4d1503f099f24ec85e1901b023e9e048c1ad042244f5f70b38b25a99f4c0a7b57d5844bb0d0137367f45f4ce2cc7746105b77414768cb97648dc5721149aed2d4c682408cc0d50d26dd0bd77e848911f8625c727cac5f32e63bcb548f41a57d718d772f23983a42f603bd\nA = a419646a6631c2c69b18f7aa65011825eb31692eecaee9d74f92d92203811b68e9764bda31a1585bdf69b6273fc6f9f508c395ac081336506525dad88473512f08a205621ac8b16e9864c7a7c5a4f17435de00d0b32badec6ce4897e3e1076c562b6d9523f63d0b2079eaa416cb090471657763f24931d955d1fa2720c80a9c9\nE = d5a6f4a1842aaee39805356dc8d0d678ee03b2c81277345beccb2742f899132feb43271f95968a01ae68aa8277201851992dc0aa7a71c90aae71b124d873ee264ea400fb131be0fc6c4ce8c04c45f6bdaca89ac743635caf6158983d257e21cef6800d7f990e912ba21bbfb8fb779afa4abd19e07e7e07eee9908493d1ca502c\nM = e739689b6cc6def1d45fb1a2ab551643beeb303f4aaa4da47ee5e4948510f8445b4c40e99ae8354dede60b2ba6694e93bc4d573b7e8adf871b7a9a9636eb7d70f2e49328e2d7978143b177cee8374ef01bd1ee2d95862765883f5e7971668b53ef0ff41b6539faf63c397522b0bdce916388e72e26c8d3d2e58dadeb9eb5d479\n\nModExp = 827e6312ec3b14600203bb83f5b277ded197b2967363630ef673240df05edd3ba8ab2b11c86251a612206569c6c33952b31e264f129909bfe723bd0ee1624b36cfcfaa893a6ec8b5a1f7de79f83e79b459a3350f89f412ad1cfd6bc4c2a7a29272c783d6ecceeb1398fa17041835643f4debef9b5e87b098d104bb8912dddf7c\nA = b8e49c637829021d32db3a39a0c1e58cdd4c6e4eda7e8e9293be379e9c2e2d184f929d278598a81ae231cfedcf69cce4a6e31cda3c8ac14d753a7311f2436e29795f0dfb60259a0f61a997918ff984aa2284b43a9d64c974059e9682adfffd018305835f74eda8c75fe4877d811c1620f654ec9f7f32d1af5ce59115e2f41785\nE = 80e0febf369d234bf1aaad4f82df2e2ff02882c3184781f6ccdf4f7cd93b6887af86830077c84dfb02109ada05b40970b1c65228b0c19030bd6361c3537fee22a8155c03b4e7007ca006c6daa3659518d05bb81ea0079456d0ef6116df248dffdb0c935f321f5a1034deefd5a9414a0652aa6548de33325b474b9e5a8507a082\nM = d5eb1d14af842a9973274f7463d90cf0ccff19c47d710edbae184478d4f29b02693ed7958bd487054327b9e6d8879e24c9af7730b92f323eeac05558da6c1b952e5dbf13de236050a77628bb5325fe0d14cc5773bf73338759d5ab43c212b414581280f1cee250007e53791b800b61c90de0328acd7bc43fbdda48158939392d\n\nModExp = 4a1efd29c7e78549f5cd4deed1454b37462c7810ee6a8a2493b764dfa479be13b314cf9ff98259517d61865567ef499a511630c0038c97914625df181c6fe07892f329f98b344a78d751e9471483eebaa7977371bf97bb25187ae7e93a9227d6c124ccb4644423c961a11ae59c4354f89d5a95164c23d9aa256e289e9cc0858e\nA = bd86c9211fa6a47a06e5016c46cb8a99e34a043a29e22f8c3196fa7197c26b38927b8d9bc0ddc11a5fa4bcc44deb69dbf37cbe7ebc9a2fad6c74e09ab5a9dd929fa04ab4319b6caad1035739be78ba631fb0748d9e53944836d37ccda6e6a62823c696d8f31139ccd7f2f86b22fa026ecf433cfb1271a3539ac4f1c83aaac059\nE = c40b9972006d28a84c2769a86e526a2b274f73afc7c5c6a2742166757f61b5f5fdbb228afa157af62af989ffe966f232bba9e6beef5403d1690ade31a6410f7f349a35bc4267a129afd647993df7d45cc0e1a1ba4678d7f1b6e8a344d8ff7037679e1f4db25a454e4246f6b55c416567fcfa188e8a3865115851d9edf0aa8902\nM = cf424d7af75ce7eef90cad75ae55ca8810cc7b4703fdb5bce701e7bac07e0c371cae06df2aa8facb55a0faa6793e4d2bd9d7969703743b9be170be82792aeea55e2bc0f7ab7617b276486bf474dee2f4556aab595ff3ef115139cfe5e21ccd4ee05c0e1cf901bd85df86cc17195a783b0be836d00bee82ce064077f9191188f9\n\nModExp = 3137a3049fd4ad2e26d870f5c998cf11bfe82101884a82e85e43facd0928cd7434a2e346ca124619769fa141bbe92ad6f36b99231032ddaec3b349a410f82b5ca36f45e56e5fb85dc63d32053dc90805d3f1854ab385281a71a57726bf97158494e7476057214ca7379ab8b70f5bdc15f70bdad3adf33c3a1f9cd1b6bbbad556\nA = 39a1dc6a4c3f14d9c350ee968d5ce139ef725952c967a2d1bedf48ace22091283525be03807e2e263d2640be77f0525247bcd07149bba50568cec5a082c87d72962cf9e43bcb5cdb1e7e9a650fb53e0ec2fad37f09a9f036c0d7dfa528fef846769f80a9a60854910ca1b4ee05dba82ed2ee018348d6b3e52a764b8ffae61e0\nE = deaee3a3f80c9f684ed7110c0653847ccc7be5ff6d982fd4b49f59b5dd35f7210b1077babbcedbc127df35cd469dc6e569a0f84e58149b5605c94b09fd7f0b098d02b4a04631328b3fae39e6c2fce25334225cab71829abdb9507cb903701559660f2c08c3b743336119d1260a0db27054cad3f28bc1b04b2289baa58fb33965\nM = 938388927d06ed3bb1286c0f06d3054cb0ee16dc7a0bbbf13a45293c09a5f40f1d611b2e1a1b0ec2ef109b508e27af4274954905cae52034f8740a744153b4d22059f0dd262ea51785522098ecacced6da07709ee6b5acc8c4e99331379a7c3de7f4e2d1431e43b19570140955b7bcba118dfbaa552cbfa2be531e8f781166ed\n\nModExp = c15ae334455d9f4d1030cd33e734726a27c63624c2afc576238cce5e0498298a4a0c93090a0d19568b41290303c4b558f3d9dd74f9cde8798710f68569ea0d6fd971ce67ec5b54495031de3d8842b8b49288725bee5c9f72b99054d64986ccd4e18d70d5f33943f08cd694eff538f84438ea993ebaba0910c95b3a694f213510\nA = def633b955a917569df3ba8517455eef0655e7a35985edda27097a063e0d82c7c3a76dc36c5d8a71ba9d540790ddd0ea514aaed98925f9a1808eb288d387aaf9605a9ef8a333ebee7ad7057bca012efd619d5867f02266f65976ef4b16da17468426ac4f99b3e8921707e01b4de20f6f9a068e6a19d872079a27f3a44449db83\nE = a465c47b0d15d48e01bb8b1d8e3b3253e11515f6874dbed6c25818adf1a8fd927124d5593beb367f685c11e46f18415be73ccdf16fa2e93a600b728163d21d232849e5278c3749d903edad3f1c4535a2f55a2ab65e7ebc64888bd2a0527e876ecf38cec3ab1980d08138709fad8eb88ae65d960adc3f0f8e92f784fe96fcb693\nM = e43cb9ac1446154356cdc31ec771c79b0e461e22d95185bbe1a279c0945e3af07903a0cb54d553380716fcdcafb4b7cf5dc6da481dc74a8c583d75ff6c1f8e429182d200246ebc473bb56e173787987c1b7fb2dd23f5b2e438a97bc4a1df628bc044fdd1e80c0cf37030adb7b04784dab827d0dcd64f0dbf37c980612570ce11\n\nModExp = 75c3f79ab7c991b98e65505342a8a563cfb08b5d3ccf8664c7db1de50256b1d17ebf7096dc98c7bb5d7f027a894ae5cbb14dee04d5d445e775ad7e239acc82673b0ac2d819a69c83864f34e73d9a636f05de8279619a067b4c90ad038db5910447e03841d2034635018f08cbcd21efa00994247763a249082594128112f95232\nA = 34def7d76f6f158a359fd12759fb889cdf6af0a24830dc3e84283a1ab4e9b2647a6a36b86482f829b2cdf3e3d6028f9a884b1f64f7262315446bea8b0231828e2f3d990fb103c17f820b39e4b8427c85643ceeca8f5dc8f191d1255768300e859bd7d88c770319ef38269660d221cb3bc061389b6fc0783485ef042b1c7d6fef\nE = c6c46453dd5aac6b37277a446b1d0c69cbe476eeff55b3ac35edb89ba97116b0e7783660f2c7b31b2a2d6c4709d0ab45d01a838100694b0777c9c9c14c959b07c437c73a5eabb7402f1001e802d797a2e7707285834fb6440a1c2f727f7bb84ddb2a49312d32fa0ce620c43872655cb5c394749c9e75d7fa25be00efe50d47d6\nM = fbbab6698a9142095c46b38a732592e4366c1838b84bf40f8c8fc7b630f73380a0d09765562365798f8c8030ed1b6728329d8bb06e882c35a1d59bfe84146a9db2afe42a414014e247390281c782fce806d62adb54778d2bcb49555459429d6ed446af5359657667f6aa19e8e3e0e24ab2bc312b2d90b5cb1ce6f2f15af15d9d\n\nModExp = ba16d7f3f6e162ce248490d164a13c00e7720d8a667e2d3ebeb13f1663e15ef5408d5b56cbc7bc793a8ca787cc50f8e15e0e9d4ee764531d04a9114eea556bb3e206ed7d85267151a056b6e68fbf35e03f2cf829708ffe1de13e95ecfe365aff1eea36340ffcd3892dee659fb1ecbe50f5080e54737c10f9c1ba638b14ef537e\nA = 9025e6183706105e948b1b0edf922f9011b9e11887d70adb00b26f272b9e76a38f3099084d9cccf12d04b1a99c0f654f8b9ed90c6dff9478c60bf05d58d734ab60eaefa14a22230ec60c90dc1f0704b61eef0bef345785ae0e6a9af7db069cf6bd2b4e0fe58a0ade83c7e46a04b9fe1d24cb9b65c6f80de713e61d70eae5b286\nE = d7e6df5d755284929b986cd9b61c9c2c8843f24c711fbdbae1a468edcae159400943725570726cdc92b3ea94f9f206729516fdda83e31d815b0c7720e7598a91d992273e3bd8ac413b441d8f1dfe5aa7c3bf3ef573adc38292676217467731e6cf440a59611b8110af88d3e62f60209b513b01fbb69a097458ad02096b5e38f0\nM = e4e784aa1fa88625a43ba0185a153a929663920be7fe674a4d33c943d3b898cff051482e7050a070cede53be5e89f31", + "515772c7aea637576f99f82708f89d9e244f6ad3a24a02cbe5c0ff7bcf2dad5491f53db7c3f2698a7c41b44f086652f17bb05fe4c5c0a92433c34086b49d7e1825b28bab6c5a9bd0bc95b53d659afa0d7\n\n\n# RSAZ 512-bit.\n#\n# These are regression tests for code which historically reached the RSAZ-512\n# code. That has since been removed, but the test vectors remain. Note that the\n# lengths of the inputs, especially the *bit* length of |M|, matter a lot.\n\n# Control: No relationship between A and M except that A < M and they're the same number of limbs.\nModExp = 7f34c1cd63377bc3abf2bb5b2d1bf5f06454e1e8040fe19a72245ce9731cbee1bf9e84532300776c8021ed4f3a8de508d85b4cf320bd82065a013754857b50c4\nA = 8e4e67da6ff890643d0599387955996ef6f0c2045eb9944576ddb965ca64cdb6247727ce128ef178d4a84e5a56d2e67eb0fe389ecbf691f9244ae80f4c11b364\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# Same as above except A is negative.\nModExp = 71fa6a4c8ae75368eda8cc6282c26afa69e2af12a97fb9444f16b7dd6c99e0a5d6034cab4248cae4357346b211039f4a2bc4c5a20a297372094162417af703cd\nA = -8e4e67da6ff890643d0599387955996ef6f0c2045eb9944576ddb965ca64cdb6247727ce128ef178d4a84e5a56d2e67eb0fe389ecbf691f9244ae80f4c11b364\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A == M - 1 == -1 (mod M) and the exponent is odd so A ^ E (mod M) == A.\nModExp = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nA = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# Same inputs as above except A is negative. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 1\nA = -f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725490\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A == M, so A == 0 (mod M) so A ^ E (mod M) == 0. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 0\nA = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n# A is negative, and A (mod M) is the right length for RSAZ.\nModExp = 8d76eb0f8c7bc3160cc8bb0e0c3590fbed26c5932f5f525b48045c0bd46dda287ba5483f97c851fb7c12c2e858ee7a4a4d1af745cbfb3eb311fa54bea12cde25\nA = -80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nE = be99d8f0650e540b9b191e9cf96f74881b902e32ed169ffd8a1776c3f3e80f0ac765aa14615713e1549f250a20fe4ee48c4e0c6176162fc7842a0dd64d640d1\nM = f12f2c19ee1ecf2c999b87bdafde60eace3790faad8f9adec13b14c6dfb69f8795a1d0fe65494250b59534014b918453042012952ae6f5786342999600725491\n\n\n# RSAZ 1024-bit.\n# Note that the lengths of the inputs, especially the *bit* length of |M|, matter a lot.\n\n# Control: No relationship between A and M except that A < M and they're the same number of limbs.\nModExp = 8984f8c16044f9c0ad7bd72347af90f58e6e003acda92b76e3c7c4a56ea8e918409d8e9b34884d4c89d0b17cb40fe898f2627c084a0f1698e46beccbf6f48eecc281e11ea9e5135adba460ddae157f2c655b5f589ce29b254d43a960a71cede8a08dbb86be4dac22458da232fb1ec2470856827302ed772c9ddafa408c931aa7\nA = 21158da5fe20356825e72b3f5384ec57720d22f727b27ce2f945c8ee311db781add73bf8fae96b775c909bd22fca75c44c2b0584284a5bb1c07f8eefcd6b0a44047a02b185df34f897f11d4fb9a86c9eb841b4cb8d0383441fdc5af3ef385b5e8380f605d73ed41bb42eb2c2a5704d6034b3ad058dafffce83dbbfb6295daaf8\nE = ecdebd112b3b5788669449dcddbd479a203ee9ab72a9bb9c406b97623513bf0ab9a22f1f23634d269e16bfd6d3b64202b71fc355057411967b6ac70f8d9cef0a4e06819a9a18cc06bbe438243fa9759303d98be8a65dc1cb13595ee9b99f138554425d50f6fbc025d8ffa3eaea828d6f3b82a3584146bafde34da257995f0575\nM = ff3a3e023db3bba929ca4ededbace13d0d1264387b5ef62734e177eaf47a78af56b58aacc8ac5d46f5b066bafb95d93d4442bb948653613eec76837b4ffb7991cb080b6c8b403fb09bc817d026e283ee47ab2fc9af274b12f626eda2fe02004a8e27b9ed7d3b614e8955c7e7c2c0700edd079455237c4475fbd41857e206e4b7\n\n# Same as above except A is negative.\nModExp = 75b54540dd6ec1e87c4e77bb93fd50477ea463fdadb5cab05119b34585d18f971617fc1194240ffa6bdfb53e4785f0a451e03f8c3c444aa6080a96af5906eaa508862a4de15b2c55c023b6f278cd04c1e24fd0711244afeda8e3444256e51261ed99fe66beedb52c43c825b4c7a1adc7d4b111e2208ecd495df91e175573ca10\nA = -21158da5fe20356825e72b3f5384ec57720d22f727b27ce2f945c8ee311db781add73bf8fae96b775c909bd22fca75c44c2b0584284a5bb1c07f8eefcd6b0a44047a02b185df34f897f11d4fb9a86c9eb841b4cb8d0383441fdc5af3ef385b5e8380f605d73ed41bb42eb2c2a5704d6034b3ad058dafffce83dbbfb6295daaf8\nE = ecdebd112b3b5788669449dcddbd479a203ee9ab72a9bb9c406b97623513bf0ab9a22f1f23634d269e16bfd6d3b64202b71fc355057411967b6ac70f8d9cef0a4e06819a9a18cc06bbe438243fa9759303d98be8a65dc1cb13595ee9b99f138554425d50f6fbc025d8ffa3eaea828d6f3b82a3584146bafde34da257995f0575\nM = ff3a3e023db3bba929ca4ededbace13d0d1264387b5ef62734e177eaf47a78af56b58aacc8ac5d46f5b066bafb95d93d4442bb948653613eec76837b4ffb7991cb080b6c8b403fb09bc817d026e283ee47ab2fc9af274b12f626eda2fe02004a8e27b9ed7d3b614e8955c7e7c2c0700edd079455237c4475fbd41857e206e4b7\n\n# A == M - 1 == -1 (mod M) and the exponent is odd so A ^ E (mod M) == A.\nModExp = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nA = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# Same inputs as above except A is negative. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 1\nA = -b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d964\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c1", + "6f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# A == M, so A == 0 (mod M) so A ^ E (mod M) == 0. Note that A mod M with a \"correct top\" isn't the right length for RSAZ.\nModExp = 0\nA = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# A is negative, and A (mod M) is the right length for RSAZ.\nModExp = 9cf810b9e89d5cbc4b79ae64e123ea06d92965e2bab077df97a1b906dc2e1ddcf96a9c4ed14e2cd96309b829ea9cc2a74a7d4b43c5f34d792a7c583201427754b8f78b783608070a84b61f18913e3ced7f7f530972de7764667c54e29d756eea38a93cd1703c676a4587231b0ebfeadddf908e2877a7a84b5bfc370ecf0d158d\nA = -8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nE = 61803d4973ae68cfb2ba6770dbed70d36760fa42c01a16d1482eacf0d01adf7a917bc86ece58a73b920295c1291b90f49167ef856ecad149330e1fd49ec71392fb62d47270b53e6d4f3c8f044b80a5736753364896932abc6d872c4c5e135d1edb200597a93ceb262ff6c99079177cd10808b9ed20c8cd7352d80ac7f6963103\nM = b5d257b2c50b050d42f0852eff5cfa2571157c500cd0bd9aa0b2ccdd89c531c9609d520eb81d928fb52b06da25dc713561aa0bd365ee56db9e62ac6787a85936990f44438363560f7af9e0c16f378e5b83f658252390d849401817624da97ec613a1b855fd901847352f434a777e4e32af0cb4033c7547fb6437d067fcd3d965\n\n# Regression test for CVE-2017-3738.\nModExp = d360792bd8210786607817c3dda64cc38c8d0f25569597cb1f363c7919a0c3587baff01a2283edaeb04fc288ac0ab3f279b2a89ffcb452d8bdf72422a9f9780f4aa702dc964cf033149d3a339883062cab8564aebdbfac0bf68985e522c6fe545b346044690c525ca85d3f4eb3e3c25cdf541545afc84a309e9b1d7807003461\nA = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2020202020df\nE = 2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020FF2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020\nM = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2020202020ff\n\n\n# Exp tests.\n#\n# These test vectors satisfy A ^ E = Exp.\n\nExp = aa6d7ac431\nA = d0e07\nE = 2\n\nExp = 12d416b110dbb4e467ff0c89a22122f4da8240\nA = 1a18cf6\nE = 6\n\nExp = 49a3b33e23d84f1ce0d5d83f5dcb651d50cf3920f0143da2310d0512a90a06cd8f38977df8a756c30883de38df092000\nA = 2a3acbd2\nE = d\n\nExp = 5b4a0d5a956f885f275712b194459980f24708bfb6393d71bd37dce852ce455724f5ee5030775fb86b4295edc98afaafc097e4d82a97c0078ec0eac763db16549c5145c4cf2d3124f88cf9a5c71da0625afb99b26801786fe49a778415dc025954021753d08691947a208b613f0be5c1\nA = 54b3ae461\nE = 1a\n\nExp = a0ea5f6a4de49beb8fb7f0dab280d6a32c5a3814c9a5153a7944cec0a9028497846a8a89044348721a0bb5f0c3ded3e980574ea321b0cdb0ead4f4e93841ea7478a7f15d9729b646a8165813a0750e8124f5465dda9b105e1bbeff18fd09c09a2e26610d9176d253b877c3a8908a6be521cbe1e472a7a1b7820e4e890f8f28aacd34609c686e76e15b01bd9324a71290812724ea564d11c874a6765b262c3e57d479da0287a76026a1e8fe53da0b02405da1d379eaa30fc65f\nA = fccec0f6df\nE = 25\n\n\n# ModSqrt tests.\n#\n# These test vectors satisfy ModSqrt * ModSqrt = A (mod P) with P a prime.\n# ModSqrt is in [0, (P-1)/2].\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = 1\nP = 2\n\nModSqrt = 1\nA = -1\nP = 2\n\nModSqrt = 1\nA = -1\nP = 2\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = -3\nP = 3\n\nModSqrt = 0\nA = -3\nP = 3\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = 0\nP = 3\n\nModSqrt = 0\nA = 0\nP = 5\n\nModSqrt = 1\nA = -4\nP = 5\n\nModSqrt = 0\nA = -5\nP = 5\n\nModSqrt = 2\nA = 4\nP = 5\n\nModSqrt = 0\nA = -5\nP = 5\n\nModSqrt = 3\nA = -5\nP = 7\n\nModSqrt = 0\nA = 0\nP = 7\n\nModSqrt = 0\nA = 0\nP = 7\n\nModSqrt = 2\nA = 4\nP = 7\n\nModSqrt = 3\nA = -5\nP = 7\n\nModSqrt = 4\nA = 10\nP = b\n\nModSqrt = 0\nA = 0\nP = b\n\nModSqrt = 3\nA = -2\nP = b\n\nModSqrt = 3\nA = -2\nP = b\n\nModSqrt = 2\nA = 4\nP = b\n\nModSqrt = 2\nA = 1e\nP = d\n\nModSqrt = 2\nA = 1e\nP = d\n\nModSqrt = 0\nA = -d\nP = d\n\nModSqrt = 0\nA = -d\nP = d\n\nModSqrt = 3\nA = 9\nP = d\n\nModSqrt = 8\nA = d\nP = 11\n\nModSqrt = 6\nA = df\nP = 11\n\nModSqrt = 4\nA = 10\nP = 11\n\nModSqrt = 5\nA = 90\nP = 11\n\nModSqrt = 3\nA = 80\nP = 11\n\nModSqrt = 9\nA = -e\nP = 13\n\nModSqrt = 7\nA = 7d\nP = 13\n\nModSqrt = 6\nA = 37\nP = 13\n\nModSqrt = 1\nA = 1\nP = 13\n\nModSqrt = 8\nA = 1a\nP = 13\n\nModSqrt = 54d4cf0fafe265056a29016778cea6b712bc66a132fb5e6b6865e9b49e4c97ec\nA = 599c10484b22d0b5a115268c7538ca99b3253a311a4ab1ca11c3665b0bec393a1167d1ad94fb84cb2c7ad7e2c933e8f613bdd08fe1f1aa4a9b0b9de0c8a7c9d4\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 38a7365a15365e911286c1be2a7afe76ef390234d76269e04dee17313f6ea54d\nA = 1c4aabb4d8369710131c664ecf2849e963c1bc31d66e0b939bacf99a870c71f24ed71bdddcf566f3908271fee43fc1ebb51eac7e3153efae641b49d2e796a12a\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 35ab18a560dece04725667f640ca61d1d59f14d191f94c79f58531acd097d444\nA = 685168ae855d60eba220d803f5296459b30a289580668db9ed51bca51cc2d453a937e13819ae34f7a9a143ac96d17420c53919167e46279b562b550be1cd9abc\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 288370029e87024175e5bec0eab0929179f42e16995e7f6194eefc61061e54f4\nA = 2a14ab77c045bdc48220ba9c463e1a4b4049cb01edb53be0937767eb2ec19b7d719855052281250a36a0b76d9a5d967d0756e1ded7a052f7056191ad66bcfc9\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 32255cf01dc943577ec2bcb221b98491d7a1130d046d6c68e95fedff643ce3a4\nA = e26f6dd46a513a1dd3fb14b71be1d4c9e9d79eda1cde10ea4d1eb8abfd4d5857572205e247184dd0cbefa37b5c0bf680ba2bd28c5741f725cfe2aae37419baf\nP = cfc4ccae35458ab5be1a1bc0664188253301f8702af4f8fb19fed12de0c653b1\n\nModSqrt = 5172345e801ada63fbc4782e32583cc3b4fea88b9e6dfd542f3542f8538ade66\nA = 40dafa8342b302bb04b1f3ddb3b9015a8fc1b597857c115b40631c7be9e22de89358fca23b331596ee5ff304dad7811e6d8e8822f7aa533c9e7c882634ea550\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 4dcf63c423bf0e39aca2293d57f6792d023db649d6719fe936446904b9f7e60d\nA = 5bcdb514bbe84261e169203e8017909b60c9bb330400c766ee01b0189378e70e61867a164a12643ddc9e94b61e09e5b158cbe85be228a3cc48f95a552958b8f2\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = cf77c5c2d12a500b75cbfb1f3e66ee75d886b9365cf4f8b4d1bd18a6be0f387\nA = 4652ddc2ea7b460d8ec3c9059b8f9b5dae6cac55b51f2ad86fcb336b25235737965cc515e2ff0b54835015b7ebeeda6fadd986471d8cb424d309fc353d1e269\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 1e0549e4c5a26023e9d24fd8c67419960746f82b1ecd113bdac66f570a475d87\nA = 5f4a6d450ab1390d96ab1deaa0ba18f897cb63daf0c9e1ef6c08e804c26b5e842f6c08f13db5d4a6e88f07af2a3cb04fa06fc3e59c410b9356f025ed81acc74\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 144481a781d831c1ca046ca9e322d79ad4d2c6dd9f780bea9d1ced9cd20b7b23\nA = 4c254fabca441017132b9eacd4ca40a336db3e5c09715773fa07af095989a91cc968ff07a9ff56ed06b0ce0c5269f7b2ab68564ecab9f4467a7e96b6cc6b21b7\nP = a6813d316f9aca30f98b4f864b8b4b8f51493af930bd4d3a1b205a710e99add3\n\nModSqrt = 216fecc7667f488a3d2d102a38b46b4860ab858300b8638af4f34e1103fd73ba\nA = 17878f8048227573a9d", + "70f53c0e76ff13fe9f56e9c984c92514d3d13dec23c816661f0618d21371b80dfd885cb59551bdf80046f65f22ea9b89c78645a6e455a\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 458e5e789ccd2417174f7e30bb31914b9656bd8cf2b9f5a9752a8737a67707bc\nA = 5c7d39a4bb04e69201aa519f80ee7e62ea14ca55e13656d1da3f45367e2fb2d061aa2940708d02ac67d35cd2ccf54a1bf95bcbc759779e692cfdcbb3aa1a05b\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 543125a16c2bb8b8f8a2c39c497e5224ec77533602d7dbe24002e32dcbd2ef1a\nA = 3413afae333b2ad9ff45c7f3c7e5934b3127e8b1a55225958ee6ccf42423e81559bf070ad3f3353b78c0ffd41475af49f59d268ef78bdae879f5155e8d1cc07\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 10e16859c67bdb2eaab52a7c847dbf37162eda258a9f6262ebacfe4cbbbc1080\nA = 21ce7905894faf220bdf4a82a2d855994ca2dc9feaecaa53c7f146e1f49934215695e9bb46ba370b7005a90c399674caa8969eb442e7914d90f749774d7fd194\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 32a00586adc6f6cc2b1a04e1be0ab569fde235e1436c38b6af92bc5ebd60bc1c\nA = 350da4fd8cf03c12f7dd6ac6d3ab801a3413964083e374662aaf878d6838b97d4feb9e52cd307a25b113e101661a865463ee2480c626aa4e2ec437d72e7bae4c\nP = bd37c850cf7d702bac879f3c21a51a5a4df2b8eb0935861e0753a6eb62261a95\n\nModSqrt = 971f75bc7afa8b4b50f1d4b05e52deac7d4836a08d30546f29649bf1ca6a247\nA = 655ed4c5d8d0afb4f9360372ee1ef1303898d2423e585108a3303faedb55064d2ef25666ed4c4d71fe6063fea1f3142b435714b0e30b339dd791d347c884654\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 48fa882b7cb6a29de9e3769f72eb67f1efd4d2af56f0c7e410c610efcbce2065\nA = 14f3503f33b243800eac1defaab33e04c01e80163fb3efd03860970cc016832431ca4fc6d1b760f4f40166b0b8b3c40dbebc81460cc10890172243770338f090\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 236fd7e397ea7f8bc2a288eb7236ca41936fa702b7dccca56c8852e147511f7d\nA = 1bbd0980feac854782813bcde4da85e8a054549a1b515e065da4236528035e756882e29e762cf60453e375cca9dc6ff637f9558bf86646e3b928f68f82af7efe\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 693f0cbe8c81b0afde0cd2f83e53795dcae6b0cc4ba930ab5c752400d787f14\nA = 7b20f9664b23907e152ab8c9a907f72e8670c1c38ab4cd1411ea7c2159c09aa131afe068929b8e6ad1409b74c04975180d1cd0a9fa74e923c3fd451e8da2c34\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 4a086c50b0bad576501ddb6280743b2c9d247841eb7f14d90561432ff7dca6f0\nA = 4367431ec0cd0d7626538b93a090c30fe0c97c18ca03b97ddae304b619112b5b4d02bf0f041fa3fd673f9ef2ceb07eb2079d11c56dd903b1a87e8252a97b8079\nP = 9810151ad4bc9c5d68fc326395b509f2625bfebca1c3801ad4da7539fdbaa6f7\n\nModSqrt = 18f8433fa468d8065157708f1f1e53b8e31d39c6011fbc2bad93de1b5548e19c\nA = 739c032bb4139c199c40f548d37234298772e4ccb9d3ba28412b60ad23b4c465b0787e2382f1c5a4a87af2d20eb978b7dcbe73f2112249477d15c8a85e54a79\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 49e3c8eef5e067cabd51a7c01384ce05ab8f4342f655559d8a689eb7b20e0106\nA = 18400c2cc3e06b99b4e39c77b9af5ff0e9c683f1708321afa4cd5b6988d13b36b1d9eb4379b7902d9ceb40c03f814b2b6a01b90509bbb4532f13ab1571c4d04a\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 35548c530745f440329325cc8a5fbd90c16a7f0788879a4869bc4d4f73acda0e\nA = 181a3c5ab02566e7166c4d6d2f2bd4a8ecc25991a98d270bde80cf4332766a7068b14240bf5f5dcd45e90ef252596da3eb05b11d68b2063f7b3a825742593ca9\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 1ab7046e6af061ade5f9719008fa4d989007e2a579a134a5b9f19ec410984096\nA = 1008a03e211fab0d45856377079bc96b0776c2d4c0175661f3493246cea2ab0a02a706c85314fb707ad9906bedb2cfd577d62092ae08ff21d7b949373ea954c7\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 2be9e3e7515960d90f115b89f60dedc173a73ce163b4036e85b7b6a76fd90852\nA = 392053a9f0100540a8e1a0c353e922068a84dad3a4a8e8962fbc0bee2b6a06e20d08ade16eb1409a16acfcac3db5c43c421505e07035ca308b15c4a6db0864c0\nP = adcd56924f73836ebe4dccfe006ad3b1e5076562cd11b161642cab7af2284659\n\nModSqrt = 5b301bb93bdcf050183107e36258b53b4805918114ea1c2227b0911d5b4dc077\nA = 55e55e5f94dc3d7aabc921f6469d85fa2e1e92a87347c57afad5872306ae69f9fb99297d1e3e793dd9e8632244208154de5da7114fd876383bf1422f7ece024\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 2df9609e2f5a5156c3260461b2ee52eacdef00bd8b091479813143a6c5283f71\nA = 2099325b7f12fe77353ddf3f2b2c5ef77b49671b150af954cf84e9675e3ecde3e057084641a633d19533b4712ab49924c8b5c31d591abcc88291f51253fa2a7\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = dfab751710e9008e25e422d1199d6fbec4dc7fba35b4da9d225a746eb4126a0\nA = c006af53d4737fb293584df6ffe2e4cb3fd8dc77fb7c1f13b97bb9c249e3ee5fb9feff7488265b3093906c08a4946f142ac7b491937d24bfba6413366ce371d\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 26bc030008d6c60a09fb0e16093a649fcb40c6c21a8e2da2353ba4b07c4f85d5\nA = 1eaabcfad2ed349ac9356e6f4da0b301266ddde811cb0f817aba8f5c10fb8b8ba9d0ef2dd386b668f16eac296118fdb8cb7afe1b865648c81c2fa3cf21f2711b\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = 35051b1482ec2578f3dc0000a422cb5111e43c37f1ac20b1844d3de2128c4556\nA = 315ff9de178681116f2a5fa78eebf4818e1d680435eacdfaf9d0e5c4fc01fc034b352c82fd52c81ca30d68864952dacc99d08269c9dd7ca99ccf22da98c3840\nP = d43280ac150f725f4a2a1dceb1c79bcac57855a4eba72ae93762d09bcb2444fb\n\nModSqrt = a5474252885cacf004c460a7793ff0b0a2187bb1a9ed700ae3470199faef71f\nA = 19856fc1351c4b02abf573bb2fc6ff92355fa369d62bb8f2260fa772fb1693f509a56cad661930abcac049dd70f4b16bed4a4c172e73e772504c9990ce7f92f\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 12daf4722387ecf47de1b0b6b110a062dc5ea2685bc9dbde66b8d15622985029\nA = fb8479787069116abc42abfd7dc0c24d2ad04fe0c04b42a6dff714af715d17e0fd77855f950f264542b06d48e8818de813ddb7975798b7debefcdaa5ff86beb\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 397996ed5c0ac6ad32e43c337e9de421b87774cc162bf7ac7bbedf4a9029255e\nA = 5aa04353321bd2de92481be740357f979da464b53aa39111fdbb734cf7af6b3857d1baa08d3a126a3dd34a2fbae2bf2b84e900686c1d31505b390185acef5fe5\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 2cf4b844a54ba359dc592ef1b49f43fcfeae84d1087edfefdd0b9174b43c0a3c\nA = 365a8650510bcfd8fa87432f167cf487234c215857403b9270b5eebeafa48cd6da47fd60dc311b94d1d72baad0447c31f0b212d755f46c256e16e5e015e6546e\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 9277c73043ff767c3fa606f0cd66b9d854a600c8c18287f191ce277758c3f31\nA = 62cec3901626d03e8df66299a87c54b1f7a55cafc99f0b6bba1b5d51a3d2b7d2171c9135a9d8a5346d436e0136b12e515e703e3cd84ecfe154eb94c6772a6d72\nP = dc315fd52684fba79e577a204de9053b11a5d7a414263fec9eff6ff62188829d\n\nModSqrt = 4189e5a90c1b1abdc1c7c05b3587e6f362e06f927b6cf5f0d271aab3d6f90765\nA = 336b8d0f9dac842c696bc020f49c6aa023842c16f2052eb02f17959006554ca0012042c80c72590f21c6bf5a3714c9cb552aa69730e33db93a56a909b273f39\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 36ccd38cb5a6bd8a73bca55936a2227c503664422c2296faf7e2b1c6a375a43a\nA = fecfd60a376befbe48d2c4f6d070d716d2f403cd5daefbce62b720df44deb605162c8f20f49fd7ec30d4f8e70d803d45b3a44b5d912baa3410d991165d7c507\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 198fc8569be172dc9b71023ed3d42d2ba94bae4099643f6517ab03f540527fdb\nA = 65bebdb00a96fc814ec44b81f98b59fba3c30203928fa5214c51e0a97091645280c947b005847f239758482b9bfc45b066fde340d1fe32fc9c1bf02e1b2d0ec\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = 21b7f74c30ded681d6138cf8e6fd798f32a049e94138e982f1845df3dc9e686f\nA = 9a30b791c1ba4f394b4e3dcd5837e474237f4fe8987b255c098a47b2c14c598ec69d2beae444dd4fe9c4ede8173d2b187677cc706a3c28f3b81627d8a5fb6fd\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\nModSqrt = a1d52989f12f204d3d2167d9b1e6c8a6174c0c786a979a5952383b7b8bd186\nA = 2eee37cf06228a387788188e650bc6d8a2ff402931443f69156a29155eca07dcb45f3aac238d92943c0c25c896098716baa433f25bd696a142f5a69d5d937e81\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f", + "2ab5ffc3e246b41c32f71e951f\n\n\n# NotModSquare tests.\n#\n# These test vectors are such that NotModSquare is not a square modulo P.\n\nNotModSquare = 03\nP = 07\n\nNotModSquare = 05\nP = 07\n\nNotModSquare = 06\nP = 07\n\nNotModSquare = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951e\nP = 9df9d6cc20b8540411af4e5357ef2b0353cb1f2ab5ffc3e246b41c32f71e951f\n\n\n# ModInv tests.\n#\n# These test vectors satisfy ModInv * A = 1 (mod M) and 0 <= ModInv < M.\n\nModInv = 00\nA = 00\nM = 01\n\nModInv = 00\nA = 01\nM = 01\n\nModInv = 00\nA = 02\nM = 01\n\nModInv = 00\nA = 03\nM = 01\n", }; -static const size_t kLen35 = 833482; +static const size_t kLen35 = 836140; static const char *kData36[] = { "# Negation tests.\n#\n# The following tests satisfy A = -B (mod P).\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000000000\nB = 0000000000000000000000000000000000000000000000000000000000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000000001\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffffe\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000000003\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffffc\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000000007\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffff8\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000000000f\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffff0\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000000001f\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffffe0\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000000003f\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffffc0\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000000007f\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffff80\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000000000ff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffff00\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000000001ff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffe00\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000000003ff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffffc00\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000000007ff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffff800\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000000fff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffff000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000001fff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffe000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000003fff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffffc000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000007fff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffff8000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000000ffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffff0000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000001ffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffe0000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000003ffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffffc0000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000007ffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffff80000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000000fffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffff00000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000001fffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffe00000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000003fffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffffc00000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000007fffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffff800000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000000ffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffff000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000001ffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffe000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000003ffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffffc000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000007ffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffff8000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000000fffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffff0000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000001fffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffe0000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000003fffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffffc0000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000007fffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffff80000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000000ffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffff00000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000001ffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffe00000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000003ffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffffc00000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000007ffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffff800000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000000fffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffff000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000001fffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffe000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000003fffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffffc000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000007fffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffff8000000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000000ffffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffff0000000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000001ffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffe0000000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000003ffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffffc0000000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000007ffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffff80000000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000000fffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffff00000000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000001fffffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffe00000000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000003fffffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffffc00000000000\n\nTest = Negate\nA = 00000000000000000000000000000000000000000000000000007fffffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffff800000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000000ffffffffffff\nB = ffffffff00000001000000000000000000000000ffffffffffff000000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000001ffffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffe000000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000003ffffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffffc000000000000\n\nTest = Negate\nA = 0000000000000000000000000000000000000000000000000007ffffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffff8000000000000\n\nTest = Negate\nA = 000000000000000000000000000000000000000000000000000fffffffffffff\nB = ffffffff00000001000000000000000000000000fffffffffff0000000000000\n\nTest = Negate\nA ", diff --git a/src/boringssl/err_data.c b/src/boringssl/err_data.c index d8dc7aa6b6..dc78ba8ce6 100644 --- a/src/boringssl/err_data.c +++ b/src/boringssl/err_data.c @@ -75,52 +75,52 @@ const uint32_t kOpenSSLReasonValues[] = { 0xc3b00ea, 0xc3b88d4, 0x10320845, - 0x10329513, - 0x1033151f, - 0x10339538, - 0x1034154b, - 0x10348eed, + 0x10329535, + 0x10331541, + 0x1033955a, + 0x1034156d, + 0x10348efc, 0x10350c5e, - 0x1035955e, - 0x10361573, - 0x10369586, - 0x103715a5, - 0x103795be, - 0x103815d3, - 0x103895f1, - 0x10391600, - 0x1039961c, - 0x103a1637, - 0x103a9646, - 0x103b1662, - 0x103b967d, - 0x103c1694, + 0x10359580, + 0x10361595, + 0x103695a8, + 0x103715c7, + 0x103795e0, + 0x103815f5, + 0x10389613, + 0x10391622, + 0x1039963e, + 0x103a1659, + 0x103a9668, + 0x103b1684, + 0x103b969f, + 0x103c16b6, 0x103c80ea, - 0x103d16a5, - 0x103d96b9, - 0x103e16d8, - 0x103e96e7, - 0x103f16fe, - 0x103f9711, + 0x103d16c7, + 0x103d96db, + 0x103e16fa, + 0x103e9709, + 0x103f1720, + 0x103f9733, 0x10400c22, - 0x10409724, - 0x10411742, - 0x10419755, - 0x1042176f, - 0x1042977f, - 0x10431793, - 0x104397a9, - 0x104417c1, - 0x104497d6, - 0x104517ea, - 0x104597fc, + 0x10409746, + 0x10411764, + 0x10419777, + 0x10421791, + 0x104297a1, + 0x104317b5, + 0x104397cb, + 0x104417e3, + 0x104497f8, + 0x1045180c, + 0x1045981e, 0x104605fb, 0x1046894d, - 0x10471811, - 0x10479828, - 0x1048183d, - 0x1048984b, - 0x10490e4f, + 0x10471833, + 0x1047984a, + 0x1048185f, + 0x1048986d, + 0x10490e5e, 0x14320c05, 0x14328c13, 0x14330c22, @@ -128,55 +128,56 @@ const uint32_t kOpenSSLReasonValues[] = { 0x143400ac, 0x143480ea, 0x18320083, - 0x18328f43, + 0x18328f52, 0x183300ac, - 0x18338f59, - 0x18340f6d, + 0x18338f68, + 0x18340f7c, 0x183480ea, - 0x18350f82, - 0x18358f9a, - 0x18360faf, - 0x18368fc3, - 0x18370fe7, - 0x18378ffd, - 0x18381011, - 0x18389021, + 0x18350f91, + 0x18358fa9, + 0x18360fbe, + 0x18368fd2, + 0x18370ff6, + 0x1837900c, + 0x18381020, + 0x18389030, 0x18390a73, - 0x18399031, - 0x183a1059, - 0x183a907f, + 0x18399040, + 0x183a1068, + 0x183a908e, 0x183b0c6a, - 0x183b90b4, - 0x183c10c6, - 0x183c90d1, - 0x183d10e1, - 0x183d90f2, - 0x183e1103, - 0x183e9115, - 0x183f113e, - 0x183f9157, - 0x1840116f, + 0x183b90c3, + 0x183c10d5, + 0x183c90e0, + 0x183d10f0, + 0x183d9101, + 0x183e1112, + 0x183e9124, + 0x183f114d, + 0x183f9166, + 0x1840117e, 0x184086d3, - 0x184110a2, - 0x1841906d, - 0x1842108c, - 0x18429046, - 0x20321196, - 0x243211a2, + 0x184110b1, + 0x1841907c, + 0x1842109b, + 0x18429055, + 0x203211b8, + 0x203291a5, + 0x243211c4, 0x24328993, - 0x243311b4, - 0x243391c1, - 0x243411ce, - 0x243491e0, - 0x243511ef, - 0x2435920c, - 0x24361219, - 0x24369227, - 0x24371235, - 0x24379243, - 0x2438124c, - 0x24389259, - 0x2439126c, + 0x243311d6, + 0x243391e3, + 0x243411f0, + 0x24349202, + 0x24351211, + 0x2435922e, + 0x2436123b, + 0x24369249, + 0x24371257, + 0x24379265, + 0x2438126e, + 0x2438927b, + 0x2439128e, 0x28320c52, 0x28328c6a, 0x28330c22, @@ -184,43 +185,43 @@ const uint32_t kOpenSSLReasonValues[] = { 0x28340c5e, 0x283480ac, 0x283500ea, - 0x2c322c30, - 0x2c329283, - 0x2c332c3e, - 0x2c33ac50, - 0x2c342c64, - 0x2c34ac76, - 0x2c352c91, - 0x2c35aca3, - 0x2c362cb6, + 0x2c322c6c, + 0x2c3292a5, + 0x2c332c7a, + 0x2c33ac8c, + 0x2c342ca0, + 0x2c34acb2, + 0x2c352ccd, + 0x2c35acdf, + 0x2c362cf2, 0x2c36832d, - 0x2c372cc3, - 0x2c37acd5, - 0x2c382cfa, - 0x2c38ad11, - 0x2c392d1f, - 0x2c39ad2f, - 0x2c3a2d41, - 0x2c3aad55, - 0x2c3b2d66, - 0x2c3bad85, - 0x2c3c1295, - 0x2c3c92ab, - 0x2c3d2d99, - 0x2c3d92c4, - 0x2c3e2db6, - 0x2c3eadc4, - 0x2c3f2ddc, - 0x2c3fadf4, - 0x2c402e01, - 0x2c409196, - 0x2c412e12, - 0x2c41ae25, - 0x2c42116f, - 0x2c42ae36, + 0x2c372cff, + 0x2c37ad11, + 0x2c382d36, + 0x2c38ad4d, + 0x2c392d5b, + 0x2c39ad6b, + 0x2c3a2d7d, + 0x2c3aad91, + 0x2c3b2da2, + 0x2c3badc1, + 0x2c3c12b7, + 0x2c3c92cd, + 0x2c3d2dd5, + 0x2c3d92e6, + 0x2c3e2df2, + 0x2c3eae00, + 0x2c3f2e18, + 0x2c3fae30, + 0x2c402e3d, + 0x2c4091b8, + 0x2c412e4e, + 0x2c41ae61, + 0x2c42117e, + 0x2c42ae72, 0x2c430720, - 0x2c43ad77, - 0x2c442ce8, + 0x2c43adb3, + 0x2c442d24, 0x30320000, 0x30328015, 0x3033001f, @@ -335,238 +336,240 @@ const uint32_t kOpenSSLReasonValues[] = { 0x3c380dc0, 0x3c388dd4, 0x3c390c6a, - 0x3c398de8, - 0x3c3a0dfc, + 0x3c398df7, + 0x3c3a0e0b, 0x3c3a890d, - 0x3c3b0e0c, - 0x3c3b8e27, - 0x3c3c0e39, - 0x3c3c8e6c, - 0x3c3d0e76, - 0x3c3d8e8a, - 0x3c3e0e98, - 0x3c3e8ebd, + 0x3c3b0e1b, + 0x3c3b8e36, + 0x3c3c0e48, + 0x3c3c8e7b, + 0x3c3d0e85, + 0x3c3d8e99, + 0x3c3e0ea7, + 0x3c3e8ecc, 0x3c3f0c93, - 0x3c3f8ea6, + 0x3c3f8eb5, 0x3c4000ac, 0x3c4080ea, 0x3c410d13, 0x3c418d52, - 0x3c420e4f, - 0x403218a4, - 0x403298ba, - 0x403318e8, - 0x403398f2, - 0x40341909, - 0x40349927, - 0x40351937, - 0x40359949, - 0x40361956, - 0x40369962, - 0x40371977, - 0x40379989, - 0x40381994, - 0x403899a6, - 0x40390eed, - 0x403999b6, - 0x403a19c9, - 0x403a99ea, - 0x403b19fb, - 0x403b9a0b, + 0x3c420e5e, + 0x3c428de8, + 0x403218c6, + 0x403298dc, + 0x4033190a, + 0x40339914, + 0x4034192b, + 0x40349949, + 0x40351959, + 0x4035996b, + 0x40361978, + 0x40369984, + 0x40371999, + 0x403799ab, + 0x403819b6, + 0x403899c8, + 0x40390efc, + 0x403999d8, + 0x403a19eb, + 0x403a9a0c, + 0x403b1a1d, + 0x403b9a2d, 0x403c0064, 0x403c8083, - 0x403d1a8f, - 0x403d9aa5, - 0x403e1ab4, - 0x403e9aec, - 0x403f1b06, - 0x403f9b14, - 0x40401b29, - 0x40409b3d, - 0x40411b5a, - 0x40419b75, - 0x40421b8e, - 0x40429ba1, - 0x40431bb5, - 0x40439bcd, - 0x40441be4, + 0x403d1ab1, + 0x403d9ac7, + 0x403e1ad6, + 0x403e9b0e, + 0x403f1b28, + 0x403f9b36, + 0x40401b4b, + 0x40409b5f, + 0x40411b7c, + 0x40419b97, + 0x40421bb0, + 0x40429bc3, + 0x40431bd7, + 0x40439bef, + 0x40441c06, 0x404480ac, - 0x40451bf9, - 0x40459c0b, - 0x40461c2f, - 0x40469c4f, - 0x40471c5d, - 0x40479c84, - 0x40481cc1, - 0x40489cda, - 0x40491cf1, - 0x40499d0b, - 0x404a1d22, - 0x404a9d40, - 0x404b1d58, - 0x404b9d6f, - 0x404c1d85, - 0x404c9d97, - 0x404d1db8, - 0x404d9dda, - 0x404e1dee, - 0x404e9dfb, - 0x404f1e28, - 0x404f9e51, - 0x40501e8c, - 0x40509ea0, - 0x40511ebb, - 0x40521ecb, - 0x40529eef, - 0x40531f07, - 0x40539f1a, - 0x40541f2f, - 0x40549f52, - 0x40551f60, - 0x40559f7d, - 0x40561f8a, - 0x40569fa3, - 0x40571fbb, - 0x40579fce, - 0x40581fe3, - 0x4058a00a, - 0x40592039, - 0x4059a066, - 0x405a207a, - 0x405aa08a, - 0x405b20a2, - 0x405ba0b3, - 0x405c20c6, - 0x405ca105, - 0x405d2112, - 0x405da129, - 0x405e2167, + 0x40451c1b, + 0x40459c2d, + 0x40461c51, + 0x40469c71, + 0x40471c7f, + 0x40479ca6, + 0x40481ce3, + 0x40489d16, + 0x40491d2d, + 0x40499d47, + 0x404a1d5e, + 0x404a9d7c, + 0x404b1d94, + 0x404b9dab, + 0x404c1dc1, + 0x404c9dd3, + 0x404d1df4, + 0x404d9e16, + 0x404e1e2a, + 0x404e9e37, + 0x404f1e64, + 0x404f9e8d, + 0x40501ec8, + 0x40509edc, + 0x40511ef7, + 0x40521f07, + 0x40529f2b, + 0x40531f43, + 0x40539f56, + 0x40541f6b, + 0x40549f8e, + 0x40551f9c, + 0x40559fb9, + 0x40561fc6, + 0x40569fdf, + 0x40571ff7, + 0x4057a00a, + 0x4058201f, + 0x4058a046, + 0x40592075, + 0x4059a0a2, + 0x405a20b6, + 0x405aa0c6, + 0x405b20de, + 0x405ba0ef, + 0x405c2102, + 0x405ca141, + 0x405d214e, + 0x405da165, + 0x405e21a3, 0x405e8ab1, - 0x405f2188, - 0x405fa195, - 0x406021a3, - 0x4060a1c5, - 0x40612209, - 0x4061a241, - 0x40622258, - 0x4062a269, - 0x4063227a, - 0x4063a28f, - 0x406422a6, - 0x4064a2d2, - 0x406522ed, - 0x4065a304, - 0x4066231c, - 0x4066a346, - 0x40672371, - 0x4067a392, - 0x406823b9, - 0x4068a3da, - 0x4069240c, - 0x4069a43a, - 0x406a245b, - 0x406aa47b, - 0x406b2603, - 0x406ba626, - 0x406c263c, - 0x406ca8b7, - 0x406d28e6, - 0x406da90e, - 0x406e293c, - 0x406ea989, - 0x406f29a8, - 0x406fa9e0, - 0x407029f3, - 0x4070aa10, + 0x405f21c4, + 0x405fa1d1, + 0x406021df, + 0x4060a201, + 0x40612245, + 0x4061a27d, + 0x40622294, + 0x4062a2a5, + 0x406322b6, + 0x4063a2cb, + 0x406422e2, + 0x4064a30e, + 0x40652329, + 0x4065a340, + 0x40662358, + 0x4066a382, + 0x406723ad, + 0x4067a3ce, + 0x406823f5, + 0x4068a416, + 0x40692448, + 0x4069a476, + 0x406a2497, + 0x406aa4b7, + 0x406b263f, + 0x406ba662, + 0x406c2678, + 0x406ca8f3, + 0x406d2922, + 0x406da94a, + 0x406e2978, + 0x406ea9c5, + 0x406f29e4, + 0x406faa1c, + 0x40702a2f, + 0x4070aa4c, 0x40710800, - 0x4071aa22, - 0x40722a35, - 0x4072aa4e, - 0x40732a66, - 0x40739482, - 0x40742a7a, - 0x4074aa94, - 0x40752aa5, - 0x4075aab9, - 0x40762ac7, - 0x40769259, - 0x40772aec, - 0x4077ab0e, - 0x40782b29, - 0x4078ab62, - 0x40792b79, - 0x4079ab8f, - 0x407a2b9b, - 0x407aabae, - 0x407b2bc3, - 0x407babd5, - 0x407c2c06, - 0x407cac0f, - 0x407d23f5, - 0x407d9e61, - 0x407e2b3e, - 0x407ea01a, - 0x407f1c71, - 0x407f9a31, - 0x40801e38, - 0x40809c99, - 0x40811edd, - 0x40819e12, - 0x40822927, - 0x40829a17, - 0x40831ff5, - 0x4083a2b7, - 0x40841cad, - 0x4084a052, - 0x408520d7, - 0x4085a1ed, - 0x40862149, - 0x40869e7b, - 0x4087296d, - 0x4087a21e, - 0x40881a78, - 0x4088a3a5, - 0x40891ac7, - 0x40899a54, - 0x408a265c, - 0x408a9862, - 0x408b2bea, - 0x408ba9bd, - 0x408c20e7, - 0x408c987e, - 0x41f4252e, - 0x41f925c0, - 0x41fe24b3, - 0x41fea6a8, - 0x41ff2799, - 0x42032547, - 0x42082569, - 0x4208a5a5, - 0x42092497, - 0x4209a5df, - 0x420a24ee, - 0x420aa4ce, - 0x420b250e, - 0x420ba587, - 0x420c27b5, - 0x420ca675, - 0x420d268f, - 0x420da6c6, - 0x421226e0, - 0x4217277c, - 0x4217a722, - 0x421c2744, - 0x421f26ff, - 0x422127cc, - 0x4226275f, - 0x422b289b, - 0x422ba849, - 0x422c2883, - 0x422ca808, - 0x422d27e7, - 0x422da868, - 0x422e282e, - 0x422ea954, + 0x4071aa5e, + 0x40722a71, + 0x4072aa8a, + 0x40732aa2, + 0x407394a4, + 0x40742ab6, + 0x4074aad0, + 0x40752ae1, + 0x4075aaf5, + 0x40762b03, + 0x4076927b, + 0x40772b28, + 0x4077ab4a, + 0x40782b65, + 0x4078ab9e, + 0x40792bb5, + 0x4079abcb, + 0x407a2bd7, + 0x407aabea, + 0x407b2bff, + 0x407bac11, + 0x407c2c42, + 0x407cac4b, + 0x407d2431, + 0x407d9e9d, + 0x407e2b7a, + 0x407ea056, + 0x407f1c93, + 0x407f9a53, + 0x40801e74, + 0x40809cbb, + 0x40811f19, + 0x40819e4e, + 0x40822963, + 0x40829a39, + 0x40832031, + 0x4083a2f3, + 0x40841ccf, + 0x4084a08e, + 0x40852113, + 0x4085a229, + 0x40862185, + 0x40869eb7, + 0x408729a9, + 0x4087a25a, + 0x40881a9a, + 0x4088a3e1, + 0x40891ae9, + 0x40899a76, + 0x408a2698, + 0x408a9884, + 0x408b2c26, + 0x408ba9f9, + 0x408c2123, + 0x408c98a0, + 0x408d1cfc, + 0x41f4256a, + 0x41f925fc, + 0x41fe24ef, + 0x41fea6e4, + 0x41ff27d5, + 0x42032583, + 0x420825a5, + 0x4208a5e1, + 0x420924d3, + 0x4209a61b, + 0x420a252a, + 0x420aa50a, + 0x420b254a, + 0x420ba5c3, + 0x420c27f1, + 0x420ca6b1, + 0x420d26cb, + 0x420da702, + 0x4212271c, + 0x421727b8, + 0x4217a75e, + 0x421c2780, + 0x421f273b, + 0x42212808, + 0x4226279b, + 0x422b28d7, + 0x422ba885, + 0x422c28bf, + 0x422ca844, + 0x422d2823, + 0x422da8a4, + 0x422e286a, + 0x422ea990, 0x4432072b, 0x4432873a, 0x44330746, @@ -584,114 +587,114 @@ const uint32_t kOpenSSLReasonValues[] = { 0x44390800, 0x4439880e, 0x443a0821, - 0x48321283, - 0x48329295, - 0x483312ab, - 0x483392c4, - 0x4c3212e9, - 0x4c3292f9, - 0x4c33130c, - 0x4c33932c, + 0x483212a5, + 0x483292b7, + 0x483312cd, + 0x483392e6, + 0x4c32130b, + 0x4c32931b, + 0x4c33132e, + 0x4c33934e, 0x4c3400ac, 0x4c3480ea, - 0x4c351338, - 0x4c359346, - 0x4c361362, - 0x4c369375, - 0x4c371384, - 0x4c379392, - 0x4c3813a7, - 0x4c3893b3, - 0x4c3913d3, - 0x4c3993fd, - 0x4c3a1416, - 0x4c3a942f, + 0x4c35135a, + 0x4c359368, + 0x4c361384, + 0x4c369397, + 0x4c3713a6, + 0x4c3793b4, + 0x4c3813c9, + 0x4c3893d5, + 0x4c3913f5, + 0x4c39941f, + 0x4c3a1438, + 0x4c3a9451, 0x4c3b05fb, - 0x4c3b9448, - 0x4c3c145a, - 0x4c3c9469, - 0x4c3d1482, + 0x4c3b946a, + 0x4c3c147c, + 0x4c3c948b, + 0x4c3d14a4, 0x4c3d8c45, - 0x4c3e14db, - 0x4c3e9491, - 0x4c3f14fd, - 0x4c3f9259, - 0x4c4014a7, - 0x4c4092d5, - 0x4c4114cb, - 0x50322e48, - 0x5032ae57, - 0x50332e62, - 0x5033ae72, - 0x50342e8b, - 0x5034aea5, - 0x50352eb3, - 0x5035aec9, - 0x50362edb, - 0x5036aef1, - 0x50372f0a, - 0x5037af1d, - 0x50382f35, - 0x5038af46, - 0x50392f5b, - 0x5039af6f, - 0x503a2f8f, - 0x503aafa5, - 0x503b2fbd, - 0x503bafcf, - 0x503c2feb, - 0x503cb002, - 0x503d301b, - 0x503db031, - 0x503e303e, - 0x503eb054, - 0x503f3066, + 0x4c3e14fd, + 0x4c3e94b3, + 0x4c3f151f, + 0x4c3f927b, + 0x4c4014c9, + 0x4c4092f7, + 0x4c4114ed, + 0x50322e84, + 0x5032ae93, + 0x50332e9e, + 0x5033aeae, + 0x50342ec7, + 0x5034aee1, + 0x50352eef, + 0x5035af05, + 0x50362f17, + 0x5036af2d, + 0x50372f46, + 0x5037af59, + 0x50382f71, + 0x5038af82, + 0x50392f97, + 0x5039afab, + 0x503a2fcb, + 0x503aafe1, + 0x503b2ff9, + 0x503bb00b, + 0x503c3027, + 0x503cb03e, + 0x503d3057, + 0x503db06d, + 0x503e307a, + 0x503eb090, + 0x503f30a2, 0x503f8382, - 0x50403079, - 0x5040b089, - 0x504130a3, - 0x5041b0b2, - 0x504230cc, - 0x5042b0e9, - 0x504330f9, - 0x5043b109, - 0x50443118, + 0x504030b5, + 0x5040b0c5, + 0x504130df, + 0x5041b0ee, + 0x50423108, + 0x5042b125, + 0x50433135, + 0x5043b145, + 0x50443154, 0x5044843f, - 0x5045312c, - 0x5045b14a, - 0x5046315d, - 0x5046b173, - 0x50473185, - 0x5047b19a, - 0x504831c0, - 0x5048b1ce, - 0x504931e1, - 0x5049b1f6, - 0x504a320c, - 0x504ab21c, - 0x504b323c, - 0x504bb24f, - 0x504c3272, - 0x504cb2a0, - 0x504d32b2, - 0x504db2cf, - 0x504e32ea, - 0x504eb306, - 0x504f3318, - 0x504fb32f, - 0x5050333e, + 0x50453168, + 0x5045b186, + 0x50463199, + 0x5046b1af, + 0x504731c1, + 0x5047b1d6, + 0x504831fc, + 0x5048b20a, + 0x5049321d, + 0x5049b232, + 0x504a3248, + 0x504ab258, + 0x504b3278, + 0x504bb28b, + 0x504c32ae, + 0x504cb2dc, + 0x504d32ee, + 0x504db30b, + 0x504e3326, + 0x504eb342, + 0x504f3354, + 0x504fb36b, + 0x5050337a, 0x505086ef, - 0x50513351, - 0x58320f2b, - 0x68320eed, + 0x5051338d, + 0x58320f3a, + 0x68320efc, 0x68328c6a, 0x68330c7d, - 0x68338efb, - 0x68340f0b, + 0x68338f0a, + 0x68340f1a, 0x683480ea, - 0x6c320ec9, + 0x6c320ed8, 0x6c328c34, - 0x6c330ed4, + 0x6c330ee3, 0x74320a19, 0x743280ac, 0x74330c45, @@ -721,7 +724,7 @@ const uint32_t kOpenSSLReasonValues[] = { 0x783d8b4a, 0x783e0aa0, 0x783e8a52, - 0x7c321185, + 0x7c321194, }; const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]); @@ -912,6 +915,7 @@ const char kOpenSSLReasonStringData[] = "INVALID_FORM\0" "INVALID_GROUP_ORDER\0" "INVALID_PRIVATE_KEY\0" + "INVALID_SCALAR\0" "MISSING_PRIVATE_KEY\0" "NON_NAMED_CURVE\0" "PKPARAMETERS2GROUP_FAILURE\0" @@ -959,6 +963,7 @@ const char kOpenSSLReasonStringData[] = "UNKNOWN_PUBLIC_KEY_TYPE\0" "UNSUPPORTED_ALGORITHM\0" "OUTPUT_TOO_LARGE\0" + "INVALID_OID_STRING\0" "UNKNOWN_NID\0" "BAD_BASE64_DECODE\0" "BAD_END_LINE\0" @@ -1094,6 +1099,7 @@ const char kOpenSSLReasonStringData[] = "DUPLICATE_EXTENSION\0" "DUPLICATE_KEY_SHARE\0" "ECC_CERT_NOT_FOR_SIGNING\0" + "EMPTY_HELLO_RETRY_REQUEST\0" "EMS_STATE_INCONSISTENT\0" "ENCRYPTED_LENGTH_TOO_LONG\0" "ERROR_ADDING_EXTENSION\0" diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py index 221cbe7fd5..593b66dc4d 100755 --- a/src/boringssl/gen_build_yaml.py +++ b/src/boringssl/gen_build_yaml.py @@ -67,7 +67,9 @@ class Grpc(object): ), 'headers': sorted( map_dir(f) - for f in files['ssl_headers'] + files['ssl_internal_headers'] + files['crypto_headers'] + files['crypto_internal_headers'] + # We want to include files['fips_fragments'], but not build them as objects. + # See https://boringssl-review.googlesource.com/c/boringssl/+/16946 + for f in files['ssl_headers'] + files['ssl_internal_headers'] + files['crypto_headers'] + files['crypto_internal_headers'] + files['fips_fragments'] ), 'boringssl': True, 'defaults': 'boringssl', diff --git a/src/compiler/csharp_generator.cc b/src/compiler/csharp_generator.cc index 7c97056402..6e2730579a 100644 --- a/src/compiler/csharp_generator.cc +++ b/src/compiler/csharp_generator.cc @@ -451,8 +451,10 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { out->Print( "public virtual $response$ $methodname$($request$ request, " "grpc::Metadata " - "headers = null, DateTime? deadline = null, CancellationToken " - "cancellationToken = default(CancellationToken))\n", + "headers = null, global::System.DateTime? deadline = null, " + "global::System.Threading.CancellationToken " + "cancellationToken = " + "default(global::System.Threading.CancellationToken))\n", "methodname", method->name(), "request", GetClassName(method->input_type()), "response", GetClassName(method->output_type())); @@ -492,8 +494,10 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { out->Print( "public virtual $returntype$ " "$methodname$($request_maybe$grpc::Metadata " - "headers = null, DateTime? deadline = null, CancellationToken " - "cancellationToken = default(CancellationToken))\n", + "headers = null, global::System.DateTime? deadline = null, " + "global::System.Threading.CancellationToken " + "cancellationToken = " + "default(global::System.Threading.CancellationToken))\n", "methodname", method_name, "request_maybe", GetMethodRequestParamMaybe(method), "returntype", GetMethodReturnTypeClient(method)); @@ -675,9 +679,6 @@ grpc::string GetServices(const FileDescriptor* file, bool generate_client, out.Print("#pragma warning disable 1591\n"); out.Print("#region Designer generated code\n"); out.Print("\n"); - out.Print("using System;\n"); - out.Print("using System.Threading;\n"); - out.Print("using System.Threading.Tasks;\n"); out.Print("using grpc = global::Grpc.Core;\n"); out.Print("\n"); diff --git a/src/compiler/objective_c_generator.cc b/src/compiler/objective_c_generator.cc index ab7d869758..ffdeb8f6b0 100644 --- a/src/compiler/objective_c_generator.cc +++ b/src/compiler/objective_c_generator.cc @@ -212,37 +212,49 @@ void PrintMethodImplementations(Printer* printer, return output; } -::grpc::string GetHeader(const ServiceDescriptor* service) { +::grpc::string GetProtocol(const ServiceDescriptor* service) { ::grpc::string output; - { - // Scope the output stream so it closes and finalizes output to the string. - grpc::protobuf::io::StringOutputStream output_stream(&output); - Printer printer(&output_stream, '$'); - - map< ::grpc::string, ::grpc::string> vars = { - {"service_class", ServiceClassName(service)}}; - printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n"); + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); - for (int i = 0; i < service->method_count(); i++) { - PrintMethodDeclarations(&printer, service->method(i)); - } - printer.Print("@end\n\n"); + map< ::grpc::string, ::grpc::string> vars = { + {"service_class", ServiceClassName(service)}}; - printer.Print( - "/**\n" - " * Basic service implementation, over gRPC, that only does\n" - " * marshalling and parsing.\n" - " */\n"); - printer.Print(vars, - "@interface $service_class$ :" - " GRPCProtoService<$service_class$>\n"); - printer.Print( - "- (instancetype)initWithHost:(NSString *)host" - " NS_DESIGNATED_INITIALIZER;\n"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); - printer.Print("@end\n"); + printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n"); + for (int i = 0; i < service->method_count(); i++) { + PrintMethodDeclarations(&printer, service->method(i)); } + printer.Print("@end\n\n"); + + return output; +} + +::grpc::string GetInterface(const ServiceDescriptor* service) { + ::grpc::string output; + + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); + + map< ::grpc::string, ::grpc::string> vars = { + {"service_class", ServiceClassName(service)}}; + + printer.Print(vars, + "/**\n" + " * Basic service implementation, over gRPC, that only does\n" + " * marshalling and parsing.\n" + " */\n"); + printer.Print(vars, + "@interface $service_class$ :" + " GRPCProtoService<$service_class$>\n"); + printer.Print( + "- (instancetype)initWithHost:(NSString *)host" + " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n"); + printer.Print("@end\n"); + return output; } @@ -258,26 +270,32 @@ void PrintMethodImplementations(Printer* printer, {"service_class", ServiceClassName(service)}, {"package", service->file()->package()}}; - printer.Print(vars, "@implementation $service_class$\n\n"); + printer.Print(vars, + "@implementation $service_class$\n\n" + "// Designated initializer\n" + "- (instancetype)initWithHost:(NSString *)host {\n" + " self = [super initWithHost:host\n" + " packageName:@\"$package$\"\n" + " serviceName:@\"$service_name$\"];\n" + " return self;\n" + "}\n\n"); - printer.Print("// Designated initializer\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); - printer.Print( - vars, - " return (self = [super initWithHost:host" - " packageName:@\"$package$\" serviceName:@\"$service_name$\"]);\n"); - printer.Print("}\n\n"); printer.Print( "// Override superclass initializer to disallow different" - " package and service names.\n"); - printer.Print("- (instancetype)initWithHost:(NSString *)host\n"); - printer.Print(" packageName:(NSString *)packageName\n"); - printer.Print(" serviceName:(NSString *)serviceName {\n"); - printer.Print(" return [self initWithHost:host];\n"); - printer.Print("}\n\n"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host {\n"); - printer.Print(" return [[self alloc] initWithHost:host];\n"); - printer.Print("}\n\n\n"); + " package and service names.\n" + "- (instancetype)initWithHost:(NSString *)host\n" + " packageName:(NSString *)packageName\n" + " serviceName:(NSString *)serviceName {\n" + " return [self initWithHost:host];\n" + "}\n\n"); + + printer.Print( + "#pragma mark - Class Methods\n\n" + "+ (instancetype)serviceWithHost:(NSString *)host {\n" + " return [[self alloc] initWithHost:host];\n" + "}\n\n"); + + printer.Print("#pragma mark - Method Implementations\n\n"); for (int i = 0; i < service->method_count(); i++) { PrintMethodImplementations(&printer, service->method(i)); diff --git a/src/compiler/objective_c_generator.h b/src/compiler/objective_c_generator.h index d3aed76c4f..eb1c7ff005 100644 --- a/src/compiler/objective_c_generator.h +++ b/src/compiler/objective_c_generator.h @@ -31,9 +31,13 @@ using ::grpc::string; // Returns forward declaration of classes in the generated header file. string GetAllMessageClasses(const FileDescriptor* file); -// Returns the content to be included in the "global_scope" insertion point of -// the generated header file. -string GetHeader(const ServiceDescriptor* service); +// Returns the content to be included defining the @protocol segment at the +// insertion point of the generated implementation file. +string GetProtocol(const ServiceDescriptor* service); + +// Returns the content to be included defining the @interface segment at the +// insertion point of the generated implementation file. +string GetInterface(const ServiceDescriptor* service); // Returns the content to be included in the "global_scope" insertion point of // the generated implementation file. diff --git a/src/compiler/objective_c_generator_helpers.h b/src/compiler/objective_c_generator_helpers.h index 4004e6aef8..a284da97f4 100644 --- a/src/compiler/objective_c_generator_helpers.h +++ b/src/compiler/objective_c_generator_helpers.h @@ -40,5 +40,45 @@ inline string ServiceClassName(const ServiceDescriptor* service) { string prefix = file->options().objc_class_prefix(); return prefix + service->name(); } + +inline ::grpc::string LocalImport(const ::grpc::string& import) { + return ::grpc::string("#import \"" + import + "\"\n"); +} + +inline ::grpc::string SystemImport(const ::grpc::string& import) { + return ::grpc::string("#import <" + import + ">\n"); +} + +inline ::grpc::string PreprocConditional(::grpc::string symbol, bool invert) { + return invert ? "!defined(" + symbol + ") || !" + symbol + : "defined(" + symbol + ") && " + symbol; +} + +inline ::grpc::string PreprocIf(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#endif\n"); +} + +inline ::grpc::string PreprocIfNot(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#endif\n"); +} + +inline ::grpc::string PreprocIfElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + +inline ::grpc::string PreprocIfNotElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + } // namespace grpc_objective_c_generator #endif // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H diff --git a/src/compiler/objective_c_plugin.cc b/src/compiler/objective_c_plugin.cc index d5d488e84d..76703d79cd 100644 --- a/src/compiler/objective_c_plugin.cc +++ b/src/compiler/objective_c_plugin.cc @@ -29,12 +29,42 @@ using ::google::protobuf::compiler::objectivec:: IsProtobufLibraryBundledProtoFile; using ::google::protobuf::compiler::objectivec::ProtobufLibraryFrameworkName; +using ::grpc_objective_c_generator::LocalImport; +using ::grpc_objective_c_generator::PreprocIfElse; +using ::grpc_objective_c_generator::PreprocIfNot; +using ::grpc_objective_c_generator::SystemImport; + +namespace { + +inline ::grpc::string ImportProtoHeaders( + const grpc::protobuf::FileDescriptor* dep, const char* indent) { + ::grpc::string header = grpc_objective_c_generator::MessageHeaderName(dep); + + if (!IsProtobufLibraryBundledProtoFile(dep)) { + return indent + LocalImport(header); + } + + ::grpc::string base_name = header; + grpc_generator::StripPrefix(&base_name, "google/protobuf/"); + // create the import code snippet + ::grpc::string framework_header = + ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name; + + static const ::grpc::string kFrameworkImportsCondition = + "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"; + return PreprocIfElse(kFrameworkImportsCondition, + indent + SystemImport(framework_header), + indent + LocalImport(header)); +} + +} // namespace class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { public: ObjectiveCGrpcGenerator() {} virtual ~ObjectiveCGrpcGenerator() {} + public: virtual bool Generate(const grpc::protobuf::FileDescriptor* file, const ::grpc::string& parameter, grpc::protobuf::compiler::GeneratorContext* context, @@ -44,97 +74,68 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { return true; } + static const ::grpc::string kNonNullBegin = "NS_ASSUME_NONNULL_BEGIN\n"; + static const ::grpc::string kNonNullEnd = "NS_ASSUME_NONNULL_END\n"; + static const ::grpc::string kProtocolOnly = "GPB_GRPC_PROTOCOL_ONLY"; + static const ::grpc::string kForwardDeclare = + "GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO"; + ::grpc::string file_name = google::protobuf::compiler::objectivec::FilePath(file); - ::grpc::string prefix = file->options().objc_class_prefix(); { // Generate .pbrpc.h - ::grpc::string imports = - ::grpc::string("#if !GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n") + - "#import \"" + file_name + - ".pbobjc.h\"\n" - "#endif\n\n" - "#import <ProtoRPC/ProtoService.h>\n" - "#import <ProtoRPC/ProtoRPC.h>\n" - "#import <RxLibrary/GRXWriteable.h>\n" - "#import <RxLibrary/GRXWriter.h>\n"; - - ::grpc::string proto_imports; - proto_imports += "#if GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO\n" + - grpc_objective_c_generator::GetAllMessageClasses(file) + - "#else\n"; + ::grpc::string imports = LocalImport(file_name + ".pbobjc.h"); + + ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriteable.h") + + SystemImport("RxLibrary/GRXWriter.h"); + + ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n"; + + ::grpc::string class_declarations = + grpc_objective_c_generator::GetAllMessageClasses(file); + + ::grpc::string class_imports; for (int i = 0; i < file->dependency_count(); i++) { - ::grpc::string header = - grpc_objective_c_generator::MessageHeaderName(file->dependency(i)); - const grpc::protobuf::FileDescriptor* dependency = file->dependency(i); - if (IsProtobufLibraryBundledProtoFile(dependency)) { - ::grpc::string base_name = header; - grpc_generator::StripPrefix(&base_name, "google/protobuf/"); - // create the import code snippet - proto_imports += - " #if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n" - " #import <" + - ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name + - ">\n" - " #else\n" - " #import \"" + - header + - "\"\n" - " #endif\n"; - } else { - proto_imports += ::grpc::string(" #import \"") + header + "\"\n"; - } + class_imports += ImportProtoHeaders(file->dependency(i), " "); } - proto_imports += "#endif\n"; - ::grpc::string declarations; + ::grpc::string protocols; for (int i = 0; i < file->service_count(); i++) { const grpc::protobuf::ServiceDescriptor* service = file->service(i); - declarations += grpc_objective_c_generator::GetHeader(service); + protocols += grpc_objective_c_generator::GetProtocol(service); } - static const ::grpc::string kNonNullBegin = - "\nNS_ASSUME_NONNULL_BEGIN\n\n"; - static const ::grpc::string kNonNullEnd = "\nNS_ASSUME_NONNULL_END\n"; + ::grpc::string interfaces; + for (int i = 0; i < file->service_count(); i++) { + const grpc::protobuf::ServiceDescriptor* service = file->service(i); + interfaces += grpc_objective_c_generator::GetInterface(service); + } Write(context, file_name + ".pbrpc.h", - imports + '\n' + proto_imports + '\n' + kNonNullBegin + - declarations + kNonNullEnd); + PreprocIfNot(kForwardDeclare, imports) + "\n" + + PreprocIfNot(kProtocolOnly, system_imports) + "\n" + + PreprocIfElse(kForwardDeclare, class_declarations, + class_imports) + + "\n" + forward_declarations + "\n" + kNonNullBegin + "\n" + + protocols + "\n" + PreprocIfNot(kProtocolOnly, interfaces) + + "\n" + kNonNullEnd + "\n"); } { // Generate .pbrpc.m - ::grpc::string imports = ::grpc::string("#import \"") + file_name + - ".pbrpc.h\"\n" - "#import \"" + - file_name + - ".pbobjc.h\"\n\n" - "#import <ProtoRPC/ProtoRPC.h>\n" - "#import <RxLibrary/GRXWriter+Immediate.h>\n"; + ::grpc::string imports = LocalImport(file_name + ".pbrpc.h") + + LocalImport(file_name + ".pbobjc.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriter+Immediate.h"); + + ::grpc::string class_imports; for (int i = 0; i < file->dependency_count(); i++) { - ::grpc::string header = - grpc_objective_c_generator::MessageHeaderName(file->dependency(i)); - const grpc::protobuf::FileDescriptor* dependency = file->dependency(i); - if (IsProtobufLibraryBundledProtoFile(dependency)) { - ::grpc::string base_name = header; - grpc_generator::StripPrefix(&base_name, "google/protobuf/"); - // create the import code snippet - imports += - "#if GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS\n" - " #import <" + - ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name + - ">\n" - "#else\n" - " #import \"" + - header + - "\"\n" - "#endif\n"; - } else { - imports += ::grpc::string("#import \"") + header + "\"\n"; - } + class_imports += ImportProtoHeaders(file->dependency(i), ""); } ::grpc::string definitions; @@ -143,7 +144,9 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { definitions += grpc_objective_c_generator::GetSource(service); } - Write(context, file_name + ".pbrpc.m", imports + '\n' + definitions); + Write(context, file_name + ".pbrpc.m", + PreprocIfNot(kProtocolOnly, + imports + "\n" + class_imports + "\n" + definitions)); } return true; diff --git a/src/compiler/php_generator.cc b/src/compiler/php_generator.cc index d9705e8077..ca084cccbd 100644 --- a/src/compiler/php_generator.cc +++ b/src/compiler/php_generator.cc @@ -18,10 +18,12 @@ #include <map> +#include <google/protobuf/compiler/php/php_generator.h> #include "src/compiler/config.h" #include "src/compiler/generator_helpers.h" #include "src/compiler/php_generator_helpers.h" +using google::protobuf::compiler::php::GeneratedClassName; using grpc::protobuf::Descriptor; using grpc::protobuf::FileDescriptor; using grpc::protobuf::MethodDescriptor; @@ -55,8 +57,10 @@ grpc::string MessageIdentifierName(const grpc::string& name, const FileDescriptor* file) { std::vector<grpc::string> tokens = grpc_generator::tokenize(name, "."); std::ostringstream oss; - oss << PackageName(file) << "\\" - << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]); + if (PackageName(file) != "") { + oss << PackageName(file) << "\\"; + } + oss << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]); return oss.str(); } @@ -67,9 +71,9 @@ void PrintMethod(const MethodDescriptor* method, Printer* out) { vars["service_name"] = method->service()->full_name(); vars["name"] = method->name(); vars["input_type_id"] = - MessageIdentifierName(input_type->full_name(), input_type->file()); - vars["output_type_id"] = - MessageIdentifierName(output_type->full_name(), output_type->file()); + MessageIdentifierName(GeneratedClassName(input_type), input_type->file()); + vars["output_type_id"] = MessageIdentifierName( + GeneratedClassName(output_type), output_type->file()); out->Print("/**\n"); out->Print(GetPHPComments(method, " *").c_str()); diff --git a/src/core/ext/census/grpc_context.cc b/src/core/ext/census/grpc_context.cc index 069e8f1486..599a798dda 100644 --- a/src/core/ext/census/grpc_context.cc +++ b/src/core/ext/census/grpc_context.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/census.h> #include <grpc/grpc.h> #include "src/core/lib/surface/api_trace.h" diff --git a/src/core/ext/filters/client_channel/backup_poller.cc b/src/core/ext/filters/client_channel/backup_poller.cc index ee90b499eb..3e2faa57bc 100644 --- a/src/core/ext/filters/client_channel/backup_poller.cc +++ b/src/core/ext/filters/client_channel/backup_poller.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015 gRPC authors. + * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/backup_poller.h" #include <grpc/grpc.h> @@ -125,13 +127,7 @@ static void run_poller(void* arg, grpc_error* error) { &p->run_poller_closure); } -void grpc_client_channel_start_backup_polling( - grpc_pollset_set* interested_parties) { - gpr_once_init(&g_once, init_globals); - if (g_poll_interval_ms == 0) { - return; - } - gpr_mu_lock(&g_poller_mu); +static void g_poller_init_locked() { if (g_poller == nullptr) { g_poller = static_cast<backup_poller*>(gpr_zalloc(sizeof(backup_poller))); g_poller->pollset = @@ -147,7 +143,16 @@ void grpc_client_channel_start_backup_polling( grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms, &g_poller->run_poller_closure); } +} +void grpc_client_channel_start_backup_polling( + grpc_pollset_set* interested_parties) { + gpr_once_init(&g_once, init_globals); + if (g_poll_interval_ms == 0) { + return; + } + gpr_mu_lock(&g_poller_mu); + g_poller_init_locked(); gpr_ref(&g_poller->refs); /* Get a reference to g_poller->pollset before releasing g_poller_mu to make * TSAN happy. Otherwise, reading from g_poller (i.e g_poller->pollset) after diff --git a/src/core/ext/filters/client_channel/backup_poller.h b/src/core/ext/filters/client_channel/backup_poller.h index 551e0331dc..7285b9b93e 100644 --- a/src/core/ext/filters/client_channel/backup_poller.h +++ b/src/core/ext/filters/client_channel/backup_poller.h @@ -1,6 +1,6 @@ /* * - * Copyright 2015 gRPC authors. + * Copyright 2017 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc index 31a5c31124..37860e82e3 100644 --- a/src/core/ext/filters/client_channel/channel_connectivity.cc +++ b/src/core/ext/filters/client_channel/channel_connectivity.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/channel.h" #include <inttypes.h> diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 9a8f25b630..bbc5160bec 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -21,6 +21,7 @@ #include "src/core/ext/filters/client_channel/client_channel.h" #include <inttypes.h> +#include <limits.h> #include <stdbool.h> #include <stdio.h> #include <string.h> @@ -33,144 +34,65 @@ #include "src/core/ext/filters/client_channel/backup_poller.h" #include "src/core/ext/filters/client_channel/http_connect_handshaker.h" #include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/method_params.h" #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" #include "src/core/ext/filters/client_channel/resolver_registry.h" #include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/status_util.h" #include "src/core/ext/filters/client_channel/subchannel.h" #include "src/core/ext/filters/deadline/deadline_filter.h" +#include "src/core/lib/backoff/backoff.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/connected_channel.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/metadata_batch.h" #include "src/core/lib/transport/service_config.h" #include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" + +using grpc_core::internal::ClientChannelMethodParams; /* Client channel implementation */ +// By default, we buffer 256 KiB per RPC for retries. +// TODO(roth): Do we have any data to suggest a better value? +#define DEFAULT_PER_RPC_RETRY_BUFFER_SIZE (256 << 10) + +// This value was picked arbitrarily. It can be changed if there is +// any even moderately compelling reason to do so. +#define RETRY_BACKOFF_JITTER 0.2 + grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel"); /************************************************************************* - * METHOD-CONFIG TABLE + * CHANNEL-WIDE FUNCTIONS */ -typedef enum { - /* zero so it can be default initialized */ - WAIT_FOR_READY_UNSET = 0, - WAIT_FOR_READY_FALSE, - WAIT_FOR_READY_TRUE -} wait_for_ready_value; - -typedef struct { - gpr_refcount refs; - grpc_millis timeout; - wait_for_ready_value wait_for_ready; -} method_parameters; - -static method_parameters* method_parameters_ref( - method_parameters* method_params) { - gpr_ref(&method_params->refs); - return method_params; -} - -static void method_parameters_unref(method_parameters* method_params) { - if (gpr_unref(&method_params->refs)) { - gpr_free(method_params); - } -} - -// Wrappers to pass to grpc_service_config_create_method_config_table(). -static void* method_parameters_ref_wrapper(void* value) { - return method_parameters_ref(static_cast<method_parameters*>(value)); -} -static void method_parameters_unref_wrapper(void* value) { - method_parameters_unref(static_cast<method_parameters*>(value)); -} - -static bool parse_wait_for_ready(grpc_json* field, - wait_for_ready_value* wait_for_ready) { - if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { - return false; - } - *wait_for_ready = field->type == GRPC_JSON_TRUE ? WAIT_FOR_READY_TRUE - : WAIT_FOR_READY_FALSE; - return true; -} - -static bool parse_timeout(grpc_json* field, grpc_millis* timeout) { - if (field->type != GRPC_JSON_STRING) return false; - size_t len = strlen(field->value); - if (field->value[len - 1] != 's') return false; - char* buf = gpr_strdup(field->value); - buf[len - 1] = '\0'; // Remove trailing 's'. - char* decimal_point = strchr(buf, '.'); - int nanos = 0; - if (decimal_point != nullptr) { - *decimal_point = '\0'; - nanos = gpr_parse_nonnegative_int(decimal_point + 1); - if (nanos == -1) { - gpr_free(buf); - return false; - } - int num_digits = static_cast<int>(strlen(decimal_point + 1)); - if (num_digits > 9) { // We don't accept greater precision than nanos. - gpr_free(buf); - return false; - } - for (int i = 0; i < (9 - num_digits); ++i) { - nanos *= 10; - } - } - int seconds = decimal_point == buf ? 0 : gpr_parse_nonnegative_int(buf); - gpr_free(buf); - if (seconds == -1) return false; - *timeout = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS; - return true; -} - -static void* method_parameters_create_from_json(const grpc_json* json) { - wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET; - grpc_millis timeout = 0; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { - if (field->key == nullptr) continue; - if (strcmp(field->key, "waitForReady") == 0) { - if (wait_for_ready != WAIT_FOR_READY_UNSET) return nullptr; // Duplicate. - if (!parse_wait_for_ready(field, &wait_for_ready)) return nullptr; - } else if (strcmp(field->key, "timeout") == 0) { - if (timeout > 0) return nullptr; // Duplicate. - if (!parse_timeout(field, &timeout)) return nullptr; - } - } - method_parameters* value = - static_cast<method_parameters*>(gpr_malloc(sizeof(method_parameters))); - gpr_ref_init(&value->refs, 1); - value->timeout = timeout; - value->wait_for_ready = wait_for_ready; - return value; -} - struct external_connectivity_watcher; -/************************************************************************* - * CHANNEL-WIDE FUNCTIONS - */ +typedef grpc_core::SliceHashTable< + grpc_core::RefCountedPtr<ClientChannelMethodParams>> + MethodParamsTable; typedef struct client_channel_channel_data { - /** resolver for this channel */ grpc_core::OrphanablePtr<grpc_core::Resolver> resolver; - /** have we started resolving this channel */ bool started_resolving; - /** is deadline checking enabled? */ bool deadline_checking_enabled; - /** client channel factory */ grpc_client_channel_factory* client_channel_factory; + bool enable_retries; + size_t per_rpc_retry_buffer_size; /** combiner protecting all variables below in this data structure */ grpc_combiner* combiner; @@ -179,7 +101,7 @@ typedef struct client_channel_channel_data { /** retry throttle data */ grpc_server_retry_throttle_data* retry_throttle_data; /** maps method names to method_parameters structs */ - grpc_slice_hash_table* method_params_table; + grpc_core::RefCountedPtr<MethodParamsTable> method_params_table; /** incoming resolver result - set by resolver.next() */ grpc_channel_args* resolver_result; /** a list of closures that are all waiting for resolver result to come in */ @@ -200,7 +122,7 @@ typedef struct client_channel_channel_data { gpr_mu external_connectivity_watcher_list_mu; struct external_connectivity_watcher* external_connectivity_watcher_list_head; - /* the following properties are guarded by a mutex since API's require them + /* the following properties are guarded by a mutex since APIs require them to be instantaneously available */ gpr_mu info_mu; char* info_lb_policy_name; @@ -306,9 +228,8 @@ typedef struct { grpc_server_retry_throttle_data* retry_throttle_data; } service_config_parsing_state; -static void parse_retry_throttle_params(const grpc_json* field, void* arg) { - service_config_parsing_state* parsing_state = - static_cast<service_config_parsing_state*>(arg); +static void parse_retry_throttle_params( + const grpc_json* field, service_config_parsing_state* parsing_state) { if (strcmp(field->key, "retryThrottling") == 0) { if (parsing_state->retry_throttle_data != nullptr) return; // Duplicate. if (field->type != GRPC_JSON_OBJECT) return; @@ -388,7 +309,7 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand, grpc_error_string(error)); } - // Extract the following fields from the resolver result, if non-NULL. + // Extract the following fields from the resolver result, if non-nullptr. bool lb_policy_updated = false; bool lb_policy_created = false; char* lb_policy_name_dup = nullptr; @@ -396,7 +317,7 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { grpc_core::OrphanablePtr<grpc_core::LoadBalancingPolicy> new_lb_policy; char* service_config_json = nullptr; grpc_server_retry_throttle_data* retry_throttle_data = nullptr; - grpc_slice_hash_table* method_params_table = nullptr; + grpc_core::RefCountedPtr<MethodParamsTable> method_params_table; if (chand->resolver_result != nullptr) { if (chand->resolver != nullptr) { // Find LB policy name. @@ -431,7 +352,6 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { // Use pick_first if nothing was specified and we didn't select grpclb // above. if (lb_policy_name == nullptr) lb_policy_name = "pick_first"; - // Check to see if we're already using the right LB policy. // Note: It's safe to use chand->info_lb_policy_name here without // taking a lock on chand->info_mu, because this function is the @@ -469,39 +389,39 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { new_lb_policy->SetReresolutionClosureLocked(&args->closure); } } + // Before we clean up, save a copy of lb_policy_name, since it might + // be pointing to data inside chand->resolver_result. + // The copy will be saved in chand->lb_policy_name below. + lb_policy_name_dup = gpr_strdup(lb_policy_name); // Find service config. channel_arg = grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVICE_CONFIG); service_config_json = gpr_strdup(grpc_channel_arg_get_string(channel_arg)); if (service_config_json != nullptr) { - grpc_service_config* service_config = - grpc_service_config_create(service_config_json); + grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config = + grpc_core::ServiceConfig::Create(service_config_json); if (service_config != nullptr) { - channel_arg = grpc_channel_args_find(chand->resolver_result, - GRPC_ARG_SERVER_URI); - const char* server_uri = grpc_channel_arg_get_string(channel_arg); - GPR_ASSERT(server_uri != nullptr); - grpc_uri* uri = grpc_uri_parse(server_uri, true); - GPR_ASSERT(uri->path[0] != '\0'); - service_config_parsing_state parsing_state; - memset(&parsing_state, 0, sizeof(parsing_state)); - parsing_state.server_name = - uri->path[0] == '/' ? uri->path + 1 : uri->path; - grpc_service_config_parse_global_params( - service_config, parse_retry_throttle_params, &parsing_state); - grpc_uri_destroy(uri); - retry_throttle_data = parsing_state.retry_throttle_data; - method_params_table = grpc_service_config_create_method_config_table( - service_config, method_parameters_create_from_json, - method_parameters_ref_wrapper, method_parameters_unref_wrapper); - grpc_service_config_destroy(service_config); + if (chand->enable_retries) { + channel_arg = grpc_channel_args_find(chand->resolver_result, + GRPC_ARG_SERVER_URI); + const char* server_uri = grpc_channel_arg_get_string(channel_arg); + GPR_ASSERT(server_uri != nullptr); + grpc_uri* uri = grpc_uri_parse(server_uri, true); + GPR_ASSERT(uri->path[0] != '\0'); + service_config_parsing_state parsing_state; + memset(&parsing_state, 0, sizeof(parsing_state)); + parsing_state.server_name = + uri->path[0] == '/' ? uri->path + 1 : uri->path; + service_config->ParseGlobalParams(parse_retry_throttle_params, + &parsing_state); + grpc_uri_destroy(uri); + retry_throttle_data = parsing_state.retry_throttle_data; + } + method_params_table = service_config->CreateMethodConfigTable( + ClientChannelMethodParams::CreateFromJson); } } - // Before we clean up, save a copy of lb_policy_name, since it might - // be pointing to data inside chand->resolver_result. - // The copy will be saved in chand->lb_policy_name below. - lb_policy_name_dup = gpr_strdup(lb_policy_name); } grpc_channel_args_destroy(chand->resolver_result); chand->resolver_result = nullptr; @@ -514,7 +434,7 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { lb_policy_name_changed ? " (changed)" : "", service_config_json); } // Now swap out fields in chand. Note that the new values may still - // be NULL if (e.g.) the resolver failed to return results or the + // be nullptr if (e.g.) the resolver failed to return results or the // results did not contain the necessary data. // // First, swap out the data used by cc_get_channel_info(). @@ -534,16 +454,13 @@ static void on_resolver_result_changed_locked(void* arg, grpc_error* error) { } chand->retry_throttle_data = retry_throttle_data; // Swap out the method params table. - if (chand->method_params_table != nullptr) { - grpc_slice_hash_table_unref(chand->method_params_table); - } - chand->method_params_table = method_params_table; + chand->method_params_table = std::move(method_params_table); // If we have a new LB policy or are shutting down (in which case - // new_lb_policy will be NULL), swap out the LB policy, unreffing the old one - // and removing its fds from chand->interested_parties. Note that we do NOT do - // this if either (a) we updated the existing LB policy above or (b) we failed - // to create the new LB policy (in which case we want to continue using the - // most recent one we had). + // new_lb_policy will be nullptr), swap out the LB policy, unreffing the + // old one and removing its fds from chand->interested_parties. + // Note that we do NOT do this if either (a) we updated the existing + // LB policy above or (b) we failed to create the new LB policy (in + // which case we want to continue using the most recent one we had). if (new_lb_policy != nullptr || error != GRPC_ERROR_NONE || chand->resolver == nullptr) { if (chand->lb_policy != nullptr) { @@ -722,9 +639,17 @@ static grpc_error* cc_init_channel_elem(grpc_channel_element* elem, grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, "client_channel"); grpc_client_channel_start_backup_polling(chand->interested_parties); + // Record max per-RPC retry buffer size. + const grpc_arg* arg = grpc_channel_args_find( + args->channel_args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE); + chand->per_rpc_retry_buffer_size = (size_t)grpc_channel_arg_get_integer( + arg, {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX}); + // Record enable_retries. + arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_ENABLE_RETRIES); + chand->enable_retries = grpc_channel_arg_get_bool(arg, true); // Record client channel factory. - const grpc_arg* arg = grpc_channel_args_find(args->channel_args, - GRPC_ARG_CLIENT_CHANNEL_FACTORY); + arg = grpc_channel_args_find(args->channel_args, + GRPC_ARG_CLIENT_CHANNEL_FACTORY); if (arg == nullptr) { return GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Missing client channel factory in args for client channel filter"); @@ -794,7 +719,7 @@ static void cc_destroy_channel_elem(grpc_channel_element* elem) { grpc_server_retry_throttle_data_unref(chand->retry_throttle_data); } if (chand->method_params_table != nullptr) { - grpc_slice_hash_table_unref(chand->method_params_table); + chand->method_params_table.reset(); } grpc_client_channel_stop_backup_polling(chand->interested_parties); grpc_connectivity_state_destroy(&chand->state_tracker); @@ -809,15 +734,123 @@ static void cc_destroy_channel_elem(grpc_channel_element* elem) { */ // Max number of batches that can be pending on a call at any given -// time. This includes: +// time. This includes one batch for each of the following ops: // recv_initial_metadata // send_initial_metadata // recv_message // send_message // recv_trailing_metadata // send_trailing_metadata -// We also add room for a single cancel_stream batch. -#define MAX_WAITING_BATCHES 7 +#define MAX_PENDING_BATCHES 6 + +// Retry support: +// +// In order to support retries, we act as a proxy for stream op batches. +// When we get a batch from the surface, we add it to our list of pending +// batches, and we then use those batches to construct separate "child" +// batches to be started on the subchannel call. When the child batches +// return, we then decide which pending batches have been completed and +// schedule their callbacks accordingly. If a subchannel call fails and +// we want to retry it, we do a new pick and start again, constructing +// new "child" batches for the new subchannel call. +// +// Note that retries are committed when receiving data from the server +// (except for Trailers-Only responses). However, there may be many +// send ops started before receiving any data, so we may have already +// completed some number of send ops (and returned the completions up to +// the surface) by the time we realize that we need to retry. To deal +// with this, we cache data for send ops, so that we can replay them on a +// different subchannel call even after we have completed the original +// batches. +// +// There are two sets of data to maintain: +// - In call_data (in the parent channel), we maintain a list of pending +// ops and cached data for send ops. +// - In the subchannel call, we maintain state to indicate what ops have +// already been sent down to that call. +// +// When constructing the "child" batches, we compare those two sets of +// data to see which batches need to be sent to the subchannel call. + +// TODO(roth): In subsequent PRs: +// - add support for transparent retries (including initial metadata) +// - figure out how to record stats in census for retries +// (census filter is on top of this one) +// - add census stats for retries + +// State used for starting a retryable batch on a subchannel call. +// This provides its own grpc_transport_stream_op_batch and other data +// structures needed to populate the ops in the batch. +// We allocate one struct on the arena for each attempt at starting a +// batch on a given subchannel call. +typedef struct { + gpr_refcount refs; + grpc_call_element* elem; + grpc_subchannel_call* subchannel_call; // Holds a ref. + // The batch to use in the subchannel call. + // Its payload field points to subchannel_call_retry_state.batch_payload. + grpc_transport_stream_op_batch batch; + // For send_initial_metadata. + // Note that we need to make a copy of the initial metadata for each + // subchannel call instead of just referring to the copy in call_data, + // because filters in the subchannel stack will probably add entries, + // so we need to start in a pristine state for each attempt of the call. + grpc_linked_mdelem* send_initial_metadata_storage; + grpc_metadata_batch send_initial_metadata; + // For send_message. + grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream> + send_message; + // For send_trailing_metadata. + grpc_linked_mdelem* send_trailing_metadata_storage; + grpc_metadata_batch send_trailing_metadata; + // For intercepting recv_initial_metadata. + grpc_metadata_batch recv_initial_metadata; + grpc_closure recv_initial_metadata_ready; + bool trailing_metadata_available; + // For intercepting recv_message. + grpc_closure recv_message_ready; + grpc_core::OrphanablePtr<grpc_core::ByteStream> recv_message; + // For intercepting recv_trailing_metadata. + grpc_metadata_batch recv_trailing_metadata; + grpc_transport_stream_stats collect_stats; + // For intercepting on_complete. + grpc_closure on_complete; +} subchannel_batch_data; + +// Retry state associated with a subchannel call. +// Stored in the parent_data of the subchannel call object. +typedef struct { + // subchannel_batch_data.batch.payload points to this. + grpc_transport_stream_op_batch_payload batch_payload; + // These fields indicate which ops have been started and completed on + // this subchannel call. + size_t started_send_message_count; + size_t completed_send_message_count; + size_t started_recv_message_count; + size_t completed_recv_message_count; + bool started_send_initial_metadata : 1; + bool completed_send_initial_metadata : 1; + bool started_send_trailing_metadata : 1; + bool completed_send_trailing_metadata : 1; + bool started_recv_initial_metadata : 1; + bool completed_recv_initial_metadata : 1; + bool started_recv_trailing_metadata : 1; + bool completed_recv_trailing_metadata : 1; + // State for callback processing. + bool retry_dispatched : 1; + bool recv_initial_metadata_ready_deferred : 1; + bool recv_message_ready_deferred : 1; + grpc_error* recv_initial_metadata_error; + grpc_error* recv_message_error; +} subchannel_call_retry_state; + +// Pending batches stored in call data. +typedef struct { + // The pending batch. If nullptr, this slot is empty. + grpc_transport_stream_op_batch* batch; + // Indicates whether payload for send ops has been cached in call data. + bool send_ops_cached; +} pending_batch; /** Call data. Holds a pointer to grpc_subchannel_call and the associated machinery to create such a pointer. @@ -841,159 +874,1593 @@ typedef struct client_channel_call_data { grpc_call_combiner* call_combiner; grpc_server_retry_throttle_data* retry_throttle_data; - method_parameters* method_params; + grpc_core::RefCountedPtr<ClientChannelMethodParams> method_params; grpc_subchannel_call* subchannel_call; - grpc_error* error; + + // Set when we get a cancel_stream op. + grpc_error* cancel_error; grpc_core::LoadBalancingPolicy::PickState pick; - grpc_closure lb_pick_closure; - grpc_closure lb_pick_cancel_closure; + grpc_closure pick_closure; + grpc_closure pick_cancel_closure; grpc_polling_entity* pollent; - grpc_transport_stream_op_batch* waiting_for_pick_batches[MAX_WAITING_BATCHES]; - size_t waiting_for_pick_batches_count; - grpc_closure handle_pending_batch_in_call_combiner[MAX_WAITING_BATCHES]; + // Batches are added to this list when received from above. + // They are removed when we are done handling the batch (i.e., when + // either we have invoked all of the batch's callbacks or we have + // passed the batch down to the subchannel call and are not + // intercepting any of its callbacks). + pending_batch pending_batches[MAX_PENDING_BATCHES]; + bool pending_send_initial_metadata : 1; + bool pending_send_message : 1; + bool pending_send_trailing_metadata : 1; + + // Retry state. + bool enable_retries : 1; + bool retry_committed : 1; + bool last_attempt_got_server_pushback : 1; + int num_attempts_completed; + size_t bytes_buffered_for_retry; + grpc_core::ManualConstructor<grpc_core::BackOff> retry_backoff; + grpc_timer retry_timer; + + // Cached data for retrying send ops. + // send_initial_metadata + bool seen_send_initial_metadata; + grpc_linked_mdelem* send_initial_metadata_storage; + grpc_metadata_batch send_initial_metadata; + uint32_t send_initial_metadata_flags; + gpr_atm* peer_string; + // send_message + // When we get a send_message op, we replace the original byte stream + // with a CachingByteStream that caches the slices to a local buffer for + // use in retries. + // Note: We inline the cache for the first 3 send_message ops and use + // dynamic allocation after that. This number was essentially picked + // at random; it could be changed in the future to tune performance. + grpc_core::InlinedVector<grpc_core::ByteStreamCache*, 3> send_messages; + // send_trailing_metadata + bool seen_send_trailing_metadata; + grpc_linked_mdelem* send_trailing_metadata_storage; + grpc_metadata_batch send_trailing_metadata; +} call_data; - grpc_transport_stream_op_batch* initial_metadata_batch; +// Forward declarations. +static void retry_commit(grpc_call_element* elem, + subchannel_call_retry_state* retry_state); +static void start_internal_recv_trailing_metadata(grpc_call_element* elem); +static void on_complete(void* arg, grpc_error* error); +static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored); +static void pick_after_resolver_result_start_locked(grpc_call_element* elem); +static void start_pick_locked(void* arg, grpc_error* ignored); + +// +// send op data caching +// + +// Caches data for send ops so that it can be retried later, if not +// already cached. +static void maybe_cache_send_ops_for_batch(call_data* calld, + pending_batch* pending) { + if (pending->send_ops_cached) return; + pending->send_ops_cached = true; + grpc_transport_stream_op_batch* batch = pending->batch; + // Save a copy of metadata for send_initial_metadata ops. + if (batch->send_initial_metadata) { + calld->seen_send_initial_metadata = true; + GPR_ASSERT(calld->send_initial_metadata_storage == nullptr); + grpc_metadata_batch* send_initial_metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + calld->send_initial_metadata_storage = (grpc_linked_mdelem*)gpr_arena_alloc( + calld->arena, + sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count); + grpc_metadata_batch_copy(send_initial_metadata, + &calld->send_initial_metadata, + calld->send_initial_metadata_storage); + calld->send_initial_metadata_flags = + batch->payload->send_initial_metadata.send_initial_metadata_flags; + calld->peer_string = batch->payload->send_initial_metadata.peer_string; + } + // Set up cache for send_message ops. + if (batch->send_message) { + grpc_core::ByteStreamCache* cache = + static_cast<grpc_core::ByteStreamCache*>( + gpr_arena_alloc(calld->arena, sizeof(grpc_core::ByteStreamCache))); + new (cache) grpc_core::ByteStreamCache( + std::move(batch->payload->send_message.send_message)); + calld->send_messages.push_back(cache); + } + // Save metadata batch for send_trailing_metadata ops. + if (batch->send_trailing_metadata) { + calld->seen_send_trailing_metadata = true; + GPR_ASSERT(calld->send_trailing_metadata_storage == nullptr); + grpc_metadata_batch* send_trailing_metadata = + batch->payload->send_trailing_metadata.send_trailing_metadata; + calld->send_trailing_metadata_storage = + (grpc_linked_mdelem*)gpr_arena_alloc( + calld->arena, + sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count); + grpc_metadata_batch_copy(send_trailing_metadata, + &calld->send_trailing_metadata, + calld->send_trailing_metadata_storage); + } +} - grpc_closure on_complete; - grpc_closure* original_on_complete; -} call_data; +// Frees cached send ops that have already been completed after +// committing the call. +static void free_cached_send_op_data_after_commit( + grpc_call_element* elem, subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (retry_state->completed_send_initial_metadata) { + grpc_metadata_batch_destroy(&calld->send_initial_metadata); + } + for (size_t i = 0; i < retry_state->completed_send_message_count; ++i) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR + "]", + chand, calld, i); + } + calld->send_messages[i]->Destroy(); + } + if (retry_state->completed_send_trailing_metadata) { + grpc_metadata_batch_destroy(&calld->send_trailing_metadata); + } +} -grpc_subchannel_call* grpc_client_channel_get_subchannel_call( - grpc_call_element* elem) { +// Frees cached send ops that were completed by the completed batch in +// batch_data. Used when batches are completed after the call is committed. +static void free_cached_send_op_data_for_completed_batch( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); call_data* calld = static_cast<call_data*>(elem->call_data); - return calld->subchannel_call; + if (batch_data->batch.send_initial_metadata) { + grpc_metadata_batch_destroy(&calld->send_initial_metadata); + } + if (batch_data->batch.send_message) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR + "]", + chand, calld, retry_state->completed_send_message_count - 1); + } + calld->send_messages[retry_state->completed_send_message_count - 1] + ->Destroy(); + } + if (batch_data->batch.send_trailing_metadata) { + grpc_metadata_batch_destroy(&calld->send_trailing_metadata); + } +} + +// +// pending_batches management +// + +// Returns the index into calld->pending_batches to be used for batch. +static size_t get_batch_index(grpc_transport_stream_op_batch* batch) { + // Note: It is important the send_initial_metadata be the first entry + // here, since the code in pick_subchannel_locked() assumes it will be. + if (batch->send_initial_metadata) return 0; + if (batch->send_message) return 1; + if (batch->send_trailing_metadata) return 2; + if (batch->recv_initial_metadata) return 3; + if (batch->recv_message) return 4; + if (batch->recv_trailing_metadata) return 5; + GPR_UNREACHABLE_CODE(return (size_t)-1); } // This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_add( - call_data* calld, grpc_transport_stream_op_batch* batch) { - if (batch->send_initial_metadata) { - GPR_ASSERT(calld->initial_metadata_batch == nullptr); - calld->initial_metadata_batch = batch; - } else { - GPR_ASSERT(calld->waiting_for_pick_batches_count < MAX_WAITING_BATCHES); - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count++] = - batch; +static void pending_batches_add(grpc_call_element* elem, + grpc_transport_stream_op_batch* batch) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + const size_t idx = get_batch_index(batch); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: adding pending batch at index %" PRIuPTR, chand, + calld, idx); + } + pending_batch* pending = &calld->pending_batches[idx]; + GPR_ASSERT(pending->batch == nullptr); + pending->batch = batch; + pending->send_ops_cached = false; + if (calld->enable_retries) { + // Update state in calld about pending batches. + // Also check if the batch takes us over the retry buffer limit. + // Note: We don't check the size of trailing metadata here, because + // gRPC clients do not send trailing metadata. + if (batch->send_initial_metadata) { + calld->pending_send_initial_metadata = true; + calld->bytes_buffered_for_retry += grpc_metadata_batch_size( + batch->payload->send_initial_metadata.send_initial_metadata); + } + if (batch->send_message) { + calld->pending_send_message = true; + calld->bytes_buffered_for_retry += + batch->payload->send_message.send_message->length(); + } + if (batch->send_trailing_metadata) { + calld->pending_send_trailing_metadata = true; + } + if (calld->bytes_buffered_for_retry > chand->per_rpc_retry_buffer_size) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: exceeded retry buffer size, committing", + chand, calld); + } + subchannel_call_retry_state* retry_state = + calld->subchannel_call == nullptr + ? nullptr + : static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + retry_commit(elem, retry_state); + // If we are not going to retry and have not yet started, pretend + // retries are disabled so that we don't bother with retry overhead. + if (calld->num_attempts_completed == 0) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: disabling retries before first attempt", + chand, calld); + } + calld->enable_retries = false; + } + } + } +} + +static void pending_batch_clear(call_data* calld, pending_batch* pending) { + if (calld->enable_retries) { + if (pending->batch->send_initial_metadata) { + calld->pending_send_initial_metadata = false; + } + if (pending->batch->send_message) { + calld->pending_send_message = false; + } + if (pending->batch->send_trailing_metadata) { + calld->pending_send_trailing_metadata = false; + } } + pending->batch = nullptr; } // This is called via the call combiner, so access to calld is synchronized. static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) { - call_data* calld = static_cast<call_data*>(arg); - if (calld->waiting_for_pick_batches_count > 0) { - --calld->waiting_for_pick_batches_count; - grpc_transport_stream_op_batch_finish_with_failure( - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count], - GRPC_ERROR_REF(error), calld->call_combiner); - } + grpc_transport_stream_op_batch* batch = + static_cast<grpc_transport_stream_op_batch*>(arg); + call_data* calld = static_cast<call_data*>(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(error), calld->call_combiner); } // This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_fail(grpc_call_element* elem, - grpc_error* error) { +// If yield_call_combiner is true, assumes responsibility for yielding +// the call combiner. +static void pending_batches_fail(grpc_call_element* elem, grpc_error* error, + bool yield_call_combiner) { + GPR_ASSERT(error != GRPC_ERROR_NONE); call_data* calld = static_cast<call_data*>(elem->call_data); if (grpc_client_channel_trace.enabled()) { + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + if (calld->pending_batches[i].batch != nullptr) ++num_batches; + } gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s", - elem->channel_data, calld, calld->waiting_for_pick_batches_count, - grpc_error_string(error)); + elem->channel_data, calld, num_batches, grpc_error_string(error)); + } + grpc_transport_stream_op_batch* + batches[GPR_ARRAY_SIZE(calld->pending_batches)]; + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr) { + batches[num_batches++] = batch; + pending_batch_clear(calld, pending); + } } - for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { - GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], - fail_pending_batch_in_call_combiner, calld, + for (size_t i = yield_call_combiner ? 1 : 0; i < num_batches; ++i) { + grpc_transport_stream_op_batch* batch = batches[i]; + batch->handler_private.extra_arg = calld; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + fail_pending_batch_in_call_combiner, batch, grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START( - calld->call_combiner, &calld->handle_pending_batch_in_call_combiner[i], - GRPC_ERROR_REF(error), "waiting_for_pick_batches_fail"); - } - if (calld->initial_metadata_batch != nullptr) { - grpc_transport_stream_op_batch_finish_with_failure( - calld->initial_metadata_batch, GRPC_ERROR_REF(error), - calld->call_combiner); - } else { - GRPC_CALL_COMBINER_STOP(calld->call_combiner, - "waiting_for_pick_batches_fail"); + GRPC_CALL_COMBINER_START(calld->call_combiner, + &batch->handler_private.closure, + GRPC_ERROR_REF(error), "pending_batches_fail"); + } + if (yield_call_combiner) { + if (num_batches > 0) { + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batches[0], GRPC_ERROR_REF(error), calld->call_combiner); + } else { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, "pending_batches_fail"); + } } GRPC_ERROR_UNREF(error); } // This is called via the call combiner, so access to calld is synchronized. -static void run_pending_batch_in_call_combiner(void* arg, grpc_error* ignored) { - call_data* calld = static_cast<call_data*>(arg); - if (calld->waiting_for_pick_batches_count > 0) { - --calld->waiting_for_pick_batches_count; - grpc_subchannel_call_process_op( - calld->subchannel_call, - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count]); - } +static void resume_pending_batch_in_call_combiner(void* arg, + grpc_error* ignored) { + grpc_transport_stream_op_batch* batch = + static_cast<grpc_transport_stream_op_batch*>(arg); + grpc_subchannel_call* subchannel_call = + static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(subchannel_call, batch); } // This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_resume(grpc_call_element* elem) { +static void pending_batches_resume(grpc_call_element* elem) { channel_data* chand = static_cast<channel_data*>(elem->channel_data); call_data* calld = static_cast<call_data*>(elem->call_data); + if (calld->enable_retries) { + start_retriable_subchannel_batches(elem, GRPC_ERROR_NONE); + return; + } + // Retries not enabled; send down batches as-is. if (grpc_client_channel_trace.enabled()) { + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + if (calld->pending_batches[i].batch != nullptr) ++num_batches; + } gpr_log(GPR_DEBUG, - "chand=%p calld=%p: sending %" PRIuPTR - " pending batches to subchannel_call=%p", - chand, calld, calld->waiting_for_pick_batches_count, - calld->subchannel_call); - } - for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { - GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], - run_pending_batch_in_call_combiner, calld, + "chand=%p calld=%p: starting %" PRIuPTR + " pending batches on subchannel_call=%p", + chand, calld, num_batches, calld->subchannel_call); + } + grpc_transport_stream_op_batch* + batches[GPR_ARRAY_SIZE(calld->pending_batches)]; + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr) { + batches[num_batches++] = batch; + pending_batch_clear(calld, pending); + } + } + for (size_t i = 1; i < num_batches; ++i) { + grpc_transport_stream_op_batch* batch = batches[i]; + batch->handler_private.extra_arg = calld->subchannel_call; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + resume_pending_batch_in_call_combiner, batch, grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START( - calld->call_combiner, &calld->handle_pending_batch_in_call_combiner[i], - GRPC_ERROR_NONE, "waiting_for_pick_batches_resume"); + GRPC_CALL_COMBINER_START(calld->call_combiner, + &batch->handler_private.closure, GRPC_ERROR_NONE, + "pending_batches_resume"); } - GPR_ASSERT(calld->initial_metadata_batch != nullptr); - grpc_subchannel_call_process_op(calld->subchannel_call, - calld->initial_metadata_batch); + GPR_ASSERT(num_batches > 0); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, batches[0]); } -// Applies service config to the call. Must be invoked once we know -// that the resolver has returned results to the channel. -static void apply_service_config_to_call_locked(grpc_call_element* elem) { +static void maybe_clear_pending_batch(grpc_call_element* elem, + pending_batch* pending) { channel_data* chand = static_cast<channel_data*>(elem->channel_data); call_data* calld = static_cast<call_data*>(elem->call_data); + grpc_transport_stream_op_batch* batch = pending->batch; + // We clear the pending batch if all of its callbacks have been + // scheduled and reset to nullptr. + if (batch->on_complete == nullptr && + (!batch->recv_initial_metadata || + batch->payload->recv_initial_metadata.recv_initial_metadata_ready == + nullptr) && + (!batch->recv_message || + batch->payload->recv_message.recv_message_ready == nullptr)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: clearing pending batch", chand, + calld); + } + pending_batch_clear(calld, pending); + } +} + +// Returns true if all ops in the pending batch have been completed. +static bool pending_batch_is_completed( + pending_batch* pending, call_data* calld, + subchannel_call_retry_state* retry_state) { + if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { + return false; + } + if (pending->batch->send_initial_metadata && + !retry_state->completed_send_initial_metadata) { + return false; + } + if (pending->batch->send_message && + retry_state->completed_send_message_count < calld->send_messages.size()) { + return false; + } + if (pending->batch->send_trailing_metadata && + !retry_state->completed_send_trailing_metadata) { + return false; + } + if (pending->batch->recv_initial_metadata && + !retry_state->completed_recv_initial_metadata) { + return false; + } + if (pending->batch->recv_message && + retry_state->completed_recv_message_count < + retry_state->started_recv_message_count) { + return false; + } + if (pending->batch->recv_trailing_metadata && + !retry_state->completed_recv_trailing_metadata) { + return false; + } + return true; +} + +// Returns true if any op in the batch was not yet started. +static bool pending_batch_is_unstarted( + pending_batch* pending, call_data* calld, + subchannel_call_retry_state* retry_state) { + if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { + return false; + } + if (pending->batch->send_initial_metadata && + !retry_state->started_send_initial_metadata) { + return true; + } + if (pending->batch->send_message && + retry_state->started_send_message_count < calld->send_messages.size()) { + return true; + } + if (pending->batch->send_trailing_metadata && + !retry_state->started_send_trailing_metadata) { + return true; + } + if (pending->batch->recv_initial_metadata && + !retry_state->started_recv_initial_metadata) { + return true; + } + if (pending->batch->recv_message && + retry_state->completed_recv_message_count == + retry_state->started_recv_message_count) { + return true; + } + if (pending->batch->recv_trailing_metadata && + !retry_state->started_recv_trailing_metadata) { + return true; + } + return false; +} + +// +// retry code +// + +// Commits the call so that no further retry attempts will be performed. +static void retry_commit(grpc_call_element* elem, + subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (calld->retry_committed) return; + calld->retry_committed = true; if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call", + gpr_log(GPR_DEBUG, "chand=%p calld=%p: committing retries", chand, calld); + } + if (retry_state != nullptr) { + free_cached_send_op_data_after_commit(elem, retry_state); + } +} + +// Starts a retry after appropriate back-off. +static void do_retry(grpc_call_element* elem, + subchannel_call_retry_state* retry_state, + grpc_millis server_pushback_ms) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + GPR_ASSERT(calld->method_params != nullptr); + const ClientChannelMethodParams::RetryPolicy* retry_policy = + calld->method_params->retry_policy(); + GPR_ASSERT(retry_policy != nullptr); + // Reset subchannel call and connected subchannel. + if (calld->subchannel_call != nullptr) { + GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call, + "client_channel_call_retry"); + calld->subchannel_call = nullptr; + } + if (calld->pick.connected_subchannel != nullptr) { + calld->pick.connected_subchannel.reset(); + } + // Compute backoff delay. + grpc_millis next_attempt_time; + if (server_pushback_ms >= 0) { + next_attempt_time = grpc_core::ExecCtx::Get()->Now() + server_pushback_ms; + calld->last_attempt_got_server_pushback = true; + } else { + if (calld->num_attempts_completed == 1 || + calld->last_attempt_got_server_pushback) { + calld->retry_backoff.Init( + grpc_core::BackOff::Options() + .set_initial_backoff(retry_policy->initial_backoff) + .set_multiplier(retry_policy->backoff_multiplier) + .set_jitter(RETRY_BACKOFF_JITTER) + .set_max_backoff(retry_policy->max_backoff)); + calld->last_attempt_got_server_pushback = false; + } + next_attempt_time = calld->retry_backoff->NextAttemptTime(); + } + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: retrying failed call in %" PRIuPTR " ms", chand, + calld, next_attempt_time - grpc_core::ExecCtx::Get()->Now()); + } + // Schedule retry after computed delay. + GRPC_CLOSURE_INIT(&calld->pick_closure, start_pick_locked, elem, + grpc_combiner_scheduler(chand->combiner)); + grpc_timer_init(&calld->retry_timer, next_attempt_time, &calld->pick_closure); + // Update bookkeeping. + if (retry_state != nullptr) retry_state->retry_dispatched = true; +} + +// Returns true if the call is being retried. +static bool maybe_retry(grpc_call_element* elem, + subchannel_batch_data* batch_data, + grpc_status_code status, + grpc_mdelem* server_pushback_md) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + // Get retry policy. + if (calld->method_params == nullptr) return false; + const ClientChannelMethodParams::RetryPolicy* retry_policy = + calld->method_params->retry_policy(); + if (retry_policy == nullptr) return false; + // If we've already dispatched a retry from this call, return true. + // This catches the case where the batch has multiple callbacks + // (i.e., it includes either recv_message or recv_initial_metadata). + subchannel_call_retry_state* retry_state = nullptr; + if (batch_data != nullptr) { + retry_state = static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + if (retry_state->retry_dispatched) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: retry already dispatched", chand, + calld); + } + return true; + } + } + // Check status. + if (status == GRPC_STATUS_OK) { + grpc_server_retry_throttle_data_record_success(calld->retry_throttle_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: call succeeded", chand, calld); + } + return false; + } + // Status is not OK. Check whether the status is retryable. + if (!retry_policy->retryable_status_codes.Contains(status)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: status %s not configured as retryable", chand, + calld, grpc_status_code_to_string(status)); + } + return false; + } + // Record the failure and check whether retries are throttled. + // Note that it's important for this check to come after the status + // code check above, since we should only record failures whose statuses + // match the configured retryable status codes, so that we don't count + // things like failures due to malformed requests (INVALID_ARGUMENT). + // Conversely, it's important for this to come before the remaining + // checks, so that we don't fail to record failures due to other factors. + if (!grpc_server_retry_throttle_data_record_failure( + calld->retry_throttle_data)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: retries throttled", chand, calld); + } + return false; + } + // Check whether the call is committed. + if (calld->retry_committed) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: retries already committed", chand, + calld); + } + return false; + } + // Check whether we have retries remaining. + ++calld->num_attempts_completed; + if (calld->num_attempts_completed >= retry_policy->max_attempts) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: exceeded %d retry attempts", chand, + calld, retry_policy->max_attempts); + } + return false; + } + // If the call was cancelled from the surface, don't retry. + if (calld->cancel_error != GRPC_ERROR_NONE) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: call cancelled from surface, not retrying", + chand, calld); + } + return false; + } + // Check server push-back. + grpc_millis server_pushback_ms = -1; + if (server_pushback_md != nullptr) { + // If the value is "-1" or any other unparseable string, we do not retry. + uint32_t ms; + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: not retrying due to server push-back", + chand, calld); + } + return false; + } else { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: server push-back: retry in %u ms", chand, + calld, ms); + } + server_pushback_ms = (grpc_millis)ms; + } + } + do_retry(elem, retry_state, server_pushback_ms); + return true; +} + +// +// subchannel_batch_data +// + +static subchannel_batch_data* batch_data_create(grpc_call_element* elem, + int refcount) { + call_data* calld = static_cast<call_data*>(elem->call_data); + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>( + gpr_arena_alloc(calld->arena, sizeof(*batch_data))); + batch_data->elem = elem; + batch_data->subchannel_call = + GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call, "batch_data_create"); + batch_data->batch.payload = &retry_state->batch_payload; + gpr_ref_init(&batch_data->refs, refcount); + GRPC_CLOSURE_INIT(&batch_data->on_complete, on_complete, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.on_complete = &batch_data->on_complete; + GRPC_CALL_STACK_REF(calld->owning_call, "batch_data"); + return batch_data; +} + +static void batch_data_unref(subchannel_batch_data* batch_data) { + if (gpr_unref(&batch_data->refs)) { + if (batch_data->send_initial_metadata_storage != nullptr) { + grpc_metadata_batch_destroy(&batch_data->send_initial_metadata); + } + if (batch_data->send_trailing_metadata_storage != nullptr) { + grpc_metadata_batch_destroy(&batch_data->send_trailing_metadata); + } + if (batch_data->batch.recv_initial_metadata) { + grpc_metadata_batch_destroy(&batch_data->recv_initial_metadata); + } + if (batch_data->batch.recv_trailing_metadata) { + grpc_metadata_batch_destroy(&batch_data->recv_trailing_metadata); + } + GRPC_SUBCHANNEL_CALL_UNREF(batch_data->subchannel_call, "batch_data_unref"); + call_data* calld = static_cast<call_data*>(batch_data->elem->call_data); + GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data"); + } +} + +// +// recv_initial_metadata callback handling +// + +// Invokes recv_initial_metadata_ready for a subchannel batch. +static void invoke_recv_initial_metadata_callback(void* arg, + grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + channel_data* chand = + static_cast<channel_data*>(batch_data->elem->channel_data); + call_data* calld = static_cast<call_data*>(batch_data->elem->call_data); + // Find pending batch. + pending_batch* pending = nullptr; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch; + if (batch != nullptr && batch->recv_initial_metadata && + batch->payload->recv_initial_metadata.recv_initial_metadata_ready != + nullptr) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: invoking recv_initial_metadata_ready for " + "pending batch at index %" PRIuPTR, + chand, calld, i); + } + pending = &calld->pending_batches[i]; + break; + } + } + GPR_ASSERT(pending != nullptr); + // Return metadata. + grpc_metadata_batch_move( + &batch_data->recv_initial_metadata, + pending->batch->payload->recv_initial_metadata.recv_initial_metadata); + // Update bookkeeping. + // Note: Need to do this before invoking the callback, since invoking + // the callback will result in yielding the call combiner. + grpc_closure* recv_initial_metadata_ready = + pending->batch->payload->recv_initial_metadata + .recv_initial_metadata_ready; + pending->batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + nullptr; + maybe_clear_pending_batch(batch_data->elem, pending); + batch_data_unref(batch_data); + // Invoke callback. + GRPC_CLOSURE_RUN(recv_initial_metadata_ready, GRPC_ERROR_REF(error)); +} + +// Intercepts recv_initial_metadata_ready callback for retries. +// Commits the call and returns the initial metadata up the stack. +static void recv_initial_metadata_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // If we got an error or a Trailers-Only response and have not yet gotten + // the recv_trailing_metadata on_complete callback, then defer + // propagating this callback back to the surface. We can evaluate whether + // to retry when recv_trailing_metadata comes back. + if ((batch_data->trailing_metadata_available || error != GRPC_ERROR_NONE) && + !retry_state->completed_recv_trailing_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: deferring recv_initial_metadata_ready " + "(Trailers-Only)", + chand, calld); + } + retry_state->recv_initial_metadata_ready_deferred = true; + retry_state->recv_initial_metadata_error = GRPC_ERROR_REF(error); + if (!retry_state->started_recv_trailing_metadata) { + // recv_trailing_metadata not yet started by application; start it + // ourselves to get status. + start_internal_recv_trailing_metadata(elem); + } else { + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "recv_initial_metadata_ready trailers-only or error"); + } + return; + } + // Received valid initial metadata, so commit the call. + retry_commit(elem, retry_state); + // Manually invoking a callback function; it does not take ownership of error. + invoke_recv_initial_metadata_callback(batch_data, error); + GRPC_ERROR_UNREF(error); +} + +// +// recv_message callback handling +// + +// Invokes recv_message_ready for a subchannel batch. +static void invoke_recv_message_callback(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + channel_data* chand = + static_cast<channel_data*>(batch_data->elem->channel_data); + call_data* calld = static_cast<call_data*>(batch_data->elem->call_data); + // Find pending op. + pending_batch* pending = nullptr; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + grpc_transport_stream_op_batch* batch = calld->pending_batches[i].batch; + if (batch != nullptr && batch->recv_message && + batch->payload->recv_message.recv_message_ready != nullptr) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: invoking recv_message_ready for " + "pending batch at index %" PRIuPTR, + chand, calld, i); + } + pending = &calld->pending_batches[i]; + break; + } + } + GPR_ASSERT(pending != nullptr); + // Return payload. + *pending->batch->payload->recv_message.recv_message = + std::move(batch_data->recv_message); + // Update bookkeeping. + // Note: Need to do this before invoking the callback, since invoking + // the callback will result in yielding the call combiner. + grpc_closure* recv_message_ready = + pending->batch->payload->recv_message.recv_message_ready; + pending->batch->payload->recv_message.recv_message_ready = nullptr; + maybe_clear_pending_batch(batch_data->elem, pending); + batch_data_unref(batch_data); + // Invoke callback. + GRPC_CLOSURE_RUN(recv_message_ready, GRPC_ERROR_REF(error)); +} + +// Intercepts recv_message_ready callback for retries. +// Commits the call and returns the message up the stack. +static void recv_message_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: got recv_message_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // If we got an error or the payload was nullptr and we have not yet gotten + // the recv_trailing_metadata on_complete callback, then defer + // propagating this callback back to the surface. We can evaluate whether + // to retry when recv_trailing_metadata comes back. + if ((batch_data->recv_message == nullptr || error != GRPC_ERROR_NONE) && + !retry_state->completed_recv_trailing_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: deferring recv_message_ready (nullptr " + "message and recv_trailing_metadata pending)", + chand, calld); + } + retry_state->recv_message_ready_deferred = true; + retry_state->recv_message_error = GRPC_ERROR_REF(error); + if (!retry_state->started_recv_trailing_metadata) { + // recv_trailing_metadata not yet started by application; start it + // ourselves to get status. + start_internal_recv_trailing_metadata(elem); + } else { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, "recv_message_ready null"); + } + return; + } + // Received a valid message, so commit the call. + retry_commit(elem, retry_state); + // Manually invoking a callback function; it does not take ownership of error. + invoke_recv_message_callback(batch_data, error); + GRPC_ERROR_UNREF(error); +} + +// +// on_complete callback handling +// + +// Updates retry_state to reflect the ops completed in batch_data. +static void update_retry_state_for_completed_batch( + subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state) { + if (batch_data->batch.send_initial_metadata) { + retry_state->completed_send_initial_metadata = true; + } + if (batch_data->batch.send_message) { + ++retry_state->completed_send_message_count; + } + if (batch_data->batch.send_trailing_metadata) { + retry_state->completed_send_trailing_metadata = true; + } + if (batch_data->batch.recv_initial_metadata) { + retry_state->completed_recv_initial_metadata = true; + } + if (batch_data->batch.recv_message) { + ++retry_state->completed_recv_message_count; + } + if (batch_data->batch.recv_trailing_metadata) { + retry_state->completed_recv_trailing_metadata = true; + } +} + +// Represents a closure that needs to run as a result of a completed batch. +typedef struct { + grpc_closure* closure; + grpc_error* error; + const char* reason; +} closure_to_execute; + +// Adds any necessary closures for deferred recv_initial_metadata and +// recv_message callbacks to closures, updating *num_closures as needed. +static void add_closures_for_deferred_recv_callbacks( + subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state, + closure_to_execute* closures, size_t* num_closures) { + if (batch_data->batch.recv_trailing_metadata && + retry_state->recv_initial_metadata_ready_deferred) { + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = + GRPC_CLOSURE_INIT(&batch_data->recv_initial_metadata_ready, + invoke_recv_initial_metadata_callback, batch_data, + grpc_schedule_on_exec_ctx); + closure->error = retry_state->recv_initial_metadata_error; + closure->reason = "resuming recv_initial_metadata_ready"; + } + if (batch_data->batch.recv_trailing_metadata && + retry_state->recv_message_ready_deferred) { + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = GRPC_CLOSURE_INIT(&batch_data->recv_message_ready, + invoke_recv_message_callback, + batch_data, grpc_schedule_on_exec_ctx); + closure->error = retry_state->recv_message_error; + closure->reason = "resuming recv_message_ready"; + } +} + +// If there are any cached ops to replay or pending ops to start on the +// subchannel call, adds a closure to closures to invoke +// start_retriable_subchannel_batches(), updating *num_closures as needed. +static void add_closures_for_replay_or_pending_send_ops( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state, closure_to_execute* closures, + size_t* num_closures) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + bool have_pending_send_message_ops = + retry_state->started_send_message_count < calld->send_messages.size(); + bool have_pending_send_trailing_metadata_op = + calld->seen_send_trailing_metadata && + !retry_state->started_send_trailing_metadata; + if (!have_pending_send_message_ops && + !have_pending_send_trailing_metadata_op) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch == nullptr || pending->send_ops_cached) continue; + if (batch->send_message) have_pending_send_message_ops = true; + if (batch->send_trailing_metadata) { + have_pending_send_trailing_metadata_op = true; + } + } + } + if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: starting next batch for pending send op(s)", + chand, calld); + } + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = GRPC_CLOSURE_INIT( + &batch_data->batch.handler_private.closure, + start_retriable_subchannel_batches, elem, grpc_schedule_on_exec_ctx); + closure->error = GRPC_ERROR_NONE; + closure->reason = "starting next batch for send_* op(s)"; + } +} + +// For any pending batch completed in batch_data, adds the necessary +// completion closures to closures, updating *num_closures as needed. +static void add_closures_for_completed_pending_batches( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state, grpc_error* error, + closure_to_execute* closures, size_t* num_closures) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + if (pending_batch_is_completed(pending, calld, retry_state)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: pending batch completed at index %" PRIuPTR, + chand, calld, i); + } + // Copy the trailing metadata to return it to the surface. + if (batch_data->batch.recv_trailing_metadata) { + grpc_metadata_batch_move(&batch_data->recv_trailing_metadata, + pending->batch->payload->recv_trailing_metadata + .recv_trailing_metadata); + } + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = pending->batch->on_complete; + closure->error = GRPC_ERROR_REF(error); + closure->reason = "on_complete for pending batch"; + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); + } + } + GRPC_ERROR_UNREF(error); +} + +// For any pending batch containing an op that has not yet been started, +// adds the pending batch's completion closures to closures, updating +// *num_closures as needed. +static void add_closures_to_fail_unstarted_pending_batches( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + grpc_error* error, closure_to_execute* closures, size_t* num_closures) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + if (pending_batch_is_unstarted(pending, calld, retry_state)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: failing unstarted pending batch at index " + "%" PRIuPTR, + chand, calld, i); + } + if (pending->batch->recv_initial_metadata) { + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = pending->batch->payload->recv_initial_metadata + .recv_initial_metadata_ready; + closure->error = GRPC_ERROR_REF(error); + closure->reason = + "failing recv_initial_metadata_ready for pending batch"; + pending->batch->payload->recv_initial_metadata + .recv_initial_metadata_ready = nullptr; + } + if (pending->batch->recv_message) { + *pending->batch->payload->recv_message.recv_message = nullptr; + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = + pending->batch->payload->recv_message.recv_message_ready; + closure->error = GRPC_ERROR_REF(error); + closure->reason = "failing recv_message_ready for pending batch"; + pending->batch->payload->recv_message.recv_message_ready = nullptr; + } + closure_to_execute* closure = &closures[(*num_closures)++]; + closure->closure = pending->batch->on_complete; + closure->error = GRPC_ERROR_REF(error); + closure->reason = "failing on_complete for pending batch"; + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); + } + } + GRPC_ERROR_UNREF(error); +} + +// Callback used to intercept on_complete from subchannel calls. +// Called only when retries are enabled. +static void on_complete(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast<subchannel_batch_data*>(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + char* batch_str = grpc_transport_stream_op_batch_string(&batch_data->batch); + gpr_log(GPR_DEBUG, "chand=%p calld=%p: got on_complete, error=%s, batch=%s", + chand, calld, grpc_error_string(error), batch_str); + gpr_free(batch_str); + } + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // If we have previously completed recv_trailing_metadata, then the + // call is finished. + bool call_finished = retry_state->completed_recv_trailing_metadata; + // Update bookkeeping in retry_state. + update_retry_state_for_completed_batch(batch_data, retry_state); + if (call_finished) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: call already finished", chand, + calld); + } + } else { + // Check if this batch finished the call, and if so, get its status. + // The call is finished if either (a) this callback was invoked with + // an error or (b) we receive status. + grpc_status_code status = GRPC_STATUS_OK; + grpc_mdelem* server_pushback_md = nullptr; + if (error != GRPC_ERROR_NONE) { // Case (a). + call_finished = true; + grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr, + nullptr); + } else if (batch_data->batch.recv_trailing_metadata) { // Case (b). + call_finished = true; + grpc_metadata_batch* md_batch = + batch_data->batch.payload->recv_trailing_metadata + .recv_trailing_metadata; + GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr); + status = grpc_get_status_code_from_metadata( + md_batch->idx.named.grpc_status->md); + if (md_batch->idx.named.grpc_retry_pushback_ms != nullptr) { + server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md; + } + } else if (retry_state->completed_recv_trailing_metadata) { + call_finished = true; + } + if (call_finished && grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: call finished, status=%s", chand, + calld, grpc_status_code_to_string(status)); + } + // If the call is finished, check if we should retry. + if (call_finished && + maybe_retry(elem, batch_data, status, server_pushback_md)) { + // Unref batch_data for deferred recv_initial_metadata_ready or + // recv_message_ready callbacks, if any. + if (batch_data->batch.recv_trailing_metadata && + retry_state->recv_initial_metadata_ready_deferred) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error); + } + if (batch_data->batch.recv_trailing_metadata && + retry_state->recv_message_ready_deferred) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_message_error); + } + batch_data_unref(batch_data); + return; + } + } + // If the call is finished or retries are committed, free cached data for + // send ops that we've just completed. + if (call_finished || calld->retry_committed) { + free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state); + } + // Call not being retried. + // Construct list of closures to execute. + // Max number of closures is number of pending batches plus one for + // each of: + // - recv_initial_metadata_ready (either deferred or unstarted) + // - recv_message_ready (either deferred or unstarted) + // - starting a new batch for pending send ops + closure_to_execute closures[GPR_ARRAY_SIZE(calld->pending_batches) + 3]; + size_t num_closures = 0; + // If there are deferred recv_initial_metadata_ready or recv_message_ready + // callbacks, add them to closures. + add_closures_for_deferred_recv_callbacks(batch_data, retry_state, closures, + &num_closures); + // Find pending batches whose ops are now complete and add their + // on_complete callbacks to closures. + add_closures_for_completed_pending_batches(elem, batch_data, retry_state, + GRPC_ERROR_REF(error), closures, + &num_closures); + // Add closures to handle any pending batches that have not yet been started. + // If the call is finished, we fail these batches; otherwise, we add a + // callback to start_retriable_subchannel_batches() to start them on + // the subchannel call. + if (call_finished) { + add_closures_to_fail_unstarted_pending_batches( + elem, retry_state, GRPC_ERROR_REF(error), closures, &num_closures); + } else { + add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state, + closures, &num_closures); + } + // Don't need batch_data anymore. + batch_data_unref(batch_data); + // Schedule all of the closures identified above. + // Note that the call combiner will be yielded for each closure that + // we schedule. We're already running in the call combiner, so one of + // the closures can be scheduled directly, but the others will + // have to re-enter the call combiner. + if (num_closures > 0) { + GRPC_CLOSURE_SCHED(closures[0].closure, closures[0].error); + for (size_t i = 1; i < num_closures; ++i) { + GRPC_CALL_COMBINER_START(calld->call_combiner, closures[i].closure, + closures[i].error, closures[i].reason); + } + } else { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "no closures to run for on_complete"); + } +} + +// +// subchannel batch construction +// + +// Helper function used to start a subchannel batch in the call combiner. +static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) { + grpc_transport_stream_op_batch* batch = + static_cast<grpc_transport_stream_op_batch*>(arg); + grpc_subchannel_call* subchannel_call = + static_cast<grpc_subchannel_call*>(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(subchannel_call, batch); +} + +// Adds retriable send_initial_metadata op to batch_data. +static void add_retriable_send_initial_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + // Maps the number of retries to the corresponding metadata value slice. + static const grpc_slice* retry_count_strings[] = { + &GRPC_MDSTR_1, &GRPC_MDSTR_2, &GRPC_MDSTR_3, &GRPC_MDSTR_4}; + // We need to make a copy of the metadata batch for each attempt, since + // the filters in the subchannel stack may modify this batch, and we don't + // want those modifications to be passed forward to subsequent attempts. + // + // If we've already completed one or more attempts, add the + // grpc-retry-attempts header. + batch_data->send_initial_metadata_storage = + static_cast<grpc_linked_mdelem*>(gpr_arena_alloc( + calld->arena, sizeof(grpc_linked_mdelem) * + (calld->send_initial_metadata.list.count + + (calld->num_attempts_completed > 0)))); + grpc_metadata_batch_copy(&calld->send_initial_metadata, + &batch_data->send_initial_metadata, + batch_data->send_initial_metadata_storage); + if (batch_data->send_initial_metadata.idx.named.grpc_previous_rpc_attempts != + nullptr) { + grpc_metadata_batch_remove( + &batch_data->send_initial_metadata, + batch_data->send_initial_metadata.idx.named.grpc_previous_rpc_attempts); + } + if (calld->num_attempts_completed > 0) { + grpc_mdelem retry_md = grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS, + *retry_count_strings[calld->num_attempts_completed - 1]); + grpc_error* error = grpc_metadata_batch_add_tail( + &batch_data->send_initial_metadata, + &batch_data->send_initial_metadata_storage[calld->send_initial_metadata + .list.count], + retry_md); + if (error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "error adding retry metadata: %s", + grpc_error_string(error)); + GPR_ASSERT(false); + } + } + retry_state->started_send_initial_metadata = true; + batch_data->batch.send_initial_metadata = true; + batch_data->batch.payload->send_initial_metadata.send_initial_metadata = + &batch_data->send_initial_metadata; + batch_data->batch.payload->send_initial_metadata.send_initial_metadata_flags = + calld->send_initial_metadata_flags; + batch_data->batch.payload->send_initial_metadata.peer_string = + calld->peer_string; +} + +// Adds retriable send_message op to batch_data. +static void add_retriable_send_message_op( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]", + chand, calld, retry_state->started_send_message_count); + } + grpc_core::ByteStreamCache* cache = + calld->send_messages[retry_state->started_send_message_count]; + ++retry_state->started_send_message_count; + batch_data->send_message.Init(cache); + batch_data->batch.send_message = true; + batch_data->batch.payload->send_message.send_message.reset( + batch_data->send_message.get()); +} + +// Adds retriable send_trailing_metadata op to batch_data. +static void add_retriable_send_trailing_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + // We need to make a copy of the metadata batch for each attempt, since + // the filters in the subchannel stack may modify this batch, and we don't + // want those modifications to be passed forward to subsequent attempts. + batch_data->send_trailing_metadata_storage = + static_cast<grpc_linked_mdelem*>(gpr_arena_alloc( + calld->arena, sizeof(grpc_linked_mdelem) * + calld->send_trailing_metadata.list.count)); + grpc_metadata_batch_copy(&calld->send_trailing_metadata, + &batch_data->send_trailing_metadata, + batch_data->send_trailing_metadata_storage); + retry_state->started_send_trailing_metadata = true; + batch_data->batch.send_trailing_metadata = true; + batch_data->batch.payload->send_trailing_metadata.send_trailing_metadata = + &batch_data->send_trailing_metadata; +} + +// Adds retriable recv_initial_metadata op to batch_data. +static void add_retriable_recv_initial_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + retry_state->started_recv_initial_metadata = true; + batch_data->batch.recv_initial_metadata = true; + grpc_metadata_batch_init(&batch_data->recv_initial_metadata); + batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata = + &batch_data->recv_initial_metadata; + batch_data->batch.payload->recv_initial_metadata.trailing_metadata_available = + &batch_data->trailing_metadata_available; + GRPC_CLOSURE_INIT(&batch_data->recv_initial_metadata_ready, + recv_initial_metadata_ready, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata_ready = + &batch_data->recv_initial_metadata_ready; +} + +// Adds retriable recv_message op to batch_data. +static void add_retriable_recv_message_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + ++retry_state->started_recv_message_count; + batch_data->batch.recv_message = true; + batch_data->batch.payload->recv_message.recv_message = + &batch_data->recv_message; + GRPC_CLOSURE_INIT(&batch_data->recv_message_ready, recv_message_ready, + batch_data, grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_message.recv_message_ready = + &batch_data->recv_message_ready; +} + +// Adds retriable recv_trailing_metadata op to batch_data. +static void add_retriable_recv_trailing_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + retry_state->started_recv_trailing_metadata = true; + batch_data->batch.recv_trailing_metadata = true; + grpc_metadata_batch_init(&batch_data->recv_trailing_metadata); + batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata = + &batch_data->recv_trailing_metadata; + batch_data->batch.collect_stats = true; + batch_data->batch.payload->collect_stats.collect_stats = + &batch_data->collect_stats; +} + +// Helper function used to start a recv_trailing_metadata batch. This +// is used in the case where a recv_initial_metadata or recv_message +// op fails in a way that we know the call is over but when the application +// has not yet started its own recv_trailing_metadata op. +static void start_internal_recv_trailing_metadata(grpc_call_element* elem) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: call failed but recv_trailing_metadata not " + "started; starting it internally", chand, calld); } - if (chand->retry_throttle_data != nullptr) { - calld->retry_throttle_data = - grpc_server_retry_throttle_data_ref(chand->retry_throttle_data); + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + subchannel_batch_data* batch_data = batch_data_create(elem, 1); + add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, &batch_data->batch); +} + +// If there are any cached send ops that need to be replayed on the +// current subchannel call, creates and returns a new subchannel batch +// to replay those ops. Otherwise, returns nullptr. +static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( + grpc_call_element* elem, subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + subchannel_batch_data* replay_batch_data = nullptr; + // send_initial_metadata. + if (calld->seen_send_initial_metadata && + !retry_state->started_send_initial_metadata && + !calld->pending_send_initial_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: replaying previously completed " + "send_initial_metadata op", + chand, calld); + } + replay_batch_data = batch_data_create(elem, 1); + add_retriable_send_initial_metadata_op(calld, retry_state, + replay_batch_data); + } + // send_message. + // Note that we can only have one send_message op in flight at a time. + if (retry_state->started_send_message_count < calld->send_messages.size() && + retry_state->started_send_message_count == + retry_state->completed_send_message_count && + !calld->pending_send_message) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: replaying previously completed " + "send_message op", + chand, calld); + } + if (replay_batch_data == nullptr) { + replay_batch_data = batch_data_create(elem, 1); + } + add_retriable_send_message_op(elem, retry_state, replay_batch_data); + } + // send_trailing_metadata. + // Note that we only add this op if we have no more send_message ops + // to start, since we can't send down any more send_message ops after + // send_trailing_metadata. + if (calld->seen_send_trailing_metadata && + retry_state->started_send_message_count == calld->send_messages.size() && + !retry_state->started_send_trailing_metadata && + !calld->pending_send_trailing_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: replaying previously completed " + "send_trailing_metadata op", + chand, calld); + } + if (replay_batch_data == nullptr) { + replay_batch_data = batch_data_create(elem, 1); + } + add_retriable_send_trailing_metadata_op(calld, retry_state, + replay_batch_data); } - if (chand->method_params_table != nullptr) { - calld->method_params = static_cast<method_parameters*>( - grpc_method_config_table_get(chand->method_params_table, calld->path)); - if (calld->method_params != nullptr) { - method_parameters_ref(calld->method_params); - // If the deadline from the service config is shorter than the one - // from the client API, reset the deadline timer. - if (chand->deadline_checking_enabled && - calld->method_params->timeout != 0) { - const grpc_millis per_method_deadline = - grpc_timespec_to_millis_round_up(calld->call_start_time) + - calld->method_params->timeout; - if (per_method_deadline < calld->deadline) { - calld->deadline = per_method_deadline; - grpc_deadline_state_reset(elem, calld->deadline); - } + return replay_batch_data; +} + +// Adds subchannel batches for pending batches to batches, updating +// *num_batches as needed. +static void add_subchannel_batches_for_pending_batches( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + grpc_transport_stream_op_batch** batches, size_t* num_batches) { + call_data* calld = static_cast<call_data*>(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch == nullptr) continue; + // Skip any batch that either (a) has already been started on this + // subchannel call or (b) we can't start yet because we're still + // replaying send ops that need to be completed first. + // TODO(roth): Note that if any one op in the batch can't be sent + // yet due to ops that we're replaying, we don't start any of the ops + // in the batch. This is probably okay, but it could conceivably + // lead to increased latency in some cases -- e.g., we could delay + // starting a recv op due to it being in the same batch with a send + // op. If/when we revamp the callback protocol in + // transport_stream_op_batch, we may be able to fix this. + if (batch->send_initial_metadata && + retry_state->started_send_initial_metadata) { + continue; + } + if (batch->send_message && retry_state->completed_send_message_count < + retry_state->started_send_message_count) { + continue; + } + // Note that we only start send_trailing_metadata if we have no more + // send_message ops to start, since we can't send down any more + // send_message ops after send_trailing_metadata. + if (batch->send_trailing_metadata && + (retry_state->started_send_message_count + batch->send_message < + calld->send_messages.size() || + retry_state->started_send_trailing_metadata)) { + continue; + } + if (batch->recv_initial_metadata && + retry_state->started_recv_initial_metadata) { + continue; + } + if (batch->recv_message && retry_state->completed_recv_message_count < + retry_state->started_recv_message_count) { + continue; + } + if (batch->recv_trailing_metadata && + retry_state->started_recv_trailing_metadata) { + continue; + } + // If we're not retrying, just send the batch as-is. + if (calld->method_params == nullptr || + calld->method_params->retry_policy() == nullptr || + calld->retry_committed) { + batches[(*num_batches)++] = batch; + pending_batch_clear(calld, pending); + continue; + } + // Create batch with the right number of callbacks. + const int num_callbacks = + 1 + batch->recv_initial_metadata + batch->recv_message; + subchannel_batch_data* batch_data = batch_data_create(elem, num_callbacks); + // Cache send ops if needed. + maybe_cache_send_ops_for_batch(calld, pending); + // send_initial_metadata. + if (batch->send_initial_metadata) { + add_retriable_send_initial_metadata_op(calld, retry_state, batch_data); + } + // send_message. + if (batch->send_message) { + add_retriable_send_message_op(elem, retry_state, batch_data); + } + // send_trailing_metadata. + if (batch->send_trailing_metadata) { + add_retriable_send_trailing_metadata_op(calld, retry_state, batch_data); + } + // recv_initial_metadata. + if (batch->recv_initial_metadata) { + // recv_flags is only used on the server side. + GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr); + add_retriable_recv_initial_metadata_op(calld, retry_state, batch_data); + } + // recv_message. + if (batch->recv_message) { + add_retriable_recv_message_op(calld, retry_state, batch_data); + } + // recv_trailing_metadata. + if (batch->recv_trailing_metadata) { + GPR_ASSERT(batch->collect_stats); + add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); + } + batches[(*num_batches)++] = &batch_data->batch; + } +} + +// Constructs and starts whatever subchannel batches are needed on the +// subchannel call. +static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) { + grpc_call_element* elem = static_cast<grpc_call_element*>(arg); + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: constructing retriable batches", + chand, calld); + } + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + // We can start up to 6 batches. + grpc_transport_stream_op_batch* + batches[GPR_ARRAY_SIZE(calld->pending_batches)]; + size_t num_batches = 0; + // Replay previously-returned send_* ops if needed. + subchannel_batch_data* replay_batch_data = + maybe_create_subchannel_batch_for_replay(elem, retry_state); + if (replay_batch_data != nullptr) { + batches[num_batches++] = &replay_batch_data->batch; + } + // Now add pending batches. + add_subchannel_batches_for_pending_batches(elem, retry_state, batches, + &num_batches); + // Start batches on subchannel call. + // Note that the call combiner will be yielded for each batch that we + // send down. We're already running in the call combiner, so one of + // the batches can be started directly, but the others will have to + // re-enter the call combiner. + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: starting %" PRIuPTR + " retriable batches on subchannel_call=%p", + chand, calld, num_batches, calld->subchannel_call); + } + if (num_batches == 0) { + // This should be fairly rare, but it can happen when (e.g.) an + // attempt completes before it has finished replaying all + // previously sent messages. + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "no retriable subchannel batches to start"); + } else { + for (size_t i = 1; i < num_batches; ++i) { + if (grpc_client_channel_trace.enabled()) { + char* batch_str = grpc_transport_stream_op_batch_string(batches[i]); + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: starting batch in call combiner: %s", chand, + calld, batch_str); + gpr_free(batch_str); } + batches[i]->handler_private.extra_arg = calld->subchannel_call; + GRPC_CLOSURE_INIT(&batches[i]->handler_private.closure, + start_batch_in_call_combiner, batches[i], + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(calld->call_combiner, + &batches[i]->handler_private.closure, + GRPC_ERROR_NONE, "start_subchannel_batch"); + } + if (grpc_client_channel_trace.enabled()) { + char* batch_str = grpc_transport_stream_op_batch_string(batches[0]); + gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting batch: %s", chand, calld, + batch_str); + gpr_free(batch_str); } + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, batches[0]); } } -static void create_subchannel_call_locked(grpc_call_element* elem, - grpc_error* error) { +// +// LB pick +// + +static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) { channel_data* chand = static_cast<channel_data*>(elem->channel_data); call_data* calld = static_cast<call_data*>(elem->call_data); + const size_t parent_data_size = + calld->enable_retries ? sizeof(subchannel_call_retry_state) : 0; const grpc_core::ConnectedSubchannel::CallArgs call_args = { calld->pollent, // pollent calld->path, // path @@ -1001,7 +2468,8 @@ static void create_subchannel_call_locked(grpc_call_element* elem, calld->deadline, // deadline calld->arena, // arena calld->pick.subchannel_call_context, // context - calld->call_combiner // call_combiner + calld->call_combiner, // call_combiner + parent_data_size // parent_data_size }; grpc_error* new_error = calld->pick.connected_subchannel->CreateCall( call_args, &calld->subchannel_call); @@ -1011,36 +2479,61 @@ static void create_subchannel_call_locked(grpc_call_element* elem, } if (new_error != GRPC_ERROR_NONE) { new_error = grpc_error_add_child(new_error, error); - waiting_for_pick_batches_fail(elem, new_error); + pending_batches_fail(elem, new_error, true /* yield_call_combiner */); } else { - waiting_for_pick_batches_resume(elem); + if (parent_data_size > 0) { + subchannel_call_retry_state* retry_state = + static_cast<subchannel_call_retry_state*>( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + retry_state->batch_payload.context = calld->pick.subchannel_call_context; + } + pending_batches_resume(elem); } GRPC_ERROR_UNREF(error); } // Invoked when a pick is completed, on both success or failure. -static void pick_done_locked(grpc_call_element* elem, grpc_error* error) { - call_data* calld = static_cast<call_data*>(elem->call_data); +static void pick_done(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast<grpc_call_element*>(arg); channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); if (calld->pick.connected_subchannel == nullptr) { // Failed to create subchannel. - GRPC_ERROR_UNREF(calld->error); - calld->error = error == GRPC_ERROR_NONE - ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Call dropped by load balancing policy") - : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to create subchannel", &error, 1); - if (grpc_client_channel_trace.enabled()) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: failed to create subchannel: error=%s", chand, - calld, grpc_error_string(calld->error)); + // If there was no error, this is an LB policy drop, in which case + // we return an error; otherwise, we may retry. + grpc_status_code status = GRPC_STATUS_OK; + grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr, + nullptr); + if (error == GRPC_ERROR_NONE || !calld->enable_retries || + !maybe_retry(elem, nullptr /* batch_data */, status, + nullptr /* server_pushback_md */)) { + grpc_error* new_error = + error == GRPC_ERROR_NONE + ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Call dropped by load balancing policy") + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to create subchannel", &error, 1); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: failed to create subchannel: error=%s", + chand, calld, grpc_error_string(new_error)); + } + pending_batches_fail(elem, new_error, true /* yield_call_combiner */); } - waiting_for_pick_batches_fail(elem, GRPC_ERROR_REF(calld->error)); } else { /* Create call on subchannel. */ - create_subchannel_call_locked(elem, GRPC_ERROR_REF(error)); + create_subchannel_call(elem, GRPC_ERROR_REF(error)); } - GRPC_ERROR_UNREF(error); +} + +// Invoked when a pick is completed to leave the client_channel combiner +// and continue processing in the call combiner. +static void pick_done_locked(grpc_call_element* elem, grpc_error* error) { + call_data* calld = static_cast<call_data*>(elem->call_data); + GRPC_CLOSURE_INIT(&calld->pick_closure, pick_done, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->pick_closure, error); } // A wrapper around pick_done_locked() that is used in cases where @@ -1088,6 +2581,45 @@ static void pick_callback_done_locked(void* arg, grpc_error* error) { GRPC_CALL_STACK_UNREF(calld->owning_call, "pick_callback"); } +// Applies service config to the call. Must be invoked once we know +// that the resolver has returned results to the channel. +static void apply_service_config_to_call_locked(grpc_call_element* elem) { + channel_data* chand = static_cast<channel_data*>(elem->channel_data); + call_data* calld = static_cast<call_data*>(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call", + chand, calld); + } + if (chand->retry_throttle_data != nullptr) { + calld->retry_throttle_data = + grpc_server_retry_throttle_data_ref(chand->retry_throttle_data); + } + if (chand->method_params_table != nullptr) { + calld->method_params = grpc_core::ServiceConfig::MethodConfigTableLookup( + *chand->method_params_table, calld->path); + if (calld->method_params != nullptr) { + // If the deadline from the service config is shorter than the one + // from the client API, reset the deadline timer. + if (chand->deadline_checking_enabled && + calld->method_params->timeout() != 0) { + const grpc_millis per_method_deadline = + grpc_timespec_to_millis_round_up(calld->call_start_time) + + calld->method_params->timeout(); + if (per_method_deadline < calld->deadline) { + calld->deadline = per_method_deadline; + grpc_deadline_state_reset(elem, calld->deadline); + } + } + } + } + // If no retry policy, disable retries. + // TODO(roth): Remove this when adding support for transparent retries. + if (calld->method_params == nullptr || + calld->method_params->retry_policy() == nullptr) { + calld->enable_retries = false; + } +} + // Starts a pick on chand->lb_policy. // Returns true if pick is completed synchronously. static bool pick_callback_start_locked(grpc_call_element* elem) { @@ -1097,33 +2629,46 @@ static bool pick_callback_start_locked(grpc_call_element* elem) { gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p", chand, calld, chand->lb_policy.get()); } - apply_service_config_to_call_locked(elem); + // Only get service config data on the first attempt. + if (calld->num_attempts_completed == 0) { + apply_service_config_to_call_locked(elem); + } // If the application explicitly set wait_for_ready, use that. // Otherwise, if the service config specified a value for this // method, use that. - uint32_t initial_metadata_flags = - calld->initial_metadata_batch->payload->send_initial_metadata - .send_initial_metadata_flags; + // + // The send_initial_metadata batch will be the first one in the list, + // as set by get_batch_index() above. + calld->pick.initial_metadata = + calld->seen_send_initial_metadata + ? &calld->send_initial_metadata + : calld->pending_batches[0] + .batch->payload->send_initial_metadata.send_initial_metadata; + uint32_t send_initial_metadata_flags = + calld->seen_send_initial_metadata + ? calld->send_initial_metadata_flags + : calld->pending_batches[0] + .batch->payload->send_initial_metadata + .send_initial_metadata_flags; const bool wait_for_ready_set_from_api = - initial_metadata_flags & + send_initial_metadata_flags & GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; const bool wait_for_ready_set_from_service_config = calld->method_params != nullptr && - calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET; + calld->method_params->wait_for_ready() != + ClientChannelMethodParams::WAIT_FOR_READY_UNSET; if (!wait_for_ready_set_from_api && wait_for_ready_set_from_service_config) { - if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) { - initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; + if (calld->method_params->wait_for_ready() == + ClientChannelMethodParams::WAIT_FOR_READY_TRUE) { + send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; } else { - initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; + send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; } } - calld->pick.initial_metadata = - calld->initial_metadata_batch->payload->send_initial_metadata - .send_initial_metadata; - calld->pick.initial_metadata_flags = initial_metadata_flags; - GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem, + calld->pick.initial_metadata_flags = send_initial_metadata_flags; + GRPC_CLOSURE_INIT(&calld->pick_closure, pick_callback_done_locked, elem, grpc_combiner_scheduler(chand->combiner)); - calld->pick.on_complete = &calld->lb_pick_closure; + calld->pick.on_complete = &calld->pick_closure; GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback"); const bool pick_done = chand->lb_policy->PickLocked(&calld->pick); if (pick_done) { @@ -1137,7 +2682,7 @@ static bool pick_callback_start_locked(grpc_call_element* elem) { GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel"); grpc_call_combiner_set_notify_on_cancel( calld->call_combiner, - GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure, + GRPC_CLOSURE_INIT(&calld->pick_cancel_closure, pick_callback_cancel_locked, elem, grpc_combiner_scheduler(chand->combiner))); } @@ -1186,8 +2731,6 @@ static void pick_after_resolver_result_cancel_locked(void* arg, "Pick cancelled", &error, 1)); } -static void pick_after_resolver_result_start_locked(grpc_call_element* elem); - static void pick_after_resolver_result_done_locked(void* arg, grpc_error* error) { pick_after_resolver_result_args* args = @@ -1224,7 +2767,7 @@ static void pick_after_resolver_result_done_locked(void* arg, async_pick_done_locked(elem, GRPC_ERROR_NONE); } } - // TODO(roth): It should be impossible for chand->lb_policy to be NULL + // TODO(roth): It should be impossible for chand->lb_policy to be nullptr // here, so the rest of this code should never actually be executed. // However, we have reports of a crash on iOS that triggers this case, // so we are temporarily adding this to restore branches that were @@ -1277,6 +2820,7 @@ static void start_pick_locked(void* arg, grpc_error* ignored) { call_data* calld = static_cast<call_data*>(elem->call_data); channel_data* chand = static_cast<channel_data*>(elem->channel_data); GPR_ASSERT(calld->pick.connected_subchannel == nullptr); + GPR_ASSERT(calld->subchannel_call == nullptr); if (chand->lb_policy != nullptr) { // We already have an LB policy, so ask it for a pick. if (pick_callback_start_locked(elem)) { @@ -1305,24 +2849,9 @@ static void start_pick_locked(void* arg, grpc_error* ignored) { chand->interested_parties); } -static void on_complete(void* arg, grpc_error* error) { - grpc_call_element* elem = static_cast<grpc_call_element*>(arg); - call_data* calld = static_cast<call_data*>(elem->call_data); - if (calld->retry_throttle_data != nullptr) { - if (error == GRPC_ERROR_NONE) { - grpc_server_retry_throttle_data_record_success( - calld->retry_throttle_data); - } else { - // TODO(roth): In a subsequent PR, check the return value here and - // decide whether or not to retry. Note that we should only - // record failures whose statuses match the configured retryable - // or non-fatal status codes. - grpc_server_retry_throttle_data_record_failure( - calld->retry_throttle_data); - } - } - GRPC_CLOSURE_RUN(calld->original_on_complete, GRPC_ERROR_REF(error)); -} +// +// filter call vtable functions +// static void cc_start_transport_stream_op_batch( grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { @@ -1333,46 +2862,47 @@ static void cc_start_transport_stream_op_batch( grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch); } // If we've previously been cancelled, immediately fail any new batches. - if (calld->error != GRPC_ERROR_NONE) { + if (calld->cancel_error != GRPC_ERROR_NONE) { if (grpc_client_channel_trace.enabled()) { gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s", - chand, calld, grpc_error_string(calld->error)); + chand, calld, grpc_error_string(calld->cancel_error)); } + // Note: This will release the call combiner. grpc_transport_stream_op_batch_finish_with_failure( - batch, GRPC_ERROR_REF(calld->error), calld->call_combiner); + batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner); return; } + // Handle cancellation. if (batch->cancel_stream) { // Stash a copy of cancel_error in our call data, so that we can use // it for subsequent operations. This ensures that if the call is // cancelled before any batches are passed down (e.g., if the deadline // is in the past when the call starts), we can return the right // error to the caller when the first batch does get passed down. - GRPC_ERROR_UNREF(calld->error); - calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); + GRPC_ERROR_UNREF(calld->cancel_error); + calld->cancel_error = + GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); if (grpc_client_channel_trace.enabled()) { gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand, - calld, grpc_error_string(calld->error)); + calld, grpc_error_string(calld->cancel_error)); } - // If we have a subchannel call, send the cancellation batch down. - // Otherwise, fail all pending batches. - if (calld->subchannel_call != nullptr) { - grpc_subchannel_call_process_op(calld->subchannel_call, batch); + // If we do not have a subchannel call (i.e., a pick has not yet + // been started), fail all pending batches. Otherwise, send the + // cancellation down to the subchannel call. + if (calld->subchannel_call == nullptr) { + pending_batches_fail(elem, GRPC_ERROR_REF(calld->cancel_error), + false /* yield_call_combiner */); + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner); } else { - waiting_for_pick_batches_add(calld, batch); - waiting_for_pick_batches_fail(elem, GRPC_ERROR_REF(calld->error)); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, batch); } return; } - // Intercept on_complete for recv_trailing_metadata so that we can - // check retry throttle status. - if (batch->recv_trailing_metadata) { - GPR_ASSERT(batch->on_complete != nullptr); - calld->original_on_complete = batch->on_complete; - GRPC_CLOSURE_INIT(&calld->on_complete, on_complete, elem, - grpc_schedule_on_exec_ctx); - batch->on_complete = &calld->on_complete; - } + // Add the batch to the pending list. + pending_batches_add(elem, batch); // Check if we've already gotten a subchannel call. // Note that once we have completed the pick, we do not need to enter // the channel combiner, which is more efficient (especially for @@ -1380,15 +2910,13 @@ static void cc_start_transport_stream_op_batch( if (calld->subchannel_call != nullptr) { if (grpc_client_channel_trace.enabled()) { gpr_log(GPR_DEBUG, - "chand=%p calld=%p: sending batch to subchannel_call=%p", chand, + "chand=%p calld=%p: starting batch on subchannel_call=%p", chand, calld, calld->subchannel_call); } - grpc_subchannel_call_process_op(calld->subchannel_call, batch); + pending_batches_resume(elem); return; } // We do not yet have a subchannel call. - // Add the batch to the waiting-for-pick list. - waiting_for_pick_batches_add(calld, batch); // For batches containing a send_initial_metadata op, enter the channel // combiner to start a pick. if (batch->send_initial_metadata) { @@ -1428,6 +2956,7 @@ static grpc_error* cc_init_call_elem(grpc_call_element* elem, grpc_deadline_state_init(elem, args->call_stack, args->call_combiner, calld->deadline); } + calld->enable_retries = chand->enable_retries; return GRPC_ERROR_NONE; } @@ -1441,10 +2970,8 @@ static void cc_destroy_call_elem(grpc_call_element* elem, grpc_deadline_state_destroy(elem); } grpc_slice_unref_internal(calld->path); - if (calld->method_params != nullptr) { - method_parameters_unref(calld->method_params); - } - GRPC_ERROR_UNREF(calld->error); + calld->method_params.reset(); + GRPC_ERROR_UNREF(calld->cancel_error); if (calld->subchannel_call != nullptr) { grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call, then_schedule_closure); @@ -1452,7 +2979,9 @@ static void cc_destroy_call_elem(grpc_call_element* elem, GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call, "client_channel_destroy_call"); } - GPR_ASSERT(calld->waiting_for_pick_batches_count == 0); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + GPR_ASSERT(calld->pending_batches[i].batch == nullptr); + } if (calld->pick.connected_subchannel != nullptr) { calld->pick.connected_subchannel.reset(); } @@ -1652,3 +3181,9 @@ void grpc_client_channel_watch_connectivity_state( grpc_combiner_scheduler(chand->combiner)), GRPC_ERROR_NONE); } + +grpc_subchannel_call* grpc_client_channel_get_subchannel_call( + grpc_call_element* elem) { + call_data* calld = static_cast<call_data*>(elem->call_data); + return calld->subchannel_call; +} diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h index 9670405cbe..a21e5623a7 100644 --- a/src/core/ext/filters/client_channel/client_channel.h +++ b/src/core/ext/filters/client_channel/client_channel.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/client_channel_factory.h" #include "src/core/ext/filters/client_channel/resolver.h" #include "src/core/lib/channel/channel_stack.h" diff --git a/src/core/ext/filters/client_channel/client_channel_factory.cc b/src/core/ext/filters/client_channel/client_channel_factory.cc index 3baf5b31ab..172e9f03c7 100644 --- a/src/core/ext/filters/client_channel/client_channel_factory.cc +++ b/src/core/ext/filters/client_channel/client_channel_factory.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/client_channel_factory.h" #include "src/core/lib/channel/channel_args.h" diff --git a/src/core/ext/filters/client_channel/client_channel_factory.h b/src/core/ext/filters/client_channel/client_channel_factory.h index 766ebb9389..601ec46b2a 100644 --- a/src/core/ext/filters/client_channel/client_channel_factory.h +++ b/src/core/ext/filters/client_channel/client_channel_factory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/ext/filters/client_channel/subchannel.h" diff --git a/src/core/ext/filters/client_channel/connector.cc b/src/core/ext/filters/client_channel/connector.cc index c8bf2f3e1c..5e04b3b453 100644 --- a/src/core/ext/filters/client_channel/connector.cc +++ b/src/core/ext/filters/client_channel/connector.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/connector.h" grpc_connector* grpc_connector_ref(grpc_connector* connector) { diff --git a/src/core/ext/filters/client_channel/connector.h b/src/core/ext/filters/client_channel/connector.h index d657658d67..556594929c 100644 --- a/src/core/ext/filters/client_channel/connector.h +++ b/src/core/ext/filters/client_channel/connector.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/transport/transport.h" diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/src/core/ext/filters/client_channel/http_connect_handshaker.cc index 248a6347d5..fb29fa788d 100644 --- a/src/core/ext/filters/client_channel/http_connect_handshaker.cc +++ b/src/core/ext/filters/client_channel/http_connect_handshaker.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/http_connect_handshaker.h" #include <string.h> diff --git a/src/core/ext/filters/client_channel/http_proxy.cc b/src/core/ext/filters/client_channel/http_proxy.cc index d42376413d..29a6c0e367 100644 --- a/src/core/ext/filters/client_channel/http_proxy.cc +++ b/src/core/ext/filters/client_channel/http_proxy.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/http_proxy.h" #include <stdbool.h> diff --git a/src/core/ext/filters/client_channel/lb_policy.cc b/src/core/ext/filters/client_channel/lb_policy.cc index 59f4cdafb3..fa63dd75b5 100644 --- a/src/core/ext/filters/client_channel/lb_policy.cc +++ b/src/core/ext/filters/client_channel/lb_policy.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy.h" #include "src/core/lib/iomgr/combiner.h" diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h index 6de652747e..c3e43e5ef6 100644 --- a/src/core/ext/filters/client_channel/lb_policy.h +++ b/src/core/ext/filters/client_channel/lb_policy.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/client_channel_factory.h" #include "src/core/ext/filters/client_channel/subchannel.h" #include "src/core/lib/gprpp/abstract.h" diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc index 1a3a1f029c..18ef1f6ff5 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" #include <grpc/support/atm.h> diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h index 04de7a04df..838e2ef1ca 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" extern const grpc_channel_filter grpc_client_load_reporting_filter; diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index e38211fbf4..47e1deef12 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -58,7 +58,10 @@ // using that endpoint. Because of various transitive includes in uv.h, // including windows.h on Windows, uv.h must be included before other system // headers. Therefore, sockaddr.h must always be included first. +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" #include <inttypes.h> #include <limits.h> @@ -189,7 +192,7 @@ class GrpcLb : public LoadBalancingPolicy { ~BalancerCallState(); GrpcLb* grpclb_policy() const { - return reinterpret_cast<GrpcLb*>(grpclb_policy_.get()); + return static_cast<GrpcLb*>(grpclb_policy_.get()); } void ScheduleNextClientLoadReportLocked(); @@ -415,20 +418,20 @@ void ParseServer(const grpc_grpclb_server* server, grpc_resolved_address* addr) { memset(addr, 0, sizeof(*addr)); if (server->drop) return; - const uint16_t netorder_port = htons((uint16_t)server->port); + const uint16_t netorder_port = grpc_htons((uint16_t)server->port); /* the addresses are given in binary format (a in(6)_addr struct) in * server->ip_address.bytes. */ const grpc_grpclb_ip_address* ip = &server->ip_address; if (ip->size == 4) { - addr->len = sizeof(struct sockaddr_in); - struct sockaddr_in* addr4 = (struct sockaddr_in*)&addr->addr; - addr4->sin_family = AF_INET; + addr->len = sizeof(grpc_sockaddr_in); + grpc_sockaddr_in* addr4 = reinterpret_cast<grpc_sockaddr_in*>(&addr->addr); + addr4->sin_family = GRPC_AF_INET; memcpy(&addr4->sin_addr, ip->bytes, ip->size); addr4->sin_port = netorder_port; } else if (ip->size == 16) { - addr->len = sizeof(struct sockaddr_in6); - struct sockaddr_in6* addr6 = (struct sockaddr_in6*)&addr->addr; - addr6->sin6_family = AF_INET6; + addr->len = sizeof(grpc_sockaddr_in6); + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)&addr->addr; + addr6->sin6_family = GRPC_AF_INET6; memcpy(&addr6->sin6_addr, ip->bytes, ip->size); addr6->sin6_port = netorder_port; } @@ -649,7 +652,7 @@ void GrpcLb::BalancerCallState::ScheduleNextClientLoadReportLocked() { void GrpcLb::BalancerCallState::MaybeSendClientLoadReportLocked( void* arg, grpc_error* error) { - BalancerCallState* lb_calld = reinterpret_cast<BalancerCallState*>(arg); + BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg); GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); lb_calld->client_load_report_timer_callback_pending_ = false; if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) { @@ -719,7 +722,7 @@ void GrpcLb::BalancerCallState::SendClientLoadReportLocked() { void GrpcLb::BalancerCallState::ClientLoadReportDoneLocked(void* arg, grpc_error* error) { - BalancerCallState* lb_calld = reinterpret_cast<BalancerCallState*>(arg); + BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg); GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); grpc_byte_buffer_destroy(lb_calld->send_message_payload_); lb_calld->send_message_payload_ = nullptr; @@ -732,7 +735,7 @@ void GrpcLb::BalancerCallState::ClientLoadReportDoneLocked(void* arg, void GrpcLb::BalancerCallState::OnInitialRequestSentLocked(void* arg, grpc_error* error) { - BalancerCallState* lb_calld = reinterpret_cast<BalancerCallState*>(arg); + BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg); grpc_byte_buffer_destroy(lb_calld->send_message_payload_); lb_calld->send_message_payload_ = nullptr; // If we attempted to send a client load report before the initial request was @@ -747,7 +750,7 @@ void GrpcLb::BalancerCallState::OnInitialRequestSentLocked(void* arg, void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked( void* arg, grpc_error* error) { - BalancerCallState* lb_calld = reinterpret_cast<BalancerCallState*>(arg); + BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg); GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); // Empty payload means the LB call was cancelled. if (lb_calld != grpclb_policy->lb_calld_.get() || @@ -880,7 +883,7 @@ void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked( void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked( void* arg, grpc_error* error) { - BalancerCallState* lb_calld = reinterpret_cast<BalancerCallState*>(arg); + BalancerCallState* lb_calld = static_cast<BalancerCallState*>(arg); GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); GPR_ASSERT(lb_calld->lb_call_ != nullptr); if (grpc_lb_glb_trace.enabled()) { @@ -1281,7 +1284,7 @@ void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) { return; } const grpc_lb_addresses* addresses = - reinterpret_cast<const grpc_lb_addresses*>(arg->value.pointer.p); + static_cast<const grpc_lb_addresses*>(arg->value.pointer.p); // Update fallback address list. if (fallback_backend_addresses_ != nullptr) { grpc_lb_addresses_destroy(fallback_backend_addresses_); @@ -1424,7 +1427,7 @@ void GrpcLb::StartBalancerCallRetryTimerLocked() { } void GrpcLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) { - GrpcLb* grpclb_policy = reinterpret_cast<GrpcLb*>(arg); + GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg); grpclb_policy->retry_timer_callback_pending_ = false; if (!grpclb_policy->shutting_down_ && error == GRPC_ERROR_NONE && grpclb_policy->lb_calld_ == nullptr) { @@ -1501,8 +1504,7 @@ grpc_error* AddLbTokenToInitialMetadata( // Destroy function used when embedding client stats in call context. void DestroyClientStats(void* arg) { - grpc_grpclb_client_stats_unref( - reinterpret_cast<grpc_grpclb_client_stats*>(arg)); + grpc_grpclb_client_stats_unref(static_cast<grpc_grpclb_client_stats*>(arg)); } void GrpcLb::PendingPickSetMetadataAndContext(PendingPick* pp) { @@ -1538,7 +1540,7 @@ void GrpcLb::PendingPickSetMetadataAndContext(PendingPick* pp) { * reference to its associated round robin instance. We wrap this closure in * order to unref the round robin instance upon its invocation */ void GrpcLb::OnPendingPickComplete(void* arg, grpc_error* error) { - PendingPick* pp = reinterpret_cast<PendingPick*>(arg); + PendingPick* pp = static_cast<PendingPick*>(arg); PendingPickSetMetadataAndContext(pp); GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error)); Delete(pp); @@ -1737,7 +1739,7 @@ void GrpcLb::CreateOrUpdateRoundRobinPolicyLocked() { void GrpcLb::OnRoundRobinRequestReresolutionLocked(void* arg, grpc_error* error) { - GrpcLb* grpclb_policy = reinterpret_cast<GrpcLb*>(arg); + GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg); if (grpclb_policy->shutting_down_ || error != GRPC_ERROR_NONE) { grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_reresolution_requested"); return; @@ -1818,7 +1820,7 @@ void GrpcLb::UpdateConnectivityStateFromRoundRobinPolicyLocked( void GrpcLb::OnRoundRobinConnectivityChangedLocked(void* arg, grpc_error* error) { - GrpcLb* grpclb_policy = reinterpret_cast<GrpcLb*>(arg); + GrpcLb* grpclb_policy = static_cast<GrpcLb*>(arg); if (grpclb_policy->shutting_down_) { grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_connectivity_changed"); return; @@ -1846,7 +1848,7 @@ class GrpcLbFactory : public LoadBalancingPolicyFactory { return nullptr; } grpc_lb_addresses* addresses = - reinterpret_cast<grpc_lb_addresses*>(arg->value.pointer.p); + static_cast<grpc_lb_addresses*>(arg->value.pointer.p); size_t num_grpclb_addrs = 0; for (size_t i = 0; i < addresses->num_addresses; ++i) { if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc index ebbe597d29..fd873f096d 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h index 6635010434..825065a9c3 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_factory.h" /// Makes any necessary modifications to \a args for use in the grpclb diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc index 254a5dfd16..441efd5e23 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" #include <string.h> @@ -28,47 +30,41 @@ #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" +#include "src/core/lib/security/transport/target_authority_table.h" #include "src/core/lib/slice/slice_internal.h" -static void destroy_balancer_name(void* balancer_name) { - gpr_free(balancer_name); -} +namespace grpc_core { +namespace { -static grpc_slice_hash_table_entry targets_info_entry_create( - const char* address, const char* balancer_name) { - grpc_slice_hash_table_entry entry; - entry.key = grpc_slice_from_copied_string(address); - entry.value = gpr_strdup(balancer_name); - return entry; +int BalancerNameCmp(const grpc_core::UniquePtr<char>& a, + const grpc_core::UniquePtr<char>& b) { + return strcmp(a.get(), b.get()); } -static int balancer_name_cmp_fn(void* a, void* b) { - const char* a_str = static_cast<const char*>(a); - const char* b_str = static_cast<const char*>(b); - return strcmp(a_str, b_str); -} - -static grpc_slice_hash_table* build_targets_info_table( +RefCountedPtr<TargetAuthorityTable> CreateTargetAuthorityTable( grpc_lb_addresses* addresses) { - grpc_slice_hash_table_entry* targets_info_entries = - static_cast<grpc_slice_hash_table_entry*>( - gpr_zalloc(sizeof(*targets_info_entries) * addresses->num_addresses)); + TargetAuthorityTable::Entry* target_authority_entries = + static_cast<TargetAuthorityTable::Entry*>(gpr_zalloc( + sizeof(*target_authority_entries) * addresses->num_addresses)); for (size_t i = 0; i < addresses->num_addresses; ++i) { char* addr_str; GPR_ASSERT(grpc_sockaddr_to_string( &addr_str, &addresses->addresses[i].address, true) > 0); - targets_info_entries[i] = targets_info_entry_create( - addr_str, addresses->addresses[i].balancer_name); + target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str); + target_authority_entries[i].value.reset( + gpr_strdup(addresses->addresses[i].balancer_name)); gpr_free(addr_str); } - grpc_slice_hash_table* targets_info = grpc_slice_hash_table_create( - addresses->num_addresses, targets_info_entries, destroy_balancer_name, - balancer_name_cmp_fn); - gpr_free(targets_info_entries); - return targets_info; + RefCountedPtr<TargetAuthorityTable> target_authority_table = + TargetAuthorityTable::Create(addresses->num_addresses, + target_authority_entries, BalancerNameCmp); + gpr_free(target_authority_entries); + return target_authority_table; } +} // namespace +} // namespace grpc_core + grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( grpc_channel_args* args) { const char* args_to_remove[1]; @@ -81,9 +77,11 @@ grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( GPR_ASSERT(arg->type == GRPC_ARG_POINTER); grpc_lb_addresses* addresses = static_cast<grpc_lb_addresses*>(arg->value.pointer.p); - grpc_slice_hash_table* targets_info = build_targets_info_table(addresses); + grpc_core::RefCountedPtr<grpc_core::TargetAuthorityTable> + target_authority_table = grpc_core::CreateTargetAuthorityTable(addresses); args_to_add[num_args_to_add++] = - grpc_lb_targets_info_create_channel_arg(targets_info); + grpc_core::CreateTargetAuthorityTableChannelArg( + target_authority_table.get()); // Substitute the channel credentials with a version without call // credentials: the load balancer is not necessarily trusted to handle // bearer token credentials. @@ -103,7 +101,6 @@ grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add); // Clean up. grpc_channel_args_destroy(args); - grpc_slice_hash_table_unref(targets_info); if (creds_sans_call_creds != nullptr) { grpc_channel_credentials_unref(creds_sans_call_creds); } diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc index 0b5a798be3..dfbaead7d5 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" #include <string.h> diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h index d4b9d06848..c971e56883 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/impl/codegen/grpc_types.h> diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc index c388b6ba77..7ef3bcf24f 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" #include "third_party/nanopb/pb_decode.h" #include "third_party/nanopb/pb_encode.h" diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h index ccb0212643..d4270f2536 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H +#include <grpc/support/port_platform.h> + #include <grpc/slice_buffer.h> #include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc index f1878a594e..9090c34412 100644 --- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc +++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> @@ -391,8 +393,8 @@ void PickFirst::UpdateLocked(const grpc_channel_args& args) { } void PickFirst::OnConnectivityChangedLocked(void* arg, grpc_error* error) { - grpc_lb_subchannel_data* sd = reinterpret_cast<grpc_lb_subchannel_data*>(arg); - PickFirst* p = reinterpret_cast<PickFirst*>(sd->subchannel_list->policy); + grpc_lb_subchannel_data* sd = static_cast<grpc_lb_subchannel_data*>(arg); + PickFirst* p = static_cast<PickFirst*>(sd->subchannel_list->policy); if (grpc_lb_pick_first_trace.enabled()) { gpr_log(GPR_DEBUG, "Pick First %p connectivity changed for subchannel %p (%" PRIuPTR diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc index 178e299b61..e534131c02 100644 --- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc +++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc @@ -24,6 +24,8 @@ * updates. Note however that updates will start picking from the beginning of * the updated list. */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> @@ -410,8 +412,8 @@ void RoundRobin::UpdateConnectivityStatusLocked(grpc_lb_subchannel_data* sd, } void RoundRobin::OnConnectivityChangedLocked(void* arg, grpc_error* error) { - grpc_lb_subchannel_data* sd = reinterpret_cast<grpc_lb_subchannel_data*>(arg); - RoundRobin* p = reinterpret_cast<RoundRobin*>(sd->subchannel_list->policy); + grpc_lb_subchannel_data* sd = static_cast<grpc_lb_subchannel_data*>(arg); + RoundRobin* p = static_cast<RoundRobin*>(sd->subchannel_list->policy); if (grpc_lb_round_robin_trace.enabled()) { gpr_log( GPR_DEBUG, diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc index f1580e8b91..79cb64c6c6 100644 --- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc +++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h index 7a8f5f1029..6889d596ac 100644 --- a/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h +++ b/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_registry.h" #include "src/core/ext/filters/client_channel/subchannel.h" #include "src/core/lib/debug/trace.h" diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.cc b/src/core/ext/filters/client_channel/lb_policy_factory.cc index 2018aabb91..80646a10cc 100644 --- a/src/core/ext/filters/client_channel/lb_policy_factory.cc +++ b/src/core/ext/filters/client_channel/lb_policy_factory.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h index e0e7d8bf5c..b8bbd32072 100644 --- a/src/core/ext/filters/client_channel/lb_policy_factory.h +++ b/src/core/ext/filters/client_channel/lb_policy_factory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/resolve_address.h" diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.cc b/src/core/ext/filters/client_channel/lb_policy_registry.cc index f495cdb3c2..d651b1120d 100644 --- a/src/core/ext/filters/client_channel/lb_policy_registry.cc +++ b/src/core/ext/filters/client_channel/lb_policy_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_registry.h" #include <string.h> diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.h b/src/core/ext/filters/client_channel/lb_policy_registry.h index 14c21dfe63..2283d848bd 100644 --- a/src/core/ext/filters/client_channel/lb_policy_registry.h +++ b/src/core/ext/filters/client_channel/lb_policy_registry.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/lib/gprpp/memory.h" #include "src/core/lib/gprpp/orphanable.h" diff --git a/src/core/ext/filters/client_channel/method_params.cc b/src/core/ext/filters/client_channel/method_params.cc new file mode 100644 index 0000000000..374b87e170 --- /dev/null +++ b/src/core/ext/filters/client_channel/method_params.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stdio.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/ext/filters/client_channel/method_params.h" +#include "src/core/ext/filters/client_channel/status_util.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" + +// As per the retry design, we do not allow more than 5 retry attempts. +#define MAX_MAX_RETRY_ATTEMPTS 5 + +namespace grpc_core { +namespace internal { + +namespace { + +bool ParseWaitForReady( + grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) { + if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { + return false; + } + *wait_for_ready = field->type == GRPC_JSON_TRUE + ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE + : ClientChannelMethodParams::WAIT_FOR_READY_FALSE; + return true; +} + +// Parses a JSON field of the form generated for a google.proto.Duration +// proto message, as per: +// https://developers.google.com/protocol-buffers/docs/proto3#json +bool ParseDuration(grpc_json* field, grpc_millis* duration) { + if (field->type != GRPC_JSON_STRING) return false; + size_t len = strlen(field->value); + if (field->value[len - 1] != 's') return false; + UniquePtr<char> buf(gpr_strdup(field->value)); + *(buf.get() + len - 1) = '\0'; // Remove trailing 's'. + char* decimal_point = strchr(buf.get(), '.'); + int nanos = 0; + if (decimal_point != nullptr) { + *decimal_point = '\0'; + nanos = gpr_parse_nonnegative_int(decimal_point + 1); + if (nanos == -1) { + return false; + } + int num_digits = static_cast<int>(strlen(decimal_point + 1)); + if (num_digits > 9) { // We don't accept greater precision than nanos. + return false; + } + for (int i = 0; i < (9 - num_digits); ++i) { + nanos *= 10; + } + } + int seconds = + decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get()); + if (seconds == -1) return false; + *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS; + return true; +} + +UniquePtr<ClientChannelMethodParams::RetryPolicy> ParseRetryPolicy( + grpc_json* field) { + auto retry_policy = MakeUnique<ClientChannelMethodParams::RetryPolicy>(); + if (field->type != GRPC_JSON_OBJECT) return nullptr; + for (grpc_json* sub_field = field->child; sub_field != nullptr; + sub_field = sub_field->next) { + if (sub_field->key == nullptr) return nullptr; + if (strcmp(sub_field->key, "maxAttempts") == 0) { + if (retry_policy->max_attempts != 0) return nullptr; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return nullptr; + retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value); + if (retry_policy->max_attempts <= 1) return nullptr; + if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) { + gpr_log(GPR_ERROR, + "service config: clamped retryPolicy.maxAttempts at %d", + MAX_MAX_RETRY_ATTEMPTS); + retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS; + } + } else if (strcmp(sub_field->key, "initialBackoff") == 0) { + if (retry_policy->initial_backoff > 0) return nullptr; // Duplicate. + if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) { + return nullptr; + } + if (retry_policy->initial_backoff == 0) return nullptr; + } else if (strcmp(sub_field->key, "maxBackoff") == 0) { + if (retry_policy->max_backoff > 0) return nullptr; // Duplicate. + if (!ParseDuration(sub_field, &retry_policy->max_backoff)) { + return nullptr; + } + if (retry_policy->max_backoff == 0) return nullptr; + } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) { + if (retry_policy->backoff_multiplier != 0) return nullptr; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return nullptr; + if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) != + 1) { + return nullptr; + } + if (retry_policy->backoff_multiplier <= 0) return nullptr; + } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) { + if (!retry_policy->retryable_status_codes.Empty()) { + return nullptr; // Duplicate. + } + if (sub_field->type != GRPC_JSON_ARRAY) return nullptr; + for (grpc_json* element = sub_field->child; element != nullptr; + element = element->next) { + if (element->type != GRPC_JSON_STRING) return nullptr; + grpc_status_code status; + if (!grpc_status_code_from_string(element->value, &status)) { + return nullptr; + } + retry_policy->retryable_status_codes.Add(status); + } + if (retry_policy->retryable_status_codes.Empty()) return nullptr; + } + } + // Make sure required fields are set. + if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 || + retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 || + retry_policy->retryable_status_codes.Empty()) { + return nullptr; + } + return retry_policy; +} + +} // namespace + +RefCountedPtr<ClientChannelMethodParams> +ClientChannelMethodParams::CreateFromJson(const grpc_json* json) { + RefCountedPtr<ClientChannelMethodParams> method_params = + MakeRefCounted<ClientChannelMethodParams>(); + for (grpc_json* field = json->child; field != nullptr; field = field->next) { + if (field->key == nullptr) continue; + if (strcmp(field->key, "waitForReady") == 0) { + if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) { + return nullptr; // Duplicate. + } + if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) { + return nullptr; + } + } else if (strcmp(field->key, "timeout") == 0) { + if (method_params->timeout_ > 0) return nullptr; // Duplicate. + if (!ParseDuration(field, &method_params->timeout_)) return nullptr; + } else if (strcmp(field->key, "retryPolicy") == 0) { + if (method_params->retry_policy_ != nullptr) { + return nullptr; // Duplicate. + } + method_params->retry_policy_ = ParseRetryPolicy(field); + if (method_params->retry_policy_ == nullptr) return nullptr; + } + } + return method_params; +} + +} // namespace internal +} // namespace grpc_core diff --git a/src/core/ext/filters/client_channel/method_params.h b/src/core/ext/filters/client_channel/method_params.h new file mode 100644 index 0000000000..48ece29867 --- /dev/null +++ b/src/core/ext/filters/client_channel/method_params.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H + +#include <grpc/support/port_platform.h> + +#include "src/core/ext/filters/client_channel/status_util.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/exec_ctx.h" // for grpc_millis +#include "src/core/lib/json/json.h" + +namespace grpc_core { +namespace internal { + +class ClientChannelMethodParams : public RefCounted<ClientChannelMethodParams> { + public: + enum WaitForReady { + WAIT_FOR_READY_UNSET = 0, + WAIT_FOR_READY_FALSE, + WAIT_FOR_READY_TRUE + }; + + struct RetryPolicy { + int max_attempts = 0; + grpc_millis initial_backoff = 0; + grpc_millis max_backoff = 0; + float backoff_multiplier = 0; + StatusCodeSet retryable_status_codes; + }; + + /// Creates a method_parameters object from \a json. + /// Intended for use with ServiceConfig::CreateMethodConfigTable(). + static RefCountedPtr<ClientChannelMethodParams> CreateFromJson( + const grpc_json* json); + + grpc_millis timeout() const { return timeout_; } + WaitForReady wait_for_ready() const { return wait_for_ready_; } + const RetryPolicy* retry_policy() const { return retry_policy_.get(); } + + private: + // So New() can call our private ctor. + template <typename T, typename... Args> + friend T* grpc_core::New(Args&&... args); + + ClientChannelMethodParams() {} + virtual ~ClientChannelMethodParams() {} + + grpc_millis timeout_ = 0; + WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET; + UniquePtr<RetryPolicy> retry_policy_; +}; + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_METHOD_PARAMS_H */ diff --git a/src/core/ext/filters/client_channel/parse_address.cc b/src/core/ext/filters/client_channel/parse_address.cc index 473c7542df..92ea259cf0 100644 --- a/src/core/ext/filters/client_channel/parse_address.cc +++ b/src/core/ext/filters/client_channel/parse_address.cc @@ -16,8 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/parse_address.h" #include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" #include <stdio.h> #include <string.h> @@ -69,10 +72,10 @@ bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr, if (!gpr_split_host_port(hostport, &host, &port)) return false; // Parse IP address. memset(addr, 0, sizeof(*addr)); - addr->len = sizeof(struct sockaddr_in); - struct sockaddr_in* in = reinterpret_cast<struct sockaddr_in*>(addr->addr); - in->sin_family = AF_INET; - if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { + addr->len = sizeof(grpc_sockaddr_in); + grpc_sockaddr_in* in = reinterpret_cast<grpc_sockaddr_in*>(addr->addr); + in->sin_family = GRPC_AF_INET; + if (grpc_inet_pton(GRPC_AF_INET, host, &in->sin_addr) == 0) { if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); goto done; } @@ -86,7 +89,7 @@ bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr, if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); goto done; } - in->sin_port = htons(static_cast<uint16_t>(port_num)); + in->sin_port = grpc_htons(static_cast<uint16_t>(port_num)); success = true; done: gpr_free(host); @@ -115,19 +118,20 @@ bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, if (!gpr_split_host_port(hostport, &host, &port)) return false; // Parse IP address. memset(addr, 0, sizeof(*addr)); - addr->len = sizeof(struct sockaddr_in6); - struct sockaddr_in6* in6 = reinterpret_cast<struct sockaddr_in6*>(addr->addr); - in6->sin6_family = AF_INET6; + addr->len = sizeof(grpc_sockaddr_in6); + grpc_sockaddr_in6* in6 = reinterpret_cast<grpc_sockaddr_in6*>(addr->addr); + in6->sin6_family = GRPC_AF_INET6; // Handle the RFC6874 syntax for IPv6 zone identifiers. char* host_end = static_cast<char*>(gpr_memrchr(host, '%', strlen(host))); if (host_end != nullptr) { GPR_ASSERT(host_end >= host); - char host_without_scope[INET6_ADDRSTRLEN]; + char host_without_scope[GRPC_INET6_ADDRSTRLEN]; size_t host_without_scope_len = static_cast<size_t>(host_end - host); uint32_t sin6_scope_id = 0; strncpy(host_without_scope, host, host_without_scope_len); host_without_scope[host_without_scope_len] = '\0'; - if (inet_pton(AF_INET6, host_without_scope, &in6->sin6_addr) == 0) { + if (grpc_inet_pton(GRPC_AF_INET6, host_without_scope, &in6->sin6_addr) == + 0) { gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); goto done; } @@ -140,7 +144,7 @@ bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. in6->sin6_scope_id = sin6_scope_id; } else { - if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { + if (grpc_inet_pton(GRPC_AF_INET6, host, &in6->sin6_addr) == 0) { gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); goto done; } @@ -155,7 +159,7 @@ bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); goto done; } - in6->sin6_port = htons(static_cast<uint16_t>(port_num)); + in6->sin6_port = grpc_htons(static_cast<uint16_t>(port_num)); success = true; done: gpr_free(host); diff --git a/src/core/ext/filters/client_channel/parse_address.h b/src/core/ext/filters/client_channel/parse_address.h index ca0a0d18f0..9a88b66edc 100644 --- a/src/core/ext/filters/client_channel/parse_address.h +++ b/src/core/ext/filters/client_channel/parse_address.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include "src/core/ext/filters/client_channel/uri_parser.h" diff --git a/src/core/ext/filters/client_channel/proxy_mapper.cc b/src/core/ext/filters/client_channel/proxy_mapper.cc index be85cfcced..c4da06778d 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper.cc +++ b/src/core/ext/filters/client_channel/proxy_mapper.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/proxy_mapper.h" void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable, diff --git a/src/core/ext/filters/client_channel/proxy_mapper.h b/src/core/ext/filters/client_channel/proxy_mapper.h index ce3e65ee46..634b0ed7bf 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper.h +++ b/src/core/ext/filters/client_channel/proxy_mapper.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/impl/codegen/grpc_types.h> diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.cc b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc index b42597e363..a02a5f5e2c 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper_registry.cc +++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" #include <string.h> diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.h b/src/core/ext/filters/client_channel/proxy_mapper_registry.h index 2ad6c04e1d..326b582b99 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper_registry.h +++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/proxy_mapper.h" void grpc_proxy_mapper_registry_init(); diff --git a/src/core/ext/filters/client_channel/resolver.cc b/src/core/ext/filters/client_channel/resolver.cc index 860c2eea1e..cd11eeb9e4 100644 --- a/src/core/ext/filters/client_channel/resolver.cc +++ b/src/core/ext/filters/client_channel/resolver.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/resolver.h" #include "src/core/lib/iomgr/combiner.h" diff --git a/src/core/ext/filters/client_channel/resolver.h b/src/core/ext/filters/client_channel/resolver.h index 62fcb49a41..1685a6c803 100644 --- a/src/core/ext/filters/client_channel/resolver.h +++ b/src/core/ext/filters/client_channel/resolver.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/gprpp/abstract.h" diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc index 0442b1e496..c63de3c509 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc @@ -17,6 +17,7 @@ */ #include <grpc/support/port_platform.h> + #if GRPC_ARES == 1 && !defined(GRPC_UV) #include <limits.h> @@ -294,7 +295,7 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { size_t num_args_to_add = 0; new_args[num_args_to_add++] = grpc_lb_addresses_create_channel_arg(r->lb_addresses_); - grpc_service_config* service_config = nullptr; + grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config; char* service_config_string = nullptr; if (r->service_config_json_ != nullptr) { service_config_string = ChooseServiceConfig(r->service_config_json_); @@ -305,10 +306,11 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG; new_args[num_args_to_add++] = grpc_channel_arg_string_create( (char*)GRPC_ARG_SERVICE_CONFIG, service_config_string); - service_config = grpc_service_config_create(service_config_string); + service_config = + grpc_core::ServiceConfig::Create(service_config_string); if (service_config != nullptr) { const char* lb_policy_name = - grpc_service_config_get_lb_policy_name(service_config); + service_config->GetLoadBalancingPolicyName(); if (lb_policy_name != nullptr) { args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME; new_args[num_args_to_add++] = grpc_channel_arg_string_create( @@ -321,7 +323,6 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { result = grpc_channel_args_copy_and_add_and_remove( r->channel_args_, args_to_remove, num_args_to_remove, new_args, num_args_to_add); - if (service_config != nullptr) grpc_service_config_destroy(service_config); gpr_free(service_config_string); grpc_lb_addresses_destroy(r->lb_addresses_); // Reset backoff state so that we start from the beginning when the @@ -439,6 +440,19 @@ class AresDnsResolverFactory : public ResolverFactory { } // namespace grpc_core +extern grpc_address_resolver_vtable* grpc_resolve_address_impl; +static grpc_address_resolver_vtable* default_resolver; + +static grpc_error* blocking_resolve_address_ares( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + return default_resolver->blocking_resolve_address(name, default_port, + addresses); +} + +static grpc_address_resolver_vtable ares_resolver = { + grpc_resolve_address_ares, blocking_resolve_address_ares}; + void grpc_resolver_dns_ares_init() { char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); /* TODO(zyc): Turn on c-ares based resolver by default after the address @@ -449,7 +463,8 @@ void grpc_resolver_dns_ares_init() { GRPC_LOG_IF_ERROR("ares_library_init() failed", error); return; } - grpc_resolve_address = grpc_resolve_address_ares; + default_resolver = grpc_resolve_address_impl; + grpc_set_resolver_impl(&ares_resolver); grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( grpc_core::UniquePtr<grpc_core::ResolverFactory>( grpc_core::New<grpc_core::AresDnsResolverFactory>())); diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h index ba7dad63cf..0bc13e35f4 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H +#include <grpc/support/port_platform.h> + #include <ares.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/pollset_set.h" diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc index 10bc8f6074..b604f2bf14 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc @@ -16,6 +16,7 @@ * */ #include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc index 82b5545601..71b06eb87e 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc @@ -17,6 +17,7 @@ */ #include <grpc/support/port_platform.h> + #if GRPC_ARES == 1 && !defined(GRPC_UV) #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index 86d870e0a6..bda9cd1729 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc index a184cf2d57..5096e480bc 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -17,6 +17,7 @@ */ #include <grpc/support/port_platform.h> + #if GRPC_ARES != 1 || defined(GRPC_UV) #include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc index b01e608c3f..4d8958f519 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc @@ -17,6 +17,8 @@ // This is similar to the sockaddr resolver, except that it supports a // bunch of query args that are useful for dependency injection in tests. +#include <grpc/support/port_platform.h> + #include <limits.h> #include <stdbool.h> #include <stdio.h> @@ -24,7 +26,6 @@ #include <string.h> #include <grpc/support/alloc.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/ext/filters/client_channel/lb_policy_factory.h" diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h index d42811d913..858f35851d 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/lib/channel/channel_args.h" diff --git a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc index 966b9fd3f2..f74ac5aebe 100644 --- a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc @@ -16,13 +16,14 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <grpc/support/alloc.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/ext/filters/client_channel/lb_policy_factory.h" diff --git a/src/core/ext/filters/client_channel/resolver_factory.h b/src/core/ext/filters/client_channel/resolver_factory.h index f9b9501236..ee3cfeeb9b 100644 --- a/src/core/ext/filters/client_channel/resolver_factory.h +++ b/src/core/ext/filters/client_channel/resolver_factory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H +#include <grpc/support/port_platform.h> + #include <grpc/support/string_util.h> #include "src/core/ext/filters/client_channel/resolver.h" diff --git a/src/core/ext/filters/client_channel/resolver_registry.cc b/src/core/ext/filters/client_channel/resolver_registry.cc index 036e81d0ae..91c0267f95 100644 --- a/src/core/ext/filters/client_channel/resolver_registry.cc +++ b/src/core/ext/filters/client_channel/resolver_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/resolver_registry.h" #include <string.h> diff --git a/src/core/ext/filters/client_channel/resolver_registry.h b/src/core/ext/filters/client_channel/resolver_registry.h index 260336de83..d6ec6811bd 100644 --- a/src/core/ext/filters/client_channel/resolver_registry.h +++ b/src/core/ext/filters/client_channel/resolver_registry.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/resolver_factory.h" #include "src/core/lib/gprpp/inlined_vector.h" #include "src/core/lib/gprpp/memory.h" diff --git a/src/core/ext/filters/client_channel/retry_throttle.cc b/src/core/ext/filters/client_channel/retry_throttle.cc index a98e27860a..45de6667c8 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.cc +++ b/src/core/ext/filters/client_channel/retry_throttle.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/retry_throttle.h" #include <limits.h> @@ -38,7 +40,7 @@ struct grpc_server_retry_throttle_data { int milli_token_ratio; gpr_atm milli_tokens; // A pointer to the replacement for this grpc_server_retry_throttle_data - // entry. If non-NULL, then this entry is stale and must not be used. + // entry. If non-nullptr, then this entry is stale and must not be used. // We hold a reference to the replacement. gpr_atm replacement; }; @@ -56,6 +58,7 @@ static void get_replacement_throttle_data_if_needed( bool grpc_server_retry_throttle_data_record_failure( grpc_server_retry_throttle_data* throttle_data) { + if (throttle_data == nullptr) return true; // First, check if we are stale and need to be replaced. get_replacement_throttle_data_if_needed(&throttle_data); // We decrement milli_tokens by 1000 (1 token) for each failure. @@ -70,6 +73,7 @@ bool grpc_server_retry_throttle_data_record_failure( void grpc_server_retry_throttle_data_record_success( grpc_server_retry_throttle_data* throttle_data) { + if (throttle_data == nullptr) return; // First, check if we are stale and need to be replaced. get_replacement_throttle_data_if_needed(&throttle_data); // We increment milli_tokens by milli_token_ratio for each success. diff --git a/src/core/ext/filters/client_channel/retry_throttle.h b/src/core/ext/filters/client_channel/retry_throttle.h index bf99297e98..0505fc27f2 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.h +++ b/src/core/ext/filters/client_channel/retry_throttle.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> /// Tracks retry throttling data for an individual server name. diff --git a/src/core/ext/filters/client_channel/status_util.cc b/src/core/ext/filters/client_channel/status_util.cc new file mode 100644 index 0000000000..11f732ab44 --- /dev/null +++ b/src/core/ext/filters/client_channel/status_util.cc @@ -0,0 +1,100 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/ext/filters/client_channel/status_util.h" + +#include "src/core/lib/gpr/useful.h" + +typedef struct { + const char* str; + grpc_status_code status; +} status_string_entry; + +static const status_string_entry g_status_string_entries[] = { + {"OK", GRPC_STATUS_OK}, + {"CANCELLED", GRPC_STATUS_CANCELLED}, + {"UNKNOWN", GRPC_STATUS_UNKNOWN}, + {"INVALID_ARGUMENT", GRPC_STATUS_INVALID_ARGUMENT}, + {"DEADLINE_EXCEEDED", GRPC_STATUS_DEADLINE_EXCEEDED}, + {"NOT_FOUND", GRPC_STATUS_NOT_FOUND}, + {"ALREADY_EXISTS", GRPC_STATUS_ALREADY_EXISTS}, + {"PERMISSION_DENIED", GRPC_STATUS_PERMISSION_DENIED}, + {"UNAUTHENTICATED", GRPC_STATUS_UNAUTHENTICATED}, + {"RESOURCE_EXHAUSTED", GRPC_STATUS_RESOURCE_EXHAUSTED}, + {"FAILED_PRECONDITION", GRPC_STATUS_FAILED_PRECONDITION}, + {"ABORTED", GRPC_STATUS_ABORTED}, + {"OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE}, + {"UNIMPLEMENTED", GRPC_STATUS_UNIMPLEMENTED}, + {"INTERNAL", GRPC_STATUS_INTERNAL}, + {"UNAVAILABLE", GRPC_STATUS_UNAVAILABLE}, + {"DATA_LOSS", GRPC_STATUS_DATA_LOSS}, +}; + +bool grpc_status_code_from_string(const char* status_str, + grpc_status_code* status) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_status_string_entries); ++i) { + if (strcmp(status_str, g_status_string_entries[i].str) == 0) { + *status = g_status_string_entries[i].status; + return true; + } + } + return false; +} + +const char* grpc_status_code_to_string(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return "OK"; + case GRPC_STATUS_CANCELLED: + return "CANCELLED"; + case GRPC_STATUS_UNKNOWN: + return "UNKNOWN"; + case GRPC_STATUS_INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case GRPC_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case GRPC_STATUS_ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case GRPC_STATUS_PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case GRPC_STATUS_UNAUTHENTICATED: + return "UNAUTHENTICATED"; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case GRPC_STATUS_FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case GRPC_STATUS_ABORTED: + return "ABORTED"; + case GRPC_STATUS_OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case GRPC_STATUS_UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case GRPC_STATUS_INTERNAL: + return "INTERNAL"; + case GRPC_STATUS_UNAVAILABLE: + return "UNAVAILABLE"; + case GRPC_STATUS_DATA_LOSS: + return "DATA_LOSS"; + default: + return "UNKNOWN"; + } +} diff --git a/src/core/ext/filters/client_channel/status_util.h b/src/core/ext/filters/client_channel/status_util.h new file mode 100644 index 0000000000..e018709730 --- /dev/null +++ b/src/core/ext/filters/client_channel/status_util.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H + +#include <grpc/support/port_platform.h> + +#include <grpc/status.h> + +#include <stdbool.h> +#include <string.h> + +/// If \a status_str is a valid status string, sets \a status to the +/// corresponding status value and returns true. +bool grpc_status_code_from_string(const char* status_str, + grpc_status_code* status); + +/// Returns the string form of \a status, or "UNKNOWN" if invalid. +const char* grpc_status_code_to_string(grpc_status_code status); + +namespace grpc_core { +namespace internal { + +/// A set of grpc_status_code values. +class StatusCodeSet { + public: + bool Empty() const { return status_code_mask_ == 0; } + + void Add(grpc_status_code status) { status_code_mask_ |= (1 << status); } + + bool Contains(grpc_status_code status) const { + return status_code_mask_ & (1 << status); + } + + private: + int status_code_mask_ = 0; // A bitfield of status codes in the set. +}; + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_STATUS_UTIL_H */ diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index fbe07c58f7..cae7cc35e3 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/subchannel.h" #include <inttypes.h> @@ -657,7 +659,6 @@ static void on_subchannel_connected(void* arg, grpc_error* error) { static void subchannel_call_destroy(void* call, grpc_error* error) { GPR_TIMER_SCOPE("grpc_subchannel_call_unref.destroy", 0); grpc_subchannel_call* c = static_cast<grpc_subchannel_call*>(call); - GPR_ASSERT(c->schedule_closure_after_destroy != nullptr); grpc_core::ConnectedSubchannel* connection = c->connection; grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c), nullptr, c->schedule_closure_after_destroy); @@ -671,9 +672,10 @@ void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call* call, call->schedule_closure_after_destroy = closure; } -void grpc_subchannel_call_ref( +grpc_subchannel_call* grpc_subchannel_call_ref( grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); + return c; } void grpc_subchannel_call_unref( @@ -703,6 +705,13 @@ const grpc_subchannel_key* grpc_subchannel_get_key( return subchannel->key; } +void* grpc_connected_subchannel_call_get_parent_data( + grpc_subchannel_call* subchannel_call) { + grpc_channel_stack* chanstk = subchannel_call->connection->channel_stack(); + return (char*)subchannel_call + sizeof(grpc_subchannel_call) + + chanstk->call_stack_size; +} + grpc_call_stack* grpc_subchannel_call_get_call_stack( grpc_subchannel_call* subchannel_call) { return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); @@ -774,8 +783,8 @@ void ConnectedSubchannel::Ping(grpc_closure* on_initiate, grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args, grpc_subchannel_call** call) { *call = static_cast<grpc_subchannel_call*>(gpr_arena_alloc( - args.arena, - sizeof(grpc_subchannel_call) + channel_stack_->call_stack_size)); + args.arena, sizeof(grpc_subchannel_call) + + channel_stack_->call_stack_size + args.parent_data_size)); grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call); RefCountedPtr<ConnectedSubchannel> connection = Ref(DEBUG_LOCATION, "subchannel_call"); diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h index d2b45ae9c8..e23aec12df 100644 --- a/src/core/ext/filters/client_channel/subchannel.h +++ b/src/core/ext/filters/client_channel/subchannel.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/connector.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/gpr/arena.h" @@ -79,6 +81,7 @@ class ConnectedSubchannel : public RefCountedWithTracing<ConnectedSubchannel> { gpr_arena* arena; grpc_call_context_element* context; grpc_call_combiner* call_combiner; + size_t parent_data_size; }; explicit ConnectedSubchannel(grpc_channel_stack* channel_stack); @@ -107,11 +110,17 @@ grpc_subchannel* grpc_subchannel_weak_ref( grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); void grpc_subchannel_weak_unref( grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_call_ref( +grpc_subchannel_call* grpc_subchannel_call_ref( grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); void grpc_subchannel_call_unref( grpc_subchannel_call* call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +/** Returns a pointer to the parent data associated with \a subchannel_call. + The data will be of the size specified in \a parent_data_size + field of the args passed to \a grpc_connected_subchannel_create_call(). */ +void* grpc_connected_subchannel_call_get_parent_data( + grpc_subchannel_call* subchannel_call); + /** poll the current connectivity state of a channel */ grpc_connectivity_state grpc_subchannel_check_connectivity( grpc_subchannel* channel, grpc_error** error); diff --git a/src/core/ext/filters/client_channel/subchannel_index.cc b/src/core/ext/filters/client_channel/subchannel_index.cc index d1dc5ee970..cb02b1a748 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.cc +++ b/src/core/ext/filters/client_channel/subchannel_index.cc @@ -16,6 +16,8 @@ // // +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/subchannel_index.h" #include <stdbool.h> diff --git a/src/core/ext/filters/client_channel/subchannel_index.h b/src/core/ext/filters/client_channel/subchannel_index.h index bd160a3b13..a7dae9d47d 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.h +++ b/src/core/ext/filters/client_channel/subchannel_index.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/subchannel.h" /** \file Provides an index of active subchannels so that they can be diff --git a/src/core/ext/filters/client_channel/uri_parser.cc b/src/core/ext/filters/client_channel/uri_parser.cc index cd07a6fbf5..0572034a9c 100644 --- a/src/core/ext/filters/client_channel/uri_parser.cc +++ b/src/core/ext/filters/client_channel/uri_parser.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/uri_parser.h" #include <string.h> @@ -23,7 +25,6 @@ #include <grpc/slice_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/lib/gpr/string.h" diff --git a/src/core/ext/filters/client_channel/uri_parser.h b/src/core/ext/filters/client_channel/uri_parser.h index 24ff06c0b5..1966da932b 100644 --- a/src/core/ext/filters/client_channel/uri_parser.h +++ b/src/core/ext/filters/client_channel/uri_parser.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/filters/deadline/deadline_filter.cc b/src/core/ext/filters/deadline/deadline_filter.cc index 76c1204090..dda3b61108 100644 --- a/src/core/ext/filters/deadline/deadline_filter.cc +++ b/src/core/ext/filters/deadline/deadline_filter.cc @@ -14,6 +14,8 @@ // limitations under the License. // +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/deadline/deadline_filter.h" #include <stdbool.h> diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h index 4de817ef54..13207cbd6f 100644 --- a/src/core/ext/filters/deadline/deadline_filter.h +++ b/src/core/ext/filters/deadline/deadline_filter.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H #define GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/iomgr/timer.h" diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc index 80643f8584..ae94ce47b9 100644 --- a/src/core/ext/filters/http/client/http_client_filter.cc +++ b/src/core/ext/filters/http/client/http_client_filter.cc @@ -15,12 +15,16 @@ * */ -#include "src/core/ext/filters/http/client/http_client_filter.h" +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> +#include <stdint.h> #include <string.h> +#include "src/core/ext/filters/http/client/http_client_filter.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/b64.h" #include "src/core/lib/slice/percent_encoding.h" @@ -56,8 +60,9 @@ struct call_data { // State for handling send_message ops. grpc_transport_stream_op_batch* send_message_batch; size_t send_message_bytes_read; - grpc_byte_stream_cache send_message_cache; - grpc_caching_byte_stream send_message_caching_stream; + grpc_core::ManualConstructor<grpc_core::ByteStreamCache> send_message_cache; + grpc_core::ManualConstructor<grpc_core::ByteStreamCache::CachingByteStream> + send_message_caching_stream; grpc_closure on_send_message_next_done; grpc_closure* original_send_message_on_complete; grpc_closure send_message_on_complete; @@ -164,7 +169,7 @@ static void recv_trailing_metadata_on_complete(void* user_data, static void send_message_on_complete(void* arg, grpc_error* error) { grpc_call_element* elem = static_cast<grpc_call_element*>(arg); call_data* calld = static_cast<call_data*>(elem->call_data); - grpc_byte_stream_cache_destroy(&calld->send_message_cache); + calld->send_message_cache.Destroy(); GRPC_CLOSURE_RUN(calld->original_send_message_on_complete, GRPC_ERROR_REF(error)); } @@ -173,8 +178,7 @@ static void send_message_on_complete(void* arg, grpc_error* error) { // calld->send_message_bytes_read. static grpc_error* pull_slice_from_send_message(call_data* calld) { grpc_slice incoming_slice; - grpc_error* error = grpc_byte_stream_pull( - &calld->send_message_caching_stream.base, &incoming_slice); + grpc_error* error = calld->send_message_caching_stream->Pull(&incoming_slice); if (error == GRPC_ERROR_NONE) { calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice); grpc_slice_unref_internal(incoming_slice); @@ -184,24 +188,23 @@ static grpc_error* pull_slice_from_send_message(call_data* calld) { // Reads as many slices as possible from the send_message byte stream. // Upon successful return, if calld->send_message_bytes_read == -// calld->send_message_caching_stream.base.length, then we have completed +// calld->send_message_caching_stream->length(), then we have completed // reading from the byte stream; otherwise, an async read has been dispatched // and on_send_message_next_done() will be invoked when it is complete. static grpc_error* read_all_available_send_message_data(call_data* calld) { - while (grpc_byte_stream_next(&calld->send_message_caching_stream.base, - ~static_cast<size_t>(0), - &calld->on_send_message_next_done)) { + while (calld->send_message_caching_stream->Next( + SIZE_MAX, &calld->on_send_message_next_done)) { grpc_error* error = pull_slice_from_send_message(calld); if (error != GRPC_ERROR_NONE) return error; if (calld->send_message_bytes_read == - calld->send_message_caching_stream.base.length) { + calld->send_message_caching_stream->length()) { break; } } return GRPC_ERROR_NONE; } -// Async callback for grpc_byte_stream_next(). +// Async callback for ByteStream::Next(). static void on_send_message_next_done(void* arg, grpc_error* error) { grpc_call_element* elem = static_cast<grpc_call_element*>(arg); call_data* calld = static_cast<call_data*>(elem->call_data); @@ -220,7 +223,7 @@ static void on_send_message_next_done(void* arg, grpc_error* error) { // here, then we know that all of the data was not available // synchronously, so we were not able to do a cached call. Instead, // we just reset the byte stream and then send down the batch as-is. - grpc_caching_byte_stream_reset(&calld->send_message_caching_stream); + calld->send_message_caching_stream->Reset(); grpc_call_next_op(elem, calld->send_message_batch); } @@ -251,7 +254,7 @@ static grpc_error* update_path_for_get(grpc_call_element* elem, size_t estimated_len = GRPC_SLICE_LENGTH(path_slice); estimated_len++; /* for the '?' */ estimated_len += grpc_base64_estimate_encoded_size( - batch->payload->send_message.send_message->length, true /* url_safe */, + batch->payload->send_message.send_message->length(), true /* url_safe */, false /* multi_line */); grpc_slice path_with_query_slice = GRPC_SLICE_MALLOC(estimated_len); /* memcopy individual pieces into this slice */ @@ -263,9 +266,9 @@ static grpc_error* update_path_for_get(grpc_call_element* elem, write_ptr += GRPC_SLICE_LENGTH(path_slice); *write_ptr++ = '?'; char* payload_bytes = - slice_buffer_to_string(&calld->send_message_cache.cache_buffer); + slice_buffer_to_string(calld->send_message_cache->cache_buffer()); grpc_base64_encode_core(write_ptr, payload_bytes, - batch->payload->send_message.send_message->length, + batch->payload->send_message.send_message->length(), true /* url_safe */, false /* multi_line */); gpr_free(payload_bytes); /* remove trailing unused memory and add trailing 0 to terminate string */ @@ -324,15 +327,14 @@ static void hc_start_transport_stream_op_batch( if (batch->send_message && (batch->payload->send_initial_metadata.send_initial_metadata_flags & GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) && - batch->payload->send_message.send_message->length < + batch->payload->send_message.send_message->length() < channeld->max_payload_size_for_get) { calld->send_message_bytes_read = 0; - grpc_byte_stream_cache_init(&calld->send_message_cache, - batch->payload->send_message.send_message); - grpc_caching_byte_stream_init(&calld->send_message_caching_stream, - &calld->send_message_cache); - batch->payload->send_message.send_message = - &calld->send_message_caching_stream.base; + calld->send_message_cache.Init( + std::move(batch->payload->send_message.send_message)); + calld->send_message_caching_stream.Init(calld->send_message_cache.get()); + batch->payload->send_message.send_message.reset( + calld->send_message_caching_stream.get()); calld->original_send_message_on_complete = batch->on_complete; batch->on_complete = &calld->send_message_on_complete; calld->send_message_batch = batch; @@ -340,12 +342,12 @@ static void hc_start_transport_stream_op_batch( if (error != GRPC_ERROR_NONE) goto done; // If all the data has been read, then we can use GET. if (calld->send_message_bytes_read == - calld->send_message_caching_stream.base.length) { + calld->send_message_caching_stream->length()) { method = GRPC_MDELEM_METHOD_GET; error = update_path_for_get(elem, batch); if (error != GRPC_ERROR_NONE) goto done; batch->send_message = false; - grpc_byte_stream_destroy(&calld->send_message_caching_stream.base); + calld->send_message_caching_stream->Orphan(); } else { // Not all data is available. The batch will be sent down // asynchronously in on_send_message_next_done(). diff --git a/src/core/ext/filters/http/client/http_client_filter.h b/src/core/ext/filters/http/client/http_client_filter.h index ec8177c436..b7cef33f5c 100644 --- a/src/core/ext/filters/http/client/http_client_filter.h +++ b/src/core/ext/filters/http/client/http_client_filter.h @@ -18,6 +18,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H #define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" /* Processes metadata on the client side for HTTP2 transports */ diff --git a/src/core/ext/filters/http/http_filters_plugin.cc b/src/core/ext/filters/http/http_filters_plugin.cc index 56fe1e5c24..f03fa0141d 100644 --- a/src/core/ext/filters/http/http_filters_plugin.cc +++ b/src/core/ext/filters/http/http_filters_plugin.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include "src/core/ext/filters/http/client/http_client_filter.h" diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc index 73220a0ea1..e7d9949386 100644 --- a/src/core/ext/filters/http/message_compress/message_compress_filter.cc +++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <assert.h> #include <string.h> @@ -30,6 +32,7 @@ #include "src/core/lib/compression/compression_internal.h" #include "src/core/lib/compression/message_compress.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" @@ -60,7 +63,8 @@ struct call_data { grpc_closure start_send_message_batch_in_call_combiner; grpc_transport_stream_op_batch* send_message_batch; grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */ - grpc_slice_buffer_stream replacement_stream; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> + replacement_stream; grpc_closure* original_send_message_on_complete; grpc_closure send_message_on_complete; grpc_closure on_send_message_next_done; @@ -218,7 +222,7 @@ static void finish_send_message(grpc_call_element* elem) { grpc_slice_buffer tmp; grpc_slice_buffer_init(&tmp); uint32_t send_flags = - calld->send_message_batch->payload->send_message.send_message->flags; + calld->send_message_batch->payload->send_message.send_message->flags(); bool did_compress = grpc_msg_compress(calld->message_compression_algorithm, &calld->slices, &tmp); if (did_compress) { @@ -251,12 +255,9 @@ static void finish_send_message(grpc_call_element* elem) { grpc_slice_buffer_destroy_internal(&tmp); // Swap out the original byte stream with our new one and send the // batch down. - grpc_byte_stream_destroy( - calld->send_message_batch->payload->send_message.send_message); - grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, - send_flags); - calld->send_message_batch->payload->send_message.send_message = - &calld->replacement_stream.base; + calld->replacement_stream.Init(&calld->slices, send_flags); + calld->send_message_batch->payload->send_message.send_message.reset( + calld->replacement_stream.get()); calld->original_send_message_on_complete = calld->send_message_batch->on_complete; calld->send_message_batch->on_complete = &calld->send_message_on_complete; @@ -276,9 +277,9 @@ static void fail_send_message_batch_in_call_combiner(void* arg, // Pulls a slice from the send_message byte stream and adds it to calld->slices. static grpc_error* pull_slice_from_send_message(call_data* calld) { grpc_slice incoming_slice; - grpc_error* error = grpc_byte_stream_pull( - calld->send_message_batch->payload->send_message.send_message, - &incoming_slice); + grpc_error* error = + calld->send_message_batch->payload->send_message.send_message->Pull( + &incoming_slice); if (error == GRPC_ERROR_NONE) { grpc_slice_buffer_add(&calld->slices, incoming_slice); } @@ -287,12 +288,11 @@ static grpc_error* pull_slice_from_send_message(call_data* calld) { // Reads as many slices as possible from the send_message byte stream. // If all data has been read, invokes finish_send_message(). Otherwise, -// an async call to grpc_byte_stream_next() has been started, which will +// an async call to ByteStream::Next() has been started, which will // eventually result in calling on_send_message_next_done(). static void continue_reading_send_message(grpc_call_element* elem) { call_data* calld = static_cast<call_data*>(elem->call_data); - while (grpc_byte_stream_next( - calld->send_message_batch->payload->send_message.send_message, + while (calld->send_message_batch->payload->send_message.send_message->Next( ~static_cast<size_t>(0), &calld->on_send_message_next_done)) { grpc_error* error = pull_slice_from_send_message(calld); if (error != GRPC_ERROR_NONE) { @@ -301,15 +301,15 @@ static void continue_reading_send_message(grpc_call_element* elem) { GRPC_ERROR_UNREF(error); return; } - if (calld->slices.length == - calld->send_message_batch->payload->send_message.send_message->length) { + if (calld->slices.length == calld->send_message_batch->payload->send_message + .send_message->length()) { finish_send_message(elem); break; } } } -// Async callback for grpc_byte_stream_next(). +// Async callback for ByteStream::Next(). static void on_send_message_next_done(void* arg, grpc_error* error) { grpc_call_element* elem = static_cast<grpc_call_element*>(arg); call_data* calld = static_cast<call_data*>(elem->call_data); @@ -326,7 +326,7 @@ static void on_send_message_next_done(void* arg, grpc_error* error) { return; } if (calld->slices.length == - calld->send_message_batch->payload->send_message.send_message->length) { + calld->send_message_batch->payload->send_message.send_message->length()) { finish_send_message(elem); } else { continue_reading_send_message(elem); @@ -338,7 +338,8 @@ static void start_send_message_batch(void* arg, grpc_error* unused) { call_data* calld = static_cast<call_data*>(elem->call_data); if (skip_compression( elem, - calld->send_message_batch->payload->send_message.send_message->flags, + calld->send_message_batch->payload->send_message.send_message + ->flags(), calld->send_initial_metadata_state == HAS_COMPRESSION_ALGORITHM)) { send_message_batch_continue(elem); } else { @@ -363,9 +364,7 @@ static void compress_start_transport_stream_op_batch( grpc_schedule_on_exec_ctx), GRPC_ERROR_REF(calld->cancel_error), "failing send_message op"); } else { - grpc_byte_stream_shutdown( - - calld->send_message_batch->payload->send_message.send_message, + calld->send_message_batch->payload->send_message.send_message->Shutdown( GRPC_ERROR_REF(calld->cancel_error)); } } diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.h b/src/core/ext/filters/http/message_compress/message_compress_filter.h index 62207911c7..e163e3cf98 100644 --- a/src/core/ext/filters/http/message_compress/message_compress_filter.h +++ b/src/core/ext/filters/http/message_compress/message_compress_filter.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H #define GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/compression_types.h> #include "src/core/lib/channel/channel_stack.h" diff --git a/src/core/ext/filters/http/server/http_server_filter.cc b/src/core/ext/filters/http/server/http_server_filter.cc index aeb0c39df9..c202015875 100644 --- a/src/core/ext/filters/http/server/http_server_filter.cc +++ b/src/core/ext/filters/http/server/http_server_filter.cc @@ -16,11 +16,14 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/http/server/http_server_filter.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <string.h> +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/b64.h" #include "src/core/lib/slice/percent_encoding.h" @@ -51,8 +54,8 @@ struct call_data { */ grpc_closure* recv_message_ready; grpc_closure* on_complete; - grpc_byte_stream** pp_recv_message; - grpc_slice_buffer_stream read_stream; + grpc_core::OrphanablePtr<grpc_core::ByteStream>* pp_recv_message; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> read_stream; /** Receive closures are chained: we inject this closure as the on_done_recv up-call on transport_op, and remember to call our on_done_recv member @@ -230,7 +233,7 @@ static grpc_error* server_filter_incoming_metadata(grpc_call_element* elem, grpc_base64_decode_with_len( reinterpret_cast<const char*> GRPC_SLICE_START_PTR(query_slice), GRPC_SLICE_LENGTH(query_slice), k_url_safe)); - grpc_slice_buffer_stream_init(&calld->read_stream, &read_slice_buffer, 0); + calld->read_stream.Init(&read_slice_buffer, 0); grpc_slice_buffer_destroy_internal(&read_slice_buffer); calld->seen_path_with_query = true; grpc_slice_unref_internal(query_slice); @@ -279,10 +282,10 @@ static void hs_on_complete(void* user_data, grpc_error* err) { call_data* calld = static_cast<call_data*>(elem->call_data); /* Call recv_message_ready if we got the payload via the path field */ if (calld->seen_path_with_query && calld->recv_message_ready != nullptr) { - *calld->pp_recv_message = - calld->payload_bin_delivered - ? nullptr - : reinterpret_cast<grpc_byte_stream*>(&calld->read_stream); + calld->pp_recv_message->reset( + calld->payload_bin_delivered ? nullptr + : reinterpret_cast<grpc_core::ByteStream*>( + calld->read_stream.get())); // Re-enter call combiner for recv_message_ready, since the surface // code will release the call combiner for each callback it receives. GRPC_CALL_COMBINER_START(calld->call_combiner, calld->recv_message_ready, @@ -403,7 +406,7 @@ static void destroy_call_elem(grpc_call_element* elem, grpc_closure* ignored) { call_data* calld = static_cast<call_data*>(elem->call_data); if (calld->seen_path_with_query && !calld->payload_bin_delivered) { - grpc_byte_stream_destroy(&calld->read_stream.base); + calld->read_stream->Orphan(); } } diff --git a/src/core/ext/filters/http/server/http_server_filter.h b/src/core/ext/filters/http/server/http_server_filter.h index c0f678a329..4eb130b1fd 100644 --- a/src/core/ext/filters/http/server/http_server_filter.h +++ b/src/core/ext/filters/http/server/http_server_filter.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H #define GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" /* Processes metadata on the client side for HTTP2 transports */ diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc index 79b144a6eb..0d349e2a89 100644 --- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc +++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/load_reporting.h> diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.h b/src/core/ext/filters/load_reporting/server_load_reporting_filter.h index 1baee5e7cd..b459a8ec5f 100644 --- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.h +++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H #define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" #include "src/core/lib/channel/channel_stack.h" diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h index 4b694d336d..c20aaa744f 100644 --- a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h +++ b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_PLUGIN_H #define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_PLUGIN_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/channel/channel_stack.h" diff --git a/src/core/ext/filters/max_age/max_age_filter.cc b/src/core/ext/filters/max_age/max_age_filter.cc index abb9d69036..1fe8288bd0 100644 --- a/src/core/ext/filters/max_age/max_age_filter.cc +++ b/src/core/ext/filters/max_age/max_age_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/max_age/max_age_filter.h" #include <limits.h> @@ -368,6 +370,9 @@ static void channel_connectivity_changed(void* arg, grpc_error* error) { max_idle_timer, and prevent max_idle_timer from being started in the future. */ increase_call_count(chand); + if (gpr_atm_acq_load(&chand->idle_state) == MAX_IDLE_STATE_SEEN_EXIT_IDLE) { + grpc_timer_cancel(&chand->max_idle_timer); + } } } diff --git a/src/core/ext/filters/max_age/max_age_filter.h b/src/core/ext/filters/max_age/max_age_filter.h index 68fb4a4ca5..989322244f 100644 --- a/src/core/ext/filters/max_age/max_age_filter.h +++ b/src/core/ext/filters/max_age/max_age_filter.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H #define GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" extern const grpc_channel_filter grpc_max_age_filter; diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc index 0fb3935609..c7fc3f2e62 100644 --- a/src/core/ext/filters/message_size/message_size_filter.cc +++ b/src/core/ext/filters/message_size/message_size_filter.cc @@ -14,6 +14,8 @@ // limitations under the License. // +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/message_size/message_size_filter.h" #include <limits.h> @@ -27,6 +29,8 @@ #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/surface/channel_init.h" #include "src/core/lib/transport/service_config.h" @@ -35,27 +39,29 @@ typedef struct { int max_recv_size; } message_size_limits; -typedef struct { - gpr_refcount refs; - message_size_limits limits; -} refcounted_message_size_limits; +namespace grpc_core { +namespace { -static void* refcounted_message_size_limits_ref(void* value) { - refcounted_message_size_limits* limits = - static_cast<refcounted_message_size_limits*>(value); - gpr_ref(&limits->refs); - return value; -} +class MessageSizeLimits : public RefCounted<MessageSizeLimits> { + public: + static RefCountedPtr<MessageSizeLimits> CreateFromJson(const grpc_json* json); + + const message_size_limits& limits() const { return limits_; } + + private: + // So New() can call our private ctor. + template <typename T, typename... Args> + friend T* grpc_core::New(Args&&... args); -static void refcounted_message_size_limits_unref(void* value) { - refcounted_message_size_limits* limits = - static_cast<refcounted_message_size_limits*>(value); - if (gpr_unref(&limits->refs)) { - gpr_free(value); + MessageSizeLimits(int max_send_size, int max_recv_size) { + limits_.max_send_size = max_send_size; + limits_.max_recv_size = max_recv_size; } -} -static void* refcounted_message_size_limits_create_from_json( + message_size_limits limits_; +}; + +RefCountedPtr<MessageSizeLimits> MessageSizeLimits::CreateFromJson( const grpc_json* json) { int max_request_message_bytes = -1; int max_response_message_bytes = -1; @@ -77,16 +83,15 @@ static void* refcounted_message_size_limits_create_from_json( if (max_response_message_bytes == -1) return nullptr; } } - refcounted_message_size_limits* value = - static_cast<refcounted_message_size_limits*>( - gpr_malloc(sizeof(refcounted_message_size_limits))); - gpr_ref_init(&value->refs, 1); - value->limits.max_send_size = max_request_message_bytes; - value->limits.max_recv_size = max_response_message_bytes; - return value; + return MakeRefCounted<MessageSizeLimits>(max_request_message_bytes, + max_response_message_bytes); } +} // namespace +} // namespace grpc_core + namespace { + struct call_data { grpc_call_combiner* call_combiner; message_size_limits limits; @@ -95,7 +100,7 @@ struct call_data { // call our next_recv_message_ready member after handling it. grpc_closure recv_message_ready; // Used by recv_message_ready. - grpc_byte_stream** recv_message; + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message; // Original recv_message_ready callback, invoked after our own. grpc_closure* next_recv_message_ready; }; @@ -103,8 +108,11 @@ struct call_data { struct channel_data { message_size_limits limits; // Maps path names to refcounted_message_size_limits structs. - grpc_slice_hash_table* method_limit_table; + grpc_core::RefCountedPtr<grpc_core::SliceHashTable< + grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits>>> + method_limit_table; }; + } // namespace // Callback invoked when we receive a message. Here we check the max @@ -113,12 +121,12 @@ static void recv_message_ready(void* user_data, grpc_error* error) { grpc_call_element* elem = static_cast<grpc_call_element*>(user_data); call_data* calld = static_cast<call_data*>(elem->call_data); if (*calld->recv_message != nullptr && calld->limits.max_recv_size >= 0 && - (*calld->recv_message)->length > + (*calld->recv_message)->length() > static_cast<size_t>(calld->limits.max_recv_size)) { char* message_string; gpr_asprintf(&message_string, "Received message larger than max (%u vs. %d)", - (*calld->recv_message)->length, calld->limits.max_recv_size); + (*calld->recv_message)->length(), calld->limits.max_recv_size); grpc_error* new_error = grpc_error_set_int( GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED); @@ -142,11 +150,11 @@ static void start_transport_stream_op_batch( call_data* calld = static_cast<call_data*>(elem->call_data); // Check max send message size. if (op->send_message && calld->limits.max_send_size >= 0 && - op->payload->send_message.send_message->length > + op->payload->send_message.send_message->length() > static_cast<size_t>(calld->limits.max_send_size)) { char* message_string; gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)", - op->payload->send_message.send_message->length, + op->payload->send_message.send_message->length(), calld->limits.max_send_size); grpc_transport_stream_op_batch_finish_with_failure( op, @@ -183,20 +191,19 @@ static grpc_error* init_call_elem(grpc_call_element* elem, // size to the receive limit. calld->limits = chand->limits; if (chand->method_limit_table != nullptr) { - refcounted_message_size_limits* limits = - static_cast<refcounted_message_size_limits*>( - grpc_method_config_table_get(chand->method_limit_table, - args->path)); + grpc_core::RefCountedPtr<grpc_core::MessageSizeLimits> limits = + grpc_core::ServiceConfig::MethodConfigTableLookup( + *chand->method_limit_table, args->path); if (limits != nullptr) { - if (limits->limits.max_send_size >= 0 && - (limits->limits.max_send_size < calld->limits.max_send_size || + if (limits->limits().max_send_size >= 0 && + (limits->limits().max_send_size < calld->limits.max_send_size || calld->limits.max_send_size < 0)) { - calld->limits.max_send_size = limits->limits.max_send_size; + calld->limits.max_send_size = limits->limits().max_send_size; } - if (limits->limits.max_recv_size >= 0 && - (limits->limits.max_recv_size < calld->limits.max_recv_size || + if (limits->limits().max_recv_size >= 0 && + (limits->limits().max_recv_size < calld->limits.max_recv_size || calld->limits.max_recv_size < 0)) { - calld->limits.max_recv_size = limits->limits.max_recv_size; + calld->limits.max_recv_size = limits->limits().max_recv_size; } } } @@ -251,15 +258,11 @@ static grpc_error* init_channel_elem(grpc_channel_element* elem, grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); const char* service_config_str = grpc_channel_arg_get_string(channel_arg); if (service_config_str != nullptr) { - grpc_service_config* service_config = - grpc_service_config_create(service_config_str); + grpc_core::UniquePtr<grpc_core::ServiceConfig> service_config = + grpc_core::ServiceConfig::Create(service_config_str); if (service_config != nullptr) { - chand->method_limit_table = - grpc_service_config_create_method_config_table( - service_config, refcounted_message_size_limits_create_from_json, - refcounted_message_size_limits_ref, - refcounted_message_size_limits_unref); - grpc_service_config_destroy(service_config); + chand->method_limit_table = service_config->CreateMethodConfigTable( + grpc_core::MessageSizeLimits::CreateFromJson); } } return GRPC_ERROR_NONE; @@ -268,7 +271,7 @@ static grpc_error* init_channel_elem(grpc_channel_element* elem, // Destructor for channel_data. static void destroy_channel_elem(grpc_channel_element* elem) { channel_data* chand = static_cast<channel_data*>(elem->channel_data); - grpc_slice_hash_table_unref(chand->method_limit_table); + chand->method_limit_table.reset(); } const grpc_channel_filter grpc_message_size_filter = { diff --git a/src/core/ext/filters/message_size/message_size_filter.h b/src/core/ext/filters/message_size/message_size_filter.h index d3667f7003..f66636e583 100644 --- a/src/core/ext/filters/message_size/message_size_filter.h +++ b/src/core/ext/filters/message_size/message_size_filter.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H #define GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" extern const grpc_channel_filter grpc_message_size_filter; diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc index 3092ed2056..c7070d4d9b 100644 --- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc @@ -14,6 +14,8 @@ // limitations under the License. // +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" #include <string.h> @@ -91,7 +93,9 @@ static void start_transport_stream_op_batch( /* Send message happens after client's user-agent (initial metadata) is * received, so workaround_active must be set already */ if (calld->workaround_active) { - op->payload->send_message.send_message->flags |= GRPC_WRITE_NO_COMPRESS; + op->payload->send_message.send_message->set_flags( + op->payload->send_message.send_message->flags() | + GRPC_WRITE_NO_COMPRESS); } } diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h index 9dae4f0734..94d20f0c4a 100644 --- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H #define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" extern const grpc_channel_filter grpc_workaround_cronet_compression_filter; diff --git a/src/core/ext/filters/workarounds/workaround_utils.cc b/src/core/ext/filters/workarounds/workaround_utils.cc index 850ed75ec9..4dabe896d3 100644 --- a/src/core/ext/filters/workarounds/workaround_utils.cc +++ b/src/core/ext/filters/workarounds/workaround_utils.cc @@ -14,6 +14,8 @@ // limitations under the License. // +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/workarounds/workaround_utils.h" #include <grpc/support/alloc.h> diff --git a/src/core/ext/filters/workarounds/workaround_utils.h b/src/core/ext/filters/workarounds/workaround_utils.h index d6ef5e84fa..f172ccc078 100644 --- a/src/core/ext/filters/workarounds/workaround_utils.h +++ b/src/core/ext/filters/workarounds/workaround_utils.h @@ -17,6 +17,8 @@ #ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H #define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H +#include <grpc/support/port_platform.h> + #include <grpc/support/workaround_list.h> #include "src/core/lib/transport/metadata.h" diff --git a/src/core/ext/transport/chttp2/alpn/alpn.cc b/src/core/ext/transport/chttp2/alpn/alpn.cc index 5c4bfd03fa..1fdab76dbf 100644 --- a/src/core/ext/transport/chttp2/alpn/alpn.cc +++ b/src/core/ext/transport/chttp2/alpn/alpn.cc @@ -16,8 +16,10 @@ * */ -#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> +#include "src/core/ext/transport/chttp2/alpn/alpn.h" #include "src/core/lib/gpr/useful.h" diff --git a/src/core/ext/transport/chttp2/alpn/alpn.h b/src/core/ext/transport/chttp2/alpn/alpn.h index fd7513c665..0042eafd95 100644 --- a/src/core/ext/transport/chttp2/alpn/alpn.h +++ b/src/core/ext/transport/chttp2/alpn/alpn.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H +#include <grpc/support/port_platform.h> + #include <string.h> /* Retuns 1 if the version is supported, 0 otherwise. */ diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc index 7eb9ebc27e..e7522ffba8 100644 --- a/src/core/ext/transport/chttp2/client/chttp2_connector.cc +++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/client/chttp2_connector.h" #include <grpc/grpc.h> diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.h b/src/core/ext/transport/chttp2/client/chttp2_connector.h index e258892cfc..04da441309 100644 --- a/src/core/ext/transport/chttp2/client/chttp2_connector.h +++ b/src/core/ext/transport/chttp2/client/chttp2_connector.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H +#include <grpc/support/port_platform.h> + #include "src/core/ext/filters/client_channel/connector.h" grpc_connector* grpc_chttp2_connector_create(); diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc index ef1d3fb53b..60800365b8 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.cc +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <string.h> diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc index 0cdea5a94e..479f0da572 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc @@ -16,10 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/grpc_posix.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #ifdef GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc index f3cc16d8cc..a82009ff69 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <string.h> @@ -28,10 +30,11 @@ #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/ext/transport/chttp2/client/chttp2_connector.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/security/credentials/credentials.h" #include "src/core/lib/security/security_connector/security_connector.h" -#include "src/core/lib/security/transport/lb_targets_info.h" +#include "src/core/lib/security/transport/target_authority_table.h" #include "src/core/lib/slice/slice_hash_table.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/surface/api_trace.h" @@ -71,11 +74,11 @@ static grpc_subchannel_args* get_secure_naming_subchannel_args( const char* server_uri_path; server_uri_path = server_uri->path[0] == '/' ? server_uri->path + 1 : server_uri->path; - const grpc_slice_hash_table* targets_info = - grpc_lb_targets_info_find_in_args(args->args); - char* target_name_to_check = nullptr; - if (targets_info != nullptr) { // LB channel - // Find the balancer name for the target. + const grpc_core::TargetAuthorityTable* target_authority_table = + grpc_core::FindTargetAuthorityTableInArgs(args->args); + grpc_core::UniquePtr<char> authority; + if (target_authority_table != nullptr) { + // Find the authority for the target. const char* target_uri_str = grpc_get_subchannel_address_uri_arg(args->args); grpc_uri* target_uri = @@ -84,37 +87,33 @@ static grpc_subchannel_args* get_secure_naming_subchannel_args( if (target_uri->path[0] != '\0') { // "path" may be empty const grpc_slice key = grpc_slice_from_static_string( target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path); - const char* value = static_cast<const char*>( - grpc_slice_hash_table_get(targets_info, key)); - if (value != nullptr) target_name_to_check = gpr_strdup(value); + const grpc_core::UniquePtr<char>* value = + target_authority_table->Get(key); + if (value != nullptr) authority.reset(gpr_strdup(value->get())); grpc_slice_unref_internal(key); } - if (target_name_to_check == nullptr) { - // If the target name to check hasn't already been set, fall back to using - // SERVER_URI - target_name_to_check = gpr_strdup(server_uri_path); - } grpc_uri_destroy(target_uri); - } else { // regular channel: the secure name is the original server URI. - target_name_to_check = gpr_strdup(server_uri_path); + } + // If the authority hasn't already been set (either because no target + // authority table was present or because the target was not present + // in the table), fall back to using the original server URI. + if (authority == nullptr) { + authority.reset(gpr_strdup(server_uri_path)); } grpc_uri_destroy(server_uri); - GPR_ASSERT(target_name_to_check != nullptr); grpc_channel_security_connector* subchannel_security_connector = nullptr; // Create the security connector using the credentials and target name. grpc_channel_args* new_args_from_connector = nullptr; const grpc_security_status security_status = grpc_channel_credentials_create_security_connector( - channel_credentials, target_name_to_check, args->args, + channel_credentials, authority.get(), args->args, &subchannel_security_connector, &new_args_from_connector); if (security_status != GRPC_SECURITY_OK) { gpr_log(GPR_ERROR, "Failed to create secure subchannel for secure name '%s'", - target_name_to_check); - gpr_free(target_name_to_check); + authority.get()); return nullptr; } - gpr_free(target_name_to_check); grpc_arg new_security_connector_arg = grpc_security_connector_to_arg(&subchannel_security_connector->base); diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc index 90b2ee1af9..687cc483f6 100644 --- a/src/core/ext/transport/chttp2/server/chttp2_server.cc +++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/server/chttp2_server.h" #include <grpc/grpc.h> diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.h b/src/core/ext/transport/chttp2/server/chttp2_server.h index 7de859da42..7b41972160 100644 --- a/src/core/ext/transport/chttp2/server/chttp2_server.h +++ b/src/core/ext/transport/chttp2/server/chttp2_server.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc index 52c42d056c..99f18cdf39 100644 --- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc +++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/support/log.h> @@ -39,6 +41,5 @@ int grpc_server_add_insecure_http2_port(grpc_server* server, const char* addr) { GRPC_ERROR_UNREF(err); } - return port_num; } diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc index dafd4af6ce..371e463814 100644 --- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc +++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc @@ -16,10 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/grpc_posix.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #ifdef GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc index 723af97ff0..6689a17da6 100644 --- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc +++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <string.h> diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/src/core/ext/transport/chttp2/transport/bin_decoder.cc index 831a36961e..f0f32da028 100644 --- a/src/core/ext/transport/chttp2/transport/bin_decoder.cc +++ b/src/core/ext/transport/chttp2/transport/bin_decoder.cc @@ -16,9 +16,11 @@ * */ -#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.h b/src/core/ext/transport/chttp2/transport/bin_decoder.h index a0d74fb20d..8a4d4a7179 100644 --- a/src/core/ext/transport/chttp2/transport/bin_decoder.h +++ b/src/core/ext/transport/chttp2/transport/bin_decoder.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <stdbool.h> diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/src/core/ext/transport/chttp2/transport/bin_encoder.cc index e3b8adbe73..bad29e3421 100644 --- a/src/core/ext/transport/chttp2/transport/bin_encoder.cc +++ b/src/core/ext/transport/chttp2/transport/bin_encoder.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/bin_encoder.h" #include <string.h> diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.h b/src/core/ext/transport/chttp2/transport/bin_encoder.h index 93ad0dfdea..1b7bb1574a 100644 --- a/src/core/ext/transport/chttp2/transport/bin_encoder.h +++ b/src/core/ext/transport/chttp2/transport/bin_encoder.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> /* base64 encode a slice. Returns a new slice, does not take ownership of the diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc index a69908116a..531ea73e9e 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gpr/env.h" diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 2fc3c4fa41..a4d616d778 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -16,10 +16,10 @@ * */ -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" - #include <grpc/support/port_platform.h> +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" + #include <inttypes.h> #include <limits.h> #include <math.h> @@ -39,6 +39,7 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/http/parser.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/timer.h" @@ -117,12 +118,6 @@ static void connectivity_state_set(grpc_chttp2_transport* t, grpc_connectivity_state state, grpc_error* error, const char* reason); -static void incoming_byte_stream_destroy_locked(void* byte_stream, - grpc_error* error_ignored); -static void incoming_byte_stream_publish_error( - grpc_chttp2_incoming_byte_stream* bs, grpc_error* error); -static void incoming_byte_stream_unref(grpc_chttp2_incoming_byte_stream* bs); - static void benign_reclaimer_locked(void* t, grpc_error* error); static void destructive_reclaimer_locked(void* t, grpc_error* error); @@ -662,8 +657,8 @@ static int init_stream(grpc_transport* gt, grpc_stream* gs, s->t = t; s->refcount = refcount; /* We reserve one 'active stream' that's dropped when the stream is - read-closed. The others are for incoming_byte_streams that are actively - reading */ + read-closed. The others are for Chttp2IncomingByteStreams that are + actively reading */ GRPC_CHTTP2_STREAM_REF(s, "chttp2"); grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[0], arena); @@ -1256,8 +1251,7 @@ static void continue_fetching_send_locked(grpc_chttp2_transport* t, abort(); /* TODO(ctiller): what cleanup here? */ return; /* early out */ } - if (s->fetched_send_message_length == s->fetching_send_message->length) { - grpc_byte_stream_destroy(s->fetching_send_message); + if (s->fetched_send_message_length == s->fetching_send_message->length()) { int64_t notify_offset = s->next_message_end_offset; if (notify_offset <= s->flow_controlled_bytes_written) { grpc_chttp2_complete_closure_step( @@ -1274,20 +1268,19 @@ static void continue_fetching_send_locked(grpc_chttp2_transport* t, cb->closure = s->fetching_send_message_finished; s->fetching_send_message_finished = nullptr; grpc_chttp2_write_cb** list = - s->fetching_send_message->flags & GRPC_WRITE_THROUGH + s->fetching_send_message->flags() & GRPC_WRITE_THROUGH ? &s->on_write_finished_cbs : &s->on_flow_controlled_cbs; cb->next = *list; *list = cb; } - s->fetching_send_message = nullptr; + s->fetching_send_message.reset(); return; /* early out */ - } else if (grpc_byte_stream_next(s->fetching_send_message, UINT32_MAX, - &s->complete_fetch_locked)) { - grpc_error* error = - grpc_byte_stream_pull(s->fetching_send_message, &s->fetching_slice); + } else if (s->fetching_send_message->Next(UINT32_MAX, + &s->complete_fetch_locked)) { + grpc_error* error = s->fetching_send_message->Pull(&s->fetching_slice); if (error != GRPC_ERROR_NONE) { - grpc_byte_stream_destroy(s->fetching_send_message); + s->fetching_send_message.reset(); grpc_chttp2_cancel_stream(t, s, error); } else { add_fetched_slice_locked(t, s); @@ -1300,14 +1293,14 @@ static void complete_fetch_locked(void* gs, grpc_error* error) { grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(gs); grpc_chttp2_transport* t = s->t; if (error == GRPC_ERROR_NONE) { - error = grpc_byte_stream_pull(s->fetching_send_message, &s->fetching_slice); + error = s->fetching_send_message->Pull(&s->fetching_slice); if (error == GRPC_ERROR_NONE) { add_fetched_slice_locked(t, s); continue_fetching_send_locked(t, s); } } if (error != GRPC_ERROR_NONE) { - grpc_byte_stream_destroy(s->fetching_send_message); + s->fetching_send_message.reset(); grpc_chttp2_cancel_stream(t, s, error); } } @@ -1439,7 +1432,7 @@ static void perform_stream_op_locked(void* stream_op, GPR_ASSERT(s->id != 0); grpc_chttp2_mark_stream_writable(t, s); if (!(op->send_message && - (op->payload->send_message.send_message->flags & + (op->payload->send_message.send_message->flags() & GRPC_WRITE_BUFFER_HINT))) { grpc_chttp2_initiate_write( t, GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA); @@ -1456,15 +1449,17 @@ static void perform_stream_op_locked(void* stream_op, } } if (op_payload->send_initial_metadata.peer_string != nullptr) { - gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string, - (gpr_atm)gpr_strdup(t->peer_string)); + char* old_peer_string = (char*)gpr_atm_full_xchg( + op_payload->send_initial_metadata.peer_string, + (gpr_atm)gpr_strdup(t->peer_string)); + gpr_free(old_peer_string); } } if (op->send_message) { GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE(); GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE( - op->payload->send_message.send_message->length); + op->payload->send_message.send_message->length()); on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; s->fetching_send_message_finished = add_closure_barrier(op->on_complete); if (s->write_closed) { @@ -1473,7 +1468,7 @@ static void perform_stream_op_locked(void* stream_op, // streaming call might send another message before getting a // recv_message failure, breaking out of its loop, and then // starting recv_trailing_metadata. - grpc_byte_stream_destroy(op->payload->send_message.send_message); + op->payload->send_message.send_message.reset(); grpc_chttp2_complete_closure_step( t, s, &s->fetching_send_message_finished, t->is_client && s->received_trailing_metadata @@ -1486,14 +1481,15 @@ static void perform_stream_op_locked(void* stream_op, GPR_ASSERT(s->fetching_send_message == nullptr); uint8_t* frame_hdr = grpc_slice_buffer_tiny_add( &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES); - uint32_t flags = op_payload->send_message.send_message->flags; + uint32_t flags = op_payload->send_message.send_message->flags(); frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; - size_t len = op_payload->send_message.send_message->length; + size_t len = op_payload->send_message.send_message->length(); frame_hdr[1] = static_cast<uint8_t>(len >> 24); frame_hdr[2] = static_cast<uint8_t>(len >> 16); frame_hdr[3] = static_cast<uint8_t>(len >> 8); frame_hdr[4] = static_cast<uint8_t>(len); - s->fetching_send_message = op_payload->send_message.send_message; + s->fetching_send_message = + std::move(op_payload->send_message.send_message); s->fetched_send_message_length = 0; s->next_message_end_offset = s->flow_controlled_bytes_written + @@ -1571,8 +1567,10 @@ static void perform_stream_op_locked(void* stream_op, s->trailing_metadata_available = op_payload->recv_initial_metadata.trailing_metadata_available; if (op_payload->recv_initial_metadata.peer_string != nullptr) { - gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string, - (gpr_atm)gpr_strdup(t->peer_string)); + char* old_peer_string = (char*)gpr_atm_full_xchg( + op_payload->recv_initial_metadata.peer_string, + (gpr_atm)gpr_strdup(t->peer_string)); + gpr_free(old_peer_string); } grpc_chttp2_maybe_complete_recv_initial_metadata(t, s); } @@ -1707,7 +1705,6 @@ static void send_goaway(grpc_chttp2_transport* t, grpc_error* error) { } void grpc_chttp2_add_ping_strike(grpc_chttp2_transport* t) { - t->ping_recv_state.ping_strikes++; if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes && t->ping_policy.max_ping_strikes != 0) { send_goaway(t, @@ -1944,12 +1941,12 @@ static void remove_stream(grpc_chttp2_transport* t, uint32_t id, } if (s->pending_byte_stream) { if (s->on_next != nullptr) { - grpc_chttp2_incoming_byte_stream* bs = s->data_parser.parsing_frame; + grpc_core::Chttp2IncomingByteStream* bs = s->data_parser.parsing_frame; if (error == GRPC_ERROR_NONE) { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); } - incoming_byte_stream_publish_error(bs, error); - incoming_byte_stream_unref(bs); + bs->PublishError(error); + bs->Unref(); s->data_parser.parsing_frame = nullptr; } else { GRPC_ERROR_UNREF(s->byte_stream_error); @@ -2093,10 +2090,7 @@ void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t, GRPC_ERROR_REF(error), "send_trailing_metadata_finished"); - if (s->fetching_send_message != nullptr) { - grpc_byte_stream_destroy(s->fetching_send_message); - s->fetching_send_message = nullptr; - } + s->fetching_send_message.reset(); grpc_chttp2_complete_closure_step(t, s, &s->fetching_send_message_finished, GRPC_ERROR_REF(error), "fetching_send_message_finished"); @@ -2712,7 +2706,6 @@ static void set_pollset_set(grpc_transport* gt, grpc_stream* gs, static void reset_byte_stream(void* arg, grpc_error* error) { grpc_chttp2_stream* s = static_cast<grpc_chttp2_stream*>(arg); - s->pending_byte_stream = false; if (error == GRPC_ERROR_NONE) { grpc_chttp2_maybe_complete_recv_message(s->t, s); @@ -2728,22 +2721,56 @@ static void reset_byte_stream(void* arg, grpc_error* error) { } } -static void incoming_byte_stream_unref(grpc_chttp2_incoming_byte_stream* bs) { - if (gpr_unref(&bs->refs)) { - gpr_free(bs); +namespace grpc_core { + +Chttp2IncomingByteStream::Chttp2IncomingByteStream( + grpc_chttp2_transport* transport, grpc_chttp2_stream* stream, + uint32_t frame_size, uint32_t flags) + : ByteStream(frame_size, flags), + transport_(transport), + stream_(stream), + remaining_bytes_(frame_size) { + gpr_ref_init(&refs_, 2); + GRPC_ERROR_UNREF(stream->byte_stream_error); + stream->byte_stream_error = GRPC_ERROR_NONE; +} + +void Chttp2IncomingByteStream::OrphanLocked(void* arg, + grpc_error* error_ignored) { + Chttp2IncomingByteStream* bs = static_cast<Chttp2IncomingByteStream*>(arg); + grpc_chttp2_stream* s = bs->stream_; + grpc_chttp2_transport* t = s->t; + bs->Unref(); + s->pending_byte_stream = false; + grpc_chttp2_maybe_complete_recv_message(t, s); + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); +} + +void Chttp2IncomingByteStream::Orphan() { + GPR_TIMER_SCOPE("incoming_byte_stream_destroy", 0); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&destroy_action_, + &Chttp2IncomingByteStream::OrphanLocked, this, + grpc_combiner_scheduler(transport_->combiner)), + GRPC_ERROR_NONE); +} + +void Chttp2IncomingByteStream::Unref() { + if (gpr_unref(&refs_)) { + Delete(this); } } -static void incoming_byte_stream_next_locked(void* argp, - grpc_error* error_ignored) { - grpc_chttp2_incoming_byte_stream* bs = - static_cast<grpc_chttp2_incoming_byte_stream*>(argp); - grpc_chttp2_transport* t = bs->transport; - grpc_chttp2_stream* s = bs->stream; +void Chttp2IncomingByteStream::Ref() { gpr_ref(&refs_); } +void Chttp2IncomingByteStream::NextLocked(void* arg, + grpc_error* error_ignored) { + Chttp2IncomingByteStream* bs = static_cast<Chttp2IncomingByteStream*>(arg); + grpc_chttp2_transport* t = bs->transport_; + grpc_chttp2_stream* s = bs->stream_; size_t cur_length = s->frame_storage.length; if (!s->read_closed) { - s->flow_control->IncomingByteStreamUpdate(bs->next_action.max_size_hint, + s->flow_control->IncomingByteStreamUpdate(bs->next_action_.max_size_hint, cur_length); grpc_chttp2_act_on_flowctl_action(s->flow_control->MakeAction(), t, s); } @@ -2752,22 +2779,22 @@ static void incoming_byte_stream_next_locked(void* argp, grpc_slice_buffer_swap(&s->frame_storage, &s->unprocessed_incoming_frames_buffer); s->unprocessed_incoming_frames_decompressed = false; - GRPC_CLOSURE_SCHED(bs->next_action.on_complete, GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, GRPC_ERROR_NONE); } else if (s->byte_stream_error != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(bs->next_action.on_complete, + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, GRPC_ERROR_REF(s->byte_stream_error)); if (s->data_parser.parsing_frame != nullptr) { - incoming_byte_stream_unref(s->data_parser.parsing_frame); + s->data_parser.parsing_frame->Unref(); s->data_parser.parsing_frame = nullptr; } } else if (s->read_closed) { - if (bs->remaining_bytes != 0) { + if (bs->remaining_bytes_ != 0) { s->byte_stream_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - GRPC_CLOSURE_SCHED(bs->next_action.on_complete, + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, GRPC_ERROR_REF(s->byte_stream_error)); if (s->data_parser.parsing_frame != nullptr) { - incoming_byte_stream_unref(s->data_parser.parsing_frame); + s->data_parser.parsing_frame->Unref(); s->data_parser.parsing_frame = nullptr; } } else { @@ -2775,122 +2802,94 @@ static void incoming_byte_stream_next_locked(void* argp, GPR_ASSERT(false); } } else { - s->on_next = bs->next_action.on_complete; + s->on_next = bs->next_action_.on_complete; } - incoming_byte_stream_unref(bs); + bs->Unref(); } -static bool incoming_byte_stream_next(grpc_byte_stream* byte_stream, - size_t max_size_hint, - grpc_closure* on_complete) { +bool Chttp2IncomingByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { GPR_TIMER_SCOPE("incoming_byte_stream_next", 0); - grpc_chttp2_incoming_byte_stream* bs = - reinterpret_cast<grpc_chttp2_incoming_byte_stream*>(byte_stream); - grpc_chttp2_stream* s = bs->stream; - if (s->unprocessed_incoming_frames_buffer.length > 0) { + if (stream_->unprocessed_incoming_frames_buffer.length > 0) { return true; } else { - gpr_ref(&bs->refs); - bs->next_action.max_size_hint = max_size_hint; - bs->next_action.on_complete = on_complete; + Ref(); + next_action_.max_size_hint = max_size_hint; + next_action_.on_complete = on_complete; GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&bs->next_action.closure, - incoming_byte_stream_next_locked, bs, - grpc_combiner_scheduler(bs->transport->combiner)), + GRPC_CLOSURE_INIT(&next_action_.closure, + &Chttp2IncomingByteStream::NextLocked, this, + grpc_combiner_scheduler(transport_->combiner)), GRPC_ERROR_NONE); return false; } } -static grpc_error* incoming_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { +grpc_error* Chttp2IncomingByteStream::Pull(grpc_slice* slice) { GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0); - grpc_chttp2_incoming_byte_stream* bs = - reinterpret_cast<grpc_chttp2_incoming_byte_stream*>(byte_stream); - grpc_chttp2_stream* s = bs->stream; grpc_error* error; - - if (s->unprocessed_incoming_frames_buffer.length > 0) { - if (!s->unprocessed_incoming_frames_decompressed) { + if (stream_->unprocessed_incoming_frames_buffer.length > 0) { + if (!stream_->unprocessed_incoming_frames_decompressed) { bool end_of_context; - if (!s->stream_decompression_ctx) { - s->stream_decompression_ctx = grpc_stream_compression_context_create( - s->stream_decompression_method); + if (!stream_->stream_decompression_ctx) { + stream_->stream_decompression_ctx = + grpc_stream_compression_context_create( + stream_->stream_decompression_method); } - if (!grpc_stream_decompress(s->stream_decompression_ctx, - &s->unprocessed_incoming_frames_buffer, - &s->decompressed_data_buffer, nullptr, + if (!grpc_stream_decompress(stream_->stream_decompression_ctx, + &stream_->unprocessed_incoming_frames_buffer, + &stream_->decompressed_data_buffer, nullptr, MAX_SIZE_T, &end_of_context)) { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error."); return error; } - GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); - grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, - &s->decompressed_data_buffer); - s->unprocessed_incoming_frames_decompressed = true; + GPR_ASSERT(stream_->unprocessed_incoming_frames_buffer.length == 0); + grpc_slice_buffer_swap(&stream_->unprocessed_incoming_frames_buffer, + &stream_->decompressed_data_buffer); + stream_->unprocessed_incoming_frames_decompressed = true; if (end_of_context) { - grpc_stream_compression_context_destroy(s->stream_decompression_ctx); - s->stream_decompression_ctx = nullptr; + grpc_stream_compression_context_destroy( + stream_->stream_decompression_ctx); + stream_->stream_decompression_ctx = nullptr; } - if (s->unprocessed_incoming_frames_buffer.length == 0) { + if (stream_->unprocessed_incoming_frames_buffer.length == 0) { *slice = grpc_empty_slice(); } } error = grpc_deframe_unprocessed_incoming_frames( - &s->data_parser, s, &s->unprocessed_incoming_frames_buffer, slice, - nullptr); + &stream_->data_parser, stream_, + &stream_->unprocessed_incoming_frames_buffer, slice, nullptr); if (error != GRPC_ERROR_NONE) { return error; } } else { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - GRPC_CLOSURE_SCHED(&s->reset_byte_stream, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); return error; } return GRPC_ERROR_NONE; } -static void incoming_byte_stream_destroy_locked(void* byte_stream, - grpc_error* error_ignored); - -static void incoming_byte_stream_destroy(grpc_byte_stream* byte_stream) { - GPR_TIMER_SCOPE("incoming_byte_stream_destroy", 0); - grpc_chttp2_incoming_byte_stream* bs = - reinterpret_cast<grpc_chttp2_incoming_byte_stream*>(byte_stream); - GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&bs->destroy_action, - incoming_byte_stream_destroy_locked, bs, - grpc_combiner_scheduler(bs->transport->combiner)), - GRPC_ERROR_NONE); -} - -static void incoming_byte_stream_publish_error( - grpc_chttp2_incoming_byte_stream* bs, grpc_error* error) { - grpc_chttp2_stream* s = bs->stream; - +void Chttp2IncomingByteStream::PublishError(grpc_error* error) { GPR_ASSERT(error != GRPC_ERROR_NONE); - GRPC_CLOSURE_SCHED(s->on_next, GRPC_ERROR_REF(error)); - s->on_next = nullptr; - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_REF(error); - grpc_chttp2_cancel_stream(bs->transport, bs->stream, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(stream_->on_next, GRPC_ERROR_REF(error)); + stream_->on_next = nullptr; + GRPC_ERROR_UNREF(stream_->byte_stream_error); + stream_->byte_stream_error = GRPC_ERROR_REF(error); + grpc_chttp2_cancel_stream(transport_, stream_, GRPC_ERROR_REF(error)); } -grpc_error* grpc_chttp2_incoming_byte_stream_push( - grpc_chttp2_incoming_byte_stream* bs, grpc_slice slice, - grpc_slice* slice_out) { - grpc_chttp2_stream* s = bs->stream; - - if (bs->remaining_bytes < GRPC_SLICE_LENGTH(slice)) { +grpc_error* Chttp2IncomingByteStream::Push(grpc_slice slice, + grpc_slice* slice_out) { + if (remaining_bytes_ < GRPC_SLICE_LENGTH(slice)) { grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many bytes in stream"); - - GRPC_CLOSURE_SCHED(&s->reset_byte_stream, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); grpc_slice_unref_internal(slice); return error; } else { - bs->remaining_bytes -= static_cast<uint32_t> GRPC_SLICE_LENGTH(slice); + remaining_bytes_ -= static_cast<uint32_t> GRPC_SLICE_LENGTH(slice); if (slice_out != nullptr) { *slice_out = slice; } @@ -2898,66 +2897,25 @@ grpc_error* grpc_chttp2_incoming_byte_stream_push( } } -grpc_error* grpc_chttp2_incoming_byte_stream_finished( - grpc_chttp2_incoming_byte_stream* bs, grpc_error* error, - bool reset_on_error) { - grpc_chttp2_stream* s = bs->stream; - +grpc_error* Chttp2IncomingByteStream::Finished(grpc_error* error, + bool reset_on_error) { if (error == GRPC_ERROR_NONE) { - if (bs->remaining_bytes != 0) { + if (remaining_bytes_ != 0) { error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); } } if (error != GRPC_ERROR_NONE && reset_on_error) { - GRPC_CLOSURE_SCHED(&s->reset_byte_stream, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); } - incoming_byte_stream_unref(bs); + Unref(); return error; } -static void incoming_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - grpc_chttp2_incoming_byte_stream* bs = - reinterpret_cast<grpc_chttp2_incoming_byte_stream*>(byte_stream); - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - bs, error, true /* reset_on_error */)); +void Chttp2IncomingByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(Finished(error, true /* reset_on_error */)); } -static const grpc_byte_stream_vtable grpc_chttp2_incoming_byte_stream_vtable = { - incoming_byte_stream_next, incoming_byte_stream_pull, - incoming_byte_stream_shutdown, incoming_byte_stream_destroy}; - -static void incoming_byte_stream_destroy_locked(void* byte_stream, - grpc_error* error_ignored) { - grpc_chttp2_incoming_byte_stream* bs = - static_cast<grpc_chttp2_incoming_byte_stream*>(byte_stream); - grpc_chttp2_stream* s = bs->stream; - grpc_chttp2_transport* t = s->t; - - GPR_ASSERT(bs->base.vtable == &grpc_chttp2_incoming_byte_stream_vtable); - incoming_byte_stream_unref(bs); - s->pending_byte_stream = false; - grpc_chttp2_maybe_complete_recv_message(t, s); - grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); -} - -grpc_chttp2_incoming_byte_stream* grpc_chttp2_incoming_byte_stream_create( - grpc_chttp2_transport* t, grpc_chttp2_stream* s, uint32_t frame_size, - uint32_t flags) { - grpc_chttp2_incoming_byte_stream* incoming_byte_stream = - static_cast<grpc_chttp2_incoming_byte_stream*>( - gpr_malloc(sizeof(*incoming_byte_stream))); - incoming_byte_stream->base.length = frame_size; - incoming_byte_stream->remaining_bytes = frame_size; - incoming_byte_stream->base.flags = flags; - incoming_byte_stream->base.vtable = &grpc_chttp2_incoming_byte_stream_vtable; - gpr_ref_init(&incoming_byte_stream->refs, 2); - incoming_byte_stream->transport = t; - incoming_byte_stream->stream = s; - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_NONE; - return incoming_byte_stream; -} +} // namespace grpc_core /******************************************************************************* * RESOURCE QUOTAS diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h index 34519ceec9..9d55b3f4b0 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/transport/transport.h" diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc index 45b6f97b05..e89c363200 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.cc +++ b/src/core/ext/transport/chttp2/transport/flow_control.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/flow_control.h" #include <inttypes.h> diff --git a/src/core/ext/transport/chttp2/transport/flow_control.h b/src/core/ext/transport/chttp2/transport/flow_control.h index b58027790d..120fefc8b7 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.h +++ b/src/core/ext/transport/chttp2/transport/flow_control.h @@ -20,6 +20,7 @@ #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H #include <grpc/support/port_platform.h> + #include <stdint.h> #include "src/core/ext/transport/chttp2/transport/http2_settings.h" diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h index dba4c004ec..083c0076e7 100644 --- a/src/core/ext/transport/chttp2/transport/frame.h +++ b/src/core/ext/transport/chttp2/transport/frame.h @@ -19,9 +19,10 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H -#include <grpc/slice.h> #include <grpc/support/port_platform.h> +#include <grpc/slice.h> + #include "src/core/lib/iomgr/error.h" /* defined in internal.h */ diff --git a/src/core/ext/transport/chttp2/transport/frame_data.cc b/src/core/ext/transport/chttp2/transport/frame_data.cc index 721f0a55a1..f8f06f6789 100644 --- a/src/core/ext/transport/chttp2/transport/frame_data.cc +++ b/src/core/ext/transport/chttp2/transport/frame_data.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_data.h" #include <string.h> @@ -25,6 +27,7 @@ #include <grpc/support/string_util.h> #include "src/core/ext/transport/chttp2/transport/internal.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/transport/transport.h" @@ -37,8 +40,7 @@ grpc_error* grpc_chttp2_data_parser_init(grpc_chttp2_data_parser* parser) { void grpc_chttp2_data_parser_destroy(grpc_chttp2_data_parser* parser) { if (parser->parsing_frame != nullptr) { - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - parser->parsing_frame, + GRPC_ERROR_UNREF(parser->parsing_frame->Finished( GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false)); } GRPC_ERROR_UNREF(parser->error); @@ -98,7 +100,7 @@ void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf, grpc_error* grpc_deframe_unprocessed_incoming_frames( grpc_chttp2_data_parser* p, grpc_chttp2_stream* s, grpc_slice_buffer* slices, grpc_slice* slice_out, - grpc_byte_stream** stream_out) { + grpc_core::OrphanablePtr<grpc_core::ByteStream>* stream_out) { grpc_error* error = GRPC_ERROR_NONE; grpc_chttp2_transport* t = s->t; @@ -195,12 +197,11 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( if (p->is_frame_compressed) { message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; } - p->parsing_frame = grpc_chttp2_incoming_byte_stream_create( + p->parsing_frame = grpc_core::New<grpc_core::Chttp2IncomingByteStream>( t, s, p->frame_size, message_flags); - *stream_out = &p->parsing_frame->base; - if (p->parsing_frame->remaining_bytes == 0) { - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - p->parsing_frame, GRPC_ERROR_NONE, true)); + stream_out->reset(p->parsing_frame); + if (p->parsing_frame->remaining_bytes() == 0) { + GRPC_ERROR_UNREF(p->parsing_frame->Finished(GRPC_ERROR_NONE, true)); p->parsing_frame = nullptr; p->state = GRPC_CHTTP2_DATA_FH_0; } @@ -224,8 +225,7 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( if (remaining == p->frame_size) { s->stats.incoming.data_bytes += remaining; if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_push( - p->parsing_frame, + (error = p->parsing_frame->Push( grpc_slice_sub(slice, static_cast<size_t>(cur - beg), static_cast<size_t>(end - beg)), slice_out))) { @@ -233,8 +233,7 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( return error; } if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_finished( - p->parsing_frame, GRPC_ERROR_NONE, true))) { + (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) { grpc_slice_unref_internal(slice); return error; } @@ -245,8 +244,7 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( } else if (remaining < p->frame_size) { s->stats.incoming.data_bytes += remaining; if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_push( - p->parsing_frame, + (error = p->parsing_frame->Push( grpc_slice_sub(slice, static_cast<size_t>(cur - beg), static_cast<size_t>(end - beg)), slice_out))) { @@ -259,18 +257,16 @@ grpc_error* grpc_deframe_unprocessed_incoming_frames( GPR_ASSERT(remaining > p->frame_size); s->stats.incoming.data_bytes += p->frame_size; if (GRPC_ERROR_NONE != - (grpc_chttp2_incoming_byte_stream_push( - p->parsing_frame, + p->parsing_frame->Push( grpc_slice_sub( slice, static_cast<size_t>(cur - beg), static_cast<size_t>(cur + p->frame_size - beg)), - slice_out))) { + slice_out)) { grpc_slice_unref_internal(slice); return error; } if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_finished( - p->parsing_frame, GRPC_ERROR_NONE, true))) { + (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) { grpc_slice_unref_internal(slice); return error; } diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h index 964cc59b1b..4b0f873644 100644 --- a/src/core/ext/transport/chttp2/transport/frame_data.h +++ b/src/core/ext/transport/chttp2/transport/frame_data.h @@ -21,6 +21,8 @@ /* Parser for GRPC streams embedded in DATA frames */ +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/slice_buffer.h> #include "src/core/ext/transport/chttp2/transport/frame.h" @@ -38,8 +40,9 @@ typedef enum { GRPC_CHTTP2_DATA_ERROR } grpc_chttp2_stream_state; -typedef struct grpc_chttp2_incoming_byte_stream - grpc_chttp2_incoming_byte_stream; +namespace grpc_core { +class Chttp2IncomingByteStream; +} // namespace grpc_core typedef struct { grpc_chttp2_stream_state state; @@ -48,7 +51,7 @@ typedef struct { grpc_error* error; bool is_frame_compressed; - grpc_chttp2_incoming_byte_stream* parsing_frame; + grpc_core::Chttp2IncomingByteStream* parsing_frame; } grpc_chttp2_data_parser; /* initialize per-stream state for data frame parsing */ @@ -77,6 +80,6 @@ void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf, grpc_error* grpc_deframe_unprocessed_incoming_frames( grpc_chttp2_data_parser* p, grpc_chttp2_stream* s, grpc_slice_buffer* slices, grpc_slice* slice_out, - grpc_byte_stream** stream_out); + grpc_core::OrphanablePtr<grpc_core::ByteStream>* stream_out); #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */ diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.cc b/src/core/ext/transport/chttp2/transport/frame_goaway.cc index 70931ed22d..2a1dd3c316 100644 --- a/src/core/ext/transport/chttp2/transport/frame_goaway.cc +++ b/src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_goaway.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.h b/src/core/ext/transport/chttp2/transport/frame_goaway.h index 064d39ac59..e17ed8d563 100644 --- a/src/core/ext/transport/chttp2/transport/frame_goaway.h +++ b/src/core/ext/transport/chttp2/transport/frame_goaway.h @@ -19,9 +19,10 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/slice_buffer.h> -#include <grpc/support/port_platform.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.cc b/src/core/ext/transport/chttp2/transport/frame_ping.cc index 6229459397..205826b779 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.cc +++ b/src/core/ext/transport/chttp2/transport/frame_ping.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_ping.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h index 75bacfb1d4..8718d6a097 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.h +++ b/src/core/ext/transport/chttp2/transport/frame_ping.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc index 79528762e0..4bdd4309a4 100644 --- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc +++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h index e76a3ca841..bb2d34f918 100644 --- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h +++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc index 737a9382e0..9ea27dcd47 100644 --- a/src/core/ext/transport/chttp2/transport/frame_settings.cc +++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_settings.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.h b/src/core/ext/transport/chttp2/transport/frame_settings.h index ce65402815..df19627194 100644 --- a/src/core/ext/transport/chttp2/transport/frame_settings.h +++ b/src/core/ext/transport/chttp2/transport/frame_settings.h @@ -19,8 +19,9 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H -#include <grpc/slice.h> #include <grpc/support/port_platform.h> + +#include <grpc/slice.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/ext/transport/chttp2/transport/http2_settings.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.cc b/src/core/ext/transport/chttp2/transport/frame_window_update.cc index 95be5b42d2..4b586dc3e7 100644 --- a/src/core/ext/transport/chttp2/transport/frame_window_update.cc +++ b/src/core/ext/transport/chttp2/transport/frame_window_update.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/frame_window_update.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.h b/src/core/ext/transport/chttp2/transport/frame_window_update.h index a32f1a9d11..30667c77e1 100644 --- a/src/core/ext/transport/chttp2/transport/frame_window_update.h +++ b/src/core/ext/transport/chttp2/transport/frame_window_update.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc index 4855455130..e4f3c1b81e 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" #include <assert.h> diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h index a26514cab0..b370932131 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h @@ -19,9 +19,10 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/slice_buffer.h> -#include <grpc/support/port_platform.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/metadata_batch.h" diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc index 26a38e3f87..fc96a8b3e4 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/hpack_parser.h" #include "src/core/ext/transport/chttp2/transport/internal.h" @@ -25,7 +27,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/ext/transport/chttp2/transport/bin_encoder.h" diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h index 060bc5ce9d..b3b8018b98 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.h +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h @@ -19,9 +19,10 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H +#include <grpc/support/port_platform.h> + #include <stddef.h> -#include <grpc/support/port_platform.h> #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/ext/transport/chttp2/transport/hpack_table.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc index d7cabbde04..f050f502f5 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/hpack_table.h" #include <assert.h> diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h index 189ad1c697..98026a4ba4 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.h +++ b/src/core/ext/transport/chttp2/transport/hpack_table.h @@ -19,8 +19,9 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H -#include <grpc/slice.h> #include <grpc/support/port_platform.h> + +#include <grpc/slice.h> #include "src/core/lib/iomgr/error.h" #include "src/core/lib/transport/metadata.h" diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.cc b/src/core/ext/transport/chttp2/transport/http2_settings.cc index 745b1656f9..294ee8e4aa 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.cc +++ b/src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -18,6 +18,8 @@ * Automatically generated by tools/codegen/core/gen_settings_ids.py */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/http2_settings.h" #include "src/core/lib/gpr/useful.h" diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.h b/src/core/ext/transport/chttp2/transport/http2_settings.h index fd15b6977b..07ce0621b2 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.h +++ b/src/core/ext/transport/chttp2/transport/http2_settings.h @@ -21,6 +21,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <stdint.h> diff --git a/src/core/ext/transport/chttp2/transport/huffsyms.cc b/src/core/ext/transport/chttp2/transport/huffsyms.cc index f28d8cc30a..813e4c91b1 100644 --- a/src/core/ext/transport/chttp2/transport/huffsyms.cc +++ b/src/core/ext/transport/chttp2/transport/huffsyms.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/huffsyms.h" /* Constants pulled from the HPACK spec, and converted to C using the vim diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc index 18d89f06d8..4d7dfd900f 100644 --- a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc +++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" #include <string.h> @@ -67,6 +69,5 @@ void grpc_chttp2_incoming_metadata_buffer_set_deadline( void grpc_chttp2_incoming_metadata_buffer_publish( grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch) { - *batch = buffer->batch; - grpc_metadata_batch_init(&buffer->batch); + grpc_metadata_batch_move(&buffer->batch, batch); } diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h index b84cd484c4..d029cf00d4 100644 --- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h +++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/transport.h" typedef struct { diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 6b6c0b28e2..6d11e5aa31 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H +#include <grpc/support/port_platform.h> + #include <assert.h> #include <stdbool.h> @@ -201,18 +203,58 @@ typedef struct grpc_chttp2_write_cb { struct grpc_chttp2_write_cb* next; } grpc_chttp2_write_cb; -/* forward declared in frame_data.h */ -struct grpc_chttp2_incoming_byte_stream { - grpc_byte_stream base; - gpr_refcount refs; +namespace grpc_core { + +class Chttp2IncomingByteStream : public ByteStream { + public: + Chttp2IncomingByteStream(grpc_chttp2_transport* transport, + grpc_chttp2_stream* stream, uint32_t frame_size, + uint32_t flags); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + // TODO(roth): When I converted this class to C++, I wanted to make it + // inherit from RefCounted or InternallyRefCounted instead of continuing + // to use its own custom ref-counting code. However, that would require + // using multiple inheritence, which sucks in general. And to make matters + // worse, it causes problems with our New<> and Delete<> wrappers. + // Specifically, unless RefCounted is first in the list of parent classes, + // it will see a different value of the address of the object than the one + // we actually allocated, in which case gpr_free() will be called on a + // different address than the one we got from gpr_malloc(), thus causing a + // crash. Given the fragility of depending on that, as well as a desire to + // avoid multiple inheritence in general, I've decided to leave this + // alone for now. We can revisit this once we're able to link against + // libc++, at which point we can eliminate New<> and Delete<> and + // switch to std::shared_ptr<>. + void Ref(); + void Unref(); + + void PublishError(grpc_error* error); - grpc_chttp2_transport* transport; /* immutable */ - grpc_chttp2_stream* stream; /* immutable */ + grpc_error* Push(grpc_slice slice, grpc_slice* slice_out); + + grpc_error* Finished(grpc_error* error, bool reset_on_error); + + uint32_t remaining_bytes() const { return remaining_bytes_; } + + private: + static void NextLocked(void* arg, grpc_error* error_ignored); + static void OrphanLocked(void* arg, grpc_error* error_ignored); + + grpc_chttp2_transport* transport_; // Immutable. + grpc_chttp2_stream* stream_; // Immutable. + + gpr_refcount refs_; /* Accessed only by transport thread when stream->pending_byte_stream == false * Accessed only by application thread when stream->pending_byte_stream == * true */ - uint32_t remaining_bytes; + uint32_t remaining_bytes_; /* Accessed only by transport thread when stream->pending_byte_stream == false * Accessed only by application thread when stream->pending_byte_stream == @@ -221,11 +263,12 @@ struct grpc_chttp2_incoming_byte_stream { grpc_closure closure; size_t max_size_hint; grpc_closure* on_complete; - } next_action; - grpc_closure destroy_action; - grpc_closure finished_action; + } next_action_; + grpc_closure destroy_action_; }; +} // namespace grpc_core + typedef enum { GRPC_CHTTP2_KEEPALIVE_STATE_WAITING, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING, @@ -454,7 +497,7 @@ struct grpc_chttp2_stream { grpc_metadata_batch* send_trailing_metadata; grpc_closure* send_trailing_metadata_finished; - grpc_byte_stream* fetching_send_message; + grpc_core::OrphanablePtr<grpc_core::ByteStream> fetching_send_message; uint32_t fetched_send_message_length; grpc_slice fetching_slice; int64_t next_message_end_offset; @@ -466,7 +509,7 @@ struct grpc_chttp2_stream { grpc_metadata_batch* recv_initial_metadata; grpc_closure* recv_initial_metadata_ready; bool* trailing_metadata_available; - grpc_byte_stream** recv_message; + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message; grpc_closure* recv_message_ready; grpc_metadata_batch* recv_trailing_metadata; grpc_closure* recv_trailing_metadata_finished; @@ -717,18 +760,6 @@ void grpc_chttp2_unref_transport(grpc_chttp2_transport* t); void grpc_chttp2_ref_transport(grpc_chttp2_transport* t); #endif -grpc_chttp2_incoming_byte_stream* grpc_chttp2_incoming_byte_stream_create( - grpc_chttp2_transport* t, grpc_chttp2_stream* s, uint32_t frame_size, - uint32_t flags); -grpc_error* grpc_chttp2_incoming_byte_stream_push( - grpc_chttp2_incoming_byte_stream* bs, grpc_slice slice, - grpc_slice* slice_out); -grpc_error* grpc_chttp2_incoming_byte_stream_finished( - grpc_chttp2_incoming_byte_stream* bs, grpc_error* error, - bool reset_on_error); -void grpc_chttp2_incoming_byte_stream_notify( - grpc_chttp2_incoming_byte_stream* bs, grpc_error* error); - void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id); /** Add a new ping strike to ping_recv_state.ping_strikes. If diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc index 9357fedb5c..988380b76c 100644 --- a/src/core/ext/transport/chttp2/transport/parsing.cc +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/internal.h" #include <string.h> diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc index 3aad8c5823..5d3ec4b53b 100644 --- a/src/core/ext/transport/chttp2/transport/stream_lists.cc +++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/ext/transport/chttp2/transport/internal.h" diff --git a/src/core/ext/transport/chttp2/transport/stream_map.cc b/src/core/ext/transport/chttp2/transport/stream_map.cc index a40eaffd5f..f300e2356c 100644 --- a/src/core/ext/transport/chttp2/transport/stream_map.cc +++ b/src/core/ext/transport/chttp2/transport/stream_map.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/stream_map.h" #include <string.h> diff --git a/src/core/ext/transport/chttp2/transport/varint.cc b/src/core/ext/transport/chttp2/transport/varint.cc index 621a8100c7..d4b01788b9 100644 --- a/src/core/ext/transport/chttp2/transport/varint.cc +++ b/src/core/ext/transport/chttp2/transport/varint.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/varint.h" uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value) { diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc index 42cba9ae60..7471d88aa1 100644 --- a/src/core/ext/transport/chttp2/transport/writing.cc +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/chttp2/transport/internal.h" #include <limits.h> diff --git a/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc index 578cbb8ac6..1a6bded6af 100644 --- a/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc +++ b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc @@ -19,6 +19,8 @@ /* This file has empty implementation of all the functions exposed by the cronet library, so we can build it in all environments */ +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/support/log.h> diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc index c367f9c465..8e3ea05706 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.cc +++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc @@ -16,9 +16,10 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> -#include <grpc/impl/codegen/port_platform.h> #include <grpc/slice_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -30,6 +31,7 @@ #include "src/core/ext/transport/cronet/transport/cronet_transport.h" #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/slice/slice_internal.h" @@ -121,7 +123,7 @@ struct read_state { bool read_stream_closed; /* vars for holding data destined for the application */ - struct grpc_slice_buffer_stream sbs; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sbs; grpc_slice_buffer read_slice_buffer; /* vars for trailing metadata */ @@ -1040,16 +1042,14 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { grpc_slice_buffer write_slice_buffer; grpc_slice slice; grpc_slice_buffer_init(&write_slice_buffer); - if (1 != grpc_byte_stream_next( - stream_op->payload->send_message.send_message, - stream_op->payload->send_message.send_message->length, + if (1 != stream_op->payload->send_message.send_message->Next( + stream_op->payload->send_message.send_message->length(), nullptr)) { /* Should never reach here */ GPR_ASSERT(false); } if (GRPC_ERROR_NONE != - grpc_byte_stream_pull(stream_op->payload->send_message.send_message, - &slice)) { + stream_op->payload->send_message.send_message->Pull(&slice)) { /* Should never reach here */ GPR_ASSERT(false); } @@ -1061,9 +1061,10 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { } if (write_slice_buffer.count > 0) { size_t write_buffer_size; - create_grpc_frame(&write_slice_buffer, &stream_state->ws.write_buffer, - &write_buffer_size, - stream_op->payload->send_message.send_message->flags); + create_grpc_frame( + &write_slice_buffer, &stream_state->ws.write_buffer, + &write_buffer_size, + stream_op->payload->send_message.send_message->flags()); CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, stream_state->ws.write_buffer); stream_state->state_callback_received[OP_SEND_MESSAGE] = false; @@ -1088,6 +1089,7 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { } stream_state->state_op_done[OP_SEND_MESSAGE] = true; oas->state.state_op_done[OP_SEND_MESSAGE] = true; + stream_op->payload->send_message.send_message.reset(); } else if (stream_op->send_trailing_metadata && op_can_be_run(stream_op, s, &oas->state, OP_SEND_TRAILING_METADATA)) { @@ -1194,14 +1196,13 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { grpc_slice_buffer_destroy_internal( &stream_state->rs.read_slice_buffer); grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); - grpc_slice_buffer_stream_init(&stream_state->rs.sbs, - &stream_state->rs.read_slice_buffer, 0); + uint32_t flags = 0; if (stream_state->rs.compressed) { - stream_state->rs.sbs.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; + flags |= GRPC_WRITE_INTERNAL_COMPRESS; } - *(reinterpret_cast<grpc_byte_buffer**>( - stream_op->payload->recv_message.recv_message)) = - reinterpret_cast<grpc_byte_buffer*>(&stream_state->rs.sbs); + stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags); + stream_op->payload->recv_message.recv_message->reset( + stream_state->rs.sbs.get()); GRPC_CLOSURE_SCHED( stream_op->payload->recv_message.recv_message_ready, GRPC_ERROR_NONE); @@ -1251,14 +1252,13 @@ static enum e_op_result execute_stream_op(struct op_and_state* oas) { grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer, read_data_slice); - grpc_slice_buffer_stream_init(&stream_state->rs.sbs, - &stream_state->rs.read_slice_buffer, 0); + uint32_t flags = 0; if (stream_state->rs.compressed) { - stream_state->rs.sbs.base.flags = GRPC_WRITE_INTERNAL_COMPRESS; + flags = GRPC_WRITE_INTERNAL_COMPRESS; } - *(reinterpret_cast<grpc_byte_buffer**>( - stream_op->payload->recv_message.recv_message)) = - reinterpret_cast<grpc_byte_buffer*>(&stream_state->rs.sbs); + stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags); + stream_op->payload->recv_message.recv_message->reset( + stream_state->rs.sbs.get()); GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, GRPC_ERROR_NONE); stream_state->state_op_done[OP_RECV_MESSAGE] = true; diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.h b/src/core/ext/transport/cronet/transport/cronet_transport.h index d9ff913326..fb7e149f10 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.h +++ b/src/core/ext/transport/cronet/transport/cronet_transport.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H #define GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/transport.h" grpc_transport* grpc_create_cronet_transport(void* engine, const char* target, diff --git a/src/core/ext/transport/inproc/inproc_plugin.cc b/src/core/ext/transport/inproc/inproc_plugin.cc index 83a7d8d52f..8e251fa2d8 100644 --- a/src/core/ext/transport/inproc/inproc_plugin.cc +++ b/src/core/ext/transport/inproc/inproc_plugin.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/ext/transport/inproc/inproc_transport.h" #include "src/core/lib/debug/trace.h" diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc index e1d4843785..67a380077b 100644 --- a/src/core/ext/transport/inproc/inproc_transport.cc +++ b/src/core/ext/transport/inproc/inproc_transport.cc @@ -16,13 +16,16 @@ * */ -#include "src/core/ext/transport/inproc/inproc_transport.h" +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/string_util.h> #include <grpc/support/sync.h> #include <grpc/support/time.h> #include <string.h> +#include "src/core/ext/transport/inproc/inproc_transport.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/channel.h" @@ -97,7 +100,7 @@ typedef struct inproc_stream { grpc_transport_stream_op_batch* recv_trailing_md_op; grpc_slice_buffer recv_message; - grpc_slice_buffer_stream recv_stream; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> recv_stream; bool recv_inited; bool initial_md_sent; @@ -480,8 +483,7 @@ static void fail_helper_locked(inproc_stream* s, grpc_error* error) { s->recv_message_op = nullptr; } if (s->send_message_op) { - grpc_byte_stream_destroy( - s->send_message_op->payload->send_message.send_message); + s->send_message_op->payload->send_message.send_message.reset(); complete_if_batch_end_locked( s, error, s->send_message_op, "fail_helper scheduling send-message-on-complete"); @@ -519,7 +521,7 @@ static void fail_helper_locked(inproc_stream* s, grpc_error* error) { static void message_transfer_locked(inproc_stream* sender, inproc_stream* receiver) { size_t remaining = - sender->send_message_op->payload->send_message.send_message->length; + sender->send_message_op->payload->send_message.send_message->length(); if (receiver->recv_inited) { grpc_slice_buffer_destroy_internal(&receiver->recv_message); } @@ -528,12 +530,12 @@ static void message_transfer_locked(inproc_stream* sender, do { grpc_slice message_slice; grpc_closure unused; - GPR_ASSERT(grpc_byte_stream_next( - sender->send_message_op->payload->send_message.send_message, SIZE_MAX, - &unused)); - grpc_error* error = grpc_byte_stream_pull( - sender->send_message_op->payload->send_message.send_message, - &message_slice); + GPR_ASSERT( + sender->send_message_op->payload->send_message.send_message->Next( + SIZE_MAX, &unused)); + grpc_error* error = + sender->send_message_op->payload->send_message.send_message->Pull( + &message_slice); if (error != GRPC_ERROR_NONE) { cancel_stream_locked(sender, GRPC_ERROR_REF(error)); break; @@ -542,13 +544,11 @@ static void message_transfer_locked(inproc_stream* sender, remaining -= GRPC_SLICE_LENGTH(message_slice); grpc_slice_buffer_add(&receiver->recv_message, message_slice); } while (remaining > 0); - grpc_byte_stream_destroy( - sender->send_message_op->payload->send_message.send_message); + sender->send_message_op->payload->send_message.send_message.reset(); - grpc_slice_buffer_stream_init(&receiver->recv_stream, &receiver->recv_message, - 0); - *receiver->recv_message_op->payload->recv_message.recv_message = - &receiver->recv_stream.base; + receiver->recv_stream.Init(&receiver->recv_message, 0); + receiver->recv_message_op->payload->recv_message.recv_message->reset( + receiver->recv_stream.get()); INPROC_LOG(GPR_DEBUG, "message_transfer_locked %p scheduling message-ready", receiver); GRPC_CLOSURE_SCHED( @@ -604,8 +604,7 @@ static void op_state_machine(void* arg, grpc_error* error) { (s->trailing_md_sent || other->recv_trailing_md_op)) { // A server send will never be matched if the client is waiting // for trailing metadata already - grpc_byte_stream_destroy( - s->send_message_op->payload->send_message.send_message); + s->send_message_op->payload->send_message.send_message.reset(); complete_if_batch_end_locked( s, GRPC_ERROR_NONE, s->send_message_op, "op_state_machine scheduling send-message-on-complete"); @@ -742,8 +741,7 @@ static void op_state_machine(void* arg, grpc_error* error) { if ((s->trailing_md_sent || s->t->is_client) && s->send_message_op) { // Nothing further will try to receive from this stream, so finish off // any outstanding send_message op - grpc_byte_stream_destroy( - s->send_message_op->payload->send_message.send_message); + s->send_message_op->payload->send_message.send_message.reset(); complete_if_batch_end_locked( s, new_err, s->send_message_op, "op_state_machine scheduling send-message-on-complete"); @@ -801,8 +799,7 @@ static void op_state_machine(void* arg, grpc_error* error) { s->send_message_op) { // Nothing further will try to receive from this stream, so finish off // any outstanding send_message op - grpc_byte_stream_destroy( - s->send_message_op->payload->send_message.send_message); + s->send_message_op->payload->send_message.send_message.reset(); complete_if_batch_end_locked( s, new_err, s->send_message_op, "op_state_machine scheduling send-message-on-complete"); diff --git a/src/core/ext/transport/inproc/inproc_transport.h b/src/core/ext/transport/inproc/inproc_transport.h index 7c0453e7ce..049d1402af 100644 --- a/src/core/ext/transport/inproc/inproc_transport.h +++ b/src/core/ext/transport/inproc/inproc_transport.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H #define GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/transport_impl.h" grpc_channel* grpc_inproc_channel_create(grpc_server* server, diff --git a/src/core/lib/avl/avl.cc b/src/core/lib/avl/avl.cc index c674d9be28..ec106ddb1d 100644 --- a/src/core/lib/avl/avl.cc +++ b/src/core/lib/avl/avl.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/avl/avl.h" #include <assert.h> diff --git a/src/core/lib/avl/avl.h b/src/core/lib/avl/avl.h index df5d2e89ec..15a9d56947 100644 --- a/src/core/lib/avl/avl.h +++ b/src/core/lib/avl/avl.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_AVL_AVL_H #define GRPC_CORE_LIB_AVL_AVL_H +#include <grpc/support/port_platform.h> + #include <grpc/support/sync.h> /** internal node of an AVL tree */ diff --git a/src/core/lib/backoff/backoff.cc b/src/core/lib/backoff/backoff.cc index 4b74afc244..e536abde0a 100644 --- a/src/core/lib/backoff/backoff.cc +++ b/src/core/lib/backoff/backoff.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/backoff/backoff.h" #include <algorithm> diff --git a/src/core/lib/backoff/backoff.h b/src/core/lib/backoff/backoff.h index de30e5268b..e769d150ef 100644 --- a/src/core/lib/backoff/backoff.h +++ b/src/core/lib/backoff/backoff.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_BACKOFF_BACKOFF_H #define GRPC_CORE_LIB_BACKOFF_BACKOFF_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/exec_ctx.h" namespace grpc_core { diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h index c59bd0c7e7..c0d6a17356 100644 --- a/src/core/lib/channel/channel_args.h +++ b/src/core/lib/channel/channel_args.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H #define GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H +#include <grpc/support/port_platform.h> + #include <grpc/compression.h> #include <grpc/grpc.h> #include "src/core/lib/iomgr/socket_mutator.h" diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc index 737d828e4b..a9459b150d 100644 --- a/src/core/lib/channel/channel_stack.cc +++ b/src/core/lib/channel/channel_stack.cc @@ -16,9 +16,11 @@ * */ -#include "src/core/lib/channel/channel_stack.h" +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/lib/channel/channel_stack.h" #include <stdlib.h> #include <string.h> diff --git a/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h index b9f9748001..4bf8218664 100644 --- a/src/core/lib/channel/channel_stack.h +++ b/src/core/lib/channel/channel_stack.h @@ -33,6 +33,8 @@ Call stacks are created by channel stacks and represent the per-call data for that stack. */ +#include <grpc/support/port_platform.h> + #include <stddef.h> #include <grpc/grpc.h> diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc index cae862ec0e..8a72449034 100644 --- a/src/core/lib/channel/channel_stack_builder.cc +++ b/src/core/lib/channel/channel_stack_builder.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack_builder.h" #include <string.h> diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h index d00ddc698c..c9a170bc88 100644 --- a/src/core/lib/channel/channel_stack_builder.h +++ b/src/core/lib/channel/channel_stack_builder.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H #define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include "src/core/lib/channel/channel_args.h" diff --git a/src/core/lib/channel/connected_channel.cc b/src/core/lib/channel/connected_channel.cc index a16e89ce00..ddd3029402 100644 --- a/src/core/lib/channel/connected_channel.cc +++ b/src/core/lib/channel/connected_channel.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/connected_channel.h" #include <stdarg.h> diff --git a/src/core/lib/channel/connected_channel.h b/src/core/lib/channel/connected_channel.h index 91de8022db..faa1c73a21 100644 --- a/src/core/lib/channel/connected_channel.h +++ b/src/core/lib/channel/connected_channel.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H #define GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack_builder.h" extern const grpc_channel_filter grpc_connected_filter; diff --git a/src/core/lib/channel/handshaker.cc b/src/core/lib/channel/handshaker.cc index 518e880c15..9b1af8d6cb 100644 --- a/src/core/lib/channel/handshaker.cc +++ b/src/core/lib/channel/handshaker.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h index 68e5463123..dfecd81004 100644 --- a/src/core/lib/channel/handshaker.h +++ b/src/core/lib/channel/handshaker.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/iomgr/closure.h" diff --git a/src/core/lib/channel/handshaker_factory.cc b/src/core/lib/channel/handshaker_factory.cc index 2380d98300..4fd43635b6 100644 --- a/src/core/lib/channel/handshaker_factory.cc +++ b/src/core/lib/channel/handshaker_factory.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/handshaker_factory.h" #include <grpc/support/log.h> diff --git a/src/core/lib/channel/handshaker_factory.h b/src/core/lib/channel/handshaker_factory.h index 8a7c0157e8..9e36443958 100644 --- a/src/core/lib/channel/handshaker_factory.h +++ b/src/core/lib/channel/handshaker_factory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/channel/handshaker.h" diff --git a/src/core/lib/channel/handshaker_registry.cc b/src/core/lib/channel/handshaker_registry.cc index 5464cc42f4..eec3e1b352 100644 --- a/src/core/lib/channel/handshaker_registry.cc +++ b/src/core/lib/channel/handshaker_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/handshaker_registry.h" #include <string.h> diff --git a/src/core/lib/channel/handshaker_registry.h b/src/core/lib/channel/handshaker_registry.h index 0b05531b7e..b42d61ff43 100644 --- a/src/core/lib/channel/handshaker_registry.h +++ b/src/core/lib/channel/handshaker_registry.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H #define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include "src/core/lib/channel/handshaker_factory.h" diff --git a/src/core/lib/compression/algorithm_metadata.h b/src/core/lib/compression/algorithm_metadata.h index 7db771ea74..1be79e59c0 100644 --- a/src/core/lib/compression/algorithm_metadata.h +++ b/src/core/lib/compression/algorithm_metadata.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H #define GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H +#include <grpc/support/port_platform.h> + #include <grpc/compression.h> #include "src/core/lib/compression/compression_internal.h" #include "src/core/lib/transport/metadata.h" diff --git a/src/core/lib/compression/compression.cc b/src/core/lib/compression/compression.cc index 69d70ea941..48717541a7 100644 --- a/src/core/lib/compression/compression.cc +++ b/src/core/lib/compression/compression.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include <string.h> diff --git a/src/core/lib/compression/compression_internal.cc b/src/core/lib/compression/compression_internal.cc index 36829c8adf..538514caf3 100644 --- a/src/core/lib/compression/compression_internal.cc +++ b/src/core/lib/compression/compression_internal.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include <string.h> diff --git a/src/core/lib/compression/compression_internal.h b/src/core/lib/compression/compression_internal.h index 72f01dd1b7..da007368b0 100644 --- a/src/core/lib/compression/compression_internal.h +++ b/src/core/lib/compression/compression_internal.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H #define GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/compression_types.h> #ifdef __cplusplus diff --git a/src/core/lib/compression/message_compress.cc b/src/core/lib/compression/message_compress.cc index 2e44551935..e06454f871 100644 --- a/src/core/lib/compression/message_compress.cc +++ b/src/core/lib/compression/message_compress.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/compression/message_compress.h" #include <string.h> diff --git a/src/core/lib/compression/message_compress.h b/src/core/lib/compression/message_compress.h index ed9e5bfa39..91654e47e3 100644 --- a/src/core/lib/compression/message_compress.h +++ b/src/core/lib/compression/message_compress.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H #define GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H +#include <grpc/support/port_platform.h> + #include <grpc/slice_buffer.h> #include "src/core/lib/compression/compression_internal.h" diff --git a/src/core/lib/compression/stream_compression.cc b/src/core/lib/compression/stream_compression.cc index b4b3e524d0..46cb3daf4c 100644 --- a/src/core/lib/compression/stream_compression.cc +++ b/src/core/lib/compression/stream_compression.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include "src/core/lib/compression/stream_compression.h" diff --git a/src/core/lib/compression/stream_compression.h b/src/core/lib/compression/stream_compression.h index 8322835c4f..c80f2f8692 100644 --- a/src/core/lib/compression/stream_compression.h +++ b/src/core/lib/compression/stream_compression.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/slice_buffer.h> diff --git a/src/core/lib/compression/stream_compression_gzip.cc b/src/core/lib/compression/stream_compression_gzip.cc index 48fe99b69f..682f712843 100644 --- a/src/core/lib/compression/stream_compression_gzip.cc +++ b/src/core/lib/compression/stream_compression_gzip.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> diff --git a/src/core/lib/compression/stream_compression_gzip.h b/src/core/lib/compression/stream_compression_gzip.h index 7cf49a0de9..740f09734a 100644 --- a/src/core/lib/compression/stream_compression_gzip.h +++ b/src/core/lib/compression/stream_compression_gzip.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/compression/stream_compression.h" extern const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable; diff --git a/src/core/lib/compression/stream_compression_identity.cc b/src/core/lib/compression/stream_compression_identity.cc index 933d24b3a1..52a6236621 100644 --- a/src/core/lib/compression/stream_compression_identity.cc +++ b/src/core/lib/compression/stream_compression_identity.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> diff --git a/src/core/lib/compression/stream_compression_identity.h b/src/core/lib/compression/stream_compression_identity.h index 41926e949e..cc77b63ecd 100644 --- a/src/core/lib/compression/stream_compression_identity.h +++ b/src/core/lib/compression/stream_compression_identity.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H #define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/compression/stream_compression.h" extern const grpc_stream_compression_vtable diff --git a/src/core/lib/debug/stats.cc b/src/core/lib/debug/stats.cc index 09a86287e4..d8ddf03ac1 100644 --- a/src/core/lib/debug/stats.cc +++ b/src/core/lib/debug/stats.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/stats.h" #include <inttypes.h> diff --git a/src/core/lib/debug/stats.h b/src/core/lib/debug/stats.h index 02eed5e844..749665262a 100644 --- a/src/core/lib/debug/stats.h +++ b/src/core/lib/debug/stats.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_DEBUG_STATS_H #define GRPC_CORE_LIB_DEBUG_STATS_H +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include "src/core/lib/debug/stats_data.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/debug/stats_data.cc b/src/core/lib/debug/stats_data.cc index e2d3a6d073..309ece94bb 100644 --- a/src/core/lib/debug/stats_data.cc +++ b/src/core/lib/debug/stats_data.cc @@ -18,8 +18,10 @@ * Automatically generated by tools/codegen/core/gen_stats_data.py */ -#include "src/core/lib/debug/stats_data.h" +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/stats_data.h" #include "src/core/lib/gpr/useful.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h index 4504be33e7..da1266ad73 100644 --- a/src/core/lib/debug/stats_data.h +++ b/src/core/lib/debug/stats_data.h @@ -21,6 +21,8 @@ #ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H #define GRPC_CORE_LIB_DEBUG_STATS_DATA_H +#include <grpc/support/port_platform.h> + #include <inttypes.h> #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc index c0cefd0639..b0e0f2ba7c 100644 --- a/src/core/lib/debug/trace.cc +++ b/src/core/lib/debug/trace.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/trace.h" #include <string.h> diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h index 69ddd80222..bfec92c529 100644 --- a/src/core/lib/debug/trace.h +++ b/src/core/lib/debug/trace.h @@ -19,8 +19,9 @@ #ifndef GRPC_CORE_LIB_DEBUG_TRACE_H #define GRPC_CORE_LIB_DEBUG_TRACE_H -#include <grpc/support/atm.h> #include <grpc/support/port_platform.h> + +#include <grpc/support/atm.h> #include <stdbool.h> void grpc_tracer_init(const char* env_var_name); diff --git a/src/core/lib/gpr/alloc.cc b/src/core/lib/gpr/alloc.cc index e0d25963ed..611e4cceee 100644 --- a/src/core/lib/gpr/alloc.cc +++ b/src/core/lib/gpr/alloc.cc @@ -16,10 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <stdlib.h> #include <string.h> #include "src/core/lib/profiling/timers.h" diff --git a/src/core/lib/gpr/arena.cc b/src/core/lib/gpr/arena.cc index 2d514df68b..b02c5b9fb6 100644 --- a/src/core/lib/gpr/arena.cc +++ b/src/core/lib/gpr/arena.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/arena.h" #include <string.h> @@ -24,6 +26,49 @@ #include <grpc/support/atm.h> #include <grpc/support/log.h> +// Uncomment this to use a simple arena that simply allocates the +// requested amount of memory for each call to gpr_arena_alloc(). This +// effectively eliminates the efficiency gain of using an arena, but it +// may be useful for debugging purposes. +//#define SIMPLE_ARENA_FOR_DEBUGGING + +#ifdef SIMPLE_ARENA_FOR_DEBUGGING + +#include <grpc/support/sync.h> + +struct gpr_arena { + gpr_mu mu; + void** ptrs; + size_t num_ptrs; +}; + +gpr_arena* gpr_arena_create(size_t ignored_initial_size) { + gpr_arena* arena = (gpr_arena*)gpr_zalloc(sizeof(*arena)); + gpr_mu_init(&arena->mu); + return arena; +} + +size_t gpr_arena_destroy(gpr_arena* arena) { + gpr_mu_destroy(&arena->mu); + for (size_t i = 0; i < arena->num_ptrs; ++i) { + gpr_free(arena->ptrs[i]); + } + gpr_free(arena->ptrs); + gpr_free(arena); + return 1; // Value doesn't matter, since it won't be used. +} + +void* gpr_arena_alloc(gpr_arena* arena, size_t size) { + gpr_mu_lock(&arena->mu); + arena->ptrs = + (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1)); + void* retval = arena->ptrs[arena->num_ptrs++] = gpr_zalloc(size); + gpr_mu_unlock(&arena->mu); + return retval; +} + +#else // SIMPLE_ARENA_FOR_DEBUGGING + // TODO(roth): We currently assume that all callers need alignment of 16 // bytes, which may be wrong in some cases. As part of converting the // arena API to C++, we should consider replacing gpr_arena_alloc() with a @@ -103,3 +148,5 @@ void* gpr_arena_alloc(gpr_arena* arena, size_t size) { ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)); return ptr + start - z->size_begin; } + +#endif // SIMPLE_ARENA_FOR_DEBUGGING diff --git a/src/core/lib/gpr/arena.h b/src/core/lib/gpr/arena.h index 339771c0e3..6d2a073dd5 100644 --- a/src/core/lib/gpr/arena.h +++ b/src/core/lib/gpr/arena.h @@ -25,6 +25,8 @@ #ifndef GRPC_CORE_LIB_GPR_ARENA_H #define GRPC_CORE_LIB_GPR_ARENA_H +#include <grpc/support/port_platform.h> + #include <stddef.h> typedef struct gpr_arena gpr_arena; diff --git a/src/core/lib/gpr/atm.cc b/src/core/lib/gpr/atm.cc index 3d0b430348..649d400d38 100644 --- a/src/core/lib/gpr/atm.cc +++ b/src/core/lib/gpr/atm.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include "src/core/lib/gpr/useful.h" diff --git a/src/core/lib/gpr/cpu_linux.cc b/src/core/lib/gpr/cpu_linux.cc index 4782f9f742..fda28916f8 100644 --- a/src/core/lib/gpr/cpu_linux.cc +++ b/src/core/lib/gpr/cpu_linux.cc @@ -45,7 +45,7 @@ static void init_num_cpus() { #endif /* This must be signed. sysconf returns -1 when the number cannot be determined */ - ncpus = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN)); + ncpus = static_cast<int>(sysconf(_SC_NPROCESSORS_CONF)); if (ncpus < 1) { gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); ncpus = 1; diff --git a/src/core/lib/gpr/cpu_posix.cc b/src/core/lib/gpr/cpu_posix.cc index 7a77f7ab64..915fd4976c 100644 --- a/src/core/lib/gpr/cpu_posix.cc +++ b/src/core/lib/gpr/cpu_posix.cc @@ -37,7 +37,7 @@ static long ncpus = 0; static pthread_key_t thread_id_key; static void init_ncpus() { - ncpus = sysconf(_SC_NPROCESSORS_ONLN); + ncpus = sysconf(_SC_NPROCESSORS_CONF); if (ncpus < 1 || ncpus > INT32_MAX) { gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); ncpus = 1; diff --git a/src/core/lib/gpr/env.h b/src/core/lib/gpr/env.h index b31e20b7d2..aec8a3166b 100644 --- a/src/core/lib/gpr/env.h +++ b/src/core/lib/gpr/env.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_ENV_H #define GRPC_CORE_LIB_GPR_ENV_H +#include <grpc/support/port_platform.h> + #include <stdio.h> /* Env utility functions */ diff --git a/src/core/lib/gpr/fork.cc b/src/core/lib/gpr/fork.cc index 4651d22595..812522b058 100644 --- a/src/core/lib/gpr/fork.cc +++ b/src/core/lib/gpr/fork.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/fork.h" #include <string.h> diff --git a/src/core/lib/gpr/host_port.cc b/src/core/lib/gpr/host_port.cc index 5a03a16296..a34e01cb51 100644 --- a/src/core/lib/gpr/host_port.cc +++ b/src/core/lib/gpr/host_port.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/host_port.h" #include <string.h> diff --git a/src/core/lib/gpr/log.cc b/src/core/lib/gpr/log.cc index 410096c0f7..72787ab724 100644 --- a/src/core/lib/gpr/log.cc +++ b/src/core/lib/gpr/log.cc @@ -16,10 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/atm.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" diff --git a/src/core/lib/gpr/log_linux.cc b/src/core/lib/gpr/log_linux.cc index d743eedf38..e4417d9d5d 100644 --- a/src/core/lib/gpr/log_linux.cc +++ b/src/core/lib/gpr/log_linux.cc @@ -32,6 +32,7 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include <grpc/support/time.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> #include <string.h> diff --git a/src/core/lib/gpr/mpscq.cc b/src/core/lib/gpr/mpscq.cc index d7718273a6..076a6bb033 100644 --- a/src/core/lib/gpr/mpscq.cc +++ b/src/core/lib/gpr/mpscq.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/mpscq.h" #include <grpc/support/log.h> diff --git a/src/core/lib/gpr/mpscq.h b/src/core/lib/gpr/mpscq.h index 4409c5c9f5..6b67880d1b 100644 --- a/src/core/lib/gpr/mpscq.h +++ b/src/core/lib/gpr/mpscq.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_MPSCQ_H #define GRPC_CORE_LIB_GPR_MPSCQ_H +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include <grpc/support/sync.h> #include <stdbool.h> diff --git a/src/core/lib/gpr/murmur_hash.cc b/src/core/lib/gpr/murmur_hash.cc index 01a7290c67..cf25abf40d 100644 --- a/src/core/lib/gpr/murmur_hash.cc +++ b/src/core/lib/gpr/murmur_hash.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/murmur_hash.h" #include <string.h> diff --git a/src/core/lib/gpr/spinlock.h b/src/core/lib/gpr/spinlock.h index f03be1d791..9f35530a86 100644 --- a/src/core/lib/gpr/spinlock.h +++ b/src/core/lib/gpr/spinlock.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_SPINLOCK_H #define GRPC_CORE_LIB_GPR_SPINLOCK_H +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> /* Simple spinlock. No backoff strategy, gpr_spinlock_lock is almost always diff --git a/src/core/lib/gpr/string.cc b/src/core/lib/gpr/string.cc index 5a16377e49..ef2a6900b4 100644 --- a/src/core/lib/gpr/string.cc +++ b/src/core/lib/gpr/string.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/string.h" #include <ctype.h> @@ -26,7 +28,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/lib/gpr/useful.h" diff --git a/src/core/lib/gpr/string.h b/src/core/lib/gpr/string.h index ef3a8c6086..2e8a4898d9 100644 --- a/src/core/lib/gpr/string.h +++ b/src/core/lib/gpr/string.h @@ -19,11 +19,11 @@ #ifndef GRPC_CORE_LIB_GPR_STRING_H #define GRPC_CORE_LIB_GPR_STRING_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <stddef.h> -#include <grpc/support/port_platform.h> - /* String utility functions */ /* Flags for gpr_dump function. */ diff --git a/src/core/lib/gpr/sync.cc b/src/core/lib/gpr/sync.cc index 347ffcd00e..2f18fc5ecb 100644 --- a/src/core/lib/gpr/sync.cc +++ b/src/core/lib/gpr/sync.cc @@ -18,6 +18,8 @@ /* Generic implementation of synchronization primitives. */ +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include <grpc/support/log.h> #include <grpc/support/sync.h> diff --git a/src/core/lib/gpr/thd.cc b/src/core/lib/gpr/thd.cc deleted file mode 100644 index 11391418b1..0000000000 --- a/src/core/lib/gpr/thd.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Platform-independent features for gpr threads. */ - -#include "src/core/lib/gpr/thd.h" - -#include <string.h> - -enum { GPR_THD_JOINABLE = 1 }; - -gpr_thd_options gpr_thd_options_default(void) { - gpr_thd_options options; - memset(&options, 0, sizeof(options)); - return options; -} - -void gpr_thd_options_set_detached(gpr_thd_options* options) { - options->flags &= ~GPR_THD_JOINABLE; -} - -void gpr_thd_options_set_joinable(gpr_thd_options* options) { - options->flags |= GPR_THD_JOINABLE; -} - -int gpr_thd_options_is_detached(const gpr_thd_options* options) { - if (!options) return 1; - return (options->flags & GPR_THD_JOINABLE) == 0; -} - -int gpr_thd_options_is_joinable(const gpr_thd_options* options) { - if (!options) return 0; - return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; -} diff --git a/src/core/lib/gpr/thd.h b/src/core/lib/gpr/thd.h deleted file mode 100644 index 58ce0d0088..0000000000 --- a/src/core/lib/gpr/thd.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GRPC_CORE_LIB_GPR_THD_H -#define GRPC_CORE_LIB_GPR_THD_H -/** Internal thread interface for GPR. - - Types - gpr_thd_options options used when creating a thread - */ - -#include <grpc/support/port_platform.h> -#include <grpc/support/thd_id.h> -#include <grpc/support/time.h> - -/** Thread creation options. */ -typedef struct { - int flags; /** Opaque field. Get and set with accessors below. */ -} gpr_thd_options; - -/** Create a new thread running (*thd_body)(arg) and place its thread identifier - in *t, and return true. If there are insufficient resources, return false. - thd_name is the name of the thread for identification purposes on platforms - that support thread naming. - If options==NULL, default options are used. - The thread is immediately runnable, and exits when (*thd_body)() returns. */ -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options); - -/** Return a gpr_thd_options struct with all fields set to defaults. */ -gpr_thd_options gpr_thd_options_default(void); - -/** Set the thread to become detached on startup - this is the default. */ -void gpr_thd_options_set_detached(gpr_thd_options* options); - -/** Set the thread to become joinable - mutually exclusive with detached. */ -void gpr_thd_options_set_joinable(gpr_thd_options* options); - -/** Returns non-zero if the option detached is set. */ -int gpr_thd_options_is_detached(const gpr_thd_options* options); - -/** Returns non-zero if the option joinable is set. */ -int gpr_thd_options_is_joinable(const gpr_thd_options* options); - -/** Blocks until the specified thread properly terminates. - Calling this on a detached thread has unpredictable results. */ -void gpr_thd_join(gpr_thd_id t); - -/* Internal interfaces between modules within the gpr support library. */ -void gpr_thd_init(); - -/* Wait for all outstanding threads to finish, up to deadline */ -int gpr_await_threads(gpr_timespec deadline); - -#endif /* GRPC_CORE_LIB_GPR_THD_H */ diff --git a/src/core/lib/gpr/thd_posix.cc b/src/core/lib/gpr/thd_posix.cc deleted file mode 100644 index fcd174bfba..0000000000 --- a/src/core/lib/gpr/thd_posix.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Posix implementation for gpr threads. */ - -#include <grpc/support/port_platform.h> - -#ifdef GPR_POSIX_SYNC - -#include "src/core/lib/gpr/thd.h" - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/sync.h> -#include <grpc/support/thd_id.h> -#include <pthread.h> -#include <stdlib.h> -#include <string.h> - -#include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/useful.h" - -static gpr_mu g_mu; -static gpr_cv g_cv; -static int g_thread_count; -static int g_awaiting_threads; - -struct thd_arg { - void (*body)(void* arg); /* body of a thread */ - void* arg; /* argument to a thread */ - const char* name; /* name of thread. Can be nullptr. */ -}; - -static void inc_thd_count(); -static void dec_thd_count(); - -/* Body of every thread started via gpr_thd_new. */ -static void* thread_body(void* v) { - struct thd_arg a = *static_cast<struct thd_arg*>(v); - free(v); - if (a.name != nullptr) { -#if GPR_APPLE_PTHREAD_NAME - /* Apple supports 64 characters, and will truncate if it's longer. */ - pthread_setname_np(a.name); -#elif GPR_LINUX_PTHREAD_NAME - /* Linux supports 16 characters max, and will error if it's longer. */ - char buf[16]; - size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; - strncpy(buf, a.name, buf_len); - buf[buf_len] = '\0'; - pthread_setname_np(pthread_self(), buf); -#endif // GPR_APPLE_PTHREAD_NAME - } - (*a.body)(a.arg); - dec_thd_count(); - return nullptr; -} - -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options) { - int thread_started; - pthread_attr_t attr; - pthread_t p; - /* don't use gpr_malloc as we may cause an infinite recursion with - * the profiling code */ - struct thd_arg* a = static_cast<struct thd_arg*>(malloc(sizeof(*a))); - GPR_ASSERT(a != nullptr); - a->body = thd_body; - a->arg = arg; - a->name = thd_name; - inc_thd_count(); - - GPR_ASSERT(pthread_attr_init(&attr) == 0); - if (gpr_thd_options_is_detached(options)) { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == - 0); - } else { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == - 0); - } - thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); - GPR_ASSERT(pthread_attr_destroy(&attr) == 0); - if (!thread_started) { - /* don't use gpr_free, as this was allocated using malloc (see above) */ - free(a); - dec_thd_count(); - } - *t = (gpr_thd_id)p; - return thread_started; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } - -void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, nullptr); } - -/***************************************** - * Only used when fork support is enabled - */ - -static void inc_thd_count() { - if (grpc_fork_support_enabled()) { - gpr_mu_lock(&g_mu); - g_thread_count++; - gpr_mu_unlock(&g_mu); - } -} - -static void dec_thd_count() { - if (grpc_fork_support_enabled()) { - gpr_mu_lock(&g_mu); - g_thread_count--; - if (g_awaiting_threads && g_thread_count == 0) { - gpr_cv_signal(&g_cv); - } - gpr_mu_unlock(&g_mu); - } -} - -void gpr_thd_init() { - gpr_mu_init(&g_mu); - gpr_cv_init(&g_cv); - g_thread_count = 0; - g_awaiting_threads = 0; -} - -int gpr_await_threads(gpr_timespec deadline) { - gpr_mu_lock(&g_mu); - g_awaiting_threads = 1; - int res = 0; - if (g_thread_count > 0) { - res = gpr_cv_wait(&g_cv, &g_mu, deadline); - } - g_awaiting_threads = 0; - gpr_mu_unlock(&g_mu); - return res == 0; -} - -#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/gpr/thd_windows.cc b/src/core/lib/gpr/thd_windows.cc deleted file mode 100644 index b467bd2662..0000000000 --- a/src/core/lib/gpr/thd_windows.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Windows implementation for gpr threads. */ - -#include <grpc/support/port_platform.h> - -#ifdef GPR_WINDOWS - -#include "src/core/lib/gpr/thd.h" - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/thd_id.h> -#include <string.h> - -#if defined(_MSC_VER) -#define thread_local __declspec(thread) -#elif defined(__GNUC__) -#define thread_local __thread -#else -#error "Unknown compiler - please file a bug report" -#endif - -struct thd_info { - void (*body)(void* arg); /* body of a thread */ - void* arg; /* argument to a thread */ - HANDLE join_event; /* if joinable, the join event */ - int joinable; /* true if not detached */ -}; - -static thread_local struct thd_info* g_thd_info; - -/* Destroys a thread info */ -static void destroy_thread(struct thd_info* t) { - if (t->joinable) CloseHandle(t->join_event); - gpr_free(t); -} - -void gpr_thd_init(void) {} - -/* Body of every thread started via gpr_thd_new. */ -static DWORD WINAPI thread_body(void* v) { - g_thd_info = (struct thd_info*)v; - g_thd_info->body(g_thd_info->arg); - if (g_thd_info->joinable) { - BOOL ret = SetEvent(g_thd_info->join_event); - GPR_ASSERT(ret); - } else { - destroy_thread(g_thd_info); - } - return 0; -} - -int gpr_thd_new(gpr_thd_id* t, const char* thd_name, - void (*thd_body)(void* arg), void* arg, - const gpr_thd_options* options) { - HANDLE handle; - struct thd_info* info = (struct thd_info*)gpr_malloc(sizeof(*info)); - info->body = thd_body; - info->arg = arg; - *t = 0; - if (gpr_thd_options_is_joinable(options)) { - info->joinable = 1; - info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (info->join_event == NULL) { - gpr_free(info); - return 0; - } - } else { - info->joinable = 0; - } - handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); - if (handle == NULL) { - destroy_thread(info); - } else { - *t = (gpr_thd_id)info; - CloseHandle(handle); - } - return handle != NULL; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } - -void gpr_thd_join(gpr_thd_id t) { - struct thd_info* info = (struct thd_info*)t; - DWORD ret = WaitForSingleObject(info->join_event, INFINITE); - GPR_ASSERT(ret == WAIT_OBJECT_0); - destroy_thread(info); -} - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/gpr/time.cc b/src/core/lib/gpr/time.cc index 39ebeb4339..64c1c98f56 100644 --- a/src/core/lib/gpr/time.cc +++ b/src/core/lib/gpr/time.cc @@ -18,6 +18,8 @@ /* Generic implementation of time calls. */ +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include <grpc/support/time.h> #include <limits.h> diff --git a/src/core/lib/gpr/time_posix.cc b/src/core/lib/gpr/time_posix.cc index 09171c9c48..28836bfa54 100644 --- a/src/core/lib/gpr/time_posix.cc +++ b/src/core/lib/gpr/time_posix.cc @@ -17,6 +17,7 @@ */ #include <grpc/support/port_platform.h> + #include "src/core/lib/gpr/time_precise.h" #ifdef GPR_POSIX_TIME diff --git a/src/core/lib/gpr/time_precise.cc b/src/core/lib/gpr/time_precise.cc index 3c7aaabc40..1b34fd7eb1 100644 --- a/src/core/lib/gpr/time_precise.cc +++ b/src/core/lib/gpr/time_precise.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include <grpc/support/time.h> #include <stdio.h> diff --git a/src/core/lib/gpr/time_precise.h b/src/core/lib/gpr/time_precise.h index acc4ee3d1b..a63ea9dc68 100644 --- a/src/core/lib/gpr/time_precise.h +++ b/src/core/lib/gpr/time_precise.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_TIME_PRECISE_H #define GRPC_CORE_LIB_GPR_TIME_PRECISE_H +#include <grpc/support/port_platform.h> + #include <grpc/support/time.h> void gpr_precise_clock_init(void); diff --git a/src/core/lib/gpr/tls_gcc.h b/src/core/lib/gpr/tls_gcc.h index 14c59eca55..72b360b021 100644 --- a/src/core/lib/gpr/tls_gcc.h +++ b/src/core/lib/gpr/tls_gcc.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_TLS_GCC_H #define GRPC_CORE_LIB_GPR_TLS_GCC_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/support/log.h> diff --git a/src/core/lib/gpr/tls_msvc.h b/src/core/lib/gpr/tls_msvc.h index a6cc4174be..f4b3f0f50f 100644 --- a/src/core/lib/gpr/tls_msvc.h +++ b/src/core/lib/gpr/tls_msvc.h @@ -20,6 +20,8 @@ #define GRPC_CORE_LIB_GPR_TLS_MSVC_H /** Thread local storage based on ms visual c compiler primitives. +#include <grpc/support/port_platform.h> + #include tls.h to use this - and see that file for documentation */ struct gpr_msvc_thread_local { diff --git a/src/core/lib/gpr/tls_pthread.h b/src/core/lib/gpr/tls_pthread.h index 9202653dcb..a15f2f3389 100644 --- a/src/core/lib/gpr/tls_pthread.h +++ b/src/core/lib/gpr/tls_pthread.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_TLS_PTHREAD_H #define GRPC_CORE_LIB_GPR_TLS_PTHREAD_H +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> /* for GPR_ASSERT */ #include <pthread.h> diff --git a/src/core/lib/gpr/tmpfile.h b/src/core/lib/gpr/tmpfile.h index f47ec7aa63..3ce3ff5e5d 100644 --- a/src/core/lib/gpr/tmpfile.h +++ b/src/core/lib/gpr/tmpfile.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPR_TMPFILE_H #define GRPC_CORE_LIB_GPR_TMPFILE_H +#include <grpc/support/port_platform.h> + #include <stdio.h> /* Creates a temporary file from a prefix. diff --git a/src/core/lib/gprpp/atomic_with_atm.h b/src/core/lib/gprpp/atomic_with_atm.h index 6abf0bc38d..3d0021bb1c 100644 --- a/src/core/lib/gprpp/atomic_with_atm.h +++ b/src/core/lib/gprpp/atomic_with_atm.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H #define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> namespace grpc_core { diff --git a/src/core/lib/gprpp/atomic_with_std.h b/src/core/lib/gprpp/atomic_with_std.h index 83322b81c1..a4ad16e5cf 100644 --- a/src/core/lib/gprpp/atomic_with_std.h +++ b/src/core/lib/gprpp/atomic_with_std.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H #define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H +#include <grpc/support/port_platform.h> + #include <atomic> namespace grpc_core { diff --git a/src/core/lib/gprpp/inlined_vector.h b/src/core/lib/gprpp/inlined_vector.h index 2ced3d74b8..ca95aecddc 100644 --- a/src/core/lib/gprpp/inlined_vector.h +++ b/src/core/lib/gprpp/inlined_vector.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H #define GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H +#include <grpc/support/port_platform.h> + #include <cassert> #include "src/core/lib/gprpp/memory.h" diff --git a/src/core/lib/gprpp/manual_constructor.h b/src/core/lib/gprpp/manual_constructor.h index cee38abc1b..7f827ca8b7 100644 --- a/src/core/lib/gprpp/manual_constructor.h +++ b/src/core/lib/gprpp/manual_constructor.h @@ -21,6 +21,8 @@ // manually construct a region of memory with some type +#include <grpc/support/port_platform.h> + #include <stddef.h> #include <stdlib.h> #include <new> @@ -154,7 +156,7 @@ class PolymorphicManualConstructor { static_assert( manual_ctor_impl::is_one_of<DerivedType, DerivedTypes...>::value, "DerivedType must be one of the predeclared DerivedTypes"); - GPR_ASSERT(reinterpret_cast<BaseType*>(static_cast<DerivedType*>(p)) == p); + GPR_ASSERT(static_cast<BaseType*>(p) == p); } typename std::aligned_storage< diff --git a/src/core/lib/gprpp/memory.h b/src/core/lib/gprpp/memory.h index f7cd6424af..ba2f546675 100644 --- a/src/core/lib/gprpp/memory.h +++ b/src/core/lib/gprpp/memory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPRPP_MEMORY_H #define GRPC_CORE_LIB_GPRPP_MEMORY_H +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <limits> @@ -28,12 +30,12 @@ namespace grpc_core { // The alignment of memory returned by gpr_malloc(). -constexpr size_t kAllignmentForDefaultAllocationInBytes = 8; +constexpr size_t kAlignmentForDefaultAllocationInBytes = 8; // Alternative to new, since we cannot use it (for fear of libstdc++) template <typename T, typename... Args> inline T* New(Args&&... args) { - void* p = alignof(T) > kAllignmentForDefaultAllocationInBytes + void* p = alignof(T) > kAlignmentForDefaultAllocationInBytes ? gpr_malloc_aligned(sizeof(T), alignof(T)) : gpr_malloc(sizeof(T)); return new (p) T(std::forward<Args>(args)...); @@ -43,7 +45,7 @@ inline T* New(Args&&... args) { template <typename T> inline void Delete(T* p) { p->~T(); - if (alignof(T) > kAllignmentForDefaultAllocationInBytes) { + if (alignof(T) > kAlignmentForDefaultAllocationInBytes) { gpr_free_aligned(p); } else { gpr_free(p); diff --git a/src/core/lib/gprpp/orphanable.h b/src/core/lib/gprpp/orphanable.h index 78d1b01ff8..a5bc8d8efc 100644 --- a/src/core/lib/gprpp/orphanable.h +++ b/src/core/lib/gprpp/orphanable.h @@ -19,9 +19,12 @@ #ifndef GRPC_CORE_LIB_GPRPP_ORPHANABLE_H #define GRPC_CORE_LIB_GPRPP_ORPHANABLE_H +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include <grpc/support/sync.h> +#include <cinttypes> #include <memory> #include "src/core/lib/debug/trace.h" diff --git a/src/core/lib/gprpp/ref_counted.h b/src/core/lib/gprpp/ref_counted.h index ab589fc01a..46bfaf7fb8 100644 --- a/src/core/lib/gprpp/ref_counted.h +++ b/src/core/lib/gprpp/ref_counted.h @@ -19,9 +19,13 @@ #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include <grpc/support/sync.h> +#include <cinttypes> + #include "src/core/lib/debug/trace.h" #include "src/core/lib/gprpp/abstract.h" #include "src/core/lib/gprpp/debug_location.h" diff --git a/src/core/lib/gprpp/ref_counted_ptr.h b/src/core/lib/gprpp/ref_counted_ptr.h index f82ba50da3..388e2ec410 100644 --- a/src/core/lib/gprpp/ref_counted_ptr.h +++ b/src/core/lib/gprpp/ref_counted_ptr.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H +#include <grpc/support/port_platform.h> + #include <utility> #include "src/core/lib/gprpp/memory.h" @@ -31,6 +33,7 @@ template <typename T> class RefCountedPtr { public: RefCountedPtr() {} + RefCountedPtr(std::nullptr_t) {} // If value is non-null, we take ownership of a ref to it. explicit RefCountedPtr(T* value) { value_ = value; } diff --git a/src/core/lib/gprpp/thd.h b/src/core/lib/gprpp/thd.h new file mode 100644 index 0000000000..05c7ded45f --- /dev/null +++ b/src/core/lib/gprpp/thd.h @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_THD_H +#define GRPC_CORE_LIB_GPRPP_THD_H + +/** Internal thread interface. */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd_id.h> +#include <grpc/support/time.h> + +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace internal { + +/// Base class for platform-specific thread-state +class ThreadInternalsInterface { + public: + virtual ~ThreadInternalsInterface() {} + virtual void Start() GRPC_ABSTRACT; + virtual void Join() GRPC_ABSTRACT; + GRPC_ABSTRACT_BASE_CLASS +}; + +} // namespace internal + +class Thread { + public: + /// Default constructor only to allow use in structs that lack constructors + /// Does not produce a validly-constructed thread; must later + /// use placement new to construct a real thread. Does not init mu_ and cv_ + Thread() : state_(FAKE), impl_(nullptr) {} + + /// Normal constructor to create a thread with name \a thd_name, + /// which will execute a thread based on function \a thd_body + /// with argument \a arg once it is started. + /// The optional \a success argument indicates whether the thread + /// is successfully created. + Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success = nullptr); + + /// Move constructor for thread. After this is called, the other thread + /// no longer represents a living thread object + Thread(Thread&& other) : state_(other.state_), impl_(other.impl_) { + other.state_ = MOVED; + other.impl_ = nullptr; + } + + /// Move assignment operator for thread. After this is called, the other + /// thread no longer represents a living thread object. Not allowed if this + /// thread actually exists + Thread& operator=(Thread&& other) { + if (this != &other) { + // TODO(vjpai): if we can be sure that all Thread's are actually + // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. + // However, as long as threads come in structures that are + // allocated via gpr_malloc, this will not be the case, so we cannot + // assert it for the time being. + state_ = other.state_; + impl_ = other.impl_; + other.state_ = MOVED; + other.impl_ = nullptr; + } + return *this; + } + + /// The destructor is strictly optional; either the thread never came to life + /// and the constructor itself killed it or it has already been joined and + /// the Join function kills it. The destructor shouldn't have to do anything. + ~Thread() { GPR_ASSERT(impl_ == nullptr); } + + void Start() { + if (impl_ != nullptr) { + GPR_ASSERT(state_ == ALIVE); + state_ = STARTED; + impl_->Start(); + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + void Join() { + if (impl_ != nullptr) { + impl_->Join(); + grpc_core::Delete(impl_); + state_ = DONE; + impl_ = nullptr; + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + static void Init(); + static bool AwaitAll(gpr_timespec deadline); + + private: + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + + /// The thread states are as follows: + /// FAKE -- just a dummy placeholder Thread created by the default constructor + /// ALIVE -- an actual thread of control exists associated with this thread + /// STARTED -- the thread of control has been started + /// DONE -- the thread of control has completed and been joined + /// FAILED -- the thread of control never came alive + /// MOVED -- contents were moved out and we're no longer tracking them + enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; + ThreadState state_; + internal::ThreadInternalsInterface* impl_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_THD_H */ diff --git a/src/core/lib/gprpp/thd_posix.cc b/src/core/lib/gprpp/thd_posix.cc new file mode 100644 index 0000000000..2f6c2edcae --- /dev/null +++ b/src/core/lib/gprpp/thd_posix.cc @@ -0,0 +1,209 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_POSIX_SYNC + +#include "src/core/lib/gprpp/thd.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd_id.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include "src/core/lib/gpr/fork.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace { +gpr_mu g_mu; +gpr_cv g_cv; +int g_thread_count; +int g_awaiting_threads; + +class ThreadInternalsPosix; +struct thd_arg { + ThreadInternalsPosix* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + const char* name; /* name of thread. Can be nullptr. */ +}; + +class ThreadInternalsPosix + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsPosix(const char* thd_name, void (*thd_body)(void* arg), + void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + pthread_attr_t attr; + /* don't use gpr_malloc as we may cause an infinite recursion with + * the profiling code */ + thd_arg* info = static_cast<thd_arg*>(malloc(sizeof(*info))); + GPR_ASSERT(info != nullptr); + info->thread = this; + info->body = thd_body; + info->arg = arg; + info->name = thd_name; + inc_thd_count(); + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + + *success = + (pthread_create(&pthread_id_, &attr, + [](void* v) -> void* { + thd_arg arg = *static_cast<thd_arg*>(v); + free(v); + if (arg.name != nullptr) { +#if GPR_APPLE_PTHREAD_NAME + /* Apple supports 64 characters, and will + * truncate if it's longer. */ + pthread_setname_np(arg.name); +#elif GPR_LINUX_PTHREAD_NAME + /* Linux supports 16 characters max, and will + * error if it's longer. */ + char buf[16]; + size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; + strncpy(buf, arg.name, buf_len); + buf[buf_len] = '\0'; + pthread_setname_np(pthread_self(), buf); +#endif // GPR_APPLE_PTHREAD_NAME + } + + gpr_mu_lock(&arg.thread->mu_); + while (!arg.thread->started_) { + gpr_cv_wait(&arg.thread->ready_, &arg.thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&arg.thread->mu_); + + (*arg.body)(arg.arg); + dec_thd_count(); + return nullptr; + }, + info) == 0); + + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + + if (!success) { + /* don't use gpr_free, as this was allocated using malloc (see above) */ + free(info); + dec_thd_count(); + } + }; + + ~ThreadInternalsPosix() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { pthread_join(pthread_id_, nullptr); } + + private: + /***************************************** + * Only used when fork support is enabled + */ + + static void inc_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count++; + gpr_mu_unlock(&g_mu); + } + } + + static void dec_thd_count() { + if (grpc_fork_support_enabled()) { + gpr_mu_lock(&g_mu); + g_thread_count--; + if (g_awaiting_threads && g_thread_count == 0) { + gpr_cv_signal(&g_cv); + } + gpr_mu_unlock(&g_mu); + } + } + + gpr_mu mu_; + gpr_cv ready_; + bool started_; + pthread_t pthread_id_; +}; + +} // namespace + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = + grpc_core::New<ThreadInternalsPosix>(thd_name, thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} + +void Thread::Init() { + gpr_mu_init(&g_mu); + gpr_cv_init(&g_cv); + g_thread_count = 0; + g_awaiting_threads = 0; +} + +bool Thread::AwaitAll(gpr_timespec deadline) { + gpr_mu_lock(&g_mu); + g_awaiting_threads = 1; + int res = 0; + while ((g_thread_count > 0) && + (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), deadline) < 0)) { + res = gpr_cv_wait(&g_cv, &g_mu, deadline); + } + g_awaiting_threads = 0; + gpr_mu_unlock(&g_mu); + return res == 0; +} + +} // namespace grpc_core + +// The following is in the external namespace as it is exposed as C89 API +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/gprpp/thd_windows.cc b/src/core/lib/gprpp/thd_windows.cc new file mode 100644 index 0000000000..59ea02f3d2 --- /dev/null +++ b/src/core/lib/gprpp/thd_windows.cc @@ -0,0 +1,162 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows implementation for gpr threads. */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_WINDOWS + +#include "src/core/lib/gprpp/thd.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/thd_id.h> +#include <string.h> + +#include "src/core/lib/gprpp/memory.h" + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#define WIN_LAMBDA +#elif defined(__GNUC__) +#define thread_local __thread +#define WIN_LAMBDA WINAPI +#else +#error "Unknown compiler - please file a bug report" +#endif + +namespace { +class ThreadInternalsWindows; +struct thd_info { + ThreadInternalsWindows* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + HANDLE join_event; /* the join event */ +}; + +thread_local struct thd_info* g_thd_info; + +class ThreadInternalsWindows + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + + HANDLE handle; + info_ = (struct thd_info*)gpr_malloc(sizeof(*info_)); + info_->thread = this; + info_->body = thd_body; + info_->arg = arg; + + info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (info_->join_event == nullptr) { + gpr_free(info_); + *success = false; + } else { + handle = CreateThread( + nullptr, 64 * 1024, + [](void* v) WIN_LAMBDA -> DWORD { + g_thd_info = static_cast<thd_info*>(v); + gpr_mu_lock(&g_thd_info->thread->mu_); + while (!g_thd_info->thread->started_) { + gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&g_thd_info->thread->mu_); + g_thd_info->body(g_thd_info->arg); + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + return 0; + }, + info_, 0, nullptr); + if (handle == nullptr) { + destroy_thread(); + *success = false; + } else { + CloseHandle(handle); + *success = true; + } + } + } + + ~ThreadInternalsWindows() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { + DWORD ret = WaitForSingleObject(info_->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(); + } + + private: + void destroy_thread() { + CloseHandle(info_->join_event); + gpr_free(info_); + } + + gpr_mu mu_; + gpr_cv ready_; + bool started_; + thd_info* info_; +}; + +} // namespace + +namespace grpc_core { + +void Thread::Init() {} + +bool Thread::AwaitAll(gpr_timespec deadline) { + // TODO: Consider adding this if needed + return false; +} + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = grpc_core::New<ThreadInternalsWindows>(thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} + +} // namespace grpc_core + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/http/format_request.cc b/src/core/lib/http/format_request.cc index 6581bef7cd..1712344648 100644 --- a/src/core/lib/http/format_request.cc +++ b/src/core/lib/http/format_request.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/http/format_request.h" #include <stdarg.h> diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h index c1919651f9..bcc332fe6e 100644 --- a/src/core/lib/http/format_request.h +++ b/src/core/lib/http/format_request.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H #define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/lib/http/httpcli.h" diff --git a/src/core/lib/http/httpcli.cc b/src/core/lib/http/httpcli.cc index 2cdca48487..12060074c5 100644 --- a/src/core/lib/http/httpcli.cc +++ b/src/core/lib/http/httpcli.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/http/httpcli.h" #include <string.h> diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h index 72d20cc7a3..b0735081f2 100644 --- a/src/core/lib/http/httpcli.h +++ b/src/core/lib/http/httpcli.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_H #define GRPC_CORE_LIB_HTTP_HTTPCLI_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include <grpc/support/time.h> diff --git a/src/core/lib/http/httpcli_security_connector.cc b/src/core/lib/http/httpcli_security_connector.cc index d754558060..180912383f 100644 --- a/src/core/lib/http/httpcli_security_connector.cc +++ b/src/core/lib/http/httpcli_security_connector.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/http/httpcli.h" #include <string.h> diff --git a/src/core/lib/http/parser.cc b/src/core/lib/http/parser.cc index 724b6d1885..a37fdda8ea 100644 --- a/src/core/lib/http/parser.cc +++ b/src/core/lib/http/parser.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/http/parser.h" #include <stdbool.h> diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h index 5fef448019..1d2e13e831 100644 --- a/src/core/lib/http/parser.h +++ b/src/core/lib/http/parser.h @@ -19,8 +19,9 @@ #ifndef GRPC_CORE_LIB_HTTP_PARSER_H #define GRPC_CORE_LIB_HTTP_PARSER_H -#include <grpc/slice.h> #include <grpc/support/port_platform.h> + +#include <grpc/slice.h> #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/error.h" diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc index 3b11e0266d..24e11b687b 100644 --- a/src/core/lib/iomgr/call_combiner.cc +++ b/src/core/lib/iomgr/call_combiner.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/call_combiner.h" #include <inttypes.h> diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h index 4814dbf8d2..16829e5707 100644 --- a/src/core/lib/iomgr/call_combiner.h +++ b/src/core/lib/iomgr/call_combiner.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H #define GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include <grpc/support/atm.h> diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc index a7edf17e9b..e66df03182 100644 --- a/src/core/lib/iomgr/combiner.cc +++ b/src/core/lib/iomgr/combiner.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/combiner.h" #include <assert.h> diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h index c62d21a051..0d63e468df 100644 --- a/src/core/lib/iomgr/combiner.h +++ b/src/core/lib/iomgr/combiner.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_COMBINER_H #define GRPC_CORE_LIB_IOMGR_COMBINER_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include <grpc/support/atm.h> diff --git a/src/core/lib/iomgr/endpoint.cc b/src/core/lib/iomgr/endpoint.cc index 9d4b102822..92e7930111 100644 --- a/src/core/lib/iomgr/endpoint.cc +++ b/src/core/lib/iomgr/endpoint.cc @@ -16,8 +16,12 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/endpoint.h" +grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); + void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb) { ep->vtable->read(ep, slices, cb); diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h index cd53099334..15db1649fa 100644 --- a/src/core/lib/iomgr/endpoint.h +++ b/src/core/lib/iomgr/endpoint.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_H #define GRPC_CORE_LIB_IOMGR_ENDPOINT_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/slice_buffer.h> #include <grpc/support/time.h> diff --git a/src/core/lib/iomgr/endpoint_pair.h b/src/core/lib/iomgr/endpoint_pair.h index 506ffc88b4..08f9e3cabc 100644 --- a/src/core/lib/iomgr/endpoint_pair.h +++ b/src/core/lib/iomgr/endpoint_pair.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H #define GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/endpoint.h" typedef struct { diff --git a/src/core/lib/iomgr/endpoint_pair_posix.cc b/src/core/lib/iomgr/endpoint_pair_posix.cc index 3ad6b47756..49850ab3a1 100644 --- a/src/core/lib/iomgr/endpoint_pair_posix.cc +++ b/src/core/lib/iomgr/endpoint_pair_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET diff --git a/src/core/lib/iomgr/endpoint_pair_uv.cc b/src/core/lib/iomgr/endpoint_pair_uv.cc index 128a947d1b..b99d178cb6 100644 --- a/src/core/lib/iomgr/endpoint_pair_uv.cc +++ b/src/core/lib/iomgr/endpoint_pair_uv.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_UV diff --git a/src/core/lib/iomgr/endpoint_pair_windows.cc b/src/core/lib/iomgr/endpoint_pair_windows.cc index cc07ac0708..177331d681 100644 --- a/src/core/lib/iomgr/endpoint_pair_windows.cc +++ b/src/core/lib/iomgr/endpoint_pair_windows.cc @@ -16,10 +16,13 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include <errno.h> @@ -44,19 +47,19 @@ static void create_sockets(SOCKET sv[2]) { memset(&addr, 0, sizeof(addr)); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_family = AF_INET; - GPR_ASSERT(bind(lst_sock, (struct sockaddr*)&addr, sizeof(addr)) != + GPR_ASSERT(bind(lst_sock, (grpc_sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR); GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); - GPR_ASSERT(getsockname(lst_sock, (struct sockaddr*)&addr, &addr_len) != + GPR_ASSERT(getsockname(lst_sock, (grpc_sockaddr*)&addr, &addr_len) != SOCKET_ERROR); cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); GPR_ASSERT(cli_sock != INVALID_SOCKET); - GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr*)&addr, addr_len, NULL, NULL, + GPR_ASSERT(WSAConnect(cli_sock, (grpc_sockaddr*)&addr, addr_len, NULL, NULL, NULL, NULL) == 0); - svr_sock = accept(lst_sock, (struct sockaddr*)&addr, &addr_len); + svr_sock = accept(lst_sock, (grpc_sockaddr*)&addr, &addr_len); GPR_ASSERT(svr_sock != INVALID_SOCKET); closesocket(lst_sock); diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h index 8c72a439f6..f8cae4da82 100644 --- a/src/core/lib/iomgr/error.h +++ b/src/core/lib/iomgr/error.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_ERROR_H #define GRPC_CORE_LIB_IOMGR_ERROR_H +#include <grpc/support/port_platform.h> + #include <inttypes.h> #include <stdbool.h> diff --git a/src/core/lib/iomgr/error_internal.h b/src/core/lib/iomgr/error_internal.h index 6cb09c2cdb..7fde347abd 100644 --- a/src/core/lib/iomgr/error_internal.h +++ b/src/core/lib/iomgr/error_internal.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H #define GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H +#include <grpc/support/port_platform.h> + #include <inttypes.h> #include <stdbool.h> // TODO, do we need this? diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc index f84f3f8205..3ebaf181c1 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.cc +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <grpc/support/log.h> diff --git a/src/core/lib/iomgr/ev_epoll1_linux.h b/src/core/lib/iomgr/ev_epoll1_linux.h index 9a1b96bd45..ca0db7250f 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.h +++ b/src/core/lib/iomgr/ev_epoll1_linux.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H #define GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc index 3ad94a4ecd..0ef7c03056 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.cc +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <grpc/support/log.h> diff --git a/src/core/lib/iomgr/ev_epollex_linux.h b/src/core/lib/iomgr/ev_epollex_linux.h index ffa7fc7f32..e70ba72a7d 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.h +++ b/src/core/lib/iomgr/ev_epollex_linux.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H #define GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc index 5c99c72aa5..1e30f6637b 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.cc +++ b/src/core/lib/iomgr/ev_epollsig_linux.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <grpc/grpc_posix.h> diff --git a/src/core/lib/iomgr/ev_epollsig_linux.h b/src/core/lib/iomgr/ev_epollsig_linux.h index 48178d3713..2ba2f0a63b 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.h +++ b/src/core/lib/iomgr/ev_epollsig_linux.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H #define GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc index e630ddf8e0..436537bc0b 100644 --- a/src/core/lib/iomgr/ev_poll_posix.cc +++ b/src/core/lib/iomgr/ev_poll_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -36,9 +38,9 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/gpr/murmur_hash.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/tls.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/wakeup_fd_cv.h" @@ -253,8 +255,13 @@ typedef struct poll_result { } poll_result; typedef struct poll_args { + grpc_core::Thread poller_thd; gpr_cv trigger; int trigger_set; + bool harvestable; + gpr_cv harvest; + bool joinable; + gpr_cv join; struct pollfd* fds; nfds_t nfds; poll_result* result; @@ -264,15 +271,17 @@ typedef struct poll_args { // This is a 2-tiered cache, we mantain a hash table // of active poll calls, so we can wait on the result -// of that call. We also maintain a freelist of inactive -// poll threads. +// of that call. We also maintain freelists of inactive +// poll args and of dead poller threads. typedef struct poll_hash_table { poll_args* free_pollers; poll_args** active_pollers; + poll_args* dead_pollers; unsigned int size; unsigned int count; } poll_hash_table; +// TODO(kpayson64): Eliminate use of global non-POD variables poll_hash_table poll_cache; grpc_cv_fd_table g_cvfds; @@ -867,7 +876,6 @@ static grpc_error* pollset_work(grpc_pollset* pollset, grpc_pollset_worker** worker_hdl, grpc_millis deadline) { GPR_TIMER_SCOPE("pollset_work", 0); - grpc_pollset_worker worker; if (worker_hdl) *worker_hdl = &worker; grpc_error* error = GRPC_ERROR_NONE; @@ -918,7 +926,8 @@ static grpc_error* pollset_work(grpc_pollset* pollset, gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); while (keep_polling) { keep_polling = 0; - if (!pollset->kicked_without_pollers) { + if (!pollset->kicked_without_pollers || + deadline <= grpc_core::ExecCtx::Get()->Now()) { if (!added_worker) { push_front_worker(pollset, &worker); added_worker = 1; @@ -1299,6 +1308,7 @@ static void pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { static void run_poll(void* args); static void cache_poller_locked(poll_args* args); +static void cache_harvest_locked(); static void cache_insert_locked(poll_args* args) { uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd), @@ -1361,6 +1371,10 @@ static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) { poll_args* pargs = static_cast<poll_args*>(gpr_malloc(sizeof(struct poll_args))); gpr_cv_init(&pargs->trigger); + gpr_cv_init(&pargs->harvest); + gpr_cv_init(&pargs->join); + pargs->harvestable = false; + pargs->joinable = false; pargs->fds = fds; pargs->nfds = count; pargs->next = nullptr; @@ -1368,11 +1382,9 @@ static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) { pargs->trigger_set = 0; init_result(pargs); cache_poller_locked(pargs); - gpr_thd_id t_id; - gpr_thd_options opt = gpr_thd_options_default(); gpr_ref(&g_cvfds.pollcount); - gpr_thd_options_set_detached(&opt); - GPR_ASSERT(gpr_thd_new(&t_id, "grpc_poller", &run_poll, pargs, &opt)); + pargs->poller_thd = grpc_core::Thread("grpc_poller", &run_poll, pargs); + pargs->poller_thd.Start(); return pargs; } @@ -1437,7 +1449,33 @@ static void cache_destroy_locked(poll_args* args) { poll_cache.free_pollers = args->next; } - gpr_free(args); + // Now move this args to the dead poller list for later join + if (poll_cache.dead_pollers != nullptr) { + poll_cache.dead_pollers->prev = args; + } + args->prev = nullptr; + args->next = poll_cache.dead_pollers; + poll_cache.dead_pollers = args; +} + +static void cache_harvest_locked() { + while (poll_cache.dead_pollers) { + poll_args* args = poll_cache.dead_pollers; + poll_cache.dead_pollers = poll_cache.dead_pollers->next; + // Keep the list consistent in case new dead pollers get added when we + // release the lock below to wait on joining + if (poll_cache.dead_pollers) { + poll_cache.dead_pollers->prev = nullptr; + } + args->harvestable = true; + gpr_cv_signal(&args->harvest); + while (!args->joinable) { + gpr_cv_wait(&args->join, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + args->poller_thd.Join(); + gpr_free(args); + } } static void decref_poll_result(poll_result* res) { @@ -1469,6 +1507,7 @@ static void run_poll(void* args) { poll_result* result = pargs->result; int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS); gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); if (retval != 0) { result->completed = 1; result->retval = retval; @@ -1488,6 +1527,7 @@ static void run_poll(void* args) { deadline = gpr_time_add(deadline, thread_grace); pargs->trigger_set = 0; gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline); + cache_harvest_locked(); if (!pargs->trigger_set) { cache_destroy_locked(pargs); break; @@ -1496,10 +1536,15 @@ static void run_poll(void* args) { gpr_mu_unlock(&g_cvfds.mu); } - // We still have the lock here if (gpr_unref(&g_cvfds.pollcount)) { gpr_cv_signal(&g_cvfds.shutdown_cv); } + while (!pargs->harvestable) { + gpr_cv_wait(&pargs->harvest, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + pargs->joinable = true; + gpr_cv_signal(&pargs->join); gpr_mu_unlock(&g_cvfds.mu); } @@ -1512,6 +1557,7 @@ static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) { nfds_t nsockfds = 0; poll_result* result = nullptr; gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); pollcv = static_cast<grpc_cv_node*>(gpr_malloc(sizeof(grpc_cv_node))); pollcv->next = nullptr; gpr_cv pollcv_cv; @@ -1575,12 +1621,14 @@ static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) { pargs->trigger_set = 1; gpr_cv_signal(&pargs->trigger); gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); res = result->retval; errno = result->err; result->watchcount--; remove_cvn(&result->watchers, pollcv); } else if (!skip_poll) { gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); } idx = 0; @@ -1637,6 +1685,7 @@ static void global_cv_fd_table_init() { for (unsigned int i = 0; i < poll_cache.size; i++) { poll_cache.active_pollers[i] = nullptr; } + poll_cache.dead_pollers = nullptr; gpr_mu_unlock(&g_cvfds.mu); } @@ -1655,6 +1704,7 @@ static void global_cv_fd_table_shutdown() { grpc_poll_function = g_cvfds.poll; gpr_free(g_cvfds.cvfds); + cache_harvest_locked(); gpr_free(poll_cache.active_pollers); gpr_mu_unlock(&g_cvfds.mu); diff --git a/src/core/lib/iomgr/ev_poll_posix.h b/src/core/lib/iomgr/ev_poll_posix.h index f6bc624d4f..ab3cd9029e 100644 --- a/src/core/lib/iomgr/ev_poll_posix.h +++ b/src/core/lib/iomgr/ev_poll_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H #define GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/ev_posix.h" const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request); diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc index 4280794428..8b80070265 100644 --- a/src/core/lib/iomgr/ev_posix.cc +++ b/src/core/lib/iomgr/ev_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -222,26 +224,26 @@ void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { g_event_engine->fd_notify_on_write(fd, closure); } -size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; } +static size_t pollset_size(void) { return g_event_engine->pollset_size; } -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { GRPC_POLLING_API_TRACE("pollset_init(%p)", pollset); g_event_engine->pollset_init(pollset, mu); } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { GRPC_POLLING_API_TRACE("pollset_shutdown(%p)", pollset); g_event_engine->pollset_shutdown(pollset, closure); } -void grpc_pollset_destroy(grpc_pollset* pollset) { +static void pollset_destroy(grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_destroy(%p)", pollset); g_event_engine->pollset_destroy(pollset); } -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker, - grpc_millis deadline) { +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRIdPTR ") begin", pollset, deadline); grpc_error* err = g_event_engine->pollset_work(pollset, worker, deadline); @@ -250,8 +252,8 @@ grpc_error* grpc_pollset_work(grpc_pollset* pollset, return err; } -grpc_error* grpc_pollset_kick(grpc_pollset* pollset, - grpc_pollset_worker* specific_worker) { +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { GRPC_POLLING_API_TRACE("pollset_kick(%p, %p)", pollset, specific_worker); return g_event_engine->pollset_kick(pollset, specific_worker); } @@ -262,43 +264,57 @@ void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd) { g_event_engine->pollset_add_fd(pollset, fd); } -grpc_pollset_set* grpc_pollset_set_create(void) { +void pollset_global_init() {} +void pollset_global_shutdown() {} + +grpc_pollset_vtable grpc_posix_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +static grpc_pollset_set* pollset_set_create(void) { grpc_pollset_set* pss = g_event_engine->pollset_set_create(); GRPC_POLLING_API_TRACE("pollset_set_create(%p)", pss); return pss; } -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) { +static void pollset_set_destroy(grpc_pollset_set* pollset_set) { GRPC_POLLING_API_TRACE("pollset_set_destroy(%p)", pollset_set); g_event_engine->pollset_set_destroy(pollset_set); } -void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) { +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_set_add_pollset(%p, %p)", pollset_set, pollset); g_event_engine->pollset_set_add_pollset(pollset_set, pollset); } -void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) { +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { GRPC_POLLING_API_TRACE("pollset_set_del_pollset(%p, %p)", pollset_set, pollset); g_event_engine->pollset_set_del_pollset(pollset_set, pollset); } -void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { GRPC_POLLING_API_TRACE("pollset_set_add_pollset_set(%p, %p)", bag, item); g_event_engine->pollset_set_add_pollset_set(bag, item); } -void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) { +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { GRPC_POLLING_API_TRACE("pollset_set_del_pollset_set(%p, %p)", bag, item); g_event_engine->pollset_set_del_pollset_set(bag, item); } +grpc_pollset_set_vtable grpc_posix_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { GRPC_POLLING_API_TRACE("pollset_set_add_fd(%p, %d)", pollset_set, grpc_fd_wrapped_fd(fd)); diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h index 62f1162a23..6a5129a74d 100644 --- a/src/core/lib/iomgr/ev_posix.h +++ b/src/core/lib/iomgr/ev_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EV_POSIX_H #define GRPC_CORE_LIB_IOMGR_EV_POSIX_H +#include <grpc/support/port_platform.h> + #include <poll.h> #include "src/core/lib/debug/trace.h" diff --git a/src/core/lib/iomgr/ev_windows.cc b/src/core/lib/iomgr/ev_windows.cc index 697697d0b0..32c62b7a76 100644 --- a/src/core/lib/iomgr/ev_windows.cc +++ b/src/core/lib/iomgr/ev_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc index 1a2284f474..2f544b20ab 100644 --- a/src/core/lib/iomgr/exec_ctx.cc +++ b/src/core/lib/iomgr/exec_ctx.cc @@ -16,12 +16,14 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/exec_ctx.h" #include <grpc/support/log.h> #include <grpc/support/sync.h> -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/combiner.h" #include "src/core/lib/profiling/timers.h" diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h index 3d9a157627..72d0ae58c1 100644 --- a/src/core/lib/iomgr/exec_ctx.h +++ b/src/core/lib/iomgr/exec_ctx.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EXEC_CTX_H #define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include <grpc/support/cpu.h> #include <grpc/support/log.h> @@ -52,23 +54,32 @@ grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec); namespace grpc_core { /** Execution context. * A bag of data that collects information along a callstack. - * Generally created at public API entry points, and passed down as - * pointer to child functions that manipulate it. + * It is created on the stack at public API entry points, and stored internally + * as a thread-local variable. + * + * Generally, to create an exec_ctx instance, add the following line at the top + * of the public API entry point or at the start of a thread's work function : + * + * grpc_core::ExecCtx exec_ctx; + * + * Access the created ExecCtx instance using : + * grpc_core::ExecCtx::Get() * * Specific responsibilities (this may grow in the future): * - track a list of work that needs to be delayed until the top of the * call stack (this provides a convenient mechanism to run callbacks * without worrying about locking issues) - * - provide a decision maker (via grpc_exec_ctx_ready_to_finish) that provides + * - provide a decision maker (via IsReadyToFinish) that provides a * signal as to whether a borrowed thread should continue to do work or * should actively try to finish up and get this thread back to its owner * * CONVENTIONS: * - Instance of this must ALWAYS be constructed on the stack, never * heap allocated. - * - Instances and pointers to them must always be called exec_ctx. - * - Instances are always passed as the first argument to a function that - * takes it, and always as a pointer (grpc_exec_ctx is never copied). + * - Exactly one instance of ExecCtx must be created per thread. Instances must + * always be called exec_ctx. + * - Do not pass exec_ctx as a parameter to a function. Always access it using + * grpc_core::ExecCtx::Get() */ class ExecCtx { public: @@ -169,6 +180,10 @@ on outside context */ return reinterpret_cast<ExecCtx*>(gpr_tls_get(&exec_ctx_)); } + static void Set(ExecCtx* exec_ctx) { + gpr_tls_set(&exec_ctx_, reinterpret_cast<intptr_t>(exec_ctx)); + } + protected: /** Check if ready to finish */ virtual bool CheckReadyToFinish() { return false; } @@ -178,9 +193,6 @@ on outside context */ private: /** Set exec_ctx_ to exec_ctx */ - void Set(ExecCtx* exec_ctx) { - gpr_tls_set(&exec_ctx_, reinterpret_cast<intptr_t>(exec_ctx)); - } grpc_closure_list closure_list_ = GRPC_CLOSURE_LIST_INIT; CombinerData combiner_data_ = {nullptr, nullptr}; diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc index d2a050919e..b017db53f8 100644 --- a/src/core/lib/iomgr/executor.cc +++ b/src/core/lib/iomgr/executor.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/executor.h" #include <string.h> @@ -27,9 +29,9 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/gpr/spinlock.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/tls.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/exec_ctx.h" #define MAX_DEPTH 2 @@ -41,7 +43,7 @@ typedef struct { size_t depth; bool shutdown; bool queued_long_job; - gpr_thd_id id; + grpc_core::Thread thd; } thread_state; static thread_state* g_thread_state; @@ -99,13 +101,13 @@ void grpc_executor_set_threading(bool threading) { for (size_t i = 0; i < g_max_threads; i++) { gpr_mu_init(&g_thread_state[i].mu); gpr_cv_init(&g_thread_state[i].cv); + g_thread_state[i].thd = grpc_core::Thread(); g_thread_state[i].elems = GRPC_CLOSURE_LIST_INIT; } - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[0].id, "grpc_executor", executor_thread, - &g_thread_state[0], &opt); + g_thread_state[0].thd = + grpc_core::Thread("grpc_executor", executor_thread, &g_thread_state[0]); + g_thread_state[0].thd.Start(); } else { if (cur_threads == 0) return; for (size_t i = 0; i < g_max_threads; i++) { @@ -119,7 +121,7 @@ void grpc_executor_set_threading(bool threading) { gpr_spinlock_lock(&g_adding_thread_lock); gpr_spinlock_unlock(&g_adding_thread_lock); for (gpr_atm i = 0; i < g_cur_threads; i++) { - gpr_thd_join(g_thread_state[i].id); + g_thread_state[i].thd.Join(); } gpr_atm_no_barrier_store(&g_cur_threads, 0); for (size_t i = 0; i < g_max_threads; i++) { @@ -262,10 +264,10 @@ static void executor_push(grpc_closure* closure, grpc_error* error, if (cur_thread_count < g_max_threads) { gpr_atm_no_barrier_store(&g_cur_threads, cur_thread_count + 1); - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[cur_thread_count].id, "gpr_executor", - executor_thread, &g_thread_state[cur_thread_count], &opt); + g_thread_state[cur_thread_count].thd = + grpc_core::Thread("grpc_executor", executor_thread, + &g_thread_state[cur_thread_count]); + g_thread_state[cur_thread_count].thd.Start(); } gpr_spinlock_unlock(&g_adding_thread_lock); } diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h index e16f11aa21..68d540af55 100644 --- a/src/core/lib/iomgr/executor.h +++ b/src/core/lib/iomgr/executor.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_EXECUTOR_H #define GRPC_CORE_LIB_IOMGR_EXECUTOR_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/closure.h" typedef enum { diff --git a/src/core/lib/iomgr/fork_posix.cc b/src/core/lib/iomgr/fork_posix.cc index c9a65c5702..f8645ab157 100644 --- a/src/core/lib/iomgr/fork_posix.cc +++ b/src/core/lib/iomgr/fork_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_FORK @@ -27,7 +29,7 @@ #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/timer_manager.h" @@ -51,7 +53,7 @@ void grpc_prefork() { grpc_timer_manager_set_threading(false); grpc_executor_set_threading(false); grpc_core::ExecCtx::Get()->Flush(); - if (!gpr_await_threads( + if (!grpc_core::Thread::AwaitAll( gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(3, GPR_TIMESPAN)))) { gpr_log(GPR_ERROR, "gRPC thread still active! Cannot fork!"); diff --git a/src/core/lib/iomgr/fork_windows.cc b/src/core/lib/iomgr/fork_windows.cc index f9986f33c7..798f671bdf 100644 --- a/src/core/lib/iomgr/fork_windows.cc +++ b/src/core/lib/iomgr/fork_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifndef GRPC_POSIX_FORK diff --git a/src/core/lib/iomgr/gethostname_fallback.cc b/src/core/lib/iomgr/gethostname_fallback.cc index 81e2c7aeec..65ae818723 100644 --- a/src/core/lib/iomgr/gethostname_fallback.cc +++ b/src/core/lib/iomgr/gethostname_fallback.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/gethostname.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/gethostname_host_name_max.cc b/src/core/lib/iomgr/gethostname_host_name_max.cc index ae95788a1e..79f5daa8f3 100644 --- a/src/core/lib/iomgr/gethostname_host_name_max.cc +++ b/src/core/lib/iomgr/gethostname_host_name_max.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/gethostname.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/gethostname_sysconf.cc b/src/core/lib/iomgr/gethostname_sysconf.cc index 3d74e03338..92c5de3338 100644 --- a/src/core/lib/iomgr/gethostname_sysconf.cc +++ b/src/core/lib/iomgr/gethostname_sysconf.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/gethostname.h" #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/iocp_windows.cc b/src/core/lib/iomgr/iocp_windows.cc index 4716872ce4..ce77231036 100644 --- a/src/core/lib/iomgr/iocp_windows.cc +++ b/src/core/lib/iomgr/iocp_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -28,7 +30,7 @@ #include <grpc/support/log_windows.h> #include "src/core/lib/debug/stats.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/socket_windows.h" diff --git a/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h index 75b0ff4a92..5079ea5d84 100644 --- a/src/core/lib/iomgr/iocp_windows.h +++ b/src/core/lib/iomgr/iocp_windows.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H #define GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H +#include <grpc/support/port_platform.h> + #include <grpc/support/sync.h> #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc index 70a80e1998..468814eaee 100644 --- a/src/core/lib/iomgr/iomgr.cc +++ b/src/core/lib/iomgr/iomgr.cc @@ -31,8 +31,8 @@ #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" @@ -47,6 +47,7 @@ static grpc_iomgr_object g_root_object; void grpc_iomgr_init() { grpc_core::ExecCtx exec_ctx; + grpc_determine_iomgr_platform(); g_shutdown = 0; gpr_mu_init(&g_mu); gpr_cv_init(&g_rcv); diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h index c7cde7ea59..e6d66e545c 100644 --- a/src/core/lib/iomgr/iomgr.h +++ b/src/core/lib/iomgr/iomgr.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H #define GRPC_CORE_LIB_IOMGR_IOMGR_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" /** Initializes the iomgr. */ diff --git a/src/core/lib/iomgr/iomgr_custom.cc b/src/core/lib/iomgr/iomgr_custom.cc new file mode 100644 index 0000000000..d34c8e7cd1 --- /dev/null +++ b/src/core/lib/iomgr/iomgr_custom.cc @@ -0,0 +1,63 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/thd_id.h> + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/pollset_set_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" + +gpr_thd_id g_init_thread; + +static void iomgr_platform_init(void) { + grpc_core::ExecCtx exec_ctx; + grpc_executor_set_threading(false); + g_init_thread = gpr_thd_currentid(); + grpc_pollset_global_init(); +} +static void iomgr_platform_flush(void) {} +static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller) { + grpc_custom_endpoint_init(socket); + grpc_custom_timer_init(timer); + grpc_custom_pollset_init(poller); + grpc_custom_pollset_set_init(); + grpc_custom_resolver_init(resolver); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#ifdef GRPC_CUSTOM_SOCKET +grpc_iomgr_platform_vtable* grpc_default_iomgr_platform_vtable() { + return &vtable; +} +#endif diff --git a/src/core/lib/iomgr/iomgr_uv.h b/src/core/lib/iomgr/iomgr_custom.h index a382f0a5ae..ceb6c65db2 100644 --- a/src/core/lib/iomgr/iomgr_uv.h +++ b/src/core/lib/iomgr/iomgr_custom.h @@ -16,22 +16,32 @@ * */ -#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_UV_H -#define GRPC_CORE_LIB_IOMGR_IOMGR_UV_H +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H -#include "src/core/lib/iomgr/iomgr_internal.h" +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" #include <grpc/support/thd_id.h> /* The thread ID of the thread on which grpc was initialized. Used to verify - * that all calls into libuv are made on that same thread */ + * that all calls into the custom iomgr are made on that same thread */ extern gpr_thd_id g_init_thread; -#ifdef GRPC_UV_THREAD_CHECK -#define GRPC_UV_ASSERT_SAME_THREAD() \ +#ifdef GRPC_CUSTOM_IOMGR_THREAD_CHECK +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() \ GPR_ASSERT(gpr_thd_currentid() == g_init_thread) #else -#define GRPC_UV_ASSERT_SAME_THREAD() +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() #endif /* GRPC_UV_THREAD_CHECK */ -#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_UV_H */ +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller); + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H */ diff --git a/src/core/lib/iomgr/iomgr_internal.cc b/src/core/lib/iomgr/iomgr_internal.cc new file mode 100644 index 0000000000..32dbabb79d --- /dev/null +++ b/src/core/lib/iomgr/iomgr_internal.cc @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stddef.h> + +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +static grpc_iomgr_platform_vtable* iomgr_platform_vtable = nullptr; + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable) { + iomgr_platform_vtable = vtable; +} + +void grpc_determine_iomgr_platform() { + if (iomgr_platform_vtable == nullptr) { + grpc_set_default_iomgr_platform(); + } +} + +void grpc_iomgr_platform_init() { iomgr_platform_vtable->init(); } + +void grpc_iomgr_platform_flush() { iomgr_platform_vtable->flush(); } + +void grpc_iomgr_platform_shutdown() { iomgr_platform_vtable->shutdown(); } diff --git a/src/core/lib/iomgr/iomgr_internal.h b/src/core/lib/iomgr/iomgr_internal.h index 20b3cb70d0..b011d9c7b1 100644 --- a/src/core/lib/iomgr/iomgr_internal.h +++ b/src/core/lib/iomgr/iomgr_internal.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H #define GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include "src/core/lib/iomgr/iomgr.h" @@ -29,9 +31,21 @@ typedef struct grpc_iomgr_object { struct grpc_iomgr_object* prev; } grpc_iomgr_object; +typedef struct grpc_iomgr_platform_vtable { + void (*init)(void); + void (*flush)(void); + void (*shutdown)(void); +} grpc_iomgr_platform_vtable; + void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name); void grpc_iomgr_unregister_object(grpc_iomgr_object* obj); +void grpc_determine_iomgr_platform(); + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable); + +void grpc_set_default_iomgr_platform(); + void grpc_iomgr_platform_init(void); /** flush any globally queued work from iomgr */ void grpc_iomgr_platform_flush(void); diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc index f8f6fe2353..66c9cb7ff7 100644 --- a/src/core/lib/iomgr/iomgr_posix.cc +++ b/src/core/lib/iomgr/iomgr_posix.cc @@ -16,25 +16,52 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" #include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_posix_pollset_vtable; +extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_posix_resolver_vtable; -void grpc_iomgr_platform_init(void) { +static void iomgr_platform_init(void) { grpc_wakeup_fd_global_init(); grpc_event_engine_init(); } -void grpc_iomgr_platform_flush(void) {} +static void iomgr_platform_flush(void) {} -void grpc_iomgr_platform_shutdown(void) { +static void iomgr_platform_shutdown(void) { grpc_event_engine_shutdown(); grpc_wakeup_fd_global_destroy(); } +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_posix_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_posix_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + #endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr_posix.h b/src/core/lib/iomgr/iomgr_posix.h index f7a4af6a89..54ec46e1bb 100644 --- a/src/core/lib/iomgr/iomgr_posix.h +++ b/src/core/lib/iomgr/iomgr_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H #define GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/iomgr_internal.h" #endif /* GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H */ diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc index c11bc28ad7..4a984446db 100644 --- a/src/core/lib/iomgr/iomgr_uv.cc +++ b/src/core/lib/iomgr/iomgr_uv.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2016 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,25 @@ * */ -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV +#include <grpc/support/port_platform.h> -#include <grpc/support/thd_id.h> +#include "src/core/lib/iomgr/port.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset_uv.h" -#include "src/core/lib/iomgr/tcp_uv.h" +#if defined(GRPC_CUSTOM_SOCKET) && defined(GRPC_UV) -gpr_thd_id g_init_thread; +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" -void grpc_iomgr_platform_init(void) { - grpc_core::ExecCtx exec_ctx; - grpc_pollset_global_init(); +extern grpc_socket_vtable grpc_uv_socket_vtable; +extern grpc_custom_resolver_vtable uv_resolver_vtable; +extern grpc_custom_timer_vtable uv_timer_vtable; +extern grpc_custom_poller_vtable uv_pollset_vtable; - grpc_executor_set_threading(false); - g_init_thread = gpr_thd_currentid(); +void grpc_set_default_iomgr_platform() { + grpc_custom_iomgr_init(&grpc_uv_socket_vtable, &uv_resolver_vtable, + &uv_timer_vtable, &uv_pollset_vtable); } -void grpc_iomgr_platform_flush(void) {} -void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } - -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/iomgr_windows.cc b/src/core/lib/iomgr/iomgr_windows.cc index 630370166d..cdef89cbf0 100644 --- a/src/core/lib/iomgr/iomgr_windows.cc +++ b/src/core/lib/iomgr/iomgr_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -27,7 +29,18 @@ #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tcp_server_vtable grpc_windows_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_windows_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_windows_pollset_vtable; +extern grpc_pollset_set_vtable grpc_windows_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_windows_resolver_vtable; /* Windows' io manager is going to be fully designed using IO completion ports. All of what we're doing here is basically make sure that @@ -44,18 +57,31 @@ static void winsock_shutdown(void) { GPR_ASSERT(status == 0); } -void grpc_iomgr_platform_init(void) { +static void iomgr_platform_init(void) { winsock_init(); grpc_iocp_init(); grpc_pollset_global_init(); } -void grpc_iomgr_platform_flush(void) { grpc_iocp_flush(); } +static void iomgr_platform_flush(void) { grpc_iocp_flush(); } -void grpc_iomgr_platform_shutdown(void) { +static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); grpc_iocp_shutdown(); winsock_shutdown(); } +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_windows_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_windows_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_windows_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_windows_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_windows_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/is_epollexclusive_available.cc b/src/core/lib/iomgr/is_epollexclusive_available.cc index c46674c1dd..036b77866f 100644 --- a/src/core/lib/iomgr/is_epollexclusive_available.cc +++ b/src/core/lib/iomgr/is_epollexclusive_available.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include "src/core/lib/iomgr/is_epollexclusive_available.h" diff --git a/src/core/lib/iomgr/is_epollexclusive_available.h b/src/core/lib/iomgr/is_epollexclusive_available.h index 9ae9c5c191..8a44113c3f 100644 --- a/src/core/lib/iomgr/is_epollexclusive_available.h +++ b/src/core/lib/iomgr/is_epollexclusive_available.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H #define GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #ifdef __cplusplus diff --git a/src/core/lib/iomgr/load_file.cc b/src/core/lib/iomgr/load_file.cc index 7f5f642c98..f6431d0f1c 100644 --- a/src/core/lib/iomgr/load_file.cc +++ b/src/core/lib/iomgr/load_file.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/load_file.h" #include <errno.h> diff --git a/src/core/lib/iomgr/load_file.h b/src/core/lib/iomgr/load_file.h index a7336527ce..1cb2b5de73 100644 --- a/src/core/lib/iomgr/load_file.h +++ b/src/core/lib/iomgr/load_file.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_LOAD_FILE_H #define GRPC_CORE_LIB_IOMGR_LOAD_FILE_H +#include <grpc/support/port_platform.h> + #include <stdio.h> #include <grpc/slice.h> diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc index 7b194e3db5..5b6b79fa91 100644 --- a/src/core/lib/iomgr/lockfree_event.cc +++ b/src/core/lib/iomgr/lockfree_event.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/lockfree_event.h" #include <grpc/support/log.h> diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h index 3bd3fd72f1..83de656f5f 100644 --- a/src/core/lib/iomgr/lockfree_event.h +++ b/src/core/lib/iomgr/lockfree_event.h @@ -21,6 +21,8 @@ /* Lock free event notification for file descriptors */ +#include <grpc/support/port_platform.h> + #include <grpc/support/atm.h> #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/iomgr/nameser.h b/src/core/lib/iomgr/nameser.h index daed6de518..22a00cdab8 100644 --- a/src/core/lib/iomgr/nameser.h +++ b/src/core/lib/iomgr/nameser.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_NAMESER_H #define GRPC_CORE_LIB_IOMGR_NAMESER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_HAVE_ARPA_NAMESER diff --git a/src/core/lib/iomgr/network_status_tracker.cc b/src/core/lib/iomgr/network_status_tracker.cc index 73f8fbf9fb..d4b7f4a57d 100644 --- a/src/core/lib/iomgr/network_status_tracker.cc +++ b/src/core/lib/iomgr/network_status_tracker.cc @@ -16,8 +16,10 @@ * */ -#include "src/core/lib/iomgr/network_status_tracker.h" +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/network_status_tracker.h" void grpc_network_status_shutdown(void) {} diff --git a/src/core/lib/iomgr/network_status_tracker.h b/src/core/lib/iomgr/network_status_tracker.h index 32244d9b77..198877f60f 100644 --- a/src/core/lib/iomgr/network_status_tracker.h +++ b/src/core/lib/iomgr/network_status_tracker.h @@ -18,6 +18,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H #define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/endpoint.h" void grpc_network_status_init(void); diff --git a/src/core/lib/iomgr/polling_entity.cc b/src/core/lib/iomgr/polling_entity.cc index 126f6f45d6..9f164f65b0 100644 --- a/src/core/lib/iomgr/polling_entity.cc +++ b/src/core/lib/iomgr/polling_entity.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/support/alloc.h> #include <grpc/support/log.h> diff --git a/src/core/lib/iomgr/polling_entity.h b/src/core/lib/iomgr/polling_entity.h index 0102d32c11..a95e08524c 100644 --- a/src/core/lib/iomgr/polling_entity.h +++ b/src/core/lib/iomgr/polling_entity.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H #define GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/iomgr/pollset_set.h" diff --git a/src/core/lib/iomgr/pollset.cc b/src/core/lib/iomgr/pollset.cc new file mode 100644 index 0000000000..ebfef1dbc7 --- /dev/null +++ b/src/core/lib/iomgr/pollset.cc @@ -0,0 +1,56 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/pollset.h" + +grpc_pollset_vtable* grpc_pollset_impl; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable) { + grpc_pollset_impl = vtable; +} + +void grpc_pollset_global_init() { grpc_pollset_impl->global_init(); } + +void grpc_pollset_global_shutdown() { grpc_pollset_impl->global_shutdown(); } + +void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + grpc_pollset_impl->init(pollset, mu); +} + +void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + grpc_pollset_impl->shutdown(pollset, closure); +} + +void grpc_pollset_destroy(grpc_pollset* pollset) { + grpc_pollset_impl->destroy(pollset); +} + +grpc_error* grpc_pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { + return grpc_pollset_impl->work(pollset, worker, deadline); +} + +grpc_error* grpc_pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + return grpc_pollset_impl->kick(pollset, specific_worker); +} + +size_t grpc_pollset_size(void) { return grpc_pollset_impl->pollset_size(); } diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h index 6bb3cd3e0c..28472b360d 100644 --- a/src/core/lib/iomgr/pollset.h +++ b/src/core/lib/iomgr/pollset.h @@ -20,6 +20,7 @@ #define GRPC_CORE_LIB_IOMGR_POLLSET_H #include <grpc/support/port_platform.h> + #include <grpc/support/sync.h> #include <grpc/support/time.h> @@ -37,6 +38,24 @@ extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount; typedef struct grpc_pollset grpc_pollset; typedef struct grpc_pollset_worker grpc_pollset_worker; +typedef struct grpc_pollset_vtable { + void (*global_init)(void); + void (*global_shutdown)(void); + void (*init)(grpc_pollset* pollset, gpr_mu** mu); + void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure); + void (*destroy)(grpc_pollset* pollset); + grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker, + grpc_millis deadline); + grpc_error* (*kick)(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker); + size_t (*pollset_size)(void); +} grpc_pollset_vtable; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable); + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + size_t grpc_pollset_size(void); /* Initialize a pollset: assumes *pollset contains all zeros */ void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu); diff --git a/src/core/lib/iomgr/pollset_custom.cc b/src/core/lib/iomgr/pollset_custom.cc new file mode 100644 index 0000000000..04bd104055 --- /dev/null +++ b/src/core/lib/iomgr/pollset_custom.cc @@ -0,0 +1,106 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <stddef.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/timer.h" + +#include "src/core/lib/debug/trace.h" + +static grpc_custom_poller_vtable* poller_vtable; + +struct grpc_pollset { + gpr_mu mu; +}; + +static size_t pollset_size() { return sizeof(grpc_pollset); } + +static void pollset_global_init() { poller_vtable->init(); } + +static void pollset_global_shutdown() { poller_vtable->shutdown(); } + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); +} + +static void pollset_destroy(grpc_pollset* pollset) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_unlock(&pollset->mu); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + size_t timeout = 0; + if (deadline > now) { + timeout = deadline - now; + } + // We yield here because the poll() call might yield + // control back to the application + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + poller_vtable->poll(timeout); + grpc_core::ExecCtx::Set(curr); + grpc_core::ExecCtx::Get()->InvalidateNow(); + if (grpc_core::ExecCtx::Get()->HasWork()) { + grpc_core::ExecCtx::Get()->Flush(); + } + gpr_mu_lock(&pollset->mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + poller_vtable->kick(); + return GRPC_ERROR_NONE; +} + +grpc_pollset_vtable custom_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable) { + poller_vtable = vtable; + grpc_set_pollset_vtable(&custom_pollset_vtable); +} diff --git a/src/core/lib/iomgr/pollset_custom.h b/src/core/lib/iomgr/pollset_custom.h new file mode 100644 index 0000000000..9e2027f7f4 --- /dev/null +++ b/src/core/lib/iomgr/pollset_custom.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include <stddef.h> + +typedef struct grpc_custom_poller_vtable { + void (*init)(); + void (*poll)(size_t timeout_ms); + void (*kick)(); + void (*shutdown)(); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H */ diff --git a/src/core/lib/iomgr/pollset_set.cc b/src/core/lib/iomgr/pollset_set.cc new file mode 100644 index 0000000000..42a647a737 --- /dev/null +++ b/src/core/lib/iomgr/pollset_set.cc @@ -0,0 +1,55 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set_vtable* grpc_pollset_set_impl; + +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable) { + grpc_pollset_set_impl = vtable; +} + +grpc_pollset_set* grpc_pollset_set_create() { + return grpc_pollset_set_impl->create(); +} + +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) { + grpc_pollset_set_impl->destroy(pollset_set); +} + +void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + grpc_pollset_set_impl->add_pollset(pollset_set, pollset); +} + +void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + grpc_pollset_set_impl->del_pollset(pollset_set, pollset); +} + +void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + grpc_pollset_set_impl->add_pollset_set(bag, item); +} + +void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + grpc_pollset_set_impl->del_pollset_set(bag, item); +} diff --git a/src/core/lib/iomgr/pollset_set.h b/src/core/lib/iomgr/pollset_set.h index a94d0afe75..d3355b8ff8 100644 --- a/src/core/lib/iomgr/pollset_set.h +++ b/src/core/lib/iomgr/pollset_set.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_H #define GRPC_CORE_LIB_IOMGR_POLLSET_SET_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/pollset.h" /* A grpc_pollset_set is a set of pollsets that are interested in an @@ -28,6 +30,17 @@ typedef struct grpc_pollset_set grpc_pollset_set; +typedef struct grpc_pollset_set_vtable { + grpc_pollset_set* (*create)(void); + void (*destroy)(grpc_pollset_set* pollset_set); + void (*add_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*del_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*add_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); + void (*del_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); +} grpc_pollset_set_vtable; + +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable); + grpc_pollset_set* grpc_pollset_set_create(void); void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set); void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, diff --git a/src/core/lib/iomgr/pollset_set_custom.cc b/src/core/lib/iomgr/pollset_set_custom.cc new file mode 100644 index 0000000000..b1ee66020d --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_custom.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set* pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +static grpc_pollset_set_vtable vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + +void grpc_custom_pollset_set_init() { grpc_set_pollset_set_vtable(&vtable); } diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/iomgr/pollset_set_custom.h index 214aaa600a..80e19a1fef 100644 --- a/src/core/lib/iomgr/timer_uv.h +++ b/src/core/lib/iomgr/pollset_set_custom.h @@ -1,6 +1,6 @@ /* * - * Copyright 2016 gRPC authors. + * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,11 @@ * */ -#ifndef GRPC_CORE_LIB_IOMGR_TIMER_UV_H -#define GRPC_CORE_LIB_IOMGR_TIMER_UV_H +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H -#include "src/core/lib/iomgr/exec_ctx.h" +#include <grpc/support/port_platform.h> -struct grpc_timer { - grpc_closure* closure; - /* This is actually a uv_timer_t*, but we want to keep platform-specific - types out of headers */ - void* uv_timer; - int pending; -}; +void grpc_custom_pollset_set_init(); -#endif /* GRPC_CORE_LIB_IOMGR_TIMER_UV_H */ +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H */ diff --git a/src/core/lib/iomgr/pollset_set_uv.cc b/src/core/lib/iomgr/pollset_set_uv.cc deleted file mode 100644 index ac5dade8a5..0000000000 --- a/src/core/lib/iomgr/pollset_set_uv.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include "src/core/lib/iomgr/pollset_set.h" - -grpc_pollset_set* grpc_pollset_set_create(void) { - return (grpc_pollset_set*)((intptr_t)0xdeafbeef); -} - -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} - -void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_set_windows.cc b/src/core/lib/iomgr/pollset_set_windows.cc index 85edc9dee1..bb9e7f5d28 100644 --- a/src/core/lib/iomgr/pollset_set_windows.cc +++ b/src/core/lib/iomgr/pollset_set_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdint.h> #include "src/core/lib/iomgr/port.h" @@ -23,22 +25,27 @@ #include "src/core/lib/iomgr/pollset_set_windows.h" -grpc_pollset_set* grpc_pollset_set_create(void) { +static grpc_pollset_set* pollset_set_create(void) { return (grpc_pollset_set*)((intptr_t)0xdeafbeef); } -void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} +static void pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} -void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} -void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} -void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} -void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, - grpc_pollset_set* item) {} +grpc_pollset_set_vtable grpc_windows_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_set_windows.h b/src/core/lib/iomgr/pollset_set_windows.h index 1173f760a0..5ac9d1823b 100644 --- a/src/core/lib/iomgr/pollset_set_windows.h +++ b/src/core/lib/iomgr/pollset_set_windows.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H #define GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/pollset_set.h" #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H */ diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc index d9e5ad81be..bade6eae6c 100644 --- a/src/core/lib/iomgr/pollset_uv.cc +++ b/src/core/lib/iomgr/pollset_uv.cc @@ -16,141 +16,78 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_UV -#include <uv.h> - -#include <string.h> - #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/sync.h> +#include "src/core/lib/iomgr/pollset_custom.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/pollset_uv.h" - -#include "src/core/lib/debug/trace.h" - -grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount"); - -struct grpc_pollset { - uv_timer_t* timer; - int shutting_down; -}; +#include <uv.h> /* Indicates that grpc_pollset_work should run an iteration of the UV loop before running callbacks. This defaults to 1, and should be disabled if grpc_pollset_work will be called within the callstack of uv_run */ -int grpc_pollset_work_run_loop; +int grpc_pollset_work_run_loop = 1; -gpr_mu grpc_polling_mu; +static bool g_kicked = false; -/* This is used solely to kick the uv loop, by setting a callback to be run - immediately in the next loop iteration. - Note: In the future, if there is a bug that involves missing wakeups in the - future, try adding a uv_async_t to kick the loop differently */ -uv_timer_t* dummy_uv_handle; +typedef struct uv_poller_handle { + uv_timer_t poll_timer; + uv_timer_t kick_timer; + int refs; +} uv_poller_handle; -size_t grpc_pollset_size() { return sizeof(grpc_pollset); } +static uv_poller_handle* g_handle; -void dummy_timer_cb(uv_timer_t* handle) {} - -void dummy_handle_close_cb(uv_handle_t* handle) { gpr_free(handle); } - -void grpc_pollset_global_init(void) { - gpr_mu_init(&grpc_polling_mu); - dummy_uv_handle = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), dummy_uv_handle); - grpc_pollset_work_run_loop = 1; -} - -void grpc_pollset_global_shutdown(void) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_destroy(&grpc_polling_mu); - uv_close((uv_handle_t*)dummy_uv_handle, dummy_handle_close_cb); +static void init() { + g_handle = (uv_poller_handle*)gpr_malloc(sizeof(uv_poller_handle)); + g_handle->refs = 2; + uv_timer_init(uv_default_loop(), &g_handle->poll_timer); + uv_timer_init(uv_default_loop(), &g_handle->kick_timer); } -static void timer_run_cb(uv_timer_t* timer) {} +static void empty_timer_cb(uv_timer_t* handle) {} -static void timer_close_cb(uv_handle_t* handle) { - handle->data = (void*)1; - gpr_free(handle); -} +static void kick_timer_cb(uv_timer_t* handle) { g_kicked = false; } -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { - GRPC_UV_ASSERT_SAME_THREAD(); - *mu = &grpc_polling_mu; - pollset->timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), pollset->timer); - pollset->shutting_down = 0; +static void run_loop(size_t timeout) { + if (grpc_pollset_work_run_loop) { + if (timeout == 0) { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } else { + uv_timer_start(&g_handle->poll_timer, empty_timer_cb, timeout, 0); + uv_run(uv_default_loop(), UV_RUN_ONCE); + uv_timer_stop(&g_handle->poll_timer); + } + } } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { - GPR_ASSERT(!pollset->shutting_down); - GRPC_UV_ASSERT_SAME_THREAD(); - pollset->shutting_down = 1; - if (grpc_pollset_work_run_loop) { - // Drain any pending UV callbacks without blocking - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } else { - // kick the loop once - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); +static void kick() { + if (!g_kicked) { + g_kicked = true; + uv_timer_start(&g_handle->kick_timer, kick_timer_cb, 0, 0); } - GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); } -void grpc_pollset_destroy(grpc_pollset* pollset) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_close((uv_handle_t*)pollset->timer, timer_close_cb); - // timer.data is a boolean indicating that the timer has finished closing - pollset->timer->data = (void*)0; - if (grpc_pollset_work_run_loop) { - while (!pollset->timer->data) { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } +static void close_timer_cb(uv_handle_t* handle) { + g_handle->refs--; + if (g_handle->refs == 0) { + gpr_free(g_handle); } } -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker_hdl, - grpc_millis deadline) { - uint64_t timeout; - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_unlock(&grpc_polling_mu); +static void shutdown() { + uv_close((uv_handle_t*)&g_handle->poll_timer, close_timer_cb); + uv_close((uv_handle_t*)&g_handle->kick_timer, close_timer_cb); if (grpc_pollset_work_run_loop) { - grpc_millis now = grpc_core::ExecCtx::Get()->Now(); - if (deadline >= now) { - timeout = deadline - now; - } else { - timeout = 0; - } - /* We special-case timeout=0 so that we don't bother with the timer when - the loop won't block anyway */ - if (timeout > 0) { - uv_timer_start(pollset->timer, timer_run_cb, timeout, 0); - /* Run until there is some I/O activity or the timer triggers. It doesn't - matter which happens */ - uv_run(uv_default_loop(), UV_RUN_ONCE); - uv_timer_stop(pollset->timer); - } else { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } - } - if (!grpc_closure_list_empty(*grpc_core::ExecCtx::Get()->closure_list())) { - grpc_core::ExecCtx::Get()->Flush(); + GPR_ASSERT(uv_run(uv_default_loop(), UV_RUN_DEFAULT) == 0); } - gpr_mu_lock(&grpc_polling_mu); - return GRPC_ERROR_NONE; } -grpc_error* grpc_pollset_kick(grpc_pollset* pollset, - grpc_pollset_worker* specific_worker) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); - return GRPC_ERROR_NONE; -} +grpc_custom_poller_vtable uv_pollset_vtable = {init, run_loop, kick, shutdown}; #endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_uv.h b/src/core/lib/iomgr/pollset_uv.h index 566c110ca6..de82bcc1d3 100644 --- a/src/core/lib/iomgr/pollset_uv.h +++ b/src/core/lib/iomgr/pollset_uv.h @@ -21,7 +21,12 @@ extern int grpc_pollset_work_run_loop; -void grpc_pollset_global_init(void); -void grpc_pollset_global_shutdown(void); +typedef struct grpc_custom_poller_vtable { + void (*init)(void); + void (*run_loop)(int blocking); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_global_init(grpc_custom_poller_vtable* vtable); +void grpc_custom_pollset_global_shutdown(void); #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc index 240a24dee1..e9a808d8ad 100644 --- a/src/core/lib/iomgr/pollset_windows.cc +++ b/src/core/lib/iomgr/pollset_windows.cc @@ -16,13 +16,15 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET #include <grpc/support/log.h> -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/pollset.h" @@ -36,7 +38,7 @@ gpr_mu grpc_polling_mu; static grpc_pollset_worker* g_active_poller; static grpc_pollset_worker g_global_root_worker; -void grpc_pollset_global_init(void) { +static void pollset_global_init(void) { gpr_mu_init(&grpc_polling_mu); g_active_poller = NULL; g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = @@ -44,7 +46,7 @@ void grpc_pollset_global_init(void) { &g_global_root_worker; } -void grpc_pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } +static void pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } static void remove_worker(grpc_pollset_worker* worker, grpc_pollset_worker_link_type type) { @@ -78,21 +80,21 @@ static void push_front_worker(grpc_pollset_worker* root, worker->links[type].next->links[type].prev = worker; } -size_t grpc_pollset_size(void) { return sizeof(grpc_pollset); } +static size_t pollset_size(void) { return sizeof(grpc_pollset); } /* There isn't really any such thing as a pollset under Windows, due to the nature of the IO completion ports. We're still going to provide a minimal set of features for the sake of the rest of grpc. But grpc_pollset_work won't actually do any polling, and return as quickly as possible. */ -void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { *mu = &grpc_polling_mu; pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = &pollset->root_worker; } -void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { pollset->shutting_down = 1; grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); if (!pollset->is_iocp_worker) { @@ -102,11 +104,11 @@ void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { } } -void grpc_pollset_destroy(grpc_pollset* pollset) {} +static void pollset_destroy(grpc_pollset* pollset) {} -grpc_error* grpc_pollset_work(grpc_pollset* pollset, - grpc_pollset_worker** worker_hdl, - grpc_millis deadline) { +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { grpc_pollset_worker worker; if (worker_hdl) *worker_hdl = &worker; @@ -180,8 +182,8 @@ done: return GRPC_ERROR_NONE; } -grpc_error* grpc_pollset_kick(grpc_pollset* p, - grpc_pollset_worker* specific_worker) { +static grpc_error* pollset_kick(grpc_pollset* p, + grpc_pollset_worker* specific_worker) { if (specific_worker != NULL) { if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { for (specific_worker = @@ -218,4 +220,10 @@ grpc_error* grpc_pollset_kick(grpc_pollset* p, return GRPC_ERROR_NONE; } +grpc_pollset_vtable grpc_windows_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_windows.h b/src/core/lib/iomgr/pollset_windows.h index 93fe7d669b..e89758c694 100644 --- a/src/core/lib/iomgr/pollset_windows.h +++ b/src/core/lib/iomgr/pollset_windows.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H #define GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H +#include <grpc/support/port_platform.h> + #include <grpc/support/sync.h> #include "src/core/lib/iomgr/port.h" diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h index 25090898ed..c1dcc52618 100644 --- a/src/core/lib/iomgr/port.h +++ b/src/core/lib/iomgr/port.h @@ -21,8 +21,11 @@ #ifndef GRPC_CORE_LIB_IOMGR_PORT_H #define GRPC_CORE_LIB_IOMGR_PORT_H -#if defined(GRPC_UV) -// Do nothing +#ifdef GRPC_UV +#define GRPC_CUSTOM_SOCKET +#endif +#if defined(GRPC_CUSTOM_SOCKET) +// Do Nothing #elif defined(GPR_MANYLINUX1) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -33,13 +36,10 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #define GRPC_LINUX_EPOLL 1 #elif defined(GPR_WINDOWS) -#define GRPC_TIMER_USE_GENERIC 1 #define GRPC_WINSOCK_SOCKET 1 #define GRPC_WINDOWS_SOCKETUTILS 1 #elif defined(GPR_ANDROID) @@ -49,10 +49,8 @@ #define GRPC_HAVE_UNIX_SOCKET 1 #define GRPC_LINUX_EVENTFD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_LINUX) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -64,9 +62,7 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_HOST_NAME_MAX 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #ifdef __GLIBC_PREREQ #if __GLIBC_PREREQ(2, 4) #define GRPC_LINUX_EPOLL 1 @@ -100,11 +96,9 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_SYSCONF 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_FREEBSD) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_HAVE_IFADDRS 1 @@ -114,36 +108,31 @@ #define GRPC_POSIX_FORK 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_OPENBSD) #define GRPC_HAVE_IFADDRS 1 #define GRPC_HAVE_IPV6_RECVPKTINFO 1 #define GRPC_HAVE_UNIX_SOCKET 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif defined(GPR_NACL) #define GRPC_HAVE_ARPA_NAMESER 1 #define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 #define GRPC_POSIX_SOCKET 1 -#define GRPC_POSIX_SOCKETADDR 1 #define GRPC_POSIX_SOCKETUTILS 1 #define GRPC_POSIX_WAKEUP_FD 1 -#define GRPC_TIMER_USE_GENERIC 1 #elif !defined(GPR_NO_AUTODETECT_PLATFORM) #error "Platform not recognized" #endif #if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \ - defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_UV) != \ + defined(GRPC_CUSTOM_SOCKET) != \ 1 -#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET +#error \ + "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET" #endif #if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF) diff --git a/src/core/lib/iomgr/resolve_address.cc b/src/core/lib/iomgr/resolve_address.cc new file mode 100644 index 0000000000..f2a4676369 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/alloc.h> +#include "src/core/lib/iomgr/resolve_address.h" + +grpc_address_resolver_vtable* grpc_resolve_address_impl; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable) { + grpc_resolve_address_impl = vtable; +} + +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { + grpc_resolve_address_impl->resolve_address( + addr, default_port, interested_parties, on_done, addresses); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { + if (addrs != nullptr) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses) { + return grpc_resolve_address_impl->blocking_resolve_address(name, default_port, + addresses); +} diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h index 12fc2ed088..7da5caaa8e 100644 --- a/src/core/lib/iomgr/resolve_address.h +++ b/src/core/lib/iomgr/resolve_address.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H #define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/pollset_set.h" @@ -35,20 +37,33 @@ typedef struct { grpc_resolved_address* addrs; } grpc_resolved_addresses; +typedef struct grpc_address_resolver_vtable { + void (*resolve_address)(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + grpc_error* (*blocking_resolve_address)(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); +} grpc_address_resolver_vtable; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable); + /* Asynchronously resolve addr. Use default_port if a port isn't designated in addr, otherwise use the port in addr. */ /* TODO(ctiller): add a timeout here */ -extern void (*grpc_resolve_address)(const char* addr, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addresses); +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + /* Destroy resolved addresses */ void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addresses); -/* Resolve addr in a blocking fashion. Returns NULL on failure. On success, +/* Resolve addr in a blocking fashion. On success, result must be freed with grpc_resolved_addresses_destroy. */ -extern grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses); +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); #endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */ diff --git a/src/core/lib/iomgr/resolve_address_custom.cc b/src/core/lib/iomgr/resolve_address_custom.cc new file mode 100644 index 0000000000..9cf7817f66 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_custom.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/string_util.h> + +#include <grpc/support/log.h> +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include <string.h> + +typedef struct grpc_custom_resolver { + grpc_closure* on_done; + grpc_resolved_addresses** addresses; + char* host; + char* port; +} grpc_custom_resolver; + +static grpc_custom_resolver_vtable* resolve_address_vtable = nullptr; + +static int retry_named_port_failure(grpc_custom_resolver* r, + grpc_resolved_addresses** res) { + // This loop is copied from resolve_address_posix.c + const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(r->port, svc[i][0]) == 0) { + gpr_free(r->port); + r->port = gpr_strdup(svc[i][1]); + if (res) { + grpc_error* error = + resolve_address_vtable->resolve(r->host, r->port, res); + if (error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + return 0; + } + } else { + resolve_address_vtable->resolve_async(r, r->host, r->port); + } + return 1; + } + } + return 0; +} + +void grpc_custom_resolve_callback(grpc_custom_resolver* r, + grpc_resolved_addresses* result, + grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + if (error == GRPC_ERROR_NONE) { + *r->addresses = result; + } else if (retry_named_port_failure(r, nullptr)) { + return; + } + if (r->on_done) { + GRPC_CLOSURE_SCHED(r->on_done, error); + } + gpr_free(r->host); + gpr_free(r->port); + gpr_free(r); +} + +static grpc_error* try_split_host_port(const char* name, + const char* default_port, char** host, + char** port) { + /* parse name, splitting it into host and port parts */ + grpc_error* error; + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + char* msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + if (*port == nullptr) { + // TODO(murgatroid99): add tests for this case + if (default_port == nullptr) { + char* msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *port = gpr_strdup(default_port); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* blocking_resolve_address_impl( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + char* host; + char* port; + grpc_error* err; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + gpr_free(host); + gpr_free(port); + return err; + } + + /* Call getaddrinfo */ + grpc_custom_resolver resolver; + resolver.host = host; + resolver.port = port; + + grpc_resolved_addresses* addrs; + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + err = resolve_address_vtable->resolve(host, port, &addrs); + if (err != GRPC_ERROR_NONE) { + if (retry_named_port_failure(&resolver, &addrs)) { + GRPC_ERROR_UNREF(err); + err = GRPC_ERROR_NONE; + } + } + grpc_core::ExecCtx::Set(curr); + if (err == GRPC_ERROR_NONE) { + *addresses = addrs; + } + gpr_free(resolver.host); + gpr_free(resolver.port); + return err; +} + +static void resolve_address_impl(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { + grpc_custom_resolver* r = nullptr; + char* host = nullptr; + char* port = nullptr; + grpc_error* err; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(on_done, err); + gpr_free(host); + gpr_free(port); + return; + } + r = (grpc_custom_resolver*)gpr_malloc(sizeof(grpc_custom_resolver)); + r->on_done = on_done; + r->addresses = addrs; + r->host = host; + r->port = port; + + /* Call getaddrinfo */ + resolve_address_vtable->resolve_async(r, r->host, r->port); +} + +static grpc_address_resolver_vtable custom_resolver_vtable = { + resolve_address_impl, blocking_resolve_address_impl}; + +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl) { + resolve_address_vtable = impl; + grpc_set_resolver_impl(&custom_resolver_vtable); +} diff --git a/src/core/lib/iomgr/resolve_address_custom.h b/src/core/lib/iomgr/resolve_address_custom.h new file mode 100644 index 0000000000..e0c6714087 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_custom_resolver grpc_custom_resolver; + +typedef struct grpc_custom_resolver_vtable { + grpc_error* (*resolve)(char* host, char* port, grpc_resolved_addresses** res); + void (*resolve_async)(grpc_custom_resolver* resolver, char* host, char* port); +} grpc_custom_resolver_vtable; + +void grpc_custom_resolve_callback(grpc_custom_resolver* resolver, + grpc_resolved_addresses* result, + grpc_error* error); + +/* Internal APIs */ +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl); + +#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H */ diff --git a/src/core/lib/iomgr/resolve_address_posix.cc b/src/core/lib/iomgr/resolve_address_posix.cc index 3dc1d871a1..a82075542f 100644 --- a/src/core/lib/iomgr/resolve_address_posix.cc +++ b/src/core/lib/iomgr/resolve_address_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -33,14 +35,14 @@ #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" -static grpc_error* blocking_resolve_address_impl( +static grpc_error* posix_blocking_resolve_address( const char* name, const char* default_port, grpc_resolved_addresses** addresses) { grpc_core::ExecCtx exec_ctx; @@ -139,10 +141,6 @@ done: return err; } -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - typedef struct { char* name; char* default_port; @@ -163,17 +161,10 @@ static void do_request_thread(void* rp, grpc_error* error) { gpr_free(r); } -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != nullptr) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addrs) { +static void posix_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { request* r = static_cast<request*>(gpr_malloc(sizeof(request))); GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); @@ -184,9 +175,6 @@ static void resolve_address_impl(const char* name, const char* default_port, GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); } -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addrs) = resolve_address_impl; - +grpc_address_resolver_vtable grpc_posix_resolver_vtable = { + posix_resolve_address, posix_blocking_resolve_address}; #endif diff --git a/src/core/lib/iomgr/resolve_address_uv.cc b/src/core/lib/iomgr/resolve_address_uv.cc deleted file mode 100644 index 6eb6fe3af5..0000000000 --- a/src/core/lib/iomgr/resolve_address_uv.cc +++ /dev/null @@ -1,284 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" -#ifdef GRPC_UV - -#include <uv.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> - -#include "src/core/lib/gpr/host_port.h" -#include "src/core/lib/gpr/useful.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" - -#include <string.h> - -typedef struct request { - grpc_closure* on_done; - grpc_resolved_addresses** addresses; - struct addrinfo* hints; - char* host; - char* port; -} request; - -static int retry_named_port_failure(int status, request* r, - uv_getaddrinfo_cb getaddrinfo_cb) { - if (status != 0) { - // This loop is copied from resolve_address_posix.c - const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; - for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { - if (strcmp(r->port, svc[i][0]) == 0) { - int retry_status; - uv_getaddrinfo_t* req = - (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - r->port = gpr_strdup(svc[i][1]); - retry_status = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_cb, - r->host, r->port, r->hints); - if (retry_status < 0 || getaddrinfo_cb == NULL) { - // The callback will not be called - gpr_free(req); - } - return retry_status; - } - } - } - /* If this function calls uv_getaddrinfo, it will return that function's - return value. That function only returns numbers <=0, so we can safely - return 1 to indicate that we never retried */ - return 1; -} - -static grpc_error* handle_addrinfo_result(int status, struct addrinfo* result, - grpc_resolved_addresses** addresses) { - struct addrinfo* resp; - size_t i; - if (status != 0) { - grpc_error* error; - *addresses = NULL; - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - (*addresses) = - (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - (*addresses)->naddrs++; - } - (*addresses)->addrs = (grpc_resolved_address*)gpr_malloc( - sizeof(grpc_resolved_address) * (*addresses)->naddrs); - i = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); - (*addresses)->addrs[i].len = resp->ai_addrlen; - i++; - } - - { - for (i = 0; i < (*addresses)->naddrs; i++) { - char* buf; - grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); - gpr_free(buf); - } - } - return GRPC_ERROR_NONE; -} - -static void getaddrinfo_callback(uv_getaddrinfo_t* req, int status, - struct addrinfo* res) { - request* r = (request*)req->data; - grpc_core::ExecCtx exec_ctx; - grpc_error* error; - int retry_status; - char* port = r->port; - - gpr_free(req); - retry_status = retry_named_port_failure(status, r, getaddrinfo_callback); - if (retry_status == 0) { - /* The request is being retried. It is using its own port string, so we free - * the original one */ - gpr_free(port); - return; - } - /* Either no retry was attempted, or the retry failed. Either way, the - original error probably has more interesting information */ - error = handle_addrinfo_result(status, res, r->addresses); - GRPC_CLOSURE_SCHED(r->on_done, error); - - gpr_free(r->hints); - gpr_free(r->host); - gpr_free(r->port); - gpr_free(r); - uv_freeaddrinfo(res); -} - -static grpc_error* try_split_host_port(const char* name, - const char* default_port, char** host, - char** port) { - /* parse name, splitting it into host and port parts */ - grpc_error* error; - gpr_split_host_port(name, host, port); - if (*host == NULL) { - char* msg; - gpr_asprintf(&msg, "unparseable host:port: '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - if (*port == NULL) { - // TODO(murgatroid99): add tests for this case - if (default_port == NULL) { - char* msg; - gpr_asprintf(&msg, "no port in name '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - *port = gpr_strdup(default_port); - } - return GRPC_ERROR_NONE; -} - -static grpc_error* blocking_resolve_address_impl( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) { - char* host; - char* port; - struct addrinfo hints; - uv_getaddrinfo_t req; - int s; - grpc_error* err; - int retry_status; - request r; - - GRPC_UV_ASSERT_SAME_THREAD(); - - req.addrinfo = NULL; - - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - goto done; - } - - /* Call getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints.ai_socktype = SOCK_STREAM; /* stream socket */ - hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ - - s = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); - r.addresses = addresses; - r.hints = &hints; - r.host = host; - r.port = port; - retry_status = retry_named_port_failure(s, &r, NULL); - if (retry_status <= 0) { - s = retry_status; - } - err = handle_addrinfo_result(s, req.addrinfo, addresses); - -done: - gpr_free(host); - gpr_free(port); - if (req.addrinfo) { - uv_freeaddrinfo(req.addrinfo); - } - return err; -} - -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addrs) { - uv_getaddrinfo_t* req = NULL; - request* r = NULL; - struct addrinfo* hints = NULL; - char* host = NULL; - char* port = NULL; - grpc_error* err; - int s; - GRPC_UV_ASSERT_SAME_THREAD(); - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(on_done, err); - gpr_free(host); - gpr_free(port); - return; - } - r = (request*)gpr_malloc(sizeof(request)); - r->on_done = on_done; - r->addresses = addrs; - r->host = host; - r->port = port; - req = (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - - /* Call getaddrinfo */ - hints = (addrinfo*)gpr_malloc(sizeof(struct addrinfo)); - memset(hints, 0, sizeof(struct addrinfo)); - hints->ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints->ai_socktype = SOCK_STREAM; /* stream socket */ - hints->ai_flags = AI_PASSIVE; /* for wildcard IP address */ - r->hints = hints; - - s = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_callback, host, port, - hints); - - if (s != 0) { - *addrs = NULL; - err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(s))); - GRPC_CLOSURE_SCHED(on_done, err); - gpr_free(r); - gpr_free(req); - gpr_free(hints); - gpr_free(host); - gpr_free(port); - } -} - -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addrs) = resolve_address_impl; - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc index 8f4fd04402..71c92615ad 100644 --- a/src/core/lib/iomgr/resolve_address_windows.cc +++ b/src/core/lib/iomgr/resolve_address_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -35,7 +37,7 @@ #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/block_annotate.h" #include "src/core/lib/iomgr/executor.h" #include "src/core/lib/iomgr/iomgr_internal.h" @@ -49,7 +51,7 @@ typedef struct { grpc_resolved_addresses** addresses; } request; -static grpc_error* blocking_resolve_address_impl( +static grpc_error* windows_blocking_resolve_address( const char* name, const char* default_port, grpc_resolved_addresses** addresses) { grpc_core::ExecCtx exec_ctx; @@ -128,10 +130,6 @@ done: return error; } -grpc_error* (*grpc_blocking_resolve_address)( - const char* name, const char* default_port, - grpc_resolved_addresses** addresses) = blocking_resolve_address_impl; - /* Callback to be passed to grpc_executor to asynch-ify * grpc_blocking_resolve_address */ static void do_request_thread(void* rp, grpc_error* error) { @@ -148,17 +146,10 @@ static void do_request_thread(void* rp, grpc_error* error) { gpr_free(r); } -void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(const char* name, const char* default_port, - grpc_pollset_set* interested_parties, - grpc_closure* on_done, - grpc_resolved_addresses** addresses) { +static void windows_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { request* r = (request*)gpr_malloc(sizeof(request)); GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); @@ -169,9 +160,6 @@ static void resolve_address_impl(const char* name, const char* default_port, GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); } -void (*grpc_resolve_address)( - const char* name, const char* default_port, - grpc_pollset_set* interested_parties, grpc_closure* on_done, - grpc_resolved_addresses** addresses) = resolve_address_impl; - +grpc_address_resolver_vtable grpc_windows_resolver_vtable = { + windows_resolve_address, windows_blocking_resolve_address}; #endif diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc index c4763f3229..8c42dd78cf 100644 --- a/src/core/lib/iomgr/resource_quota.cc +++ b/src/core/lib/iomgr/resource_quota.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/resource_quota.h" #include <inttypes.h> diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h index 39e3aabf18..89e8a39118 100644 --- a/src/core/lib/iomgr/resource_quota.h +++ b/src/core/lib/iomgr/resource_quota.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H #define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/debug/trace.h" @@ -137,8 +139,4 @@ void grpc_resource_user_alloc_slices( grpc_resource_user_slice_allocator* slice_allocator, size_t length, size_t count, grpc_slice_buffer* dest); -/* Allocate one slice of length \a size synchronously. */ -grpc_slice grpc_resource_user_slice_malloc(grpc_resource_user* resource_user, - size_t size); - #endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */ diff --git a/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h index 206d596ccd..5edf735cd1 100644 --- a/src/core/lib/iomgr/sockaddr.h +++ b/src/core/lib/iomgr/sockaddr.h @@ -23,18 +23,10 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_H -#include "src/core/lib/iomgr/port.h" +#include <grpc/support/port_platform.h> -#ifdef GRPC_UV -#include <uv.h> -#endif - -#ifdef GPR_WINDOWS -#include "src/core/lib/iomgr/sockaddr_windows.h" -#endif - -#ifdef GRPC_POSIX_SOCKETADDR +#include "src/core/lib/iomgr/sockaddr_custom.h" #include "src/core/lib/iomgr/sockaddr_posix.h" -#endif +#include "src/core/lib/iomgr/sockaddr_windows.h" #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_H */ diff --git a/src/core/lib/iomgr/sockaddr_custom.h b/src/core/lib/iomgr/sockaddr_custom.h new file mode 100644 index 0000000000..d85cc504d3 --- /dev/null +++ b/src/core/lib/iomgr/sockaddr_custom.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include <stddef.h> +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <uv.h> + +// TODO(kpayson) It would be nice to abstract this so we don't +// depend on anything uv specific +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif // GRPC_UV + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H */ diff --git a/src/core/lib/iomgr/sockaddr_posix.h b/src/core/lib/iomgr/sockaddr_posix.h index 22d57ca6bb..5b18bbc465 100644 --- a/src/core/lib/iomgr/sockaddr_posix.h +++ b/src/core/lib/iomgr/sockaddr_posix.h @@ -19,6 +19,11 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> @@ -26,4 +31,25 @@ #include <sys/types.h> #include <unistd.h> +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif + #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H */ diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc index 69be87168e..bc3550a679 100644 --- a/src/core/lib/iomgr/sockaddr_utils.cc +++ b/src/core/lib/iomgr/sockaddr_utils.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/sockaddr_utils.h" #include <errno.h> @@ -24,7 +26,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> #include "src/core/lib/gpr/host_port.h" @@ -39,25 +40,25 @@ static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr, grpc_resolved_address* resolved_addr4_out) { GPR_ASSERT(resolved_addr != resolved_addr4_out); - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - struct sockaddr_in* addr4_out = + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + grpc_sockaddr_in* addr4_out = resolved_addr4_out == nullptr ? nullptr - : reinterpret_cast<struct sockaddr_in*>(resolved_addr4_out->addr); - if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + : reinterpret_cast<grpc_sockaddr_in*>(resolved_addr4_out->addr); + if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, sizeof(kV4MappedPrefix)) == 0) { if (resolved_addr4_out != nullptr) { /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); - addr4_out->sin_family = AF_INET; + addr4_out->sin_family = GRPC_AF_INET; /* s6_addr32 would be nice, but it's non-standard. */ memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); addr4_out->sin_port = addr6->sin6_port; - resolved_addr4_out->len = sizeof(struct sockaddr_in); + resolved_addr4_out->len = sizeof(grpc_sockaddr_in); } return 1; } @@ -68,19 +69,19 @@ int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr, int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr, grpc_resolved_address* resolved_addr6_out) { GPR_ASSERT(resolved_addr != resolved_addr6_out); - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - struct sockaddr_in6* addr6_out = - reinterpret_cast<struct sockaddr_in6*>(resolved_addr6_out->addr); - if (addr->sa_family == AF_INET) { - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + grpc_sockaddr_in6* addr6_out = + reinterpret_cast<grpc_sockaddr_in6*>(resolved_addr6_out->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); - addr6_out->sin6_family = AF_INET6; + addr6_out->sin6_family = GRPC_AF_INET6; memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); addr6_out->sin6_port = addr4->sin_port; - resolved_addr6_out->len = sizeof(struct sockaddr_in6); + resolved_addr6_out->len = sizeof(grpc_sockaddr_in6); return 1; } return 0; @@ -88,32 +89,32 @@ int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr, int grpc_sockaddr_is_wildcard(const grpc_resolved_address* resolved_addr, int* port_out) { - const struct sockaddr* addr; + const grpc_sockaddr* addr; grpc_resolved_address addr4_normalized; if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { resolved_addr = &addr4_normalized; } - addr = reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - if (addr->sa_family == AF_INET) { + addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { /* Check for 0.0.0.0 */ - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); if (addr4->sin_addr.s_addr != 0) { return 0; } - *port_out = ntohs(addr4->sin_port); + *port_out = grpc_ntohs(addr4->sin_port); return 1; - } else if (addr->sa_family == AF_INET6) { + } else if (addr->sa_family == GRPC_AF_INET6) { /* Check for :: */ - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); int i; for (i = 0; i < 16; i++) { if (addr6->sin6_addr.s6_addr[i] != 0) { return 0; } } - *port_out = ntohs(addr6->sin6_port); + *port_out = grpc_ntohs(addr6->sin6_port); return 1; } else { return 0; @@ -128,33 +129,33 @@ void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out, void grpc_sockaddr_make_wildcard4(int port, grpc_resolved_address* resolved_wild_out) { - struct sockaddr_in* wild_out = - reinterpret_cast<struct sockaddr_in*>(resolved_wild_out->addr); + grpc_sockaddr_in* wild_out = + reinterpret_cast<grpc_sockaddr_in*>(resolved_wild_out->addr); GPR_ASSERT(port >= 0 && port < 65536); memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin_family = AF_INET; - wild_out->sin_port = htons(static_cast<uint16_t>(port)); - resolved_wild_out->len = sizeof(struct sockaddr_in); + wild_out->sin_family = GRPC_AF_INET; + wild_out->sin_port = grpc_htons(static_cast<uint16_t>(port)); + resolved_wild_out->len = sizeof(grpc_sockaddr_in); } void grpc_sockaddr_make_wildcard6(int port, grpc_resolved_address* resolved_wild_out) { - struct sockaddr_in6* wild_out = - reinterpret_cast<struct sockaddr_in6*>(resolved_wild_out->addr); + grpc_sockaddr_in6* wild_out = + reinterpret_cast<grpc_sockaddr_in6*>(resolved_wild_out->addr); GPR_ASSERT(port >= 0 && port < 65536); memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin6_family = AF_INET6; - wild_out->sin6_port = htons(static_cast<uint16_t>(port)); - resolved_wild_out->len = sizeof(struct sockaddr_in6); + wild_out->sin6_family = GRPC_AF_INET6; + wild_out->sin6_port = grpc_htons(static_cast<uint16_t>(port)); + resolved_wild_out->len = sizeof(grpc_sockaddr_in6); } int grpc_sockaddr_to_string(char** out, const grpc_resolved_address* resolved_addr, int normalize) { - const struct sockaddr* addr; + const grpc_sockaddr* addr; const int save_errno = errno; grpc_resolved_address addr_normalized; - char ntop_buf[INET6_ADDRSTRLEN]; + char ntop_buf[GRPC_INET6_ADDRSTRLEN]; const void* ip = nullptr; int port = 0; uint32_t sin6_scope_id = 0; @@ -164,17 +165,17 @@ int grpc_sockaddr_to_string(char** out, if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { resolved_addr = &addr_normalized; } - addr = reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); - if (addr->sa_family == AF_INET) { - const struct sockaddr_in* addr4 = - reinterpret_cast<const struct sockaddr_in*>(addr); + addr = reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast<const grpc_sockaddr_in*>(addr); ip = &addr4->sin_addr; - port = ntohs(addr4->sin_port); - } else if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6* addr6 = - reinterpret_cast<const struct sockaddr_in6*>(addr); + port = grpc_ntohs(addr4->sin_port); + } else if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast<const grpc_sockaddr_in6*>(addr); ip = &addr6->sin6_addr; - port = ntohs(addr6->sin6_port); + port = grpc_ntohs(addr6->sin6_port); sin6_scope_id = addr6->sin6_scope_id; } if (ip != nullptr && grpc_inet_ntop(addr->sa_family, ip, ntop_buf, @@ -196,6 +197,22 @@ int grpc_sockaddr_to_string(char** out, return ret; } +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port) { + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)out->addr; + grpc_sockaddr_in* addr4 = (grpc_sockaddr_in*)out->addr; + + if (grpc_inet_pton(GRPC_AF_INET6, addr, &addr6->sin6_addr) == 1) { + addr6->sin6_family = GRPC_AF_INET6; + out->len = sizeof(grpc_sockaddr_in6); + } else if (grpc_inet_pton(GRPC_AF_INET, addr, &addr4->sin_addr) == 1) { + addr4->sin_family = GRPC_AF_INET; + out->len = sizeof(grpc_sockaddr_in); + } else { + GPR_ASSERT(0); + } + grpc_sockaddr_set_port(out, port); +} + char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { grpc_resolved_address addr_normalized; if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { @@ -218,33 +235,33 @@ char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { const char* grpc_sockaddr_get_uri_scheme( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: + case GRPC_AF_INET: return "ipv4"; - case AF_INET6: + case GRPC_AF_INET6: return "ipv6"; - case AF_UNIX: + case GRPC_AF_UNIX: return "unix"; } return nullptr; } int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); return addr->sa_family; } int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in*)addr)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6*)addr)->sin6_port); + case GRPC_AF_INET: + return grpc_ntohs(((grpc_sockaddr_in*)addr)->sin_port); + case GRPC_AF_INET6: + return grpc_ntohs(((grpc_sockaddr_in6*)addr)->sin6_port); default: if (grpc_is_unix_socket(resolved_addr)) { return 1; @@ -257,18 +274,18 @@ int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) { int grpc_sockaddr_set_port(const grpc_resolved_address* resolved_addr, int port) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); switch (addr->sa_family) { - case AF_INET: + case GRPC_AF_INET: GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in*)addr)->sin_port = - htons(static_cast<uint16_t>(port)); + ((grpc_sockaddr_in*)addr)->sin_port = + grpc_htons(static_cast<uint16_t>(port)); return 1; - case AF_INET6: + case GRPC_AF_INET6: GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in6*)addr)->sin6_port = - htons(static_cast<uint16_t>(port)); + ((grpc_sockaddr_in6*)addr)->sin6_port = + grpc_htons(static_cast<uint16_t>(port)); return 1; default: gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h index e3bd51a4ad..a4e90a73ab 100644 --- a/src/core/lib/iomgr/sockaddr_utils.h +++ b/src/core/lib/iomgr/sockaddr_utils.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/resolve_address.h" /* Returns true if addr is an IPv4-mapped IPv6 address within the @@ -69,6 +71,8 @@ int grpc_sockaddr_set_port(const grpc_resolved_address* addr, int port); int grpc_sockaddr_to_string(char** out, const grpc_resolved_address* addr, int normalize); +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port); + /* Returns the URI string corresponding to \a addr */ char* grpc_sockaddr_to_uri(const grpc_resolved_address* addr); diff --git a/src/core/lib/iomgr/sockaddr_windows.h b/src/core/lib/iomgr/sockaddr_windows.h index 20e37c9fc4..4d637251a1 100644 --- a/src/core/lib/iomgr/sockaddr_windows.h +++ b/src/core/lib/iomgr/sockaddr_windows.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -29,6 +31,25 @@ // must be included after the above #include <mswsock.h> +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + #endif #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H */ diff --git a/src/core/lib/iomgr/socket_factory_posix.cc b/src/core/lib/iomgr/socket_factory_posix.cc index 3e696c2e10..1d1e36c0e3 100644 --- a/src/core/lib/iomgr/socket_factory_posix.cc +++ b/src/core/lib/iomgr/socket_factory_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET diff --git a/src/core/lib/iomgr/socket_factory_posix.h b/src/core/lib/iomgr/socket_factory_posix.h index af57cc5b60..9a52f4ea4e 100644 --- a/src/core/lib/iomgr/socket_factory_posix.h +++ b/src/core/lib/iomgr/socket_factory_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H #define GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include <grpc/support/sync.h> #include "src/core/lib/iomgr/resolve_address.h" diff --git a/src/core/lib/iomgr/socket_mutator.cc b/src/core/lib/iomgr/socket_mutator.cc index eb219d7cb3..b9b8eaf4ad 100644 --- a/src/core/lib/iomgr/socket_mutator.cc +++ b/src/core/lib/iomgr/socket_mutator.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/socket_mutator.h" #include <grpc/impl/codegen/grpc_types.h> diff --git a/src/core/lib/iomgr/socket_mutator.h b/src/core/lib/iomgr/socket_mutator.h index f8fd21d15a..6c7781c51d 100644 --- a/src/core/lib/iomgr/socket_mutator.h +++ b/src/core/lib/iomgr/socket_mutator.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H #define GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include <grpc/support/sync.h> diff --git a/src/core/lib/iomgr/socket_utils.h b/src/core/lib/iomgr/socket_utils.h index 9fd141b6de..cf1a7be648 100644 --- a/src/core/lib/iomgr/socket_utils.h +++ b/src/core/lib/iomgr/socket_utils.h @@ -19,8 +19,19 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H #define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H +#include <grpc/support/port_platform.h> + #include <stddef.h> +/* A wrapper for htons on POSIX and Windows */ +uint16_t grpc_htons(uint16_t hostshort); + +/* A wrapper for ntohs on POSIX and WINDOWS */ +uint16_t grpc_ntohs(uint16_t netshort); + +/* A wrapper for inet_pton on POSIX and WINDOWS */ +int grpc_inet_pton(int af, const char* src, void* dst); + /* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */ const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size); diff --git a/src/core/lib/iomgr/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc index a0cca6195b..c52e237fa8 100644 --- a/src/core/lib/iomgr/socket_utils_common_posix.cc +++ b/src/core/lib/iomgr/socket_utils_common_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -37,11 +39,11 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include <grpc/support/sync.h> #include "src/core/lib/gpr/host_port.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" /* set a socket to non blocking mode */ @@ -214,12 +216,11 @@ static void probe_ipv6_once(void) { if (fd < 0) { gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); } else { - struct sockaddr_in6 addr; + grpc_sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ - if (bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == - 0) { + if (bind(fd, reinterpret_cast<grpc_sockaddr*>(&addr), sizeof(addr)) == 0) { g_ipv6_loopback_available = 1; } else { gpr_log(GPR_INFO, @@ -279,8 +280,8 @@ static int create_socket(grpc_socket_factory* factory, int domain, int type, grpc_error* grpc_create_dualstack_socket_using_factory( grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr, int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); int family = addr->sa_family; if (family == AF_INET6) { if (grpc_ipv6_loopback_available()) { @@ -310,6 +311,14 @@ grpc_error* grpc_create_dualstack_socket_using_factory( return error_for_fd(*newfd, resolved_addr); } +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { GPR_ASSERT(size <= (socklen_t)-1); return inet_ntop(af, src, dst, static_cast<socklen_t>(size)); diff --git a/src/core/lib/iomgr/socket_utils_linux.cc b/src/core/lib/iomgr/socket_utils_linux.cc index e8bf05c3a8..1364cd35f6 100644 --- a/src/core/lib/iomgr/socket_utils_linux.cc +++ b/src/core/lib/iomgr/socket_utils_linux.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_LINUX_SOCKETUTILS @@ -35,8 +37,7 @@ int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); flags |= nonblock ? SOCK_NONBLOCK : 0; flags |= cloexec ? SOCK_CLOEXEC : 0; - return accept4(sockfd, - reinterpret_cast<struct sockaddr*>(resolved_addr->addr), + return accept4(sockfd, reinterpret_cast<grpc_sockaddr*>(resolved_addr->addr), reinterpret_cast<socklen_t*>(&resolved_addr->len), flags); } diff --git a/src/core/lib/iomgr/socket_utils_posix.cc b/src/core/lib/iomgr/socket_utils_posix.cc index c49cbb203b..d5d00af976 100644 --- a/src/core/lib/iomgr/socket_utils_posix.cc +++ b/src/core/lib/iomgr/socket_utils_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKETUTILS @@ -34,7 +36,7 @@ int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, int fd, flags; GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); - fd = accept(sockfd, (struct sockaddr*)resolved_addr->addr, + fd = accept(sockfd, (grpc_sockaddr*)resolved_addr->addr, (socklen_t*)&resolved_addr->len); if (fd >= 0) { if (nonblock) { diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h index 77df4205ff..1f50e8d315 100644 --- a/src/core/lib/iomgr/socket_utils_posix.h +++ b/src/core/lib/iomgr/socket_utils_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H #define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/resolve_address.h" #include <sys/socket.h> diff --git a/src/core/lib/iomgr/socket_utils_uv.cc b/src/core/lib/iomgr/socket_utils_uv.cc index 75316d8c24..8538abc7e4 100644 --- a/src/core/lib/iomgr/socket_utils_uv.cc +++ b/src/core/lib/iomgr/socket_utils_uv.cc @@ -16,19 +16,30 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_UV -#include <uv.h> - +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/socket_utils.h" #include <grpc/support/log.h> +#include <uv.h> + +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { - uv_inet_ntop(af, src, dst, size); - return dst; + /* Windows InetNtopA wants a mutable ip pointer */ + return inet_ntop(af, src, dst, (socklen_t)size); } #endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/socket_utils_windows.cc b/src/core/lib/iomgr/socket_utils_windows.cc index 0482a1783d..3e7b5b812d 100644 --- a/src/core/lib/iomgr/socket_utils_windows.cc +++ b/src/core/lib/iomgr/socket_utils_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINDOWS_SOCKETUTILS @@ -25,6 +27,14 @@ #include <grpc/support/log.h> +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { /* Windows InetNtopA wants a mutable ip pointer */ return InetNtopA(af, (void*)src, dst, size); diff --git a/src/core/lib/iomgr/socket_windows.cc b/src/core/lib/iomgr/socket_windows.cc index 9bb6a75dd8..2e23409582 100644 --- a/src/core/lib/iomgr/socket_windows.cc +++ b/src/core/lib/iomgr/socket_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/socket_windows.h b/src/core/lib/iomgr/socket_windows.h index cb28f2b8df..3ff2c307e2 100644 --- a/src/core/lib/iomgr/socket_windows.h +++ b/src/core/lib/iomgr/socket_windows.h @@ -20,6 +20,7 @@ #define GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H #include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/sys_epoll_wrapper.h b/src/core/lib/iomgr/sys_epoll_wrapper.h index 3fa5357156..d21d853665 100644 --- a/src/core/lib/iomgr/sys_epoll_wrapper.h +++ b/src/core/lib/iomgr/sys_epoll_wrapper.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H #define GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H +#include <grpc/support/port_platform.h> + #include <sys/epoll.h> #ifndef EPOLLEXCLUSIVE diff --git a/src/core/lib/iomgr/tcp_client.cc b/src/core/lib/iomgr/tcp_client.cc new file mode 100644 index 0000000000..6c0ba40781 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/tcp_client.h" + +grpc_tcp_client_vtable* grpc_tcp_client_impl; + +void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { + grpc_tcp_client_impl->connect(closure, ep, interested_parties, channel_args, + addr, deadline); +} + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl) { + grpc_tcp_client_impl = impl; +} diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h index 5f55d30955..d209eeb8c2 100644 --- a/src/core/lib/iomgr/tcp_client.h +++ b/src/core/lib/iomgr/tcp_client.h @@ -19,12 +19,21 @@ #ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H #define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> #include <grpc/support/time.h> #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/resolve_address.h" +typedef struct grpc_tcp_client_vtable { + void (*connect)(grpc_closure* on_connect, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, grpc_millis deadline); +} grpc_tcp_client_vtable; + /* Asynchronously connect to an address (specified as (addr, len)), and call cb with arg and the completed connection when done (or call cb with arg and NULL on failure). @@ -36,4 +45,8 @@ void grpc_tcp_client_connect(grpc_closure* on_connect, grpc_endpoint** endpoint, const grpc_resolved_address* addr, grpc_millis deadline); +void grpc_tcp_client_global_init(); + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl); + #endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ diff --git a/src/core/lib/iomgr/tcp_client_custom.cc b/src/core/lib/iomgr/tcp_client_custom.cc new file mode 100644 index 0000000000..55632a55a1 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_custom.cc @@ -0,0 +1,151 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +struct grpc_custom_tcp_connect { + grpc_custom_socket* socket; + grpc_timer alarm; + grpc_closure on_alarm; + grpc_closure* closure; + grpc_endpoint** endpoint; + int refs; + char* addr_name; + grpc_resource_quota* resource_quota; +}; + +static void custom_tcp_connect_cleanup(grpc_custom_tcp_connect* connect) { + grpc_custom_socket* socket = connect->socket; + grpc_resource_quota_unref_internal(connect->resource_quota); + gpr_free(connect->addr_name); + gpr_free(connect); + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +static void custom_close_callback(grpc_custom_socket* socket) {} + +static void on_alarm(void* acp, grpc_error* error) { + int done; + grpc_custom_socket* socket = (grpc_custom_socket*)acp; + grpc_custom_tcp_connect* connect = socket->connector; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", + connect->addr_name, str); + } + if (error == GRPC_ERROR_NONE) { + /* error == NONE implies that the timer ran out, and wasn't cancelled. If + it was cancelled, then the handler that cancelled it also should close + the handle, if applicable */ + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } + done = (--connect->refs == 0); + if (done) { + custom_tcp_connect_cleanup(connect); + } +} + +static void custom_connect_callback(grpc_custom_socket* socket, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_custom_tcp_connect* connect = socket->connector; + int done; + grpc_closure* closure = connect->closure; + grpc_timer_cancel(&connect->alarm); + if (error == GRPC_ERROR_NONE) { + *connect->endpoint = custom_tcp_endpoint_create( + socket, connect->resource_quota, connect->addr_name); + } + done = (--connect->refs == 0); + if (done) { + grpc_core::ExecCtx::Get()->Flush(); + custom_tcp_connect_cleanup(connect); + } + GRPC_CLOSURE_SCHED(closure, error); +} + +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* resolved_addr, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + (void)channel_args; + (void)interested_parties; + grpc_custom_tcp_connect* connect; + grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr); + if (channel_args != nullptr) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)channel_args->args[i].value.pointer.p); + } + } + } + grpc_custom_socket* socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 2; + grpc_custom_socket_vtable->init(socket, GRPC_AF_UNSPEC); + connect = + (grpc_custom_tcp_connect*)gpr_malloc(sizeof(grpc_custom_tcp_connect)); + connect->closure = closure; + connect->endpoint = ep; + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + connect->resource_quota = resource_quota; + connect->socket = socket; + socket->connector = connect; + socket->endpoint = nullptr; + socket->listener = nullptr; + connect->refs = 2; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %p %s: asynchronously connecting", + socket, connect->addr_name); + } + + grpc_custom_socket_vtable->connect( + socket, (const grpc_sockaddr*)resolved_addr->addr, resolved_addr->len, + custom_connect_callback); + GRPC_CLOSURE_INIT(&connect->on_alarm, on_alarm, socket, + grpc_schedule_on_exec_ctx); + grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); +} + +grpc_tcp_client_vtable custom_tcp_client_vtable = {tcp_connect}; diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc index 33a0b0404d..c21fb40ab1 100644 --- a/src/core/lib/iomgr/tcp_client_posix.cc +++ b/src/core/lib/iomgr/tcp_client_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -36,6 +38,7 @@ #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_mutator.h" #include "src/core/lib/iomgr/socket_utils_posix.h" @@ -291,7 +294,7 @@ void grpc_tcp_client_create_from_prepared_fd( async_connect* ac; do { GPR_ASSERT(addr->len < ~(socklen_t)0); - err = connect(fd, reinterpret_cast<const struct sockaddr*>(addr->addr), + err = connect(fd, reinterpret_cast<const grpc_sockaddr*>(addr->addr), static_cast<socklen_t>(addr->len)); } while (err < 0 && errno == EINTR); if (err >= 0) { @@ -334,11 +337,11 @@ void grpc_tcp_client_create_from_prepared_fd( gpr_mu_unlock(&ac->mu); } -static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { grpc_resolved_address mapped_addr; grpc_fd* fdobj = nullptr; grpc_error* error; @@ -353,20 +356,5 @@ static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, ep); } -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} - +grpc_tcp_client_vtable grpc_posix_tcp_client_vtable = {tcp_connect}; #endif diff --git a/src/core/lib/iomgr/tcp_client_posix.h b/src/core/lib/iomgr/tcp_client_posix.h index 57e50a67d2..d0168ef133 100644 --- a/src/core/lib/iomgr/tcp_client_posix.h +++ b/src/core/lib/iomgr/tcp_client_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H #define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/tcp_client.h" diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc deleted file mode 100644 index 4e9c7cc11d..0000000000 --- a/src/core/lib/iomgr/tcp_client_uv.cc +++ /dev/null @@ -1,175 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/iomgr/tcp_uv.h" -#include "src/core/lib/iomgr/timer.h" - -extern grpc_core::TraceFlag grpc_tcp_trace; - -typedef struct grpc_uv_tcp_connect { - uv_connect_t connect_req; - grpc_timer alarm; - grpc_closure on_alarm; - uv_tcp_t* tcp_handle; - grpc_closure* closure; - grpc_endpoint** endpoint; - int refs; - char* addr_name; - grpc_resource_quota* resource_quota; -} grpc_uv_tcp_connect; - -static void uv_tcp_connect_cleanup(grpc_uv_tcp_connect* connect) { - grpc_resource_quota_unref_internal(connect->resource_quota); - gpr_free(connect->addr_name); - gpr_free(connect); -} - -static void tcp_close_callback(uv_handle_t* handle) { gpr_free(handle); } - -static void uv_tc_on_alarm(void* acp, grpc_error* error) { - int done; - grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)acp; - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", - connect->addr_name, str); - } - if (error == GRPC_ERROR_NONE) { - /* error == NONE implies that the timer ran out, and wasn't cancelled. If - it was cancelled, then the handler that cancelled it also should close - the handle, if applicable */ - uv_close((uv_handle_t*)connect->tcp_handle, tcp_close_callback); - } - done = (--connect->refs == 0); - if (done) { - uv_tcp_connect_cleanup(connect); - } -} - -static void uv_tc_on_connect(uv_connect_t* req, int status) { - grpc_uv_tcp_connect* connect = (grpc_uv_tcp_connect*)req->data; - grpc_core::ExecCtx exec_ctx; - grpc_error* error = GRPC_ERROR_NONE; - int done; - grpc_closure* closure = connect->closure; - grpc_timer_cancel(&connect->alarm); - if (status == 0) { - *connect->endpoint = grpc_tcp_create( - connect->tcp_handle, connect->resource_quota, connect->addr_name); - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to connect to remote host"); - error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, -status); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - if (status == UV_ECANCELED) { - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string("Timeout occurred")); - // This should only happen if the handle is already closed - } else { - error = grpc_error_set_str( - error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - uv_close((uv_handle_t*)connect->tcp_handle, tcp_close_callback); - } - } - done = (--connect->refs == 0); - if (done) { - grpc_core::ExecCtx::Get()->Flush(); - uv_tcp_connect_cleanup(connect); - } - GRPC_CLOSURE_SCHED(closure, error); -} - -static void tcp_client_connect_impl(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* resolved_addr, - grpc_millis deadline) { - grpc_uv_tcp_connect* connect; - grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL); - (void)channel_args; - (void)interested_parties; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (channel_args != NULL) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - grpc_resource_quota_unref_internal(resource_quota); - resource_quota = grpc_resource_quota_ref_internal( - (grpc_resource_quota*)channel_args->args[i].value.pointer.p); - } - } - } - - connect = (grpc_uv_tcp_connect*)gpr_zalloc(sizeof(grpc_uv_tcp_connect)); - connect->closure = closure; - connect->endpoint = ep; - connect->tcp_handle = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); - connect->resource_quota = resource_quota; - uv_tcp_init(uv_default_loop(), connect->tcp_handle); - connect->connect_req.data = connect; - connect->refs = 2; // One for the connect operation, one for the timer. - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", - connect->addr_name); - } - - // TODO(murgatroid99): figure out what the return value here means - uv_tcp_connect(&connect->connect_req, connect->tcp_handle, - (const struct sockaddr*)resolved_addr->addr, uv_tc_on_connect); - GRPC_CLOSURE_INIT(&connect->on_alarm, uv_tc_on_alarm, connect, - grpc_schedule_on_exec_ctx); - grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); -} - -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc index d46569d7cc..e5b5502597 100644 --- a/src/core/lib/iomgr/tcp_client_windows.cc +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <inttypes.h> @@ -120,12 +122,11 @@ static void on_connect(void* acp, grpc_error* error) { /* Tries to issue one async connection, then schedules both an IOCP notification request for the connection, and one timeout alert. */ -static void tcp_client_connect_impl(grpc_closure* on_done, - grpc_endpoint** endpoint, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { +static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { SOCKET sock = INVALID_SOCKET; BOOL success; int status; @@ -173,7 +174,7 @@ static void tcp_client_connect_impl(grpc_closure* on_done, grpc_sockaddr_make_wildcard6(0, &local_address); status = - bind(sock, (struct sockaddr*)&local_address.addr, (int)local_address.len); + bind(sock, (grpc_sockaddr*)&local_address.addr, (int)local_address.len); if (status != 0) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; @@ -181,7 +182,7 @@ static void tcp_client_connect_impl(grpc_closure* on_done, socket = grpc_winsocket_create(sock, "client"); info = &socket->write_info; - success = ConnectEx(sock, (struct sockaddr*)&addr->addr, (int)addr->len, NULL, + success = ConnectEx(sock, (grpc_sockaddr*)&addr->addr, (int)addr->len, NULL, 0, NULL, &info->overlapped); /* It wouldn't be unusual to get a success immediately. But we'll still get @@ -225,20 +226,6 @@ failure: GRPC_CLOSURE_SCHED(on_done, final_error); } -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, - grpc_pollset_set* interested_parties, - const grpc_channel_args* channel_args, - const grpc_resolved_address* addr, - grpc_millis deadline) { - grpc_tcp_client_connect_impl(closure, ep, interested_parties, channel_args, - addr, deadline); -} +grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_custom.cc b/src/core/lib/iomgr/tcp_custom.cc new file mode 100644 index 0000000000..2b1fc93028 --- /dev/null +++ b/src/core/lib/iomgr/tcp_custom.cc @@ -0,0 +1,365 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <limits.h> +#include <string.h> + +#include <grpc/slice_buffer.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 + +extern grpc_core::TraceFlag grpc_tcp_trace; + +grpc_socket_vtable* grpc_custom_socket_vtable = nullptr; +extern grpc_tcp_server_vtable custom_tcp_server_vtable; +extern grpc_tcp_client_vtable custom_tcp_client_vtable; + +void grpc_custom_endpoint_init(grpc_socket_vtable* impl) { + grpc_custom_socket_vtable = impl; + grpc_set_tcp_client_impl(&custom_tcp_client_vtable); + grpc_set_tcp_server_impl(&custom_tcp_server_vtable); +} + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + grpc_custom_socket* socket; + + grpc_closure* read_cb; + grpc_closure* write_cb; + + grpc_slice_buffer* read_slices; + grpc_slice_buffer* write_slices; + + grpc_resource_user* resource_user; + grpc_resource_user_slice_allocator slice_allocator; + + bool shutting_down; + + char* peer_string; +} custom_tcp_endpoint; + +static void tcp_free(grpc_custom_socket* s) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)s->endpoint; + grpc_resource_user_unref(tcp->resource_user); + gpr_free(tcp->peer_string); + gpr_free(tcp); + s->refs--; + if (s->refs == 0) { + grpc_custom_socket_vtable->destroy(s); + gpr_free(s); + } +} + +#ifndef NDEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val + 1); + } + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(custom_tcp_endpoint* tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) { + grpc_closure* cb = tcp->read_cb; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp->socket, cb, cb->cb, + cb->cb_arg); + size_t i; + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + + for (i = 0; i < tcp->read_slices->count; i++) { + char* dump = grpc_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); + gpr_free(dump); + } + } + TCP_UNREF(tcp, "read"); + tcp->read_slices = nullptr; + tcp->read_cb = nullptr; + GRPC_CLOSURE_RUN(cb, error); +} + +static void custom_read_callback(grpc_custom_socket* socket, size_t nread, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer garbage; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + if (error == GRPC_ERROR_NONE && nread == 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } + if (error == GRPC_ERROR_NONE) { + // Successful read + if ((size_t)nread < tcp->read_slices->length) { + /* TODO(murgatroid99): Instead of discarding the unused part of the read + * buffer, reuse it as the next read buffer. */ + grpc_slice_buffer_init(&garbage); + grpc_slice_buffer_trim_end( + tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage); + grpc_slice_buffer_reset_and_unref_internal(&garbage); + } + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + } + call_read_cb(tcp, error); +} + +static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)tcpp; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp->socket, + grpc_error_string(error)); + } + if (error == GRPC_ERROR_NONE) { + /* Before calling read, we allocate a buffer with exactly one slice + * to tcp->read_slices and wait for the callback indicating that the + * allocation was successful. So slices[0] should always exist here */ + char* buffer = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]); + size_t len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]); + grpc_custom_socket_vtable->read(tcp->socket, buffer, len, + custom_read_callback); + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + call_read_cb(tcp, GRPC_ERROR_REF(error)); + } + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp->socket, str); + } +} + +static void endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, + grpc_closure* cb) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GPR_ASSERT(tcp->read_cb == nullptr); + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(read_slices); + TCP_REF(tcp, "read"); + grpc_resource_user_alloc_slices(&tcp->slice_allocator, + GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, + tcp->read_slices); +} + +static void custom_write_callback(grpc_custom_socket* socket, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + grpc_closure* cb = tcp->write_cb; + tcp->write_cb = nullptr; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp->socket, str); + } + TCP_UNREF(tcp, "write"); + GRPC_CLOSURE_SCHED(cb, error); +} + +static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices, + grpc_closure* cb) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (grpc_tcp_trace.enabled()) { + size_t j; + + for (j = 0; j < write_slices->count; j++) { + char* data = grpc_dump_slice(write_slices->slices[j], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp->socket, + tcp->peer_string, data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TCP socket is shutting down")); + return; + } + + GPR_ASSERT(tcp->write_cb == nullptr); + tcp->write_slices = write_slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count == 0) { + // No slices means we don't have to do anything, + // and libuv doesn't like empty writes + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE); + return; + } + tcp->write_cb = cb; + TCP_REF(tcp, "write"); + grpc_custom_socket_vtable->write(tcp->socket, tcp->write_slices, + custom_write_callback); +} + +static void endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_add_to_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + if (!tcp->shutting_down) { + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(why); + gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->socket, str); + } + tcp->shutting_down = true; + // GRPC_CLOSURE_SCHED(tcp->read_cb, GRPC_ERROR_REF(why)); + // GRPC_CLOSURE_SCHED(tcp->write_cb, GRPC_ERROR_REF(why)); + // tcp->read_cb = nullptr; + // tcp->write_cb = nullptr; + grpc_resource_user_shutdown(tcp->resource_user); + grpc_custom_socket_vtable->shutdown(tcp->socket); + } + GRPC_ERROR_UNREF(why); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } else if (socket->endpoint) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + TCP_UNREF(tcp, "destroy"); + } +} + +static void endpoint_destroy(grpc_endpoint* ep) { + grpc_network_status_unregister_endpoint(ep); + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + grpc_custom_socket_vtable->close(tcp->socket, custom_close_callback); +} + +static char* endpoint_get_peer(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return tcp->resource_user; +} + +static int endpoint_get_fd(grpc_endpoint* ep) { return -1; } + +static grpc_endpoint_vtable vtable = {endpoint_read, + endpoint_write, + endpoint_add_to_pollset, + endpoint_add_to_pollset_set, + endpoint_delete_from_pollset_set, + endpoint_shutdown, + endpoint_destroy, + endpoint_get_resource_user, + endpoint_get_peer, + endpoint_get_fd}; + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string) { + custom_tcp_endpoint* tcp = + (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint)); + grpc_core::ExecCtx exec_ctx; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", socket); + } + memset(tcp, 0, sizeof(custom_tcp_endpoint)); + socket->refs++; + socket->endpoint = (grpc_endpoint*)tcp; + tcp->socket = socket; + tcp->base.vtable = &vtable; + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + tcp->shutting_down = false; + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init( + &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + + return &tcp->base; +} diff --git a/src/core/lib/iomgr/tcp_custom.h b/src/core/lib/iomgr/tcp_custom.h new file mode 100644 index 0000000000..22caa149f8 --- /dev/null +++ b/src/core/lib/iomgr/tcp_custom.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_tcp_listener grpc_tcp_listener; +typedef struct grpc_custom_tcp_connect grpc_custom_tcp_connect; + +typedef struct grpc_custom_socket { + // Implementation defined + void* impl; + grpc_endpoint* endpoint; + grpc_tcp_listener* listener; + grpc_custom_tcp_connect* connector; + int refs; +} grpc_custom_socket; + +typedef void (*grpc_custom_connect_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_write_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_read_callback)(grpc_custom_socket* socket, + size_t nread, grpc_error* error); +typedef void (*grpc_custom_accept_callback)(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); +typedef void (*grpc_custom_close_callback)(grpc_custom_socket* socket); + +typedef struct grpc_socket_vtable { + grpc_error* (*init)(grpc_custom_socket* socket, int domain); + void (*connect)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, grpc_custom_connect_callback cb); + void (*destroy)(grpc_custom_socket* socket); + void (*shutdown)(grpc_custom_socket* socket); + void (*close)(grpc_custom_socket* socket, grpc_custom_close_callback cb); + void (*write)(grpc_custom_socket* socket, grpc_slice_buffer* slices, + grpc_custom_write_callback cb); + void (*read)(grpc_custom_socket* socket, char* buffer, size_t length, + grpc_custom_read_callback cb); + grpc_error* (*getpeername)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*getsockname)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*setsockopt)(grpc_custom_socket* socket, int level, int optname, + const void* optval, uint32_t optlen); + grpc_error* (*bind)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, int flags); + grpc_error* (*listen)(grpc_custom_socket* socket); + void (*accept)(grpc_custom_socket* socket, grpc_custom_socket* client, + grpc_custom_accept_callback cb); +} grpc_socket_vtable; + +/* Internal APIs */ +void grpc_custom_endpoint_init(grpc_socket_vtable* impl); + +void grpc_custom_close_server_callback(grpc_tcp_listener* listener); + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H */ diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc index 50efd04382..205af22531 100644 --- a/src/core/lib/iomgr/tcp_posix.cc +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -61,7 +63,7 @@ typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; typedef size_t msg_iovlen_type; #endif -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +extern grpc_core::TraceFlag grpc_tcp_trace; namespace { struct grpc_tcp { diff --git a/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h index 4529c02beb..af89bd24db 100644 --- a/src/core/lib/iomgr/tcp_posix.h +++ b/src/core/lib/iomgr/tcp_posix.h @@ -29,6 +29,8 @@ otherwise specified. */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" diff --git a/src/core/lib/iomgr/tcp_server.cc b/src/core/lib/iomgr/tcp_server.cc new file mode 100644 index 0000000000..ea745f266b --- /dev/null +++ b/src/core/lib/iomgr/tcp_server.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/tcp_server.h" + +grpc_tcp_server_vtable* grpc_tcp_server_impl; + +grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + return grpc_tcp_server_impl->create(shutdown_complete, args, server); +} + +void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_server_impl->start(server, pollsets, pollset_count, on_accept_cb, + cb_arg); +} + +grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { + return grpc_tcp_server_impl->add_port(s, addr, out_port); +} + +unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return grpc_tcp_server_impl->port_fd_count(s, port_index); +} + +int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return grpc_tcp_server_impl->port_fd(s, port_index, fd_index); +} + +grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { + return grpc_tcp_server_impl->ref(s); +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_tcp_server_impl->shutdown_starting_add(s, shutdown_starting); +} + +void grpc_tcp_server_unref(grpc_tcp_server* s) { + grpc_tcp_server_impl->unref(s); +} + +void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { + grpc_tcp_server_impl->shutdown_listeners(s); +} + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl) { + grpc_tcp_server_impl = impl; +} diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h index 038c765c6c..8fcbb2f680 100644 --- a/src/core/lib/iomgr/tcp_server.h +++ b/src/core/lib/iomgr/tcp_server.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H #define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/iomgr/closure.h" @@ -43,6 +45,24 @@ typedef void (*grpc_tcp_server_cb)(void* arg, grpc_endpoint* ep, grpc_pollset* accepting_pollset, grpc_tcp_server_acceptor* acceptor); +typedef struct grpc_tcp_server_vtable { + grpc_error* (*create)(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server); + void (*start)(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, grpc_tcp_server_cb on_accept_cb, + void* cb_arg); + grpc_error* (*add_port)(grpc_tcp_server* s, const grpc_resolved_address* addr, + int* out_port); + unsigned (*port_fd_count)(grpc_tcp_server* s, unsigned port_index); + int (*port_fd)(grpc_tcp_server* s, unsigned port_index, unsigned fd_index); + grpc_tcp_server* (*ref)(grpc_tcp_server* s); + void (*shutdown_starting_add)(grpc_tcp_server* s, + grpc_closure* shutdown_starting); + void (*unref)(grpc_tcp_server* s); + void (*shutdown_listeners)(grpc_tcp_server* s); +} grpc_tcp_server_vtable; + /* Create a server, initially not bound to any ports. The caller owns one ref. If shutdown_complete is not NULL, it will be used by grpc_tcp_server_unref() when the ref count reaches zero. */ @@ -95,4 +115,8 @@ void grpc_tcp_server_unref(grpc_tcp_server* s); /* Shutdown the fds of listeners. */ void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s); +void grpc_tcp_server_global_init(); + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl); + #endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */ diff --git a/src/core/lib/iomgr/tcp_server_custom.cc b/src/core/lib/iomgr/tcp_server_custom.cc new file mode 100644 index 0000000000..be92e61b62 --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_custom.cc @@ -0,0 +1,479 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <assert.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +/* one listening port */ +struct grpc_tcp_listener { + grpc_tcp_server* server; + unsigned port_index; + int port; + + grpc_custom_socket* socket; + + /* linked list */ + struct grpc_tcp_listener* next; + + bool closed; +}; + +struct grpc_tcp_server { + gpr_refcount refs; + + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void* on_accept_cb_arg; + + int open_ports; + + /* linked list of server ports */ + grpc_tcp_listener* head; + grpc_tcp_listener* tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + bool shutdown; + + grpc_resource_quota* resource_quota; +}; + +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); + s->resource_quota = grpc_resource_quota_create(nullptr); + for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_POINTER) { + grpc_resource_quota_unref_internal(s->resource_quota); + s->resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)args->args[i].value.pointer.p); + } else { + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); + } + } + } + gpr_ref_init(&s->refs, 1); + s->on_accept_cb = nullptr; + s->on_accept_cb_arg = nullptr; + s->open_ports = 0; + s->head = nullptr; + s->tail = nullptr; + s->shutdown_starting.head = nullptr; + s->shutdown_starting.tail = nullptr; + s->shutdown_complete = shutdown_complete; + s->shutdown = false; + *server = s; + return GRPC_ERROR_NONE; +} + +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_ref(&s->refs); + return s; +} + +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); +} + +static void finish_shutdown(grpc_tcp_server* s) { + GPR_ASSERT(s->shutdown); + if (s->shutdown_complete != nullptr) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + while (s->head) { + grpc_tcp_listener* sp = s->head; + s->head = sp->next; + sp->next = nullptr; + gpr_free(sp); + } + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + grpc_tcp_listener* sp = socket->listener; + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +void grpc_custom_close_server_callback(grpc_tcp_listener* sp) { + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } +} + +static void close_listener(grpc_tcp_listener* sp) { + grpc_custom_socket* socket = sp->socket; + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } +} + +static void tcp_server_destroy(grpc_tcp_server* s) { + int immediately_done = 0; + grpc_tcp_listener* sp; + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + if (s->open_ports == 0) { + immediately_done = 1; + } + for (sp = s->head; sp; sp = sp->next) { + close_listener(sp); + } + + if (immediately_done) { + finish_shutdown(s); + } +} + +static void tcp_server_unref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (gpr_unref(&s->refs)) { + /* Complete shutdown_starting work before destroying. */ + grpc_core::ExecCtx exec_ctx; + GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); + grpc_core::ExecCtx::Get()->Flush(); + tcp_server_destroy(s); + } +} + +static void finish_accept(grpc_tcp_listener* sp, grpc_custom_socket* socket) { + grpc_tcp_server_acceptor* acceptor = + (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); + grpc_endpoint* ep = nullptr; + grpc_resolved_address peer_name; + char* peer_name_string; + grpc_error* err; + + peer_name_string = nullptr; + memset(&peer_name, 0, sizeof(grpc_resolved_address)); + peer_name.len = GRPC_MAX_SOCKADDR_SIZE; + err = grpc_custom_socket_vtable->getpeername( + socket, (grpc_sockaddr*)&peer_name.addr, (int*)&peer_name.len); + if (err == GRPC_ERROR_NONE) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + GRPC_LOG_IF_ERROR("getpeername error", err); + GRPC_ERROR_UNREF(err); + } + if (grpc_tcp_trace.enabled()) { + if (peer_name_string) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", + sp->server, peer_name_string); + } else { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); + } + } + ep = custom_tcp_endpoint_create(socket, sp->server->resource_quota, + peer_name_string); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, nullptr, acceptor); + gpr_free(peer_name_string); +} + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_tcp_listener* sp = socket->listener; + if (error != GRPC_ERROR_NONE) { + if (!sp->closed) { + gpr_log(GPR_ERROR, "Accept failed: %s", grpc_error_string(error)); + } + gpr_free(client); + GRPC_ERROR_UNREF(error); + return; + } + finish_accept(sp, client); + if (!sp->closed) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static grpc_error* add_socket_to_server(grpc_tcp_server* s, + grpc_custom_socket* socket, + const grpc_resolved_address* addr, + unsigned port_index, + grpc_tcp_listener** listener) { + grpc_tcp_listener* sp = nullptr; + int port = -1; + grpc_error* error; + grpc_resolved_address sockname_temp; + + // The last argument to uv_tcp_bind is flags + error = grpc_custom_socket_vtable->bind(socket, (grpc_sockaddr*)addr->addr, + addr->len, 0); + if (error != GRPC_ERROR_NONE) { + return error; + } + + error = grpc_custom_socket_vtable->listen(socket); + if (error != GRPC_ERROR_NONE) { + return error; + } + + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + error = grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, (int*)&sockname_temp.len); + if (error != GRPC_ERROR_NONE) { + return error; + } + + port = grpc_sockaddr_get_port(&sockname_temp); + + GPR_ASSERT(port >= 0); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener)); + sp->next = nullptr; + if (s->head == nullptr) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->socket = socket; + sp->port = port; + sp->port_index = port_index; + sp->closed = false; + s->open_ports++; + *listener = sp; + + return GRPC_ERROR_NONE; +} + +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { + // This function is mostly copied from tcp_server_windows.c + grpc_tcp_listener* sp = nullptr; + grpc_custom_socket* socket; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address* allocated_addr = nullptr; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + grpc_error* error = GRPC_ERROR_NONE; + int family; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (s->tail != nullptr) { + port_index = s->tail->port_index + 1; + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + socket = sp->socket; + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + if (nullptr == grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, + (int*)&sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = + (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, port)) { + grpc_sockaddr_make_wildcard6(*port, &wildcard); + + addr = &wildcard; + } + + if (grpc_tcp_trace.enabled()) { + char* port_string; + grpc_sockaddr_to_string(&port_string, addr, 0); + const char* str = grpc_error_string(error); + if (port_string) { + gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); + gpr_free(port_string); + } else { + gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); + } + } + + family = grpc_sockaddr_get_family(addr); + socket = (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 1; + socket->endpoint = nullptr; + socket->listener = nullptr; + socket->connector = nullptr; + grpc_custom_socket_vtable->init(socket, family); + + if (error == GRPC_ERROR_NONE) { +#if defined(GPR_LINUX) && defined(SO_REUSEPORT) + if (family == AF_INET || family == AF_INET6) { + int enable = 1; + grpc_custom_socket_vtable->setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, + &enable, sizeof(enable)); + } +#endif /* GPR_LINUX && SO_REUSEPORT */ + error = add_socket_to_server(s, socket, addr, port_index, &sp); + } + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != nullptr); + *port = sp->port; + } + socket->listener = sp; + return error; +} + +static void tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_listener* sp; + (void)pollsets; + (void)pollset_count; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "SERVER_START %p", server); + } + GPR_ASSERT(on_accept_cb); + GPR_ASSERT(!server->on_accept_cb); + server->on_accept_cb = on_accept_cb; + server->on_accept_cb_arg = cb_arg; + for (sp = server->head; sp; sp = sp->next) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} + +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { + for (grpc_tcp_listener* sp = s->head; sp; sp = sp->next) { + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(sp->socket, custom_close_callback); + } + } +} + +grpc_tcp_server_vtable custom_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; + +#ifdef GRPC_UV_TEST +grpc_tcp_server_vtable* default_tcp_server_vtable = &custom_tcp_server_vtable; +#endif diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc index fe6108e4ac..aba5d6cdb0 100644 --- a/src/core/lib/iomgr/tcp_server_posix.cc +++ b/src/core/lib/iomgr/tcp_server_posix.cc @@ -21,6 +21,8 @@ #define _GNU_SOURCE #endif +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -67,9 +69,9 @@ static void init(void) { #endif } -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { gpr_once_init(&check_init, init); grpc_tcp_server* s = @@ -390,9 +392,9 @@ static grpc_error* clone_port(grpc_tcp_listener* listener, unsigned count) { return GRPC_ERROR_NONE; } -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* out_port) { +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { grpc_tcp_listener* sp; grpc_resolved_address sockname_temp; grpc_resolved_address addr6_v4mapped; @@ -413,7 +415,7 @@ grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, sockname_temp.len = sizeof(struct sockaddr_storage); if (0 == getsockname(sp->fd, - reinterpret_cast<struct sockaddr*>(&sockname_temp.addr), + reinterpret_cast<grpc_sockaddr*>(&sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len))) { int used_port = grpc_sockaddr_get_port(&sockname_temp); if (used_port > 0) { @@ -456,8 +458,7 @@ static grpc_tcp_listener* get_port_index(grpc_tcp_server* s, return nullptr; } -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, - unsigned port_index) { +unsigned tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index) { unsigned num_fds = 0; gpr_mu_lock(&s->mu); grpc_tcp_listener* sp = get_port_index(s, port_index); @@ -468,8 +469,8 @@ unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, return num_fds; } -int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, - unsigned fd_index) { +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { gpr_mu_lock(&s->mu); grpc_tcp_listener* sp = get_port_index(s, port_index); for (; sp; sp = sp->sibling, --fd_index) { @@ -482,10 +483,10 @@ int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, return -1; } -void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void* on_accept_cb_arg) { +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { size_t i; grpc_tcp_listener* sp; GPR_ASSERT(on_accept_cb); @@ -524,20 +525,20 @@ void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, gpr_mu_unlock(&s->mu); } -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { +grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { gpr_ref_non_zero(&s->refs); return s; } -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { gpr_mu_lock(&s->mu); grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, GRPC_ERROR_NONE); gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_unref(grpc_tcp_server* s) { +static void tcp_server_unref(grpc_tcp_server* s) { if (gpr_unref(&s->refs)) { grpc_tcp_server_shutdown_listeners(s); gpr_mu_lock(&s->mu); @@ -547,7 +548,7 @@ void grpc_tcp_server_unref(grpc_tcp_server* s) { } } -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { gpr_mu_lock(&s->mu); s->shutdown_listeners = true; /* shutdown all fd's */ @@ -561,4 +562,14 @@ void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { gpr_mu_unlock(&s->mu); } +grpc_tcp_server_vtable grpc_posix_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; #endif diff --git a/src/core/lib/iomgr/tcp_server_utils_posix.h b/src/core/lib/iomgr/tcp_server_utils_posix.h index 6046f257f9..34d68130c9 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix.h +++ b/src/core/lib/iomgr/tcp_server_utils_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H #define GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/socket_utils_posix.h" diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc index be2a00a9dc..76d3d62940 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -169,8 +171,7 @@ grpc_error* grpc_tcp_server_prepare_socket(int fd, if (err != GRPC_ERROR_NONE) goto error; GPR_ASSERT(addr->len < ~(socklen_t)0); - if (bind(fd, - reinterpret_cast<struct sockaddr*>(const_cast<char*>(addr->addr)), + if (bind(fd, reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)), static_cast<socklen_t>(addr->len)) < 0) { err = GRPC_OS_ERROR(errno, "bind"); goto error; @@ -183,7 +184,7 @@ grpc_error* grpc_tcp_server_prepare_socket(int fd, sockname_temp.len = sizeof(struct sockaddr_storage); - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(sockname_temp.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len)) < 0) { err = GRPC_OS_ERROR(errno, "getsockname"); goto error; diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc index 612c2584bc..29ff9ecda1 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_HAVE_IFADDRS @@ -66,13 +68,13 @@ static grpc_error* get_unused_port(int* port) { if (dsmode == GRPC_DSMODE_IPV4) { grpc_sockaddr_make_wildcard4(0, &wild); } - if (bind(fd, reinterpret_cast<const struct sockaddr*>(wild.addr), + if (bind(fd, reinterpret_cast<const grpc_sockaddr*>(wild.addr), static_cast<socklen_t>(wild.len)) != 0) { err = GRPC_OS_ERROR(errno, "bind"); close(fd); return err; } - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(wild.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(wild.addr), reinterpret_cast<socklen_t*>(&wild.len)) != 0) { err = GRPC_OS_ERROR(errno, "getsockname"); close(fd); @@ -117,9 +119,9 @@ grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s, if (ifa_it->ifa_addr == nullptr) { continue; } else if (ifa_it->ifa_addr->sa_family == AF_INET) { - addr.len = sizeof(struct sockaddr_in); + addr.len = sizeof(grpc_sockaddr_in); } else if (ifa_it->ifa_addr->sa_family == AF_INET6) { - addr.len = sizeof(struct sockaddr_in6); + addr.len = sizeof(grpc_sockaddr_in6); } else { continue; } diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc index 2d72b95def..86ee14f285 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc +++ b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc deleted file mode 100644 index 1ac49190fb..0000000000 --- a/src/core/lib/iomgr/tcp_server_uv.cc +++ /dev/null @@ -1,471 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <assert.h> -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_server.h" -#include "src/core/lib/iomgr/tcp_uv.h" - -/* one listening port */ -typedef struct grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - uv_tcp_t* handle; - grpc_tcp_server* server; - unsigned port_index; - int port; - /* linked list */ - struct grpc_tcp_listener* next; - - bool closed; - - bool has_pending_connection; -}; - -struct grpc_tcp_server { - gpr_refcount refs; - - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void* on_accept_cb_arg; - - int open_ports; - - /* linked list of server ports */ - grpc_tcp_listener* head; - grpc_tcp_listener* tail; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure* shutdown_complete; - - bool shutdown; - - grpc_resource_quota* resource_quota; -}; - -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { - grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); - s->resource_quota = grpc_resource_quota_create(NULL); - for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { - if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { - if (args->args[i].type == GRPC_ARG_POINTER) { - grpc_resource_quota_unref_internal(s->resource_quota); - s->resource_quota = grpc_resource_quota_ref_internal( - (grpc_resource_quota*)args->args[i].value.pointer.p); - } else { - grpc_resource_quota_unref_internal(s->resource_quota); - gpr_free(s); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); - } - } - } - gpr_ref_init(&s->refs, 1); - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->open_ports = 0; - s->head = NULL; - s->tail = NULL; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - s->shutdown = false; - *server = s; - return GRPC_ERROR_NONE; -} - -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_ref(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { - grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, - GRPC_ERROR_NONE); -} - -static void finish_shutdown(grpc_tcp_server* s) { - GPR_ASSERT(s->shutdown); - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); - } - - while (s->head) { - grpc_tcp_listener* sp = s->head; - s->head = sp->next; - sp->next = NULL; - gpr_free(sp->handle); - gpr_free(sp); - } - grpc_resource_quota_unref_internal(s->resource_quota); - gpr_free(s); -} - -static void handle_close_callback(uv_handle_t* handle) { - grpc_tcp_listener* sp = (grpc_tcp_listener*)handle->data; - grpc_core::ExecCtx exec_ctx; - sp->server->open_ports--; - if (sp->server->open_ports == 0 && sp->server->shutdown) { - finish_shutdown(sp->server); - } -} - -static void close_listener(grpc_tcp_listener* sp) { - if (!sp->closed) { - sp->closed = true; - uv_close((uv_handle_t*)sp->handle, handle_close_callback); - } -} - -static void tcp_server_destroy(grpc_tcp_server* s) { - int immediately_done = 0; - grpc_tcp_listener* sp; - - GPR_ASSERT(!s->shutdown); - s->shutdown = true; - - if (s->open_ports == 0) { - immediately_done = 1; - } - for (sp = s->head; sp; sp = sp->next) { - close_listener(sp); - } - - if (immediately_done) { - finish_shutdown(s); - } -} - -void grpc_tcp_server_unref(grpc_tcp_server* s) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (gpr_unref(&s->refs)) { - /* Complete shutdown_starting work before destroying. */ - grpc_core::ExecCtx exec_ctx; - GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); - grpc_core::ExecCtx::Get()->Flush(); - tcp_server_destroy(s); - } -} - -static void finish_accept(grpc_tcp_listener* sp) { - grpc_tcp_server_acceptor* acceptor = - (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); - uv_tcp_t* client = NULL; - grpc_endpoint* ep = NULL; - grpc_resolved_address peer_name; - char* peer_name_string; - int err; - uv_tcp_t* server = sp->handle; - - client = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - uv_tcp_init(uv_default_loop(), client); - // UV documentation says this is guaranteed to succeed - uv_accept((uv_stream_t*)server, (uv_stream_t*)client); - peer_name_string = NULL; - memset(&peer_name, 0, sizeof(grpc_resolved_address)); - peer_name.len = sizeof(struct sockaddr_storage); - err = uv_tcp_getpeername(client, (struct sockaddr*)&peer_name.addr, - (int*)&peer_name.len); - if (err == 0) { - peer_name_string = grpc_sockaddr_to_uri(&peer_name); - } else { - gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err)); - } - if (grpc_tcp_trace.enabled()) { - if (peer_name_string) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", - sp->server, peer_name_string); - } else { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); - } - } - ep = grpc_tcp_create(client, sp->server->resource_quota, peer_name_string); - acceptor->from_server = sp->server; - acceptor->port_index = sp->port_index; - acceptor->fd_index = 0; - sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, NULL, acceptor); - gpr_free(peer_name_string); -} - -static void on_connect(uv_stream_t* server, int status) { - grpc_tcp_listener* sp = (grpc_tcp_listener*)server->data; - grpc_core::ExecCtx exec_ctx; - - if (status < 0) { - switch (status) { - case UV_EINTR: - case UV_EAGAIN: - return; - default: - close_listener(sp); - return; - } - } - - GPR_ASSERT(!sp->has_pending_connection); - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server); - } - - // Create acceptor. - if (sp->server->on_accept_cb) { - finish_accept(sp); - } else { - sp->has_pending_connection = true; - } -} - -static grpc_error* add_addr_to_server(grpc_tcp_server* s, - const grpc_resolved_address* addr, - unsigned port_index, - grpc_tcp_listener** listener) { - grpc_tcp_listener* sp = NULL; - int port = -1; - int status; - grpc_error* error; - grpc_resolved_address sockname_temp; - uv_tcp_t* handle = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); - int family = grpc_sockaddr_get_family(addr); - - status = uv_tcp_init_ex(uv_default_loop(), handle, (unsigned int)family); -#if defined(GPR_LINUX) && defined(SO_REUSEPORT) - if (family == AF_INET || family == AF_INET6) { - int fd; - uv_fileno((uv_handle_t*)handle, &fd); - int enable = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)); - } -#endif /* GPR_LINUX && SO_REUSEPORT */ - - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to initialize UV tcp handle"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - // The last argument to uv_tcp_bind is flags - status = uv_tcp_bind(handle, (struct sockaddr*)addr->addr, 0); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to bind to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - status = uv_listen((uv_stream_t*)handle, SOMAXCONN, on_connect); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to listen to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - sockname_temp.len = (int)sizeof(struct sockaddr_storage); - status = uv_tcp_getsockname(handle, (struct sockaddr*)&sockname_temp.addr, - (int*)&sockname_temp.len); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getsockname failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - port = grpc_sockaddr_get_port(&sockname_temp); - - GPR_ASSERT(port >= 0); - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->handle = handle; - sp->port = port; - sp->port_index = port_index; - sp->closed = false; - handle->data = sp; - s->open_ports++; - GPR_ASSERT(sp->handle); - *listener = sp; - - return GRPC_ERROR_NONE; -} - -static grpc_error* add_wildcard_addrs_to_server(grpc_tcp_server* s, - unsigned port_index, - int requested_port, - grpc_tcp_listener** listener) { - grpc_resolved_address wild4; - grpc_resolved_address wild6; - grpc_tcp_listener* sp = nullptr; - grpc_tcp_listener* sp2 = nullptr; - grpc_error* v6_err = GRPC_ERROR_NONE; - grpc_error* v4_err = GRPC_ERROR_NONE; - - grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); - /* Try listening on IPv6 first. */ - if ((v6_err = add_addr_to_server(s, &wild6, port_index, &sp)) == - GRPC_ERROR_NONE) { - *listener = sp; - return GRPC_ERROR_NONE; - } - - if ((v4_err = add_addr_to_server(s, &wild4, port_index, &sp2)) == - GRPC_ERROR_NONE) { - *listener = sp2; - return GRPC_ERROR_NONE; - } - - grpc_error* root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to add any wildcard listeners"); - root_err = grpc_error_add_child(root_err, v6_err); - root_err = grpc_error_add_child(root_err, v4_err); - return root_err; -} - -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* port) { - // This function is mostly copied from tcp_server_windows.c - grpc_tcp_listener* sp = NULL; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address* allocated_addr = NULL; - grpc_resolved_address sockname_temp; - unsigned port_index = 0; - grpc_error* error = GRPC_ERROR_NONE; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { - sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == uv_tcp_getsockname(sp->handle, - (struct sockaddr*)&sockname_temp.addr, - (int*)&sockname_temp.len)) { - *port = grpc_sockaddr_get_port(&sockname_temp); - if (*port > 0) { - allocated_addr = - (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); - memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(allocated_addr, *port); - addr = allocated_addr; - break; - } - } - } - } - - /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ - if (grpc_sockaddr_is_wildcard(addr, port)) { - error = add_wildcard_addrs_to_server(s, port_index, *port, &sp); - } else { - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - error = add_addr_to_server(s, addr, port_index, &sp); - } - - gpr_free(allocated_addr); - - if (grpc_tcp_trace.enabled()) { - char* port_string; - grpc_sockaddr_to_string(&port_string, addr, 0); - const char* str = grpc_error_string(error); - if (port_string) { - gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); - gpr_free(port_string); - } else { - gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); - } - } - - if (error != GRPC_ERROR_NONE) { - grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to add port to server", &error, 1); - GRPC_ERROR_UNREF(error); - error = error_out; - *port = -1; - } else { - GPR_ASSERT(sp != NULL); - *port = sp->port; - } - return error; -} - -void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, void* cb_arg) { - grpc_tcp_listener* sp; - (void)pollsets; - (void)pollset_count; - GRPC_UV_ASSERT_SAME_THREAD(); - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "SERVER_START %p", server); - } - GPR_ASSERT(on_accept_cb); - GPR_ASSERT(!server->on_accept_cb); - server->on_accept_cb = on_accept_cb; - server->on_accept_cb_arg = cb_arg; - for (sp = server->head; sp; sp = sp->next) { - if (sp->has_pending_connection) { - finish_accept(sp); - sp->has_pending_connection = false; - } - } -} - -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc index 8a30dfde43..77f3811dca 100644 --- a/src/core/lib/iomgr/tcp_server_windows.cc +++ b/src/core/lib/iomgr/tcp_server_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -48,7 +50,7 @@ typedef struct grpc_tcp_listener grpc_tcp_listener; struct grpc_tcp_listener { /* This seemingly magic number comes from AcceptEx's documentation. each address buffer needs to have at least 16 more bytes at their end. */ - uint8_t addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; + uint8_t addresses[(sizeof(grpc_sockaddr_in6) + 16) * 2]; /* This will hold the socket for the next accept. */ SOCKET new_socket; /* The listener winsocket. */ @@ -94,9 +96,9 @@ struct grpc_tcp_server { /* Public function. Allocates the proper data structures to hold a grpc_tcp_server. */ -grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, - const grpc_channel_args* args, - grpc_tcp_server** server) { +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); s->channel_args = grpc_channel_args_copy(args); gpr_ref_init(&s->refs, 1); @@ -140,13 +142,13 @@ static void finish_shutdown_locked(grpc_tcp_server* s) { GRPC_ERROR_NONE); } -grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { gpr_ref_non_zero(&s->refs); return s; } -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, - grpc_closure* shutdown_starting) { +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { gpr_mu_lock(&s->mu); grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, GRPC_ERROR_NONE); @@ -170,7 +172,7 @@ static void tcp_server_destroy(grpc_tcp_server* s) { gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_unref(grpc_tcp_server* s) { +static void tcp_server_unref(grpc_tcp_server* s) { if (gpr_unref(&s->refs)) { grpc_tcp_server_shutdown_listeners(s); gpr_mu_lock(&s->mu); @@ -193,7 +195,7 @@ static grpc_error* prepare_socket(SOCKET sock, goto failure; } - if (bind(sock, (const struct sockaddr*)addr->addr, (int)addr->len) == + if (bind(sock, (const grpc_sockaddr*)addr->addr, (int)addr->len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; @@ -205,7 +207,7 @@ static grpc_error* prepare_socket(SOCKET sock, } sockname_temp_len = sizeof(struct sockaddr_storage); - if (getsockname(sock, (struct sockaddr*)sockname_temp.addr, + if (getsockname(sock, (grpc_sockaddr*)sockname_temp.addr, &sockname_temp_len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); goto failure; @@ -243,7 +245,7 @@ static void decrement_active_ports_and_notify_locked(grpc_tcp_listener* sp) { static grpc_error* start_accept_locked(grpc_tcp_listener* port) { SOCKET sock = INVALID_SOCKET; BOOL success; - DWORD addrlen = sizeof(struct sockaddr_in6) + 16; + DWORD addrlen = sizeof(grpc_sockaddr_in6) + 16; DWORD bytes_received = 0; grpc_error* error = GRPC_ERROR_NONE; @@ -341,7 +343,7 @@ static void on_accept(void* arg, grpc_error* error) { gpr_free(utf8_message); } int peer_name_len = (int)peer_name.len; - err = getpeername(sock, (struct sockaddr*)peer_name.addr, &peer_name_len); + err = getpeername(sock, (grpc_sockaddr*)peer_name.addr, &peer_name_len); peer_name.len = (size_t)peer_name_len; if (!err) { peer_name_string = grpc_sockaddr_to_uri(&peer_name); @@ -440,9 +442,9 @@ static grpc_error* add_socket_to_server(grpc_tcp_server* s, SOCKET sock, return GRPC_ERROR_NONE; } -grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, - const grpc_resolved_address* addr, - int* port) { +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { grpc_tcp_listener* sp = NULL; SOCKET sock; grpc_resolved_address addr6_v4mapped; @@ -462,7 +464,7 @@ grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, for (sp = s->head; sp; sp = sp->next) { int sockname_temp_len = sizeof(struct sockaddr_storage); if (0 == getsockname(sp->socket->socket, - (struct sockaddr*)sockname_temp.addr, + (grpc_sockaddr*)sockname_temp.addr, &sockname_temp_len)) { sockname_temp.len = (size_t)sockname_temp_len; *port = grpc_sockaddr_get_port(&sockname_temp); @@ -514,10 +516,10 @@ done: return error; } -void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, - size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void* on_accept_cb_arg) { +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { grpc_tcp_listener* sp; GPR_ASSERT(on_accept_cb); gpr_mu_lock(&s->mu); @@ -532,6 +534,26 @@ void grpc_tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, gpr_mu_unlock(&s->mu); } -void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) {} +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {} + +grpc_tcp_server_vtable grpc_windows_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; #endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc index b384623a5e..5e3166926b 100644 --- a/src/core/lib/iomgr/tcp_uv.cc +++ b/src/core/lib/iomgr/tcp_uv.cc @@ -16,10 +16,11 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_UV - #include <limits.h> #include <string.h> @@ -31,393 +32,393 @@ #include "src/core/lib/gpr/string.h" #include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/iomgr_custom.h" #include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" #include "src/core/lib/iomgr/resource_quota.h" -#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/iomgr/tcp_custom.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +#include <uv.h> -typedef struct { - grpc_endpoint base; - gpr_refcount refcount; +#define IGNORE_CONST(addr) ((grpc_sockaddr*)(uintptr_t)(addr)) +typedef struct uv_socket_t { + uv_connect_t connect_req; uv_write_t write_req; uv_shutdown_t shutdown_req; - uv_tcp_t* handle; - - grpc_closure* read_cb; - grpc_closure* write_cb; - - grpc_slice_buffer* read_slices; - grpc_slice_buffer* write_slices; uv_buf_t* write_buffers; - grpc_resource_user* resource_user; - grpc_resource_user_slice_allocator slice_allocator; + char* read_buf; + size_t read_len; - bool shutting_down; + bool pending_connection; + grpc_custom_socket* accept_socket; + grpc_error* accept_error; - char* peer_string; - grpc_pollset* pollset; -} grpc_tcp; + grpc_custom_connect_callback connect_cb; + grpc_custom_write_callback write_cb; + grpc_custom_read_callback read_cb; + grpc_custom_accept_callback accept_cb; + grpc_custom_close_callback close_cb; -static grpc_error* tcp_annotate_error(grpc_error* src_error, grpc_tcp* tcp) { - return grpc_error_set_str( - grpc_error_set_int( - src_error, - /* All tcp errors are marked with UNAVAILABLE so that application may - * choose to retry. */ - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), - GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(tcp->peer_string)); -} +} uv_socket_t; -static void tcp_free(grpc_tcp* tcp) { - grpc_resource_user_unref(tcp->resource_user); - gpr_free(tcp->handle); - gpr_free(tcp->peer_string); - gpr_free(tcp); -} - -#ifndef NDEBUG -#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_tcp* tcp, const char* reason, const char* file, - int line) { - if (grpc_tcp_trace.enabled()) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val - 1); - } - if (gpr_unref(&tcp->refcount)) { - tcp_free(tcp); - } -} - -static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file, - int line) { - if (grpc_tcp_trace.enabled()) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val + 1); - } - gpr_ref(&tcp->refcount); -} -#else -#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_tcp* tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(tcp); +static grpc_error* tcp_error_create(const char* desc, int status) { + if (status == 0) { + return GRPC_ERROR_NONE; } + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc); + /* All tcp errors are marked with UNAVAILABLE so that application may + * choose to retry. */ + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + return grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); } -static void tcp_ref(grpc_tcp* tcp) { gpr_ref(&tcp->refcount); } -#endif - -static void uv_close_callback(uv_handle_t* handle) { - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)handle->data; - TCP_UNREF(tcp, "destroy"); +static void uv_socket_destroy(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->handle); + gpr_free(uv_socket); } static void alloc_uv_buf(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)handle->data; + uv_socket_t* uv_socket = + (uv_socket_t*)((grpc_custom_socket*)handle->data)->impl; (void)suggested_size; - /* Before calling uv_read_start, we allocate a buffer with exactly one slice - * to tcp->read_slices and wait for the callback indicating that the - * allocation was successful. So slices[0] should always exist here */ - buf->base = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]); - buf->len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]); -} - -static void call_read_cb(grpc_tcp* tcp, grpc_error* error) { - grpc_closure* cb = tcp->read_cb; - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg); - size_t i; - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "read: error=%s", str); - - for (i = 0; i < tcp->read_slices->count; i++) { - char* dump = grpc_dump_slice(tcp->read_slices->slices[i], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); - gpr_free(dump); - } - } - tcp->read_slices = NULL; - tcp->read_cb = NULL; - GRPC_CLOSURE_RUN(cb, error); + buf->base = uv_socket->read_buf; + buf->len = uv_socket->read_len; } -static void read_callback(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf) { - grpc_error* error; - grpc_core::ExecCtx exec_ctx; - grpc_tcp* tcp = (grpc_tcp*)stream->data; - grpc_slice_buffer garbage; +static void uv_read_callback(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + grpc_error* error = GRPC_ERROR_NONE; if (nread == 0) { // Nothing happened. Wait for the next callback return; } - TCP_UNREF(tcp, "read"); // TODO(murgatroid99): figure out what the return value here means uv_read_stop(stream); if (nread == UV_EOF) { - error = - tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), tcp); - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); - } else if (nread > 0) { - // Successful read - error = GRPC_ERROR_NONE; - if ((size_t)nread < tcp->read_slices->length) { - /* TODO(murgatroid99): Instead of discarding the unused part of the read - * buffer, reuse it as the next read buffer. */ - grpc_slice_buffer_init(&garbage); - grpc_slice_buffer_trim_end( - tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage); - grpc_slice_buffer_reset_and_unref_internal(&garbage); - } - } else { - // nread < 0: Error - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed"), tcp); - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } else if (nread < 0) { + error = tcp_error_create("TCP Read failed", nread); } - call_read_cb(tcp, error); + grpc_custom_socket* socket = (grpc_custom_socket*)stream->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->read_cb(socket, (size_t)nread, error); } -static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { - int status; - grpc_tcp* tcp = (grpc_tcp*)tcpp; - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp, - grpc_error_string(error)); - } - if (error == GRPC_ERROR_NONE) { - status = - uv_read_start((uv_stream_t*)tcp->handle, alloc_uv_buf, read_callback); - if (status != 0) { - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start"), - tcp); - error = grpc_error_set_str( - error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - } - } - if (error != GRPC_ERROR_NONE) { - grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); - call_read_cb(tcp, GRPC_ERROR_REF(error)); - TCP_UNREF(tcp, "read"); +static void uv_close_callback(uv_handle_t* handle) { + grpc_custom_socket* socket = (grpc_custom_socket*)handle->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (uv_socket->accept_socket) { + uv_socket->accept_cb(socket, uv_socket->accept_socket, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket closed")); } - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str); + uv_socket->close_cb(socket); +} + +static void uv_socket_read(grpc_custom_socket* socket, char* buffer, + size_t length, grpc_custom_read_callback read_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status; + grpc_error* error; + uv_socket->read_cb = read_cb; + uv_socket->read_buf = buffer; + uv_socket->read_len = length; + // TODO(murgatroid99): figure out what the return value here means + status = + uv_read_start((uv_stream_t*)uv_socket->handle, (uv_alloc_cb)alloc_uv_buf, + (uv_read_cb)uv_read_callback); + if (status != 0) { + error = tcp_error_create("TCP Read failed at start", status); + uv_socket->read_cb(socket, 0, error); } } -static void uv_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, - grpc_closure* cb) { - grpc_tcp* tcp = (grpc_tcp*)ep; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(tcp->read_cb == NULL); - tcp->read_cb = cb; - tcp->read_slices = read_slices; - grpc_slice_buffer_reset_and_unref_internal(read_slices); - TCP_REF(tcp, "read"); - grpc_resource_user_alloc_slices(&tcp->slice_allocator, - GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, - tcp->read_slices); +static void uv_write_callback(uv_write_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->write_buffers); + uv_socket->write_cb(socket, tcp_error_create("TCP Write failed", status)); } -static void write_callback(uv_write_t* req, int status) { - grpc_tcp* tcp = (grpc_tcp*)req->data; - grpc_error* error; - grpc_core::ExecCtx exec_ctx; - grpc_closure* cb = tcp->write_cb; - tcp->write_cb = NULL; - TCP_UNREF(tcp, "write"); - if (status == 0) { - error = GRPC_ERROR_NONE; - } else { - error = tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed"), tcp); - } - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str); +void uv_socket_write(grpc_custom_socket* socket, + grpc_slice_buffer* write_slices, + grpc_custom_write_callback write_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->write_cb = write_cb; + uv_buf_t* uv_buffers; + uv_write_t* write_req; + + uv_buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * write_slices->count); + for (size_t i = 0; i < write_slices->count; i++) { + uv_buffers[i].base = (char*)GRPC_SLICE_START_PTR(write_slices->slices[i]); + uv_buffers[i].len = GRPC_SLICE_LENGTH(write_slices->slices[i]); } - gpr_free(tcp->write_buffers); - GRPC_CLOSURE_SCHED(cb, error); + + uv_socket->write_buffers = uv_buffers; + write_req = &uv_socket->write_req; + write_req->data = socket; + // TODO(murgatroid99): figure out what the return value here means + uv_write(write_req, (uv_stream_t*)uv_socket->handle, uv_buffers, + write_slices->count, uv_write_callback); } -static void uv_endpoint_write(grpc_endpoint* ep, - grpc_slice_buffer* write_slices, - grpc_closure* cb) { - grpc_tcp* tcp = (grpc_tcp*)ep; - uv_buf_t* buffers; - unsigned int buffer_count; - unsigned int i; - grpc_slice* slice; - uv_write_t* write_req; - GRPC_UV_ASSERT_SAME_THREAD(); +static void shutdown_callback(uv_shutdown_t* req, int status) {} - if (grpc_tcp_trace.enabled()) { - size_t j; +static void uv_socket_shutdown(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_shutdown_t* req = &uv_socket->shutdown_req; + uv_shutdown(req, (uv_stream_t*)uv_socket->handle, shutdown_callback); +} - for (j = 0; j < write_slices->count; j++) { - char* data = grpc_dump_slice(write_slices->slices[j], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); - gpr_free(data); - } +static void uv_socket_close(grpc_custom_socket* socket, + grpc_custom_close_callback close_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->close_cb = close_cb; + uv_close((uv_handle_t*)uv_socket->handle, uv_close_callback); +} + +static grpc_error* uv_socket_init_helper(uv_socket_t* uv_socket, int domain) { + uv_tcp_t* tcp = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); + uv_socket->handle = tcp; + int status = uv_tcp_init_ex(uv_default_loop(), tcp, (unsigned int)domain); + if (status != 0) { + return tcp_error_create("Failed to initialize UV tcp handle", status); } + uv_socket->write_buffers = nullptr; + uv_socket->read_len = 0; + uv_tcp_nodelay(uv_socket->handle, 1); + uv_socket->pending_connection = false; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + return GRPC_ERROR_NONE; +} - if (tcp->shutting_down) { - GRPC_CLOSURE_SCHED(cb, - tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TCP socket is shutting down"), - tcp)); - return; +static grpc_error* uv_socket_init(grpc_custom_socket* socket, int domain) { + uv_socket_t* uv_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + grpc_error* error = uv_socket_init_helper(uv_socket, domain); + if (error != GRPC_ERROR_NONE) { + return error; } + uv_socket->handle->data = socket; + socket->impl = uv_socket; + return GRPC_ERROR_NONE; +} + +static grpc_error* uv_socket_getpeername(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getpeername(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getpeername failed", err); +} + +static grpc_error* uv_socket_getsockname(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getsockname(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getsockname failed", err); +} - GPR_ASSERT(tcp->write_cb == NULL); - tcp->write_slices = write_slices; - GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); - if (tcp->write_slices->count == 0) { - // No slices means we don't have to do anything, - // and libuv doesn't like empty writes - GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE); +static void accept_new_connection(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (!uv_socket->pending_connection || !uv_socket->accept_socket) { return; } + grpc_custom_socket* new_socket = uv_socket->accept_socket; + grpc_error* error = uv_socket->accept_error; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + uv_socket->pending_connection = false; + if (uv_socket->accept_error != GRPC_ERROR_NONE) { + uv_stream_t dummy_handle; + uv_accept((uv_stream_t*)uv_socket->handle, &dummy_handle); + uv_socket->accept_cb(socket, new_socket, error); + } else { + uv_socket_t* uv_new_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + uv_socket_init_helper(uv_new_socket, AF_UNSPEC); + // UV documentation says this is guaranteed to succeed + GPR_ASSERT(uv_accept((uv_stream_t*)uv_socket->handle, + (uv_stream_t*)uv_new_socket->handle) == 0); + new_socket->impl = uv_new_socket; + uv_new_socket->handle->data = new_socket; + uv_socket->accept_cb(socket, new_socket, error); + } +} - tcp->write_cb = cb; - buffer_count = (unsigned int)tcp->write_slices->count; - buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * buffer_count); - for (i = 0; i < buffer_count; i++) { - slice = &tcp->write_slices->slices[i]; - buffers[i].base = (char*)GRPC_SLICE_START_PTR(*slice); - buffers[i].len = GRPC_SLICE_LENGTH(*slice); +static void uv_on_connect(uv_stream_t* server, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)server->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + GPR_ASSERT(!uv_socket->pending_connection); + uv_socket->pending_connection = true; + if (status < 0) { + switch (status) { + case UV_EINTR: + case UV_EAGAIN: + return; + default: + uv_socket->accept_error = tcp_error_create("accept failed", status); + } } - tcp->write_buffers = buffers; - write_req = &tcp->write_req; - write_req->data = tcp; - TCP_REF(tcp, "write"); - // TODO(murgatroid99): figure out what the return value here means - uv_write(write_req, (uv_stream_t*)tcp->handle, buffers, buffer_count, - write_callback); + accept_new_connection(socket); } -static void uv_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; - grpc_tcp* tcp = (grpc_tcp*)ep; - tcp->pollset = pollset; +void uv_socket_accept(grpc_custom_socket* socket, + grpc_custom_socket* new_socket, + grpc_custom_accept_callback accept_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->accept_cb = accept_cb; + GPR_ASSERT(uv_socket->accept_socket == nullptr); + uv_socket->accept_socket = new_socket; + accept_new_connection(socket); } -static void uv_add_to_pollset_set(grpc_endpoint* ep, - grpc_pollset_set* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; +static grpc_error* uv_socket_bind(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + int flags) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_tcp_bind((uv_tcp_t*)uv_socket->handle, (struct sockaddr*)addr, 0); + return tcp_error_create("Failed to bind to port", status); } -static void uv_delete_from_pollset_set(grpc_endpoint* ep, - grpc_pollset_set* pollset) { - // No-op. We're ignoring pollsets currently - (void)ep; - (void)pollset; +static grpc_error* uv_socket_listen(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_listen((uv_stream_t*)uv_socket->handle, SOMAXCONN, uv_on_connect); + return tcp_error_create("Failed to listen to port", status); } -static void shutdown_callback(uv_shutdown_t* req, int status) {} +static grpc_error* uv_socket_setsockopt(grpc_custom_socket* socket, int level, + int option_name, const void* optval, + socklen_t option_len) { + int fd; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_fileno((uv_handle_t*)uv_socket->handle, &fd); + // TODO Handle error here. Also, does this work on windows?? + setsockopt(fd, level, option_name, &optval, (socklen_t)option_len); + return GRPC_ERROR_NONE; +} -static void uv_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { - grpc_tcp* tcp = (grpc_tcp*)ep; - if (!tcp->shutting_down) { - if (grpc_tcp_trace.enabled()) { - const char* str = grpc_error_string(why); - gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str); - } - tcp->shutting_down = true; - uv_shutdown_t* req = &tcp->shutdown_req; - uv_shutdown(req, (uv_stream_t*)tcp->handle, shutdown_callback); - grpc_resource_user_shutdown(tcp->resource_user); +static void uv_tc_on_connect(uv_connect_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + grpc_error* error; + if (status == UV_ECANCELED) { + // This should only happen if the handle is already closed + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timeout occurred"); + } else { + error = tcp_error_create("Failed to connect to remote host", status); } - GRPC_ERROR_UNREF(why); + uv_socket->connect_cb(socket, error); } -static void uv_destroy(grpc_endpoint* ep) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp* tcp = (grpc_tcp*)ep; - uv_close((uv_handle_t*)tcp->handle, uv_close_callback); +static void uv_socket_connect(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + grpc_custom_connect_callback connect_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->connect_cb = connect_cb; + uv_socket->connect_req.data = socket; + int status = uv_tcp_connect(&uv_socket->connect_req, uv_socket->handle, + (struct sockaddr*)addr, uv_tc_on_connect); + if (status != 0) { + // The callback will not be called + uv_socket->connect_cb(socket, tcp_error_create("connect failed", status)); + } +} + +static grpc_resolved_addresses* handle_addrinfo_result( + struct addrinfo* result) { + struct addrinfo* resp; + struct addrinfo* prev; + size_t i; + grpc_resolved_addresses* addresses = + (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); + addresses->naddrs = 0; + for (resp = result; resp != nullptr; resp = resp->ai_next) { + addresses->naddrs++; + } + addresses->addrs = (grpc_resolved_address*)gpr_malloc( + sizeof(grpc_resolved_address) * addresses->naddrs); + i = 0; + resp = result; + while (resp != nullptr) { + memcpy(&addresses->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + addresses->addrs[i].len = resp->ai_addrlen; + i++; + prev = resp; + resp = resp->ai_next; + gpr_free(prev); + } + return addresses; } -static char* uv_get_peer(grpc_endpoint* ep) { - grpc_tcp* tcp = (grpc_tcp*)ep; - return gpr_strdup(tcp->peer_string); +static void uv_resolve_callback(uv_getaddrinfo_t* req, int status, + struct addrinfo* res) { + grpc_custom_resolver* r = (grpc_custom_resolver*)req->data; + gpr_free(req); + grpc_resolved_addresses* result = nullptr; + if (status == 0) { + result = handle_addrinfo_result(res); + } + grpc_custom_resolve_callback(r, result, + tcp_error_create("getaddrinfo failed", status)); } -static grpc_resource_user* uv_get_resource_user(grpc_endpoint* ep) { - grpc_tcp* tcp = (grpc_tcp*)ep; - return tcp->resource_user; +static grpc_error* uv_resolve(char* host, char* port, + grpc_resolved_addresses** result) { + int status; + uv_getaddrinfo_t req; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); + if (status != 0) { + *result = nullptr; + } else { + *result = handle_addrinfo_result(req.addrinfo); + } + return tcp_error_create("getaddrinfo failed", status); } -static int uv_get_fd(grpc_endpoint* ep) { return -1; } - -static grpc_endpoint_vtable vtable = {uv_endpoint_read, - uv_endpoint_write, - uv_add_to_pollset, - uv_add_to_pollset_set, - uv_delete_from_pollset_set, - uv_endpoint_shutdown, - uv_destroy, - uv_get_resource_user, - uv_get_peer, - uv_get_fd}; - -grpc_endpoint* grpc_tcp_create(uv_tcp_t* handle, - grpc_resource_quota* resource_quota, - char* peer_string) { - grpc_tcp* tcp = (grpc_tcp*)gpr_malloc(sizeof(grpc_tcp)); - grpc_core::ExecCtx exec_ctx; - - if (grpc_tcp_trace.enabled()) { - gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); +static void uv_resolve_async(grpc_custom_resolver* r, char* host, char* port) { + int status; + uv_getaddrinfo_t* req = + (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = GRPC_AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = GRPC_SOCK_STREAM; /* stream socket */ + hints.ai_flags = GRPC_AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), req, uv_resolve_callback, host, + port, &hints); + if (status != 0) { + gpr_free(req); + grpc_error* error = tcp_error_create("getaddrinfo failed", status); + grpc_custom_resolve_callback(r, NULL, error); } +} - /* Disable Nagle's Algorithm */ - uv_tcp_nodelay(handle, 1); - - memset(tcp, 0, sizeof(grpc_tcp)); - tcp->base.vtable = &vtable; - tcp->handle = handle; - handle->data = tcp; - gpr_ref_init(&tcp->refcount, 1); - tcp->peer_string = gpr_strdup(peer_string); - tcp->shutting_down = false; - tcp->read_slices = NULL; - tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); - grpc_resource_user_slice_allocator_init( - &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); - /* Tell network status tracking code about the new endpoint */ - grpc_network_status_register_endpoint(&tcp->base); - -#ifndef GRPC_UV_TCP_HOLD_LOOP - uv_unref((uv_handle_t*)handle); -#endif +grpc_custom_resolver_vtable uv_resolver_vtable = {uv_resolve, uv_resolve_async}; - return &tcp->base; -} +grpc_socket_vtable grpc_uv_socket_vtable = { + uv_socket_init, uv_socket_connect, uv_socket_destroy, + uv_socket_shutdown, uv_socket_close, uv_socket_write, + uv_socket_read, uv_socket_getpeername, uv_socket_getsockname, + uv_socket_setsockopt, uv_socket_bind, uv_socket_listen, + uv_socket_accept}; -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h deleted file mode 100644 index fd6d19049a..0000000000 --- a/src/core/lib/iomgr/tcp_uv.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef GRPC_CORE_LIB_IOMGR_TCP_UV_H -#define GRPC_CORE_LIB_IOMGR_TCP_UV_H -/* - Low level TCP "bottom half" implementation, for use by transports built on - top of a TCP connection. - - Note that this file does not (yet) include APIs for creating the socket in - the first place. - - All calls passing slice transfer ownership of a slice refcount unless - otherwise specified. -*/ - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/endpoint.h" - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include <uv.h> - -extern grpc_core::TraceFlag grpc_tcp_trace; - -#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 - -grpc_endpoint* grpc_tcp_create(uv_tcp_t* handle, - grpc_resource_quota* resource_quota, - char* peer_string); - -#endif /* GRPC_UV */ - -#endif /* GRPC_CORE_LIB_IOMGR_TCP_UV_H */ diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc index 6777719785..04e6f11eee 100644 --- a/src/core/lib/iomgr/tcp_windows.cc +++ b/src/core/lib/iomgr/tcp_windows.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET @@ -49,7 +51,7 @@ #define GRPC_FIONBIO FIONBIO #endif -grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); +extern grpc_core::TraceFlag grpc_tcp_trace; static grpc_error* set_non_block(SOCKET sock) { int status; diff --git a/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h index 8578a358ea..161a545a2a 100644 --- a/src/core/lib/iomgr/tcp_windows.h +++ b/src/core/lib/iomgr/tcp_windows.h @@ -29,6 +29,8 @@ otherwise specified. */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/time_averaged_stats.cc b/src/core/lib/iomgr/time_averaged_stats.cc index 3bddec04dd..6369e48db0 100644 --- a/src/core/lib/iomgr/time_averaged_stats.cc +++ b/src/core/lib/iomgr/time_averaged_stats.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/time_averaged_stats.h" void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, diff --git a/src/core/lib/iomgr/timer.cc b/src/core/lib/iomgr/timer.cc new file mode 100644 index 0000000000..e647cdefa0 --- /dev/null +++ b/src/core/lib/iomgr/timer.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +grpc_timer_vtable* grpc_timer_impl; + +void grpc_set_timer_impl(grpc_timer_vtable* vtable) { + grpc_timer_impl = vtable; +} + +void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + grpc_timer_impl->init(timer, deadline, closure); +} + +void grpc_timer_cancel(grpc_timer* timer) { grpc_timer_impl->cancel(timer); } + +grpc_timer_check_result grpc_timer_check(grpc_millis* next) { + return grpc_timer_impl->check(next); +} + +void grpc_timer_list_init() { grpc_timer_impl->list_init(); } + +void grpc_timer_list_shutdown() { grpc_timer_impl->list_shutdown(); } + +void grpc_timer_consume_kick() { grpc_timer_impl->consume_kick(); } diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h index 82049859c5..5ff10d3aee 100644 --- a/src/core/lib/iomgr/timer.h +++ b/src/core/lib/iomgr/timer.h @@ -19,20 +19,45 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIMER_H #define GRPC_CORE_LIB_IOMGR_TIMER_H -#include "src/core/lib/iomgr/port.h" +#include <grpc/support/port_platform.h> -#ifdef GRPC_UV -#include "src/core/lib/iomgr/timer_uv.h" -#else -#include "src/core/lib/iomgr/timer_generic.h" -#endif /* GRPC_UV */ +#include "src/core/lib/iomgr/port.h" -#include <grpc/support/port_platform.h> #include <grpc/support/time.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" -typedef struct grpc_timer grpc_timer; +typedef struct grpc_timer { + gpr_atm deadline; + uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ + bool pending; + struct grpc_timer* next; + struct grpc_timer* prev; + grpc_closure* closure; +#ifndef NDEBUG + struct grpc_timer* hash_table_next; +#endif + + // Optional field used by custom timers + void* custom_timer; +} grpc_timer; + +typedef enum { + GRPC_TIMERS_NOT_CHECKED, + GRPC_TIMERS_CHECKED_AND_EMPTY, + GRPC_TIMERS_FIRED, +} grpc_timer_check_result; + +typedef struct grpc_timer_vtable { + void (*init)(grpc_timer* timer, grpc_millis, grpc_closure* closure); + void (*cancel)(grpc_timer* timer); + + /* Internal API */ + grpc_timer_check_result (*check)(grpc_millis* next); + void (*list_init)(); + void (*list_shutdown)(void); + void (*consume_kick)(void); +} grpc_timer_vtable; /* Initialize *timer. When expired or canceled, closure will be called with error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled @@ -77,12 +102,6 @@ void grpc_timer_cancel(grpc_timer* timer); /* iomgr internal api for dealing with timers */ -typedef enum { - GRPC_TIMERS_NOT_CHECKED, - GRPC_TIMERS_CHECKED_AND_EMPTY, - GRPC_TIMERS_FIRED, -} grpc_timer_check_result; - /* Check for timers to be run, and run them. Return true if timer callbacks were executed. If next is non-null, TRY to update *next with the next running timer @@ -98,7 +117,9 @@ void grpc_timer_list_shutdown(); void grpc_timer_consume_kick(void); /* the following must be implemented by each iomgr implementation */ - void grpc_kick_poller(void); +/* Sets the timer implementation */ +void grpc_set_timer_impl(grpc_timer_vtable* vtable); + #endif /* GRPC_CORE_LIB_IOMGR_TIMER_H */ diff --git a/src/core/lib/iomgr/timer_custom.cc b/src/core/lib/iomgr/timer_custom.cc new file mode 100644 index 0000000000..71d825ff9f --- /dev/null +++ b/src/core/lib/iomgr/timer_custom.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/port.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" + +static grpc_custom_timer_vtable* custom_timer_impl; + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + grpc_timer* timer = t->original; + GPR_ASSERT(timer->pending); + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); + custom_timer_impl->stop(t); + gpr_free(t); +} + +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + uint64_t timeout; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + if (deadline <= grpc_core::ExecCtx::Get()->Now()) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + timer->pending = false; + return; + } else { + timeout = deadline - now; + } + timer->pending = true; + timer->closure = closure; + grpc_custom_timer* timer_wrapper = + (grpc_custom_timer*)gpr_malloc(sizeof(grpc_custom_timer)); + timer_wrapper->timeout_ms = timeout; + timer->custom_timer = (void*)timer_wrapper; + timer_wrapper->original = timer; + custom_timer_impl->start(timer_wrapper); +} + +static void timer_cancel(grpc_timer* timer) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_custom_timer* tw = (grpc_custom_timer*)timer->custom_timer; + if (timer->pending) { + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); + custom_timer_impl->stop(tw); + gpr_free(tw); + } +} + +static grpc_timer_check_result timer_check(grpc_millis* next) { + return GRPC_TIMERS_NOT_CHECKED; +} + +static void timer_list_init() {} +static void timer_list_shutdown() {} + +static void timer_consume_kick(void) {} + +static grpc_timer_vtable custom_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl) { + custom_timer_impl = impl; + grpc_set_timer_impl(&custom_timer_vtable); +} diff --git a/src/core/lib/iomgr/timer_custom.h b/src/core/lib/iomgr/timer_custom.h new file mode 100644 index 0000000000..bfea8bafa6 --- /dev/null +++ b/src/core/lib/iomgr/timer_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/iomgr/timer.h" + +typedef struct grpc_custom_timer { + // Implementation defined + void* timer; + uint64_t timeout_ms; + + grpc_timer* original; +} grpc_custom_timer; + +typedef struct grpc_custom_timer_vtable { + void (*start)(grpc_custom_timer* t); + void (*stop)(grpc_custom_timer* t); +} grpc_custom_timer_vtable; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl); + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H */ diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 697162f7a8..93e654b7fa 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -16,12 +16,12 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <inttypes.h> -#ifdef GRPC_TIMER_USE_GENERIC - #include "src/core/lib/iomgr/timer.h" #include <grpc/support/alloc.h> @@ -236,7 +236,7 @@ static gpr_atm compute_min_deadline(timer_shard* shard) { : grpc_timer_heap_top(&shard->heap)->deadline; } -void grpc_timer_list_init() { +static void timer_list_init() { uint32_t i; g_num_shards = GPR_MIN(1, 2 * gpr_cpu_num_cores()); @@ -268,7 +268,7 @@ void grpc_timer_list_init() { INIT_TIMER_HASH_TABLE(); } -void grpc_timer_list_shutdown() { +static void timer_list_shutdown() { size_t i; run_some_expired_timers( GPR_ATM_MAX, nullptr, @@ -324,8 +324,8 @@ static void note_deadline_change(timer_shard* shard) { void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = false; } -void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, - grpc_closure* closure) { +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { int is_first_timer = 0; timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)]; timer->closure = closure; @@ -410,12 +410,12 @@ void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, } } -void grpc_timer_consume_kick(void) { +static void timer_consume_kick(void) { /* force re-evaluation of last seeen min */ gpr_tls_set(&g_last_seen_min_timer, 0); } -void grpc_timer_cancel(grpc_timer* timer) { +static void timer_cancel(grpc_timer* timer) { if (!g_shared_mutables.initialized) { /* must have already been cancelled, also the shard mutex is invalid */ return; @@ -602,7 +602,7 @@ static grpc_timer_check_result run_some_expired_timers(gpr_atm now, return result; } -grpc_timer_check_result grpc_timer_check(grpc_millis* next) { +static grpc_timer_check_result timer_check(grpc_millis* next) { // prelude grpc_millis now = grpc_core::ExecCtx::Get()->Now(); @@ -658,4 +658,6 @@ grpc_timer_check_result grpc_timer_check(grpc_millis* next) { return r; } -#endif /* GRPC_TIMER_USE_GENERIC */ +grpc_timer_vtable grpc_generic_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h index 190381e904..97a4513355 100644 --- a/src/core/lib/iomgr/timer_generic.h +++ b/src/core/lib/iomgr/timer_generic.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H #define GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H +#include <grpc/support/port_platform.h> + #include <grpc/support/time.h> #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/iomgr/timer_heap.cc b/src/core/lib/iomgr/timer_heap.cc index c26896ee4a..0c17d607eb 100644 --- a/src/core/lib/iomgr/timer_heap.cc +++ b/src/core/lib/iomgr/timer_heap.cc @@ -16,9 +16,9 @@ * */ -#include "src/core/lib/iomgr/port.h" +#include <grpc/support/port_platform.h> -#ifdef GRPC_TIMER_USE_GENERIC +#include "src/core/lib/iomgr/port.h" #include "src/core/lib/iomgr/timer_heap.h" @@ -133,5 +133,3 @@ grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap) { void grpc_timer_heap_pop(grpc_timer_heap* heap) { grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); } - -#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_heap.h b/src/core/lib/iomgr/timer_heap.h index 436eef55a6..503365d4cd 100644 --- a/src/core/lib/iomgr/timer_heap.h +++ b/src/core/lib/iomgr/timer_heap.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H #define GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/timer.h" typedef struct { diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc index 94e7953fde..94f288af27 100644 --- a/src/core/lib/iomgr/timer_manager.cc +++ b/src/core/lib/iomgr/timer_manager.cc @@ -16,22 +16,22 @@ * */ -#include "src/core/lib/iomgr/timer_manager.h" - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> #include <grpc/support/port_platform.h> #include <inttypes.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + #include "src/core/lib/debug/trace.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" -typedef struct completed_thread { - gpr_thd_id t; - struct completed_thread* next; -} completed_thread; +struct completed_thread { + grpc_core::Thread thd; + completed_thread* next; +}; extern grpc_core::TraceFlag grpc_timer_check_trace; @@ -67,7 +67,7 @@ static void gc_completed_threads(void) { g_completed_threads = nullptr; gpr_mu_unlock(&g_mu); while (to_gc != nullptr) { - gpr_thd_join(to_gc->t); + to_gc->thd.Join(); completed_thread* next = to_gc->next; gpr_free(to_gc); to_gc = next; @@ -84,18 +84,10 @@ static void start_timer_thread_and_unlock(void) { if (grpc_timer_check_trace.enabled()) { gpr_log(GPR_DEBUG, "Spawn timer thread"); } - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); completed_thread* ct = static_cast<completed_thread*>(gpr_malloc(sizeof(*ct))); - // The call to gpr_thd_new() has to be under the same lock used by - // gc_completed_threads(), particularly due to ct->t, which is written here - // (internally by gpr_thd_new) and read there. Otherwise it's possible for ct - // to leak through g_completed_threads and be freed in gc_completed_threads() - // before "&ct->t" is written to, causing a use-after-free. - gpr_mu_lock(&g_mu); - gpr_thd_new(&ct->t, "grpc_global_timer", timer_thread, ct, &opt); - gpr_mu_unlock(&g_mu); + ct->thd = grpc_core::Thread("grpc_global_timer", timer_thread, ct); + ct->thd.Start(); } void grpc_timer_manager_tick() { diff --git a/src/core/lib/iomgr/timer_manager.h b/src/core/lib/iomgr/timer_manager.h index 0ba502928a..3c4cdda2c8 100644 --- a/src/core/lib/iomgr/timer_manager.h +++ b/src/core/lib/iomgr/timer_manager.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H #define GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> /* Timer Manager tries to keep one thread waiting for the next timeout at all diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc index 5d238da089..dadeb960b2 100644 --- a/src/core/lib/iomgr/timer_uv.cc +++ b/src/core/lib/iomgr/timer_uv.cc @@ -16,22 +16,22 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" -#if GRPC_UV +#ifdef GRPC_UV #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/iomgr_custom.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" #include <uv.h> -grpc_core::TraceFlag grpc_timer_trace(false, "timer"); -grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check"); - static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); } static void stop_uv_timer(uv_timer_t* handle) { @@ -41,57 +41,23 @@ static void stop_uv_timer(uv_timer_t* handle) { } void run_expired_timer(uv_timer_t* handle) { - grpc_timer* timer = (grpc_timer*)handle->data; - grpc_core::ExecCtx exec_ctx; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(timer->pending); - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); - stop_uv_timer(handle); + grpc_custom_timer* timer_wrapper = (grpc_custom_timer*)handle->data; + grpc_custom_timer_callback(timer_wrapper, GRPC_ERROR_NONE); } -void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, - grpc_closure* closure) { - uint64_t timeout; +static void timer_start(grpc_custom_timer* t) { uv_timer_t* uv_timer; - GRPC_UV_ASSERT_SAME_THREAD(); - timer->closure = closure; - if (deadline <= grpc_core::ExecCtx::Get()->Now()) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); - return; - } - timer->pending = 1; - timeout = (uint64_t)(deadline - grpc_core::ExecCtx::Get()->Now()); uv_timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); uv_timer_init(uv_default_loop(), uv_timer); - uv_timer->data = timer; - timer->uv_timer = uv_timer; - uv_timer_start(uv_timer, run_expired_timer, timeout, 0); - /* We assume that gRPC timers are only used alongside other active gRPC - objects, and that there will therefore always be something else keeping - the uv loop alive whenever there is a timer */ - uv_unref((uv_handle_t*)uv_timer); -} - -void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = 0; } - -void grpc_timer_cancel(grpc_timer* timer) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (timer->pending) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); - stop_uv_timer((uv_timer_t*)timer->uv_timer); - } + uv_timer->data = t; + t->timer = (void*)uv_timer; + uv_timer_start(uv_timer, run_expired_timer, t->timeout_ms, 0); } -grpc_timer_check_result grpc_timer_check(grpc_millis* next) { - return GRPC_TIMERS_NOT_CHECKED; +static void timer_stop(grpc_custom_timer* t) { + stop_uv_timer((uv_timer_t*)t->timer); } -void grpc_timer_list_init() {} -void grpc_timer_list_shutdown() {} - -void grpc_timer_consume_kick(void) {} +grpc_custom_timer_vtable uv_timer_vtable = {timer_start, timer_stop}; -#endif /* GRPC_UV */ +#endif diff --git a/src/core/lib/iomgr/udp_server.cc b/src/core/lib/iomgr/udp_server.cc index eecb3b09d9..15a242abe2 100644 --- a/src/core/lib/iomgr/udp_server.cc +++ b/src/core/lib/iomgr/udp_server.cc @@ -25,6 +25,8 @@ #define SO_RXQ_OVFL 40 #endif +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_SOCKET @@ -50,6 +52,8 @@ #include <grpc/support/time.h> #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/executor.h" @@ -60,41 +64,102 @@ #include "src/core/lib/iomgr/socket_utils_posix.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" -/* one listening port */ -typedef struct grpc_udp_listener grpc_udp_listener; -struct grpc_udp_listener { - int fd; - grpc_fd* emfd; - grpc_udp_server* server; - grpc_resolved_address addr; - grpc_closure read_closure; - grpc_closure write_closure; +/* A listener which implements basic features of Listening on a port for + * I/O events*/ +class GrpcUdpListener { + public: + GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr); + ~GrpcUdpListener(); + + /* Called when grpc server starts to listening on the grpc_fd. */ + void StartListening(grpc_pollset** pollsets, size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory); + + /* Called when data is available to read from the socket. + * Return true if there is more data to read from fd. */ + void OnRead(grpc_error* error, void* do_read_arg); + + /* Called when the socket is writeable. The given closure should be scheduled + * when the socket becomes blocked next time. */ + void OnCanWrite(grpc_error* error, void* do_write_arg); + + /* Called when the grpc_fd is about to be orphaned (and the FD closed). */ + void OnFdAboutToOrphan(); + + /* Called to orphan fd of this listener.*/ + void OrphanFd(); + + /* Called when this listener is going to be destroyed. */ + void OnDestroy(); + + int fd() const { return fd_; } + + protected: + grpc_fd* emfd() const { return emfd_; } + + gpr_mu* mutex() { return &mutex_; } + + private: + /* event manager callback when reads are ready */ + static void on_read(void* arg, grpc_error* error); + static void on_write(void* arg, grpc_error* error); + + static void do_read(void* arg, grpc_error* error); + static void do_write(void* arg, grpc_error* error); + // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback + // interface. + static void fd_notify_on_write_wrapper(void* arg, grpc_error* error); + + static void shutdown_fd(void* args, grpc_error* error); + + int fd_; + grpc_fd* emfd_; + grpc_udp_server* server_; + grpc_resolved_address addr_; + grpc_closure read_closure_; + grpc_closure write_closure_; // To be called when corresponding QuicGrpcServer closes all active // connections. - grpc_closure orphan_fd_closure; - grpc_closure destroyed_closure; - grpc_udp_server_read_cb read_cb; - grpc_udp_server_write_cb write_cb; - grpc_udp_server_orphan_cb orphan_cb; - grpc_udp_server_start_cb start_cb; + grpc_closure orphan_fd_closure_; + grpc_closure destroyed_closure_; // To be scheduled on another thread to actually read/write. - grpc_closure do_read_closure; - grpc_closure do_write_closure; - grpc_closure notify_on_write_closure; + grpc_closure do_read_closure_; + grpc_closure do_write_closure_; + grpc_closure notify_on_write_closure_; // True if orphan_cb is trigered. - bool orphan_notified; + bool orphan_notified_; // True if grpc_fd_notify_on_write() is called after on_write() call. - bool notify_on_write_armed; + bool notify_on_write_armed_; // True if fd has been shutdown. - bool already_shutdown; - - struct grpc_udp_listener* next; + bool already_shutdown_; + // Object actually handles I/O events. Assigned in StartListening(). + GrpcUdpHandler* udp_handler_ = nullptr; + // To be notified on destruction. + GrpcUdpHandlerFactory* handler_factory_ = nullptr; + // Required to access above fields. + gpr_mu mutex_; }; -struct shutdown_fd_args { - grpc_udp_listener* sp; - gpr_mu* server_mu; -}; +GrpcUdpListener::GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr) + : fd_(fd), + server_(server), + orphan_notified_(false), + already_shutdown_(false) { + char* addr_str; + char* name; + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_free(addr_str); + emfd_ = grpc_fd_create(fd, name); + memcpy(&addr_, addr, sizeof(grpc_resolved_address)); + GPR_ASSERT(emfd_); + gpr_free(name); + gpr_mu_init(&mutex_); +} + +GrpcUdpListener::~GrpcUdpListener() { gpr_mu_destroy(&mutex_); } /* the overall server */ struct grpc_udp_server { @@ -111,10 +176,11 @@ struct grpc_udp_server { /* is this server shutting down? (boolean) */ int shutdown; - /* linked list of server ports */ - grpc_udp_listener* head; - grpc_udp_listener* tail; - unsigned nports; + /* An array of listeners */ + grpc_core::InlinedVector<GrpcUdpListener, 16> listeners; + + /* factory for use to create udp listeners */ + GrpcUdpHandlerFactory* handler_factory; /* shutdown callback */ grpc_closure* shutdown_complete; @@ -139,8 +205,7 @@ static grpc_socket_factory* get_socket_factory(const grpc_channel_args* args) { } grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) { - grpc_udp_server* s = - static_cast<grpc_udp_server*>(gpr_malloc(sizeof(grpc_udp_server))); + grpc_udp_server* s = grpc_core::New<grpc_udp_server>(); gpr_mu_init(&s->mu); s->socket_factory = get_socket_factory(args); if (s->socket_factory) { @@ -149,33 +214,27 @@ grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) { s->active_ports = 0; s->destroyed_ports = 0; s->shutdown = 0; - s->head = nullptr; - s->tail = nullptr; - s->nports = 0; - return s; } -static void shutdown_fd(void* args, grpc_error* error) { - struct shutdown_fd_args* shutdown_args = - static_cast<struct shutdown_fd_args*>(args); - grpc_udp_listener* sp = shutdown_args->sp; - gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd); - gpr_mu_lock(shutdown_args->server_mu); - grpc_fd_shutdown(sp->emfd, GRPC_ERROR_REF(error)); - sp->already_shutdown = true; - if (!sp->notify_on_write_armed) { +// static +void GrpcUdpListener::shutdown_fd(void* args, grpc_error* error) { + if (args == nullptr) { + // No-op if shutdown args are null. + return; + } + auto sp = static_cast<GrpcUdpListener*>(args); + gpr_mu_lock(sp->mutex()); + gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd_); + grpc_fd_shutdown(sp->emfd_, GRPC_ERROR_REF(error)); + sp->already_shutdown_ = true; + if (!sp->notify_on_write_armed_) { // Re-arm write notification to notify listener with error. This is // necessary to decrement active_ports. - sp->notify_on_write_armed = true; - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + sp->notify_on_write_armed_ = true; + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); } - gpr_mu_unlock(shutdown_args->server_mu); - gpr_free(shutdown_args); -} - -static void dummy_cb(void* arg, grpc_error* error) { - // No-op. + gpr_mu_unlock(sp->mutex()); } static void finish_shutdown(grpc_udp_server* s) { @@ -186,24 +245,22 @@ static void finish_shutdown(grpc_udp_server* s) { gpr_mu_destroy(&s->mu); gpr_log(GPR_DEBUG, "Destroy all listeners."); - while (s->head) { - grpc_udp_listener* sp = s->head; - s->head = sp->next; - gpr_free(sp); + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OnDestroy(); } if (s->socket_factory) { grpc_socket_factory_unref(s->socket_factory); } - gpr_free(s); + grpc_core::Delete(s); } static void destroyed_port(void* server, grpc_error* error) { grpc_udp_server* s = static_cast<grpc_udp_server*>(server); gpr_mu_lock(&s->mu); s->destroyed_ports++; - if (s->destroyed_ports == s->nports) { + if (s->destroyed_ports == s->listeners.size()) { gpr_mu_unlock(&s->mu); finish_shutdown(s); } else { @@ -220,35 +277,30 @@ static void deactivated_all_ports(grpc_udp_server* s) { GPR_ASSERT(s->shutdown); - if (s->head) { - grpc_udp_listener* sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_unlink_if_unix_domain_socket(&sp->addr); - - GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, - grpc_schedule_on_exec_ctx); - if (!sp->orphan_notified) { - /* Call the orphan_cb to signal that the FD is about to be closed and - * should no longer be used. Because at this point, all listening ports - * have been shutdown already, no need to shutdown again.*/ - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, dummy_cb, sp, - grpc_schedule_on_exec_ctx); - GPR_ASSERT(sp->orphan_cb); - gpr_log(GPR_DEBUG, "Orphan fd %d", sp->fd); - sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data); - } - grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, nullptr, - false /* already_closed */, "udp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { + if (s->listeners.size() == 0) { gpr_mu_unlock(&s->mu); finish_shutdown(s); + return; + } + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OrphanFd(); } + gpr_mu_unlock(&s->mu); +} + +void GrpcUdpListener::OrphanFd() { + gpr_log(GPR_DEBUG, "Orphan fd %d, emfd %p", fd_, emfd_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + /* Because at this point, all listening sockets have been shutdown already, no + * need to call OnFdAboutToOrphan() to notify the handler again. */ + grpc_fd_orphan(emfd_, &destroyed_closure_, nullptr, + false /* already_closed */, "udp_listener_shutdown"); } void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { - grpc_udp_listener* sp; gpr_mu_lock(&s->mu); GPR_ASSERT(!s->shutdown); @@ -259,16 +311,9 @@ void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { gpr_log(GPR_DEBUG, "start to destroy udp_server"); /* shutdown all fd's */ if (s->active_ports) { - for (sp = s->head; sp; sp = sp->next) { - GPR_ASSERT(sp->orphan_cb); - struct shutdown_fd_args* args = - static_cast<struct shutdown_fd_args*>(gpr_malloc(sizeof(*args))); - args->sp = sp; - args->server_mu = &s->mu; - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, shutdown_fd, args, - grpc_schedule_on_exec_ctx); - sp->orphan_cb(sp->emfd, &sp->orphan_fd_closure, sp->server->user_data); - sp->orphan_notified = true; + for (size_t i = 0; i < s->listeners.size(); ++i) { + GrpcUdpListener* sp = &s->listeners[i]; + sp->OnFdAboutToOrphan(); } gpr_mu_unlock(&s->mu); } else { @@ -277,12 +322,30 @@ void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { } } +void GrpcUdpListener::OnFdAboutToOrphan() { + gpr_mu_lock(&mutex_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + if (!orphan_notified_ && udp_handler_ != nullptr) { + /* Singals udp_handler that the FD is about to be closed and + * should no longer be used. */ + GRPC_CLOSURE_INIT(&orphan_fd_closure_, shutdown_fd, this, + grpc_schedule_on_exec_ctx); + gpr_log(GPR_DEBUG, "fd %d about to be orphaned", fd_); + udp_handler_->OnFdAboutToOrphan(&orphan_fd_closure_, server_->user_data); + orphan_notified_ = true; + } + gpr_mu_unlock(&mutex_); +} + static int bind_socket(grpc_socket_factory* socket_factory, int sockfd, const grpc_resolved_address* addr) { return (socket_factory != nullptr) ? grpc_socket_factory_bind(socket_factory, sockfd, addr) : bind(sockfd, - reinterpret_cast<struct sockaddr*>( + reinterpret_cast<grpc_sockaddr*>( const_cast<char*>(addr->addr)), static_cast<socklen_t>(addr->len)); } @@ -292,8 +355,8 @@ static int prepare_socket(grpc_socket_factory* socket_factory, int fd, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size) { grpc_resolved_address sockname_temp; - struct sockaddr* addr_ptr = - reinterpret_cast<struct sockaddr*>(const_cast<char*>(addr->addr)); + grpc_sockaddr* addr_ptr = + reinterpret_cast<grpc_sockaddr*>(const_cast<char*>(addr->addr)); if (fd < 0) { goto error; @@ -329,7 +392,7 @@ static int prepare_socket(grpc_socket_factory* socket_factory, int fd, sockname_temp.len = sizeof(struct sockaddr_storage); - if (getsockname(fd, reinterpret_cast<struct sockaddr*>(sockname_temp.addr), + if (getsockname(fd, reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), reinterpret_cast<socklen_t*>(&sockname_temp.len)) < 0) { goto error; } @@ -362,163 +425,140 @@ error: return -1; } -static void do_read(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - GPR_ASSERT(sp->read_cb && error == GRPC_ERROR_NONE); +// static +void GrpcUdpListener::do_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + GPR_ASSERT(error == GRPC_ERROR_NONE); /* TODO: the reason we hold server->mu here is merely to prevent fd * shutdown while we are reading. However, it blocks do_write(). Switch to * read lock if available. */ - gpr_mu_lock(&sp->server->mu); + gpr_mu_lock(sp->mutex()); /* Tell the registered callback that data is available to read. */ - if (!sp->already_shutdown && sp->read_cb(sp->emfd)) { + if (!sp->already_shutdown_ && sp->udp_handler_->Read()) { /* There maybe more packets to read. Schedule read_more_cb_ closure to run * after finishing this event loop. */ - GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(&sp->do_read_closure_, GRPC_ERROR_NONE); } else { /* Finish reading all the packets, re-arm the notification event so we can * get another chance to read. Or fd already shutdown, re-arm to get a * notification with shutdown error. */ - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(sp->emfd_, &sp->read_closure_); } - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(sp->mutex()); } -/* event manager callback when reads are ready */ -static void on_read(void* arg, grpc_error* error) { - grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg); +// static +void GrpcUdpListener::on_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + sp->OnRead(error, arg); +} - gpr_mu_lock(&sp->server->mu); +void GrpcUdpListener::OnRead(grpc_error* error, void* do_read_arg) { if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); } else { - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(&server_->mu); } return; } + /* Read once. If there is more data to read, off load the work to another * thread to finish. */ - GPR_ASSERT(sp->read_cb); - if (sp->read_cb(sp->emfd)) { + if (udp_handler_->Read()) { /* There maybe more packets to read. Schedule read_more_cb_ closure to run * after finishing this event loop. */ - GRPC_CLOSURE_INIT(&sp->do_read_closure, do_read, arg, + GRPC_CLOSURE_INIT(&do_read_closure_, do_read, do_read_arg, grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); - GRPC_CLOSURE_SCHED(&sp->do_read_closure, GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(&do_read_closure_, GRPC_ERROR_NONE); } else { /* Finish reading all the packets, re-arm the notification event so we can * get another chance to read. Or fd already shutdown, re-arm to get a * notification with shutdown error. */ - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + grpc_fd_notify_on_read(emfd_, &read_closure_); } - gpr_mu_unlock(&sp->server->mu); } +// static // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback interface. -void fd_notify_on_write_wrapper(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - gpr_mu_lock(&sp->server->mu); - if (!sp->notify_on_write_armed) { - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); - sp->notify_on_write_armed = true; - } - gpr_mu_unlock(&sp->server->mu); +void GrpcUdpListener::fd_notify_on_write_wrapper(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + gpr_mu_lock(sp->mutex()); + if (!sp->notify_on_write_armed_) { + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); + sp->notify_on_write_armed_ = true; + } + gpr_mu_unlock(sp->mutex()); } -static void do_write(void* arg, grpc_error* error) { - grpc_udp_listener* sp = reinterpret_cast<grpc_udp_listener*>(arg); - gpr_mu_lock(&sp->server->mu); - if (sp->already_shutdown) { +// static +void GrpcUdpListener::do_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + gpr_mu_lock(sp->mutex()); + if (sp->already_shutdown_) { // If fd has been shutdown, don't write any more and re-arm notification. - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); } else { - sp->notify_on_write_armed = false; + sp->notify_on_write_armed_ = false; /* Tell the registered callback that the socket is writeable. */ - GPR_ASSERT(sp->write_cb && error == GRPC_ERROR_NONE); - GRPC_CLOSURE_INIT(&sp->notify_on_write_closure, fd_notify_on_write_wrapper, + GPR_ASSERT(error == GRPC_ERROR_NONE); + GRPC_CLOSURE_INIT(&sp->notify_on_write_closure_, fd_notify_on_write_wrapper, arg, grpc_schedule_on_exec_ctx); - sp->write_cb(sp->emfd, sp->server->user_data, &sp->notify_on_write_closure); + sp->udp_handler_->OnCanWrite(sp->server_->user_data, + &sp->notify_on_write_closure_); } - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(sp->mutex()); } -static void on_write(void* arg, grpc_error* error) { - grpc_udp_listener* sp = static_cast<grpc_udp_listener*>(arg); +// static +void GrpcUdpListener::on_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast<GrpcUdpListener*>(arg); + sp->OnCanWrite(error, arg); +} - gpr_mu_lock(&sp->server->mu); +void GrpcUdpListener::OnCanWrite(grpc_error* error, void* do_write_arg) { if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(sp->server); + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); } else { - gpr_mu_unlock(&sp->server->mu); + gpr_mu_unlock(&server_->mu); } return; } /* Schedule actual write in another thread. */ - GRPC_CLOSURE_INIT(&sp->do_write_closure, do_write, arg, + GRPC_CLOSURE_INIT(&do_write_closure_, do_write, do_write_arg, grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); - GRPC_CLOSURE_SCHED(&sp->do_write_closure, GRPC_ERROR_NONE); - gpr_mu_unlock(&sp->server->mu); + GRPC_CLOSURE_SCHED(&do_write_closure_, GRPC_ERROR_NONE); } static int add_socket_to_server(grpc_udp_server* s, int fd, const grpc_resolved_address* addr, - int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener* sp; - int port; - char* addr_str; - char* name; + int rcv_buf_size, int snd_buf_size) { + gpr_log(GPR_DEBUG, "add socket %d to server", fd); - port = + int port = prepare_socket(s->socket_factory, fd, addr, rcv_buf_size, snd_buf_size); if (port >= 0) { - grpc_sockaddr_to_string(&addr_str, addr, 1); - gpr_asprintf(&name, "udp-server-listener:%s", addr_str); - gpr_free(addr_str); gpr_mu_lock(&s->mu); - s->nports++; - sp = static_cast<grpc_udp_listener*>(gpr_malloc(sizeof(grpc_udp_listener))); - sp->next = nullptr; - if (s->head == nullptr) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd, name); - memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); - sp->read_cb = read_cb; - sp->write_cb = write_cb; - sp->orphan_cb = orphan_cb; - sp->start_cb = start_cb; - sp->orphan_notified = false; - sp->already_shutdown = false; - GPR_ASSERT(sp->emfd); + s->listeners.emplace_back(s, fd, addr); + gpr_log(GPR_DEBUG, + "add socket %d to server for port %d, %zu listener(s) in total", fd, + port, s->listeners.size()); gpr_mu_unlock(&s->mu); - gpr_free(name); } - return port; } int grpc_udp_server_add_port(grpc_udp_server* s, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener* sp; + GrpcUdpHandlerFactory* handler_factory) { int allocated_port1 = -1; int allocated_port2 = -1; int fd; @@ -534,12 +574,11 @@ int grpc_udp_server_add_port(grpc_udp_server* s, /* Check if this is a wildcard port, and if so, try to keep the port the same as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { + for (size_t i = 0; i < s->listeners.size(); ++i) { sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == - getsockname(sp->fd, - reinterpret_cast<struct sockaddr*>(sockname_temp.addr), - reinterpret_cast<socklen_t*>(&sockname_temp.len))) { + if (0 == getsockname(s->listeners[i].fd(), + reinterpret_cast<grpc_sockaddr*>(sockname_temp.addr), + reinterpret_cast<socklen_t*>(&sockname_temp.len))) { port = grpc_sockaddr_get_port(&sockname_temp); if (port > 0) { allocated_addr = static_cast<grpc_resolved_address*>( @@ -557,6 +596,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, addr = &addr6_v4mapped; } + s->handler_factory = handler_factory; /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ if (grpc_sockaddr_is_wildcard(addr, &port)) { grpc_sockaddr_make_wildcards(port, &wild4, &wild6); @@ -567,8 +607,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); allocated_port1 = - add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb, - read_cb, write_cb, orphan_cb); + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { goto done; } @@ -591,8 +630,7 @@ int grpc_udp_server_add_port(grpc_udp_server* s, addr = &addr4_copy; } allocated_port2 = - add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size, start_cb, - read_cb, write_cb, orphan_cb); + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); done: gpr_free(allocated_addr); @@ -600,52 +638,55 @@ done: } int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index) { - grpc_udp_listener* sp; - if (port_index >= s->nports) { + if (port_index >= s->listeners.size()) { return -1; } - for (sp = s->head; sp && port_index != 0; sp = sp->next) { - --port_index; - } - GPR_ASSERT(sp); // if this fails, our check earlier was bogus - return sp->fd; + return s->listeners[port_index].fd(); } void grpc_udp_server_start(grpc_udp_server* s, grpc_pollset** pollsets, size_t pollset_count, void* user_data) { gpr_log(GPR_DEBUG, "grpc_udp_server_start"); - size_t i; gpr_mu_lock(&s->mu); - grpc_udp_listener* sp; GPR_ASSERT(s->active_ports == 0); s->pollsets = pollsets; s->user_data = user_data; - sp = s->head; - while (sp != nullptr) { - sp->start_cb(sp->emfd, sp->server->user_data); - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(pollsets[i], sp->emfd); - } - GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].StartListening(pollsets, pollset_count, s->handler_factory); + } - GRPC_CLOSURE_INIT(&sp->write_closure, on_write, sp, - grpc_schedule_on_exec_ctx); - sp->notify_on_write_armed = true; - grpc_fd_notify_on_write(sp->emfd, &sp->write_closure); + gpr_mu_unlock(&s->mu); +} - /* Registered for both read and write callbacks: increment active_ports - * twice to account for this, and delay free-ing of memory until both - * on_read and on_write have fired. */ - s->active_ports += 2; +void GrpcUdpListener::StartListening(grpc_pollset** pollsets, + size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory) { + gpr_mu_lock(&mutex_); + handler_factory_ = handler_factory; + udp_handler_ = handler_factory->CreateUdpHandler(emfd_, server_->user_data); + for (size_t i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(pollsets[i], emfd_); + } + GRPC_CLOSURE_INIT(&read_closure_, on_read, this, grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(emfd_, &read_closure_); + + GRPC_CLOSURE_INIT(&write_closure_, on_write, this, grpc_schedule_on_exec_ctx); + notify_on_write_armed_ = true; + grpc_fd_notify_on_write(emfd_, &write_closure_); + + /* Registered for both read and write callbacks: increment active_ports + * twice to account for this, and delay free-ing of memory until both + * on_read and on_write have fired. */ + server_->active_ports += 2; + gpr_mu_unlock(&mutex_); +} - sp = sp->next; +void GrpcUdpListener::OnDestroy() { + if (udp_handler_ != nullptr) { + handler_factory_->DestroyUdpHandler(udp_handler_); } - - gpr_mu_unlock(&s->mu); } #endif diff --git a/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h index c1aa49f15d..4e384d2cdf 100644 --- a/src/core/lib/iomgr/udp_server.h +++ b/src/core/lib/iomgr/udp_server.h @@ -19,6 +19,9 @@ #ifndef GRPC_CORE_LIB_IOMGR_UDP_SERVER_H #define GRPC_CORE_LIB_IOMGR_UDP_SERVER_H +#include <grpc/support/port_platform.h> + +#include "src/core/lib/gprpp/abstract.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/resolve_address.h" @@ -30,22 +33,46 @@ struct grpc_server; /* Forward decl of grpc_udp_server */ typedef struct grpc_udp_server grpc_udp_server; -/* Called when grpc server starts to listening on the grpc_fd. */ -typedef void (*grpc_udp_server_start_cb)(grpc_fd* emfd, void* user_data); - -/* Called when data is available to read from the socket. - * Return true if there is more data to read from fd. */ -typedef bool (*grpc_udp_server_read_cb)(grpc_fd* emfd); - -/* Called when the socket is writeable. The given closure should be scheduled - * when the socket becomes blocked next time. */ -typedef void (*grpc_udp_server_write_cb)(grpc_fd* emfd, void* user_data, - grpc_closure* notify_on_write_closure); - -/* Called when the grpc_fd is about to be orphaned (and the FD closed). */ -typedef void (*grpc_udp_server_orphan_cb)(grpc_fd* emfd, - grpc_closure* shutdown_fd_callback, - void* user_data); +/* An interface associated with a socket. udp server delivers I/O event on that + * socket to the subclass of this interface which is created through + * GrpcUdpHandlerFactory. + * Its implementation should do the real IO work, e.g. read packet and write. */ +class GrpcUdpHandler { + public: + GrpcUdpHandler(grpc_fd* emfd, void* user_data) {} + virtual ~GrpcUdpHandler() {} + + // Interfaces to be implemented by subclasses to do the actual setup/tear down + // or I/O. + + // Called when data is available to read from the socket. Returns true if + // there is more data to read after this call. + virtual bool Read() GRPC_ABSTRACT; + // Called when socket becomes write unblocked. The given closure should be + // scheduled when the socket becomes blocked next time. + virtual void OnCanWrite(void* user_data, + grpc_closure* notify_on_write_closure) GRPC_ABSTRACT; + // Called before the gRPC FD is orphaned. Notify udp server to continue + // orphaning fd by scheduling the given closure, afterwards the associated fd + // will be closed. + virtual void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure, + void* user_data) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +class GrpcUdpHandlerFactory { + public: + virtual ~GrpcUdpHandlerFactory() {} + /* Called when start to listen on a socket. + * Return an instance of the implementation of GrpcUdpHandler interface which + * will process I/O events for this socket from now on. */ + virtual GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd, + void* user_data) GRPC_ABSTRACT; + virtual void DestroyUdpHandler(GrpcUdpHandler* handler) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; /* Create a server, initially not bound to any ports */ grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args); @@ -69,10 +96,7 @@ int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index); int grpc_udp_server_add_port(grpc_udp_server* s, const grpc_resolved_address* addr, int rcv_buf_size, int snd_buf_size, - grpc_udp_server_start_cb start_cb, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb); + GrpcUdpHandlerFactory* handler_factory); void grpc_udp_server_destroy(grpc_udp_server* server, grpc_closure* on_done); diff --git a/src/core/lib/iomgr/unix_sockets_posix.cc b/src/core/lib/iomgr/unix_sockets_posix.cc index b603916c47..5d09b4a9b1 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.cc +++ b/src/core/lib/iomgr/unix_sockets_posix.cc @@ -15,6 +15,8 @@ * limitations under the License. * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_HAVE_UNIX_SOCKET @@ -64,15 +66,15 @@ grpc_error* grpc_resolve_unix_domain_address(const char* name, } int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); return addr->sa_family == AF_UNIX; } void grpc_unlink_if_unix_domain_socket( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); if (addr->sa_family != AF_UNIX) { return; } @@ -87,8 +89,8 @@ void grpc_unlink_if_unix_domain_socket( char* grpc_sockaddr_to_uri_unix_if_possible( const grpc_resolved_address* resolved_addr) { - const struct sockaddr* addr = - reinterpret_cast<const struct sockaddr*>(resolved_addr->addr); + const grpc_sockaddr* addr = + reinterpret_cast<const grpc_sockaddr*>(resolved_addr->addr); if (addr->sa_family != AF_UNIX) { return nullptr; } diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h index 1c079e6e76..917d0327a9 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.h +++ b/src/core/lib/iomgr/unix_sockets_posix.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H #define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #include <grpc/support/string_util.h> diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.cc b/src/core/lib/iomgr/unix_sockets_posix_noop.cc index fbd9602e1b..dfab3e0acb 100644 --- a/src/core/lib/iomgr/unix_sockets_posix_noop.cc +++ b/src/core/lib/iomgr/unix_sockets_posix_noop.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/unix_sockets_posix.h" #ifndef GRPC_HAVE_UNIX_SOCKET diff --git a/src/core/lib/iomgr/wakeup_fd_cv.cc b/src/core/lib/iomgr/wakeup_fd_cv.cc index 41d35cb1fd..74faa6379e 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.cc +++ b/src/core/lib/iomgr/wakeup_fd_cv.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_WAKEUP_FD @@ -30,8 +32,8 @@ #include <grpc/support/sync.h> #include <grpc/support/time.h> -#include "src/core/lib/gpr/thd.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" #define MAX_TABLE_RESIZE 256 diff --git a/src/core/lib/iomgr/wakeup_fd_cv.h b/src/core/lib/iomgr/wakeup_fd_cv.h index 399620af76..86365f07e1 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.h +++ b/src/core/lib/iomgr/wakeup_fd_cv.h @@ -33,6 +33,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H +#include <grpc/support/port_platform.h> + #include <grpc/support/sync.h> #include "src/core/lib/iomgr/ev_posix.h" diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.cc b/src/core/lib/iomgr/wakeup_fd_eventfd.cc index 421ac55b00..dcf7dab71f 100644 --- a/src/core/lib/iomgr/wakeup_fd_eventfd.cc +++ b/src/core/lib/iomgr/wakeup_fd_eventfd.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_LINUX_EVENTFD diff --git a/src/core/lib/iomgr/wakeup_fd_nospecial.cc b/src/core/lib/iomgr/wakeup_fd_nospecial.cc index c2b525a254..64778929f0 100644 --- a/src/core/lib/iomgr/wakeup_fd_nospecial.cc +++ b/src/core/lib/iomgr/wakeup_fd_nospecial.cc @@ -21,6 +21,8 @@ * systems without anything better than pipe. */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.cc b/src/core/lib/iomgr/wakeup_fd_pipe.cc index 05d69dc9cc..cb173903a9 100644 --- a/src/core/lib/iomgr/wakeup_fd_pipe.cc +++ b/src/core/lib/iomgr/wakeup_fd_pipe.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_WAKEUP_FD diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.h b/src/core/lib/iomgr/wakeup_fd_pipe.h index 326a0c4e01..1756976305 100644 --- a/src/core/lib/iomgr/wakeup_fd_pipe.h +++ b/src/core/lib/iomgr/wakeup_fd_pipe.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/wakeup_fd_posix.h" extern const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable; diff --git a/src/core/lib/iomgr/wakeup_fd_posix.cc b/src/core/lib/iomgr/wakeup_fd_posix.cc index e8de208a25..b5b8b37a9a 100644 --- a/src/core/lib/iomgr/wakeup_fd_posix.cc +++ b/src/core/lib/iomgr/wakeup_fd_posix.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/port.h" #ifdef GRPC_POSIX_WAKEUP_FD diff --git a/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h index a9584d0d48..670c319593 100644 --- a/src/core/lib/iomgr/wakeup_fd_posix.h +++ b/src/core/lib/iomgr/wakeup_fd_posix.h @@ -47,6 +47,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H #define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/error.h" void grpc_wakeup_fd_global_init(void); diff --git a/src/core/lib/json/json.cc b/src/core/lib/json/json.cc index adf35b9088..2141db4c5b 100644 --- a/src/core/lib/json/json.cc +++ b/src/core/lib/json/json.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index bbd43025eb..3a62ef9cfb 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_JSON_JSON_H #define GRPC_CORE_LIB_JSON_JSON_H +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include "src/core/lib/json/json_common.h" diff --git a/src/core/lib/json/json_reader.cc b/src/core/lib/json/json_reader.cc index 6dadea5006..819572e4de 100644 --- a/src/core/lib/json/json_reader.cc +++ b/src/core/lib/json/json_reader.cc @@ -16,10 +16,10 @@ * */ -#include <string.h> - #include <grpc/support/port_platform.h> +#include <string.h> + #include <grpc/support/log.h> #include "src/core/lib/json/json_reader.h" diff --git a/src/core/lib/json/json_reader.h b/src/core/lib/json/json_reader.h index 03185cb2b6..78f7ad9f3a 100644 --- a/src/core/lib/json/json_reader.h +++ b/src/core/lib/json/json_reader.h @@ -20,6 +20,7 @@ #define GRPC_CORE_LIB_JSON_JSON_READER_H #include <grpc/support/port_platform.h> + #include "src/core/lib/json/json_common.h" typedef enum { diff --git a/src/core/lib/json/json_string.cc b/src/core/lib/json/json_string.cc index 8200900956..4f9175b9e7 100644 --- a/src/core/lib/json/json_string.cc +++ b/src/core/lib/json/json_string.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include <string.h> diff --git a/src/core/lib/json/json_writer.cc b/src/core/lib/json/json_writer.cc index 6d442b8716..7bbdccc7a3 100644 --- a/src/core/lib/json/json_writer.cc +++ b/src/core/lib/json/json_writer.cc @@ -16,10 +16,10 @@ * */ -#include <string.h> - #include <grpc/support/port_platform.h> +#include <string.h> + #include "src/core/lib/json/json_writer.h" static void json_writer_output_char(grpc_json_writer* writer, char c) { diff --git a/src/core/lib/json/json_writer.h b/src/core/lib/json/json_writer.h index a4f2d4daeb..ba0bedde7f 100644 --- a/src/core/lib/json/json_writer.h +++ b/src/core/lib/json/json_writer.h @@ -31,6 +31,8 @@ #ifndef GRPC_CORE_LIB_JSON_JSON_WRITER_H #define GRPC_CORE_LIB_JSON_JSON_WRITER_H +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include "src/core/lib/json/json_common.h" diff --git a/src/core/lib/profiling/basic_timers.cc b/src/core/lib/profiling/basic_timers.cc index ca6705a6b3..43384fd0ca 100644 --- a/src/core/lib/profiling/basic_timers.cc +++ b/src/core/lib/profiling/basic_timers.cc @@ -30,7 +30,7 @@ #include <string.h> #include "src/core/lib/gpr/env.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; @@ -68,7 +68,7 @@ static pthread_cond_t g_cv; static gpr_timer_log_list g_in_progress_logs; static gpr_timer_log_list g_done_logs; static int g_shutdown; -static gpr_thd_id g_writing_thread; +static grpc_core::Thread* g_writing_thread; static __thread int g_thread_id; static int g_next_thread_id; static int g_writing_enabled = 1; @@ -182,7 +182,8 @@ static void finish_writing(void) { g_shutdown = 1; pthread_cond_signal(&g_cv); pthread_mutex_unlock(&g_mu); - gpr_thd_join(g_writing_thread); + g_writing_thread->Join(); + grpc_core::Delete(g_writing_thread); gpr_log(GPR_INFO, "flushing logs"); @@ -201,10 +202,8 @@ void gpr_timers_set_log_filename(const char* filename) { } static void init_output() { - gpr_thd_options options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&options); - GPR_ASSERT(gpr_thd_new(&g_writing_thread, "timer_output_thread", - writing_thread, NULL, &options)); + g_writing_thread = grpc_core::New<grpc_core::Thread>("timer_output_thread", + writing_thread, nullptr); atexit(finish_writing); } diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc index c7e212bb21..14051a3f00 100644 --- a/src/core/lib/security/context/security_context.cc +++ b/src/core/lib/security/context/security_context.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include "src/core/lib/channel/channel_args.h" diff --git a/src/core/lib/security/context/security_context.h b/src/core/lib/security/context/security_context.h index 34f8c2487e..e782e4f28f 100644 --- a/src/core/lib/security/context/security_context.h +++ b/src/core/lib/security/context/security_context.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H #define GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/security/credentials/credentials.h" diff --git a/src/core/lib/security/credentials/alts/alts_credentials.cc b/src/core/lib/security/credentials/alts/alts_credentials.cc new file mode 100644 index 0000000000..fa05d901bf --- /dev/null +++ b/src/core/lib/security/credentials/alts/alts_credentials.cc @@ -0,0 +1,119 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/alts_credentials.h" + +#include <cstring> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" +#include "src/core/lib/security/security_connector/alts_security_connector.h" + +#define GRPC_CREDENTIALS_TYPE_ALTS "Alts" +#define GRPC_ALTS_HANDSHAKER_SERVICE_URL "metadata.google.internal:8080" + +static void alts_credentials_destruct(grpc_channel_credentials* creds) { + grpc_alts_credentials* alts_creds = + reinterpret_cast<grpc_alts_credentials*>(creds); + grpc_alts_credentials_options_destroy(alts_creds->options); + gpr_free(alts_creds->handshaker_service_url); +} + +static void alts_server_credentials_destruct(grpc_server_credentials* creds) { + grpc_alts_server_credentials* alts_creds = + reinterpret_cast<grpc_alts_server_credentials*>(creds); + grpc_alts_credentials_options_destroy(alts_creds->options); + gpr_free(alts_creds->handshaker_service_url); +} + +static grpc_security_status alts_create_security_connector( + grpc_channel_credentials* creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + const grpc_channel_args* args, grpc_channel_security_connector** sc, + grpc_channel_args** new_args) { + return grpc_alts_channel_security_connector_create( + creds, request_metadata_creds, target_name, sc); +} + +static grpc_security_status alts_server_create_security_connector( + grpc_server_credentials* creds, grpc_server_security_connector** sc) { + return grpc_alts_server_security_connector_create(creds, sc); +} + +static const grpc_channel_credentials_vtable alts_credentials_vtable = { + alts_credentials_destruct, alts_create_security_connector, + /*duplicate_without_call_credentials=*/nullptr}; + +static const grpc_server_credentials_vtable alts_server_credentials_vtable = { + alts_server_credentials_destruct, alts_server_create_security_connector}; + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + auto creds = static_cast<grpc_alts_credentials*>( + gpr_zalloc(sizeof(grpc_alts_credentials))); + creds->options = grpc_alts_credentials_options_copy(options); + creds->handshaker_service_url = + handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url); + creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS; + creds->base.vtable = &alts_credentials_vtable; + gpr_ref_init(&creds->base.refcount, 1); + return &creds->base; +} + +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + auto creds = static_cast<grpc_alts_server_credentials*>( + gpr_zalloc(sizeof(grpc_alts_server_credentials))); + creds->options = grpc_alts_credentials_options_copy(options); + creds->handshaker_service_url = + handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url); + creds->base.type = GRPC_CREDENTIALS_TYPE_ALTS; + creds->base.vtable = &alts_server_credentials_vtable; + gpr_ref_init(&creds->base.refcount, 1); + return &creds->base; +} + +grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} + +grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_server_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} diff --git a/src/core/lib/security/credentials/alts/alts_credentials.h b/src/core/lib/security/credentials/alts/alts_credentials.h new file mode 100644 index 0000000000..621789cf65 --- /dev/null +++ b/src/core/lib/security/credentials/alts/alts_credentials.h @@ -0,0 +1,102 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H + +#include <grpc/support/port_platform.h> + +#include <grpc/grpc_security.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/lib/security/credentials/credentials.h" + +/* Main struct for grpc ALTS channel credential. */ +typedef struct grpc_alts_credentials { + grpc_channel_credentials base; + grpc_alts_credentials_options* options; + char* handshaker_service_url; +} grpc_alts_credentials; + +/* Main struct for grpc ALTS server credential. */ +typedef struct grpc_alts_server_credentials { + grpc_server_credentials base; + grpc_alts_credentials_options* options; + char* handshaker_service_url; +} grpc_alts_server_credentials; + +/** + * This method creates an ALTS channel credential object. + * + * - options: grpc ALTS credentials options instance for client. + * + * It returns the created ALTS channel credential object. + */ +grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS server credential object. + * + * - options: grpc ALTS credentials options instance for server. + * + * It returns the created ALTS server credential object. + */ +grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS channel credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for client. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled AND ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +/** + * This method creates an ALTS server credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for server. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled and ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.cc b/src/core/lib/security/credentials/alts/check_gcp_environment.cc new file mode 100644 index 0000000000..96807876cf --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +const size_t kBiosDataBufferSize = 256; + +static char* trim(const char* src) { + if (src == nullptr) { + return nullptr; + } + char* des = nullptr; + size_t start = 0, end = strlen(src) - 1; + /* find the last character that is not a whitespace. */ + while (end != 0 && isspace(src[end])) { + end--; + } + /* find the first character that is not a whitespace. */ + while (start < strlen(src) && isspace(src[start])) { + start++; + } + if (start <= end) { + des = static_cast<char*>( + gpr_zalloc(sizeof(char) * (end - start + 2 /* '\0' */))); + memcpy(des, src + start, end - start + 1); + } + return des; +} + +namespace grpc_core { +namespace internal { + +char* read_bios_file(const char* bios_file) { + FILE* fp = fopen(bios_file, "r"); + if (!fp) { + gpr_log(GPR_ERROR, "BIOS data file cannot be opened."); + return nullptr; + } + char buf[kBiosDataBufferSize + 1]; + size_t ret = fread(buf, sizeof(char), kBiosDataBufferSize, fp); + buf[ret] = '\0'; + char* trimmed_buf = trim(buf); + fclose(fp); + return trimmed_buf; +} + +} // namespace internal +} // namespace grpc_core diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment.h b/src/core/lib/security/credentials/alts/check_gcp_environment.h new file mode 100644 index 0000000000..aea4cea643 --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H + +namespace grpc_core { +namespace internal { + +/** + * This method is a helper function that reads a file containing system bios + * data. Exposed for testing only. + * + * - bios_file: a file containing BIOS data used to determine GCE tenancy + * information. + * + * It returns a buffer containing the data read from the file. + */ +char* read_bios_file(const char* bios_file); + +/** + * This method checks if system BIOS data contains Google-specific phrases. + * Exposed for testing only. + * + * - bios_data: a buffer containing system BIOS data. + * + * It returns true if the BIOS data contains Google-specific phrases, and false + * otherwise. + */ +bool check_bios_data(const char* bios_data); + +} // namespace internal +} // namespace grpc_core + +/** + * This method checks if a VM (Windows or Linux) is running within Google + * compute Engine (GCE) or not. It returns true if the VM is running in GCE and + * false otherwise. + */ +bool grpc_alts_is_running_on_gcp(); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H */ diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc new file mode 100644 index 0000000000..7c4d7a71cd --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_LINUX + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/sync.h> + +#include <string.h> + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_EXPECT_NAME_GCE "Google Compute Engine" +#define GRPC_ALTS_PRODUCT_NAME_FILE "/sys/class/dmi/id/product_name" + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE)) || + (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GCE)); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + grpc_core::internal::check_bios_data(GRPC_ALTS_PRODUCT_NAME_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_LINUX diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc new file mode 100644 index 0000000000..d97681b86d --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <grpc/support/log.h> + +bool grpc_alts_is_running_on_gcp() { + gpr_log(GPR_ERROR, + "Platforms other than Linux and Windows are not supported"); + return false; +} + +#endif // !defined(LINUX) && !defined(GPR_WINDOWS) diff --git a/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc new file mode 100644 index 0000000000..55efe0e9dd --- /dev/null +++ b/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_WINDOWS + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include <shellapi.h> +#include <stdio.h> +#include <tchar.h> +#include <windows.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND "powershell.exe" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS \ + "(Get-WmiObject -Class Win32_BIOS).Manufacturer" +#define GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE "windows_bios.data" + +const size_t kBiosDataBufferSize = 256; + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = !strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE); + remove(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +static bool run_powershell() { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + HANDLE h = CreateFile(_T(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE), GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + gpr_log(GPR_ERROR, "CreateFile failed (%d).", GetLastError()); + return false; + } + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD flags = CREATE_NO_WINDOW; + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = NULL; + si.hStdError = h; + si.hStdOutput = h; + TCHAR cmd[kBiosDataBufferSize]; + _sntprintf(cmd, kBiosDataBufferSize, _T("%s %s"), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS)); + if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, + &pi)) { + gpr_log(GPR_ERROR, "CreateProcess failed (%d).\n", GetLastError()); + return false; + } + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(h); + return true; +} + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + run_powershell() && + grpc_core::internal::check_bios_data(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_WINDOWS diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc new file mode 100644 index 0000000000..7d54e8346f --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc @@ -0,0 +1,126 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_client_options_destroy(grpc_alts_credentials_options* options); + +static target_service_account* target_service_account_create( + const char* service_account) { + if (service_account == nullptr) { + return nullptr; + } + auto* sa = static_cast<target_service_account*>( + gpr_zalloc(sizeof(target_service_account))); + sa->data = gpr_strdup(service_account); + return sa; +} + +bool grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_client_options* options, + const char* service_account) { + if (options == nullptr || service_account == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_alts_credentials_client_options_add_target_service_account()"); + return false; + } + target_service_account* node = target_service_account_create(service_account); + node->next = options->target_account_list_head; + options->target_account_list_head = node; + return true; +} + +static void target_service_account_destroy( + target_service_account* service_account) { + if (service_account == nullptr) { + return; + } + gpr_free(service_account->data); + gpr_free(service_account); +} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_client_options_copy, alts_client_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create() { + auto client_options = static_cast<grpc_alts_credentials_client_options*>( + gpr_zalloc(sizeof(grpc_alts_credentials_client_options))); + client_options->base.vtable = &vtable; + return &client_options->base; +} + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_client_options_create(); + auto new_client_options = + reinterpret_cast<grpc_alts_credentials_client_options*>(new_options); + /* Copy target service accounts. */ + target_service_account* prev = nullptr; + auto node = + (reinterpret_cast<const grpc_alts_credentials_client_options*>(options)) + ->target_account_list_head; + while (node != nullptr) { + target_service_account* new_node = + target_service_account_create(node->data); + if (prev == nullptr) { + new_client_options->target_account_list_head = new_node; + } else { + prev->next = new_node; + } + prev = new_node; + node = node->next; + } + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} + +static void alts_client_options_destroy( + grpc_alts_credentials_options* options) { + if (options == nullptr) { + return; + } + auto* client_options = + reinterpret_cast<grpc_alts_credentials_client_options*>(options); + target_service_account* node = client_options->target_account_list_head; + while (node != nullptr) { + target_service_account* next_node = node->next; + target_service_account_destroy(node); + node = next_node; + } +} diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc new file mode 100644 index 0000000000..d428171540 --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options) { + if (options != nullptr && options->vtable != nullptr && + options->vtable->copy != nullptr) { + return options->vtable->copy(options); + } + /* An error occurred. */ + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_credentials_options_copy()"); + return nullptr; +} + +void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options) { + if (options != nullptr) { + if (options->vtable != nullptr && options->vtable->destruct != nullptr) { + options->vtable->destruct(options); + } + gpr_free(options); + } +} diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h new file mode 100644 index 0000000000..4e46d9f2de --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h @@ -0,0 +1,112 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +/** + * Main interface for ALTS credentials options. The options will contain + * information that will be passed from grpc to TSI layer such as RPC protocol + * versions. ALTS client (channel) and server credentials will have their own + * implementation of this interface. The APIs listed in this header are + * thread-compatible. + */ +typedef struct grpc_alts_credentials_options grpc_alts_credentials_options; + +/* V-table for grpc_alts_credentials_options */ +typedef struct grpc_alts_credentials_options_vtable { + grpc_alts_credentials_options* (*copy)( + const grpc_alts_credentials_options* options); + void (*destruct)(grpc_alts_credentials_options* options); +} grpc_alts_credentials_options_vtable; + +struct grpc_alts_credentials_options { + const struct grpc_alts_credentials_options_vtable* vtable; + grpc_gcp_rpc_protocol_versions rpc_versions; +}; + +typedef struct target_service_account { + struct target_service_account* next; + char* data; +} target_service_account; + +/** + * Main struct for ALTS client credentials options. The options contain a + * a list of target service accounts (if specified) used for secure naming + * check. + */ +typedef struct grpc_alts_credentials_client_options { + grpc_alts_credentials_options base; + target_service_account* target_account_list_head; +} grpc_alts_credentials_client_options; + +/** + * Main struct for ALTS server credentials options. The options currently + * do not contain any server-specific fields. + */ +typedef struct grpc_alts_credentials_server_options { + grpc_alts_credentials_options base; +} grpc_alts_credentials_server_options; + +/** + * This method performs a deep copy on grpc_alts_credentials_options instance. + * + * - options: a grpc_alts_credentials_options instance that needs to be copied. + * + * It returns a new grpc_alts_credentials_options instance on success and NULL + * on failure. + */ +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options); + +/** + * This method destroys a grpc_alts_credentials_options instance by + * de-allocating all of its occupied memory. + * + * - options: a grpc_alts_credentials_options instance that needs to be + * destroyed. + */ +void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options); + +/* This method creates a grpc ALTS credentials client options instance. */ +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create(); + +/* This method creates a grpc ALTS credentials server options instance. */ +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create(); + +/** + * This method adds a target service account to grpc ALTS credentials client + * options instance. + * + * - options: grpc ALTS credentials client options instance. + * - service_account: service account of target endpoint. + * + * It returns true on success and false on failure. + */ +bool grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_client_options* options, const char* service_account); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H \ + */ diff --git a/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc new file mode 100644 index 0000000000..62aa7a620a --- /dev/null +++ b/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_server_options_destroy( + grpc_alts_credentials_options* options) {} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_server_options_copy, alts_server_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create() { + grpc_alts_credentials_server_options* server_options = + static_cast<grpc_alts_credentials_server_options*>( + gpr_zalloc(sizeof(*server_options))); + server_options->base.vtable = &vtable; + return &server_options->base; +} + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_server_options_create(); + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} diff --git a/src/core/lib/security/credentials/composite/composite_credentials.cc b/src/core/lib/security/credentials/composite/composite_credentials.cc index ea2c4e208f..b8f409260f 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.cc +++ b/src/core/lib/security/credentials/composite/composite_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/composite/composite_credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/composite/composite_credentials.h b/src/core/lib/security/credentials/composite/composite_credentials.h index 11990d38ff..a952ad57f1 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.h +++ b/src/core/lib/security/credentials/composite/composite_credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" typedef struct { diff --git a/src/core/lib/security/credentials/credentials.cc b/src/core/lib/security/credentials/credentials.cc index 17f7de58be..c43cb440eb 100644 --- a/src/core/lib/security/credentials/credentials.cc +++ b/src/core/lib/security/credentials/credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" #include <stdio.h> diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h index 50beca897e..b1421e83c5 100644 --- a/src/core/lib/security/credentials/credentials.h +++ b/src/core/lib/security/credentials/credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/grpc_security.h> #include <grpc/support/sync.h> diff --git a/src/core/lib/security/credentials/credentials_metadata.cc b/src/core/lib/security/credentials/credentials_metadata.cc index 250e384155..703de4aaaf 100644 --- a/src/core/lib/security/credentials/credentials_metadata.cc +++ b/src/core/lib/security/credentials/credentials_metadata.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" #include <grpc/support/alloc.h> diff --git a/src/core/lib/security/credentials/fake/fake_credentials.cc b/src/core/lib/security/credentials/fake/fake_credentials.cc index fa0f89c583..858ab6b41b 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.cc +++ b/src/core/lib/security/credentials/fake/fake_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/fake/fake_credentials.h" #include <string.h> @@ -30,9 +32,6 @@ /* -- Fake transport security credentials. -- */ -#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ - "grpc.fake_security.expected_targets" - static grpc_security_status fake_transport_security_create_security_connector( grpc_channel_credentials* c, grpc_call_credentials* call_creds, const char* target, const grpc_channel_args* args, diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h index 0e9ff155d8..e89e6e24cc 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.h +++ b/src/core/lib/security/credentials/fake/fake_credentials.h @@ -19,8 +19,13 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" +#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ + "grpc.fake_security.expected_targets" + /* -- Fake transport security credentials. -- */ /* Creates a fake transport security credentials object for testing. */ diff --git a/src/core/lib/security/credentials/google_default/credentials_generic.cc b/src/core/lib/security/credentials/google_default/credentials_generic.cc index 15ae9d6428..10ff0f620f 100644 --- a/src/core/lib/security/credentials/google_default/credentials_generic.cc +++ b/src/core/lib/security/credentials/google_default/credentials_generic.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/google_default/google_default_credentials.h" #include <grpc/support/alloc.h> diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc index 65455f94b3..70d4c3ea51 100644 --- a/src/core/lib/security/credentials/google_default/google_default_credentials.cc +++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/iam/iam_credentials.cc b/src/core/lib/security/credentials/iam/iam_credentials.cc index 7f19fbc785..5d92fa88c4 100644 --- a/src/core/lib/security/credentials/iam/iam_credentials.cc +++ b/src/core/lib/security/credentials/iam/iam_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/iam/iam_credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/iam/iam_credentials.h b/src/core/lib/security/credentials/iam/iam_credentials.h index 5e3cf65bad..a45710fe0f 100644 --- a/src/core/lib/security/credentials/iam/iam_credentials.h +++ b/src/core/lib/security/credentials/iam/iam_credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" typedef struct { diff --git a/src/core/lib/security/credentials/jwt/json_token.cc b/src/core/lib/security/credentials/jwt/json_token.cc index f7c9f57808..1c4827df0f 100644 --- a/src/core/lib/security/credentials/jwt/json_token.cc +++ b/src/core/lib/security/credentials/jwt/json_token.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/jwt/json_token.h" #include <string.h> diff --git a/src/core/lib/security/credentials/jwt/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h index 9b774882b7..d0fb4ebd0a 100644 --- a/src/core/lib/security/credentials/jwt/json_token.h +++ b/src/core/lib/security/credentials/jwt/json_token.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <openssl/rsa.h> diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h index f58a8b67ba..5c3d34aa56 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.h +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" #include "src/core/lib/security/credentials/jwt/json_token.h" diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/src/core/lib/security/credentials/jwt/jwt_verifier.cc index f5c1ada6ca..5c47276e32 100644 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.cc +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/jwt/jwt_verifier.h" #include <limits.h> diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h index b3805e75cd..cdb09870bd 100644 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.h +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/json/json.h" diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc index afae7d8f2f..2129029737 100644 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h index e5b8df8eb9..c0dd1546e3 100644 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/json/json.h" #include "src/core/lib/security/credentials/credentials.h" diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc index ddb86e153b..73946ce039 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.cc +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/plugin/plugin_credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h index e1467b0824..caf990efa1 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.h +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" extern grpc_core::TraceFlag grpc_plugin_credentials_trace; diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc index a4fce25f3a..252b25bc0a 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.cc +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/ssl/ssl_credentials.h" #include <string.h> diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.h b/src/core/lib/security/credentials/ssl/ssl_credentials.h index 0003905857..712d34c733 100644 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.h +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.h @@ -18,6 +18,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" typedef struct { diff --git a/src/core/lib/security/security_connector/alts_security_connector.cc b/src/core/lib/security/security_connector/alts_security_connector.cc new file mode 100644 index 0000000000..5ff7d7938b --- /dev/null +++ b/src/core/lib/security/security_connector/alts_security_connector.cc @@ -0,0 +1,287 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/security_connector/alts_security_connector.h" + +#include <stdbool.h> +#include <string.h> + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/security/credentials/alts/alts_credentials.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/transport/transport.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" + +typedef struct { + grpc_channel_security_connector base; + char* target_name; +} grpc_alts_channel_security_connector; + +typedef struct { + grpc_server_security_connector base; +} grpc_alts_server_security_connector; + +static void alts_channel_destroy(grpc_security_connector* sc) { + if (sc == nullptr) { + return; + } + auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + grpc_call_credentials_unref(c->base.request_metadata_creds); + grpc_channel_credentials_unref(c->base.channel_creds); + gpr_free(c->target_name); + gpr_free(sc); +} + +static void alts_server_destroy(grpc_security_connector* sc) { + if (sc == nullptr) { + return; + } + auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc); + grpc_server_credentials_unref(c->base.server_creds); + gpr_free(sc); +} + +static void alts_channel_add_handshakers( + grpc_channel_security_connector* sc, + grpc_handshake_manager* handshake_manager) { + tsi_handshaker* handshaker = nullptr; + auto c = reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + grpc_alts_credentials* creds = + reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds); + GPR_ASSERT(alts_tsi_handshaker_create(creds->options, c->target_name, + creds->handshaker_service_url, true, + &handshaker) == TSI_OK); + grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create( + handshaker, &sc->base)); +} + +static void alts_server_add_handshakers( + grpc_server_security_connector* sc, + grpc_handshake_manager* handshake_manager) { + tsi_handshaker* handshaker = nullptr; + auto c = reinterpret_cast<grpc_alts_server_security_connector*>(sc); + grpc_alts_server_credentials* creds = + reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds); + GPR_ASSERT(alts_tsi_handshaker_create(creds->options, nullptr, + creds->handshaker_service_url, false, + &handshaker) == TSI_OK); + grpc_handshake_manager_add(handshake_manager, grpc_security_handshaker_create( + handshaker, &sc->base)); +} + +static void alts_set_rpc_protocol_versions( + grpc_gcp_rpc_protocol_versions* rpc_versions) { + grpc_gcp_rpc_protocol_versions_set_max(rpc_versions, + GRPC_PROTOCOL_VERSION_MAX_MAJOR, + GRPC_PROTOCOL_VERSION_MAX_MINOR); + grpc_gcp_rpc_protocol_versions_set_min(rpc_versions, + GRPC_PROTOCOL_VERSION_MIN_MAJOR, + GRPC_PROTOCOL_VERSION_MIN_MINOR); +} + +namespace grpc_core { +namespace internal { + +grpc_security_status grpc_alts_auth_context_from_tsi_peer( + const tsi_peer* peer, grpc_auth_context** ctx) { + if (peer == nullptr || ctx == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_auth_context_from_tsi_peer()"); + return GRPC_SECURITY_ERROR; + } + *ctx = nullptr; + /* Validate certificate type. */ + const tsi_peer_property* cert_type_prop = + tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); + if (cert_type_prop == nullptr || + strncmp(cert_type_prop->value.data, TSI_ALTS_CERTIFICATE_TYPE, + cert_type_prop->value.length) != 0) { + gpr_log(GPR_ERROR, "Invalid or missing certificate type property."); + return GRPC_SECURITY_ERROR; + } + /* Validate RPC protocol versions. */ + const tsi_peer_property* rpc_versions_prop = + tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS); + if (rpc_versions_prop == nullptr) { + gpr_log(GPR_ERROR, "Missing rpc protocol versions property."); + return GRPC_SECURITY_ERROR; + } + grpc_gcp_rpc_protocol_versions local_versions, peer_versions; + alts_set_rpc_protocol_versions(&local_versions); + grpc_slice slice = grpc_slice_from_copied_buffer( + rpc_versions_prop->value.data, rpc_versions_prop->value.length); + bool decode_result = + grpc_gcp_rpc_protocol_versions_decode(slice, &peer_versions); + grpc_slice_unref(slice); + if (!decode_result) { + gpr_log(GPR_ERROR, "Invalid peer rpc protocol versions."); + return GRPC_SECURITY_ERROR; + } + /* TODO: Pass highest common rpc protocol version to grpc caller. */ + bool check_result = grpc_gcp_rpc_protocol_versions_check( + &local_versions, &peer_versions, nullptr); + if (!check_result) { + gpr_log(GPR_ERROR, "Mismatch of local and peer rpc protocol versions."); + return GRPC_SECURITY_ERROR; + } + /* Create auth context. */ + *ctx = grpc_auth_context_create(nullptr); + grpc_auth_context_add_cstring_property( + *ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_ALTS_TRANSPORT_SECURITY_TYPE); + size_t i = 0; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* tsi_prop = &peer->properties[i]; + /* Add service account to auth context. */ + if (strcmp(tsi_prop->name, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property( + *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, tsi_prop->value.data, + tsi_prop->value.length); + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + *ctx, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 1); + } + } + if (!grpc_auth_context_peer_is_authenticated(*ctx)) { + gpr_log(GPR_ERROR, "Invalid unauthenticated peer."); + GRPC_AUTH_CONTEXT_UNREF(*ctx, "test"); + *ctx = nullptr; + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; +} + +} // namespace internal +} // namespace grpc_core + +static void alts_check_peer(grpc_security_connector* sc, tsi_peer peer, + grpc_auth_context** auth_context, + grpc_closure* on_peer_checked) { + grpc_security_status status; + status = grpc_core::internal::grpc_alts_auth_context_from_tsi_peer( + &peer, auth_context); + tsi_peer_destruct(&peer); + grpc_error* error = + status == GRPC_SECURITY_OK + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not get ALTS auth context from TSI peer"); + GRPC_CLOSURE_SCHED(on_peer_checked, error); +} + +static int alts_channel_cmp(grpc_security_connector* sc1, + grpc_security_connector* sc2) { + grpc_alts_channel_security_connector* c1 = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc1); + grpc_alts_channel_security_connector* c2 = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc2); + int c = grpc_channel_security_connector_cmp(&c1->base, &c2->base); + if (c != 0) return c; + return strcmp(c1->target_name, c2->target_name); +} + +static int alts_server_cmp(grpc_security_connector* sc1, + grpc_security_connector* sc2) { + grpc_alts_server_security_connector* c1 = + reinterpret_cast<grpc_alts_server_security_connector*>(sc1); + grpc_alts_server_security_connector* c2 = + reinterpret_cast<grpc_alts_server_security_connector*>(sc2); + return grpc_server_security_connector_cmp(&c1->base, &c2->base); +} + +static grpc_security_connector_vtable alts_channel_vtable = { + alts_channel_destroy, alts_check_peer, alts_channel_cmp}; + +static grpc_security_connector_vtable alts_server_vtable = { + alts_server_destroy, alts_check_peer, alts_server_cmp}; + +static bool alts_check_call_host(grpc_channel_security_connector* sc, + const char* host, + grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) { + grpc_alts_channel_security_connector* alts_sc = + reinterpret_cast<grpc_alts_channel_security_connector*>(sc); + if (host == nullptr || alts_sc == nullptr || + strcmp(host, alts_sc->target_name) != 0) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "ALTS call host does not match target name"); + } + return true; +} + +static void alts_cancel_check_call_host(grpc_channel_security_connector* sc, + grpc_closure* on_call_host_checked, + grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_security_status grpc_alts_channel_security_connector_create( + grpc_channel_credentials* channel_creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + grpc_channel_security_connector** sc) { + if (channel_creds == nullptr || sc == nullptr || target_name == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_channel_security_connector_create()"); + return GRPC_SECURITY_ERROR; + } + auto c = static_cast<grpc_alts_channel_security_connector*>( + gpr_zalloc(sizeof(grpc_alts_channel_security_connector))); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &alts_channel_vtable; + c->base.add_handshakers = alts_channel_add_handshakers; + c->base.channel_creds = grpc_channel_credentials_ref(channel_creds); + c->base.request_metadata_creds = + grpc_call_credentials_ref(request_metadata_creds); + c->base.check_call_host = alts_check_call_host; + c->base.cancel_check_call_host = alts_cancel_check_call_host; + grpc_alts_credentials* creds = + reinterpret_cast<grpc_alts_credentials*>(c->base.channel_creds); + alts_set_rpc_protocol_versions(&creds->options->rpc_versions); + c->target_name = gpr_strdup(target_name); + *sc = &c->base; + return GRPC_SECURITY_OK; +} + +grpc_security_status grpc_alts_server_security_connector_create( + grpc_server_credentials* server_creds, + grpc_server_security_connector** sc) { + if (server_creds == nullptr || sc == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_server_security_connector_create()"); + return GRPC_SECURITY_ERROR; + } + auto c = static_cast<grpc_alts_server_security_connector*>( + gpr_zalloc(sizeof(grpc_alts_server_security_connector))); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &alts_server_vtable; + c->base.server_creds = grpc_server_credentials_ref(server_creds); + c->base.add_handshakers = alts_server_add_handshakers; + grpc_alts_server_credentials* creds = + reinterpret_cast<grpc_alts_server_credentials*>(c->base.server_creds); + alts_set_rpc_protocol_versions(&creds->options->rpc_versions); + *sc = &c->base; + return GRPC_SECURITY_OK; +} diff --git a/src/core/lib/security/security_connector/alts_security_connector.h b/src/core/lib/security/security_connector/alts_security_connector.h new file mode 100644 index 0000000000..e7e4cffe2a --- /dev/null +++ b/src/core/lib/security/security_connector/alts_security_connector.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#define GRPC_ALTS_TRANSPORT_SECURITY_TYPE "alts" + +/** + * This method creates an ALTS channel security connector. + * + * - channel_creds: channel credential instance. + * - request_metadata_creds: credential object which will be sent with each + * request. This parameter can be nullptr. + * - target_name: the name of the endpoint that the channel is connecting to. + * - sc: address of ALTS channel security connector instance to be returned from + * the method. + * + * It returns GRPC_SECURITY_OK on success, and an error stauts code on failure. + */ +grpc_security_status grpc_alts_channel_security_connector_create( + grpc_channel_credentials* channel_creds, + grpc_call_credentials* request_metadata_creds, const char* target_name, + grpc_channel_security_connector** sc); + +/** + * This method creates an ALTS server security connector. + * + * - server_creds: server credential instance. + * - sc: address of ALTS server security connector instance to be returned from + * the method. + * + * It returns GRPC_SECURITY_OK on success, and an error status code on failure. + */ +grpc_security_status grpc_alts_server_security_connector_create( + grpc_server_credentials* server_creds, grpc_server_security_connector** sc); + +namespace grpc_core { +namespace internal { + +/* Exposed only for testing. */ +grpc_security_status grpc_alts_auth_context_from_tsi_peer( + const tsi_peer* peer, grpc_auth_context** ctx); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_SECURITY_CONNECTOR_H \ + */ diff --git a/src/core/lib/security/security_connector/security_connector.cc b/src/core/lib/security/security_connector/security_connector.cc index eb88045711..3cc151bec7 100644 --- a/src/core/lib/security/security_connector/security_connector.cc +++ b/src/core/lib/security/security_connector/security_connector.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/security_connector/security_connector.h" #include <stdbool.h> @@ -37,9 +39,9 @@ #include "src/core/lib/security/credentials/credentials.h" #include "src/core/lib/security/credentials/fake/fake_credentials.h" #include "src/core/lib/security/credentials/ssl/ssl_credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" #include "src/core/lib/security/transport/secure_endpoint.h" #include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/security/transport/target_authority_table.h" #include "src/core/tsi/fake_transport_security.h" #include "src/core/tsi/ssl_transport_security.h" #include "src/core/tsi/transport_security_adapter.h" @@ -461,6 +463,15 @@ static bool fake_channel_check_call_host(grpc_channel_security_connector* sc, grpc_auth_context* auth_context, grpc_closure* on_call_host_checked, grpc_error** error) { + grpc_fake_channel_security_connector* c = + reinterpret_cast<grpc_fake_channel_security_connector*>(sc); + if (c->is_lb_channel) { + // TODO(dgq): verify that the host (ie, authority header) matches that of + // the LB, as opposed to that of the backends. + } else { + // TODO(dgq): verify that the host (ie, authority header) matches that of + // the backend, not the LB's. + } return true; } @@ -512,7 +523,7 @@ grpc_channel_security_connector* grpc_fake_channel_security_connector_create( c->target = gpr_strdup(target); const char* expected_targets = grpc_fake_transport_get_expected_targets(args); c->expected_targets = gpr_strdup(expected_targets); - c->is_lb_channel = (grpc_lb_targets_info_find_in_args(args) != nullptr); + c->is_lb_channel = grpc_core::FindTargetAuthorityTableInArgs(args) != nullptr; return &c->base; } diff --git a/src/core/lib/security/security_connector/security_connector.h b/src/core/lib/security/security_connector/security_connector.h index 0c972a7125..130c8ecd3e 100644 --- a/src/core/lib/security/security_connector/security_connector.h +++ b/src/core/lib/security/security_connector/security_connector.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H #define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/grpc_security.h> diff --git a/src/core/lib/security/transport/auth_filters.h b/src/core/lib/security/transport/auth_filters.h index e999a027ae..af2104cfbc 100644 --- a/src/core/lib/security/transport/auth_filters.h +++ b/src/core/lib/security/transport/auth_filters.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H #define GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc_security.h> #include "src/core/lib/channel/channel_stack.h" diff --git a/src/core/lib/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc index 506f5a0666..048e390a71 100644 --- a/src/core/lib/security/transport/client_auth_filter.cc +++ b/src/core/lib/security/transport/client_auth_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/transport/auth_filters.h" #include <string.h> @@ -43,8 +45,6 @@ struct call_data { grpc_call_stack* owning_call; grpc_call_combiner* call_combiner; grpc_call_credentials* creds; - bool have_host; - bool have_method; grpc_slice host; grpc_slice method; /* pollset{_set} bound to this call; if we need to make external @@ -292,27 +292,15 @@ static void auth_start_transport_stream_op_batch( } if (batch->send_initial_metadata) { - for (grpc_linked_mdelem* l = batch->payload->send_initial_metadata - .send_initial_metadata->list.head; - l != nullptr; l = l->next) { - grpc_mdelem md = l->md; - /* Pointer comparison is OK for md_elems created from the same context. - */ - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) { - if (calld->have_host) { - grpc_slice_unref_internal(calld->host); - } - calld->host = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_host = true; - } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) { - if (calld->have_method) { - grpc_slice_unref_internal(calld->method); - } - calld->method = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_method = true; - } + grpc_metadata_batch* metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + if (metadata->idx.named.path != nullptr) { + calld->method = + grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md)); } - if (calld->have_host) { + if (metadata->idx.named.authority != nullptr) { + calld->host = grpc_slice_ref_internal( + GRPC_MDVALUE(metadata->idx.named.authority->md)); batch->handler_private.extra_arg = elem; GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host"); GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, @@ -349,6 +337,8 @@ static grpc_error* init_call_elem(grpc_call_element* elem, call_data* calld = static_cast<call_data*>(elem->call_data); calld->owning_call = args->call_stack; calld->call_combiner = args->call_combiner; + calld->host = grpc_empty_slice(); + calld->method = grpc_empty_slice(); return GRPC_ERROR_NONE; } @@ -365,12 +355,8 @@ static void destroy_call_elem(grpc_call_element* elem, call_data* calld = static_cast<call_data*>(elem->call_data); grpc_credentials_mdelem_array_destroy(&calld->md_array); grpc_call_credentials_unref(calld->creds); - if (calld->have_host) { - grpc_slice_unref_internal(calld->host); - } - if (calld->have_method) { - grpc_slice_unref_internal(calld->method); - } + grpc_slice_unref_internal(calld->host); + grpc_slice_unref_internal(calld->method); grpc_auth_metadata_context_reset(&calld->auth_md_context); } diff --git a/src/core/lib/security/transport/lb_targets_info.cc b/src/core/lib/security/transport/lb_targets_info.cc deleted file mode 100644 index 67a3c7449d..0000000000 --- a/src/core/lib/security/transport/lb_targets_info.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include <grpc/support/log.h> - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/transport/lb_targets_info.h" - -/* Channel arg key for the mapping of LB server addresses to their names for - * secure naming purposes. */ -#define GRPC_ARG_LB_SECURE_NAMING_MAP "grpc.lb_secure_naming_map" - -static void* targets_info_copy(void* p) { - return grpc_slice_hash_table_ref(static_cast<grpc_slice_hash_table*>(p)); -} -static void targets_info_destroy(void* p) { - grpc_slice_hash_table_unref(static_cast<grpc_slice_hash_table*>(p)); -} -static int targets_info_cmp(void* a, void* b) { - return grpc_slice_hash_table_cmp( - static_cast<const grpc_slice_hash_table*>(a), - static_cast<const grpc_slice_hash_table*>(b)); -} -static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = { - targets_info_copy, targets_info_destroy, targets_info_cmp}; - -grpc_arg grpc_lb_targets_info_create_channel_arg( - grpc_slice_hash_table* targets_info) { - return grpc_channel_arg_pointer_create((char*)GRPC_ARG_LB_SECURE_NAMING_MAP, - targets_info, - &server_to_balancer_names_vtable); -} - -grpc_slice_hash_table* grpc_lb_targets_info_find_in_args( - const grpc_channel_args* args) { - const grpc_arg* targets_info_arg = - grpc_channel_args_find(args, GRPC_ARG_LB_SECURE_NAMING_MAP); - if (targets_info_arg != nullptr) { - GPR_ASSERT(targets_info_arg->type == GRPC_ARG_POINTER); - return static_cast<grpc_slice_hash_table*>( - targets_info_arg->value.pointer.p); - } - return nullptr; -} diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc index f72f8b6121..31b779e333 100644 --- a/src/core/lib/security/transport/secure_endpoint.cc +++ b/src/core/lib/security/transport/secure_endpoint.cc @@ -20,6 +20,8 @@ using that endpoint. Because of various transitive includes in uv.h, including windows.h on Windows, uv.h must be included before other system headers. Therefore, sockaddr.h must always be included first */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/sockaddr.h" #include <grpc/slice.h> diff --git a/src/core/lib/security/transport/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h index b2556a0182..e7e3351678 100644 --- a/src/core/lib/security/transport/secure_endpoint.h +++ b/src/core/lib/security/transport/secure_endpoint.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H #define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/lib/iomgr/endpoint.h" diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc index b37392ab81..0c97dfa6b3 100644 --- a/src/core/lib/security/transport/security_handshaker.cc +++ b/src/core/lib/security/transport/security_handshaker.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/transport/security_handshaker.h" #include <stdbool.h> diff --git a/src/core/lib/security/transport/security_handshaker.h b/src/core/lib/security/transport/security_handshaker.h index f109a51996..ecf59ec5c5 100644 --- a/src/core/lib/security/transport/security_handshaker.h +++ b/src/core/lib/security/transport/security_handshaker.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H #define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/handshaker.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/security/security_connector/security_connector.h" diff --git a/src/core/lib/security/transport/server_auth_filter.cc b/src/core/lib/security/transport/server_auth_filter.cc index 409aded650..a560a4a02e 100644 --- a/src/core/lib/security/transport/server_auth_filter.cc +++ b/src/core/lib/security/transport/server_auth_filter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/security/transport/target_authority_table.cc b/src/core/lib/security/transport/target_authority_table.cc new file mode 100644 index 0000000000..1eeb557f6a --- /dev/null +++ b/src/core/lib/security/transport/target_authority_table.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/log.h> + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/transport/target_authority_table.h" + +// Channel arg key for the mapping of target addresses to their authorities. +#define GRPC_ARG_TARGET_AUTHORITY_TABLE "grpc.target_authority_table" + +namespace grpc_core { +namespace { + +void* target_authority_table_copy(void* p) { + TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p); + // TODO(roth): When channel_args are converted to C++, pass the + // RefCountedPtr<> directly instead of managing the ref manually. + table->Ref().release(); + return p; +} +void target_authority_table_destroy(void* p) { + TargetAuthorityTable* table = static_cast<TargetAuthorityTable*>(p); + table->Unref(); +} +int target_authority_table_cmp(void* a, void* b) { + return TargetAuthorityTable::Cmp( + *static_cast<const TargetAuthorityTable*>(a), + *static_cast<const TargetAuthorityTable*>(b)); +} +const grpc_arg_pointer_vtable target_authority_table_arg_vtable = { + target_authority_table_copy, target_authority_table_destroy, + target_authority_table_cmp}; + +} // namespace + +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_TARGET_AUTHORITY_TABLE, + table, + &target_authority_table_arg_vtable); +} + +TargetAuthorityTable* FindTargetAuthorityTableInArgs( + const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_TARGET_AUTHORITY_TABLE); + if (arg != nullptr) { + if (arg->type == GRPC_ARG_POINTER) { + return static_cast<TargetAuthorityTable*>(arg->value.pointer.p); + } else { + gpr_log(GPR_ERROR, "value of " GRPC_ARG_TARGET_AUTHORITY_TABLE + " channel arg was not pointer type; ignoring"); + } + } + return nullptr; +} + +} // namespace grpc_core diff --git a/src/core/lib/security/transport/target_authority_table.h b/src/core/lib/security/transport/target_authority_table.h new file mode 100644 index 0000000000..a2e7dc6ac2 --- /dev/null +++ b/src/core/lib/security/transport/target_authority_table.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/slice/slice_hash_table.h" + +namespace grpc_core { + +/// A hash table mapping target addresses to authorities. +typedef SliceHashTable<UniquePtr<char>> TargetAuthorityTable; + +/// Returns a channel argument containing \a table. +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table); + +/// Returns the target authority table from \a args or nullptr. +TargetAuthorityTable* FindTargetAuthorityTableInArgs( + const grpc_channel_args* args); + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H */ diff --git a/src/core/lib/security/transport/tsi_error.cc b/src/core/lib/security/transport/tsi_error.cc index f71696d35d..f78bb8df38 100644 --- a/src/core/lib/security/transport/tsi_error.cc +++ b/src/core/lib/security/transport/tsi_error.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/transport/tsi_error.h" grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result) { diff --git a/src/core/lib/security/transport/tsi_error.h b/src/core/lib/security/transport/tsi_error.h index 8fa6c480ac..16e04f70f1 100644 --- a/src/core/lib/security/transport/tsi_error.h +++ b/src/core/lib/security/transport/tsi_error.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H #define GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/error.h" #include "src/core/tsi/transport_security_interface.h" diff --git a/src/core/lib/security/util/json_util.cc b/src/core/lib/security/util/json_util.cc index fef1a1f51d..75512a19c9 100644 --- a/src/core/lib/security/util/json_util.cc +++ b/src/core/lib/security/util/json_util.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/util/json_util.h" #include <string.h> diff --git a/src/core/lib/security/util/json_util.h b/src/core/lib/security/util/json_util.h index b7e46d4062..89deffcc08 100644 --- a/src/core/lib/security/util/json_util.h +++ b/src/core/lib/security/util/json_util.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H #define GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include "src/core/lib/json/json.h" diff --git a/src/core/lib/slice/b64.cc b/src/core/lib/slice/b64.cc index 3e19b7197f..27f2724002 100644 --- a/src/core/lib/slice/b64.cc +++ b/src/core/lib/slice/b64.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/slice/b64.h" #include <stdint.h> diff --git a/src/core/lib/slice/b64.h b/src/core/lib/slice/b64.h index 17e7306303..4475568c25 100644 --- a/src/core/lib/slice/b64.h +++ b/src/core/lib/slice/b64.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SLICE_B64_H #define GRPC_CORE_LIB_SLICE_B64_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> /* Encodes data using base64. It is the caller's responsability to free diff --git a/src/core/lib/slice/percent_encoding.cc b/src/core/lib/slice/percent_encoding.cc index 84fb554454..45cd2cc47f 100644 --- a/src/core/lib/slice/percent_encoding.cc +++ b/src/core/lib/slice/percent_encoding.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/slice/percent_encoding.h" #include <grpc/support/log.h> diff --git a/src/core/lib/slice/percent_encoding.h b/src/core/lib/slice/percent_encoding.h index a1009ff01f..6b13ffc3fe 100644 --- a/src/core/lib/slice/percent_encoding.h +++ b/src/core/lib/slice/percent_encoding.h @@ -26,6 +26,8 @@ and another which applies percent encoding only to non-http2 header bytes (the 'compatible' variant) */ +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/slice.h> diff --git a/src/core/lib/slice/slice.cc b/src/core/lib/slice/slice.cc index 476d941fbb..585b41cf91 100644 --- a/src/core/lib/slice/slice.cc +++ b/src/core/lib/slice/slice.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/slice/slice_internal.h" #include <grpc/slice.h> diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc index 0416c9d371..e418ab10ef 100644 --- a/src/core/lib/slice/slice_buffer.cc +++ b/src/core/lib/slice/slice_buffer.cc @@ -16,9 +16,10 @@ * */ -#include <grpc/slice_buffer.h> #include <grpc/support/port_platform.h> +#include <grpc/slice_buffer.h> + #include <string.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/slice/slice_hash_table.cc b/src/core/lib/slice/slice_hash_table.cc deleted file mode 100644 index 2342e90485..0000000000 --- a/src/core/lib/slice/slice_hash_table.cc +++ /dev/null @@ -1,145 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/lib/slice/slice_hash_table.h" - -#include <stdbool.h> -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> - -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/transport/metadata.h" - -struct grpc_slice_hash_table { - gpr_refcount refs; - void (*destroy_value)(void* value); - int (*value_cmp)(void* a, void* b); - size_t size; - size_t max_num_probes; - grpc_slice_hash_table_entry* entries; -}; - -static bool is_empty(grpc_slice_hash_table_entry* entry) { - return entry->value == nullptr; -} - -static void grpc_slice_hash_table_add(grpc_slice_hash_table* table, - grpc_slice key, void* value) { - GPR_ASSERT(value != nullptr); - const size_t hash = grpc_slice_hash(key); - for (size_t offset = 0; offset < table->size; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) { - table->entries[idx].key = key; - table->entries[idx].value = value; - // Keep track of the maximum number of probes needed, since this - // provides an upper bound for lookups. - if (offset > table->max_num_probes) table->max_num_probes = offset; - return; - } - } - GPR_ASSERT(false); // Table should never be full. -} - -grpc_slice_hash_table* grpc_slice_hash_table_create( - size_t num_entries, grpc_slice_hash_table_entry* entries, - void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b)) { - grpc_slice_hash_table* table = - static_cast<grpc_slice_hash_table*>(gpr_zalloc(sizeof(*table))); - gpr_ref_init(&table->refs, 1); - table->destroy_value = destroy_value; - table->value_cmp = value_cmp; - // Keep load factor low to improve performance of lookups. - table->size = num_entries * 2; - const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size; - table->entries = - static_cast<grpc_slice_hash_table_entry*>(gpr_zalloc(entry_size)); - for (size_t i = 0; i < num_entries; ++i) { - grpc_slice_hash_table_entry* entry = &entries[i]; - grpc_slice_hash_table_add(table, entry->key, entry->value); - } - return table; -} - -grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) { - if (table != nullptr) gpr_ref(&table->refs); - return table; -} - -void grpc_slice_hash_table_unref(grpc_slice_hash_table* table) { - if (table != nullptr && gpr_unref(&table->refs)) { - for (size_t i = 0; i < table->size; ++i) { - grpc_slice_hash_table_entry* entry = &table->entries[i]; - if (!is_empty(entry)) { - grpc_slice_unref_internal(entry->key); - table->destroy_value(entry->value); - } - } - gpr_free(table->entries); - gpr_free(table); - } -} - -void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, - const grpc_slice key) { - const size_t hash = grpc_slice_hash(key); - // We cap the number of probes at the max number recorded when - // populating the table. - for (size_t offset = 0; offset <= table->max_num_probes; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) break; - if (grpc_slice_eq(table->entries[idx].key, key)) { - return table->entries[idx].value; - } - } - return nullptr; // Not found. -} - -static int pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } -int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, - const grpc_slice_hash_table* b) { - int (*const value_cmp_fn_a)(void* a, void* b) = - a->value_cmp != nullptr ? a->value_cmp : pointer_cmp; - int (*const value_cmp_fn_b)(void* a, void* b) = - b->value_cmp != nullptr ? b->value_cmp : pointer_cmp; - // Compare value_fns - const int value_fns_cmp = - GPR_ICMP((void*)value_cmp_fn_a, (void*)value_cmp_fn_b); - if (value_fns_cmp != 0) return value_fns_cmp; - // Compare sizes - if (a->size < b->size) return -1; - if (a->size > b->size) return 1; - // Compare rows. - for (size_t i = 0; i < a->size; ++i) { - if (is_empty(&a->entries[i])) { - if (!is_empty(&b->entries[i])) { - return -1; // a empty but b non-empty - } - continue; // both empty, no need to check key or value - } else if (is_empty(&b->entries[i])) { - return 1; // a non-empty but b empty - } - // neither entry is empty - const int key_cmp = grpc_slice_cmp(a->entries[i].key, b->entries[i].key); - if (key_cmp != 0) return key_cmp; - const int value_cmp = - value_cmp_fn_a(a->entries[i].value, b->entries[i].value); - if (value_cmp != 0) return value_cmp; - } - return 0; -} diff --git a/src/core/lib/slice/slice_hash_table.h b/src/core/lib/slice/slice_hash_table.h index db69da662a..fbe9cc58e8 100644 --- a/src/core/lib/slice/slice_hash_table.h +++ b/src/core/lib/slice/slice_hash_table.h @@ -17,52 +17,185 @@ #ifndef GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H #define GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H -#include "src/core/lib/transport/metadata.h" +#include <grpc/support/port_platform.h> -/** Hash table implementation. - * - * This implementation uses open addressing - * (https://en.wikipedia.org/wiki/Open_addressing) with linear - * probing (https://en.wikipedia.org/wiki/Linear_probing). - * - * The keys are \a grpc_slice objects. The values are arbitrary pointers - * with a common destroy function. - * - * Hash tables are intentionally immutable, to avoid the need for locking. - */ +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Hash table implementation. +/// +/// This implementation uses open addressing +/// (https://en.wikipedia.org/wiki/Open_addressing) with linear +/// probing (https://en.wikipedia.org/wiki/Linear_probing). +/// +/// The keys are \a grpc_slice objects. The values can be any type. +/// +/// Hash tables are intentionally immutable, to avoid the need for locking. + +namespace grpc_core { + +template <typename T> +class SliceHashTable : public RefCounted<SliceHashTable<T>> { + public: + struct Entry { + grpc_slice key; + T value; + bool is_set; + }; + + // Function for comparing values. + // TODO(roth): Eliminate this and the Cmp() method from this API once + // grpc_channel_args is redesigned to require that keys are unique. + typedef int (*ValueCmp)(const T&, const T&); + + /// Creates a new hash table containing \a entries, which is an array + /// of length \a num_entries. Takes ownership of all keys and values in \a + /// entries. If not null, \a value_cmp will be used to compare values in + /// the context of \a Cmp(). If null, raw pointer (\a GPR_ICMP) comparison + /// will be used. + static RefCountedPtr<SliceHashTable> Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp); + + /// Returns the value from the table associated with \a key. + /// Returns null if \a key is not found. + const T* Get(const grpc_slice& key) const; + + /// Compares \a a vs. \a b. + /// A table is considered "smaller" (resp. "greater") if: + /// - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1), + /// - else, it contains fewer (resp. more) entries, + /// - else, if strcmp(a_key, b_key) < 1 (resp. > 1), + /// - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). + static int Cmp(const SliceHashTable& a, const SliceHashTable& b); + + private: + // So New() can call our private ctor. + template <typename T2, typename... Args> + friend T2* New(Args&&... args); + + SliceHashTable(size_t num_entries, Entry* entries, ValueCmp value_cmp); + virtual ~SliceHashTable(); + + void Add(grpc_slice key, T& value); + + // Default value comparison function, if none specified by caller. + static int DefaultValueCmp(const T& a, const T& b) { return GPR_ICMP(a, b); } + + const ValueCmp value_cmp_; + const size_t size_; + size_t max_num_probes_; + Entry* entries_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template <typename T> +RefCountedPtr<SliceHashTable<T>> SliceHashTable<T>::Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp) { + return MakeRefCounted<SliceHashTable<T>>(num_entries, entries, value_cmp); +} + +template <typename T> +SliceHashTable<T>::SliceHashTable(size_t num_entries, Entry* entries, + ValueCmp value_cmp) + : value_cmp_(value_cmp), + // Keep load factor low to improve performance of lookups. + size_(num_entries * 2), + max_num_probes_(0) { + entries_ = static_cast<Entry*>(gpr_zalloc(sizeof(Entry) * size_)); + for (size_t i = 0; i < num_entries; ++i) { + Entry* entry = &entries[i]; + Add(entry->key, entry->value); + } +} + +template <typename T> +SliceHashTable<T>::~SliceHashTable() { + for (size_t i = 0; i < size_; ++i) { + Entry& entry = entries_[i]; + if (entry.is_set) { + grpc_slice_unref_internal(entry.key); + entry.value.~T(); + } + } + gpr_free(entries_); +} + +template <typename T> +void SliceHashTable<T>::Add(grpc_slice key, T& value) { + const size_t hash = grpc_slice_hash(key); + for (size_t offset = 0; offset < size_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) { + entries_[idx].is_set = true; + entries_[idx].key = key; + entries_[idx].value = std::move(value); + // Keep track of the maximum number of probes needed, since this + // provides an upper bound for lookups. + if (offset > max_num_probes_) max_num_probes_ = offset; + return; + } + } + GPR_ASSERT(false); // Table should never be full. +} + +template <typename T> +const T* SliceHashTable<T>::Get(const grpc_slice& key) const { + const size_t hash = grpc_slice_hash(key); + // We cap the number of probes at the max number recorded when + // populating the table. + for (size_t offset = 0; offset <= max_num_probes_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) break; + if (grpc_slice_eq(entries_[idx].key, key)) { + return &entries_[idx].value; + } + } + return nullptr; // Not found. +} + +template <typename T> +int SliceHashTable<T>::Cmp(const SliceHashTable& a, const SliceHashTable& b) { + ValueCmp value_cmp_a = + a.value_cmp_ != nullptr ? a.value_cmp_ : DefaultValueCmp; + ValueCmp value_cmp_b = + b.value_cmp_ != nullptr ? b.value_cmp_ : DefaultValueCmp; + // Compare value_fns + const int value_fns_cmp = GPR_ICMP((void*)value_cmp_a, (void*)value_cmp_b); + if (value_fns_cmp != 0) return value_fns_cmp; + // Compare sizes + if (a.size_ < b.size_) return -1; + if (a.size_ > b.size_) return 1; + // Compare rows. + for (size_t i = 0; i < a.size_; ++i) { + if (!a.entries_[i].is_set) { + if (b.entries_[i].is_set) { + return -1; // a empty but b non-empty + } + continue; // both empty, no need to check key or value + } else if (!b.entries_[i].is_set) { + return 1; // a non-empty but b empty + } + // neither entry is empty + const int key_cmp = grpc_slice_cmp(a.entries_[i].key, b.entries_[i].key); + if (key_cmp != 0) return key_cmp; + const int value_cmp = value_cmp_a(a.entries_[i].value, b.entries_[i].value); + if (value_cmp != 0) return value_cmp; + } + return 0; +} -typedef struct grpc_slice_hash_table grpc_slice_hash_table; - -typedef struct grpc_slice_hash_table_entry { - grpc_slice key; - void* value; /* Must not be NULL. */ -} grpc_slice_hash_table_entry; - -/** Creates a new hash table of containing \a entries, which is an array - of length \a num_entries. Takes ownership of all keys and values in \a - entries. Values will be cleaned up via \a destroy_value(). If not NULL, \a - value_cmp will be used to compare values in the context of \a - grpc_slice_hash_table_cmp. If NULL, raw pointer (\a GPR_ICMP) comparison - will be used. */ -grpc_slice_hash_table* grpc_slice_hash_table_create( - size_t num_entries, grpc_slice_hash_table_entry* entries, - void (*destroy_value)(void* value), int (*value_cmp)(void* a, void* b)); - -grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table); -void grpc_slice_hash_table_unref(grpc_slice_hash_table* table); - -/** Returns the value from \a table associated with \a key. - Returns NULL if \a key is not found. */ -void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, - const grpc_slice key); - -/** Compares \a a vs. \a b. - * A table is considered "smaller" (resp. "greater") if: - * - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1), - * - else, it contains fewer (resp. more) entries, - * - else, if strcmp(a_key, b_key) < 1 (resp. > 1), - * - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). */ -int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, - const grpc_slice_hash_table* b); +} // namespace grpc_core #endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */ diff --git a/src/core/lib/slice/slice_intern.cc b/src/core/lib/slice/slice_intern.cc index 2d633c4903..e53c040e1a 100644 --- a/src/core/lib/slice/slice_intern.cc +++ b/src/core/lib/slice/slice_intern.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/slice/slice_internal.h" #include <inttypes.h> diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h index 4e9ab80261..065c25c90c 100644 --- a/src/core/lib/slice/slice_internal.h +++ b/src/core/lib/slice/slice_internal.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H #define GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/slice_buffer.h> diff --git a/src/core/lib/slice/slice_string_helpers.cc b/src/core/lib/slice/slice_string_helpers.cc index f91ece3b98..6af9c33eb5 100644 --- a/src/core/lib/slice/slice_string_helpers.cc +++ b/src/core/lib/slice/slice_string_helpers.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/slice/slice_string_helpers.h" #include <string.h> diff --git a/src/core/lib/slice/slice_string_helpers.h b/src/core/lib/slice/slice_string_helpers.h index 429f9ff4b5..976f72411a 100644 --- a/src/core/lib/slice/slice_string_helpers.h +++ b/src/core/lib/slice/slice_string_helpers.h @@ -19,12 +19,13 @@ #ifndef GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H #define GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <stddef.h> #include <grpc/slice.h> #include <grpc/slice_buffer.h> -#include <grpc/support/port_platform.h> #include "src/core/lib/gpr/string.h" diff --git a/src/core/lib/slice/slice_traits.h b/src/core/lib/slice/slice_traits.h index 4b898bdcd4..ee01916525 100644 --- a/src/core/lib/slice/slice_traits.h +++ b/src/core/lib/slice/slice_traits.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H #define GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <stdbool.h> diff --git a/src/core/lib/slice/slice_weak_hash_table.h b/src/core/lib/slice/slice_weak_hash_table.h new file mode 100644 index 0000000000..9d0ddfc2d2 --- /dev/null +++ b/src/core/lib/slice/slice_weak_hash_table.h @@ -0,0 +1,105 @@ +/* + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H +#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Weak hash table implementation. +/// +/// This entries in this table are weak: an entry may be removed at any time due +/// to a number of reasons: memory pressure, hash collisions, etc. +/// +/// The keys are \a grpc_slice objects. The values are of arbitrary type. +/// +/// This class is thread unsafe. It's the caller's responsibility to ensure +/// proper locking when accessing its methods. + +namespace grpc_core { + +template <typename T, size_t Size> +class SliceWeakHashTable : public RefCounted<SliceWeakHashTable<T, Size>> { + public: + /// Creates a new table of at most \a size entries. + static RefCountedPtr<SliceWeakHashTable> Create() { + return MakeRefCounted<SliceWeakHashTable<T, Size>>(); + } + + /// Add a mapping from \a key to \a value, taking ownership of \a key. This + /// operation will always succeed. It may discard older entries. + void Add(grpc_slice key, T value) { + const size_t idx = grpc_slice_hash(key) % Size; + entries_[idx].Set(key, std::move(value)); + return; + } + + /// Returns the value from the table associated with / \a key or null if not + /// found. + const T* Get(const grpc_slice key) const { + const size_t idx = grpc_slice_hash(key) % Size; + const auto& entry = entries_[idx]; + return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr; + } + + private: + // So New() can call our private ctor. + template <typename T2, typename... Args> + friend T2* New(Args&&... args); + + SliceWeakHashTable() = default; + ~SliceWeakHashTable() = default; + + /// The type of the table "rows". + class Entry { + public: + Entry() = default; + ~Entry() { + if (is_set_) grpc_slice_unref_internal(key_); + } + grpc_slice key() const { return key_; } + + /// Return the entry's value, or null if unset. + const T* value() const { + if (!is_set_) return nullptr; + return &value_; + } + + /// Set the \a key and \a value (which is moved) for the entry. + void Set(grpc_slice key, T&& value) { + if (is_set_) grpc_slice_unref_internal(key_); + key_ = key; + value_ = std::move(value); + is_set_ = true; + } + + private: + grpc_slice key_; + T value_; + bool is_set_ = false; + }; + + Entry entries_[Size]; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */ diff --git a/src/core/lib/surface/api_trace.cc b/src/core/lib/surface/api_trace.cc index 7ab836a9ba..bab5a7910c 100644 --- a/src/core/lib/surface/api_trace.cc +++ b/src/core/lib/surface/api_trace.cc @@ -16,7 +16,9 @@ * */ -#include "src/core/lib/surface/api_trace.h" +#include <grpc/support/port_platform.h> + #include "src/core/lib/debug/trace.h" +#include "src/core/lib/surface/api_trace.h" grpc_core::TraceFlag grpc_api_trace(false, "api"); diff --git a/src/core/lib/surface/api_trace.h b/src/core/lib/surface/api_trace.h index a4e11ce154..72ed830554 100644 --- a/src/core/lib/surface/api_trace.h +++ b/src/core/lib/surface/api_trace.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_API_TRACE_H #define GRPC_CORE_LIB_SURFACE_API_TRACE_H +#include <grpc/support/port_platform.h> + #include <grpc/support/log.h> #include "src/core/lib/debug/trace.h" diff --git a/src/core/lib/surface/byte_buffer.cc b/src/core/lib/surface/byte_buffer.cc index 01cbf7354a..fce87dc611 100644 --- a/src/core/lib/surface/byte_buffer.cc +++ b/src/core/lib/surface/byte_buffer.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/byte_buffer.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> diff --git a/src/core/lib/surface/byte_buffer_reader.cc b/src/core/lib/surface/byte_buffer_reader.cc index f7ea5161c7..a10f1a3933 100644 --- a/src/core/lib/surface/byte_buffer_reader.cc +++ b/src/core/lib/surface/byte_buffer_reader.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/byte_buffer_reader.h> #include <string.h> diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 2d5077525c..c683cc02de 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <assert.h> #include <limits.h> #include <stdio.h> @@ -35,6 +37,7 @@ #include "src/core/lib/gpr/arena.h" #include "src/core/lib/gpr/string.h" #include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/manual_constructor.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/profiling/timers.h" #include "src/core/lib/slice/slice_internal.h" @@ -48,6 +51,7 @@ #include "src/core/lib/transport/error_utils.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" #include "src/core/lib/transport/transport.h" /** The maximum number of concurrent batches possible. @@ -218,9 +222,9 @@ struct grpc_call { int send_extra_metadata_count; grpc_millis send_deadline; - grpc_slice_buffer_stream sending_stream; + grpc_core::ManualConstructor<grpc_core::SliceBufferByteStream> sending_stream; - grpc_byte_stream* receiving_stream; + grpc_core::OrphanablePtr<grpc_core::ByteStream> receiving_stream; grpc_byte_buffer** receiving_buffer; grpc_slice receiving_slice; grpc_closure receiving_slice_ready; @@ -376,7 +380,7 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, bool immediately_cancel = false; if (args->parent != nullptr) { - child_call* cc = call->child = + call->child = static_cast<child_call*>(gpr_arena_alloc(arena, sizeof(child_call))); call->child->parent = args->parent; @@ -384,10 +388,6 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, GPR_ASSERT(call->is_client); GPR_ASSERT(!args->parent->is_client); - parent_call* pc = get_or_create_parent_call(args->parent); - - gpr_mu_lock(&pc->child_list_mu); - if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) { send_deadline = GPR_MIN(send_deadline, args->parent->send_deadline); } @@ -415,18 +415,6 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, immediately_cancel = true; } } - - if (pc->first_child == nullptr) { - pc->first_child = call; - cc->sibling_next = cc->sibling_prev = call; - } else { - cc->sibling_next = pc->first_child; - cc->sibling_prev = pc->first_child->child->sibling_prev; - cc->sibling_next->child->sibling_prev = - cc->sibling_prev->child->sibling_next = call; - } - - gpr_mu_unlock(&pc->child_list_mu); } call->send_deadline = send_deadline; @@ -443,6 +431,22 @@ grpc_error* grpc_call_create(const grpc_call_create_args* args, &call->call_combiner}; add_init_error(&error, grpc_call_stack_init(channel_stack, 1, destroy_call, call, &call_args)); + // Publish this call to parent only after the call stack has been initialized. + if (args->parent != nullptr) { + child_call* cc = call->child; + parent_call* pc = get_or_create_parent_call(args->parent); + gpr_mu_lock(&pc->child_list_mu); + if (pc->first_child == nullptr) { + pc->first_child = call; + cc->sibling_next = cc->sibling_prev = call; + } else { + cc->sibling_next = pc->first_child; + cc->sibling_prev = pc->first_child->child->sibling_prev; + cc->sibling_next->child->sibling_prev = + cc->sibling_prev->child->sibling_next = call; + } + gpr_mu_unlock(&pc->child_list_mu); + } if (error != GRPC_ERROR_NONE) { cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); } @@ -519,9 +523,7 @@ static void destroy_call(void* call, grpc_error* error) { grpc_metadata_batch_destroy( &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); } - if (c->receiving_stream != nullptr) { - grpc_byte_stream_destroy(c->receiving_stream); - } + c->receiving_stream.reset(); parent_call* pc = get_parent_call(c); if (pc != nullptr) { gpr_mu_destroy(&pc->child_list_mu); @@ -974,32 +976,6 @@ static int prepare_application_metadata(grpc_call* call, int count, return 1; } -/* we offset status by a small amount when storing it into transport metadata - as metadata cannot store a 0 value (which is used as OK for grpc_status_codes - */ -#define STATUS_OFFSET 1 -static void destroy_status(void* ignored) {} - -static uint32_t decode_status(grpc_mdelem md) { - uint32_t status; - void* user_data; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2; - user_data = grpc_mdelem_get_user_data(md, destroy_status); - if (user_data != nullptr) { - status = (static_cast<uint32_t>((intptr_t)user_data)) - STATUS_OFFSET; - } else { - if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { - status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ - } - grpc_mdelem_set_user_data( - md, destroy_status, - (void*)static_cast<intptr_t>(status + STATUS_OFFSET)); - } - return status; -} - static grpc_message_compression_algorithm decode_message_compression( grpc_mdelem md) { grpc_message_compression_algorithm algorithm = @@ -1091,7 +1067,8 @@ static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) { static void recv_trailing_filter(void* args, grpc_metadata_batch* b) { grpc_call* call = static_cast<grpc_call*>(args); if (b->idx.named.grpc_status != nullptr) { - uint32_t status_code = decode_status(b->idx.named.grpc_status->md); + grpc_status_code status_code = + grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md); grpc_error* error = status_code == GRPC_STATUS_OK ? GRPC_ERROR_NONE @@ -1303,25 +1280,21 @@ static void continue_receiving_slices(batch_control* bctl) { grpc_error* error; grpc_call* call = bctl->call; for (;;) { - size_t remaining = call->receiving_stream->length - + size_t remaining = call->receiving_stream->length() - (*call->receiving_buffer)->data.raw.slice_buffer.length; if (remaining == 0) { call->receiving_message = 0; - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); finish_batch_step(bctl); return; } - if (grpc_byte_stream_next(call->receiving_stream, remaining, - &call->receiving_slice_ready)) { - error = - grpc_byte_stream_pull(call->receiving_stream, &call->receiving_slice); + if (call->receiving_stream->Next(remaining, &call->receiving_slice_ready)) { + error = call->receiving_stream->Pull(&call->receiving_slice); if (error == GRPC_ERROR_NONE) { grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, call->receiving_slice); } else { - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); grpc_byte_buffer_destroy(*call->receiving_buffer); *call->receiving_buffer = nullptr; call->receiving_message = 0; @@ -1337,19 +1310,17 @@ static void continue_receiving_slices(batch_control* bctl) { static void receiving_slice_ready(void* bctlp, grpc_error* error) { batch_control* bctl = static_cast<batch_control*>(bctlp); grpc_call* call = bctl->call; - grpc_byte_stream* bs = call->receiving_stream; bool release_error = false; if (error == GRPC_ERROR_NONE) { grpc_slice slice; - error = grpc_byte_stream_pull(bs, &slice); + error = call->receiving_stream->Pull(&slice); if (error == GRPC_ERROR_NONE) { grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, slice); continue_receiving_slices(bctl); } else { - /* Error returned by grpc_byte_stream_pull needs to be released manually - */ + /* Error returned by ByteStream::Pull() needs to be released manually */ release_error = true; } } @@ -1358,8 +1329,7 @@ static void receiving_slice_ready(void* bctlp, grpc_error* error) { if (grpc_trace_operation_failures.enabled()) { GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error)); } - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; + call->receiving_stream.reset(); grpc_byte_buffer_destroy(*call->receiving_buffer); *call->receiving_buffer = nullptr; call->receiving_message = 0; @@ -1377,8 +1347,8 @@ static void process_data_after_md(batch_control* bctl) { call->receiving_message = 0; finish_batch_step(bctl); } else { - call->test_only_last_message_flags = call->receiving_stream->flags; - if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && + call->test_only_last_message_flags = call->receiving_stream->flags(); + if ((call->receiving_stream->flags() & GRPC_WRITE_INTERNAL_COMPRESS) && (call->incoming_message_compression_algorithm > GRPC_MESSAGE_COMPRESS_NONE)) { grpc_compression_algorithm algo; @@ -1401,10 +1371,7 @@ static void receiving_stream_ready(void* bctlp, grpc_error* error) { batch_control* bctl = static_cast<batch_control*>(bctlp); grpc_call* call = bctl->call; if (error != GRPC_ERROR_NONE) { - if (call->receiving_stream != nullptr) { - grpc_byte_stream_destroy(call->receiving_stream); - call->receiving_stream = nullptr; - } + call->receiving_stream.reset(); add_batch_error(bctl, GRPC_ERROR_REF(error), true); cancel_with_error(call, STATUS_FROM_SURFACE, GRPC_ERROR_REF(error)); } @@ -1698,21 +1665,20 @@ static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; goto done_with_error; } - stream_op->send_message = true; - call->sending_message = true; - grpc_slice_buffer_stream_init( - &call->sending_stream, - &op->data.send_message.send_message->data.raw.slice_buffer, - op->flags); + uint32_t flags = op->flags; /* If the outgoing buffer is already compressed, mark it as so in the flags. These will be picked up by the compression filter and further (wasteful) attempts at compression skipped. */ if (op->data.send_message.send_message->data.raw.compression > GRPC_COMPRESS_NONE) { - call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; + flags |= GRPC_WRITE_INTERNAL_COMPRESS; } - stream_op_payload->send_message.send_message = - &call->sending_stream.base; + stream_op->send_message = true; + call->sending_message = true; + call->sending_stream.Init( + &op->data.send_message.send_message->data.raw.slice_buffer, flags); + stream_op_payload->send_message.send_message.reset( + call->sending_stream.get()); break; } case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { @@ -1931,7 +1897,7 @@ done_with_error: } if (stream_op->send_message) { call->sending_message = false; - grpc_byte_stream_destroy(&call->sending_stream.base); + call->sending_stream->Orphan(); } if (stream_op->send_trailing_metadata) { call->sent_final_op = false; diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h index 189329ccc4..793cce4efa 100644 --- a/src/core/lib/surface/call.h +++ b/src/core/lib/surface/call.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_CALL_H #define GRPC_CORE_LIB_SURFACE_CALL_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/context.h" #include "src/core/lib/surface/api_trace.h" diff --git a/src/core/lib/surface/call_details.cc b/src/core/lib/surface/call_details.cc index cd0b14586a..7f20b1dae7 100644 --- a/src/core/lib/surface/call_details.cc +++ b/src/core/lib/surface/call_details.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/surface/call_log_batch.cc b/src/core/lib/surface/call_log_batch.cc index d56ea2a932..f0c82c0357 100644 --- a/src/core/lib/surface/call_log_batch.cc +++ b/src/core/lib/surface/call_log_batch.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/call.h" #include <inttypes.h> diff --git a/src/core/lib/surface/call_test_only.h b/src/core/lib/surface/call_test_only.h index 9eb32f03fb..dbd1a866ca 100644 --- a/src/core/lib/surface/call_test_only.h +++ b/src/core/lib/surface/call_test_only.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H #define GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> /** Return the message compression algorithm from \a call. diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index d536027c08..03353d6beb 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/channel.h" #include <inttypes.h> diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h index 26d8fceb2f..288313951e 100644 --- a/src/core/lib/surface/channel.h +++ b/src/core/lib/surface/channel.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_H #define GRPC_CORE_LIB_SURFACE_CHANNEL_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/surface/channel_stack_type.h" diff --git a/src/core/lib/surface/channel_init.cc b/src/core/lib/surface/channel_init.cc index b1e1dceed3..62eb1c3f9d 100644 --- a/src/core/lib/surface/channel_init.cc +++ b/src/core/lib/surface/channel_init.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/channel_init.h" #include <grpc/support/alloc.h> diff --git a/src/core/lib/surface/channel_init.h b/src/core/lib/surface/channel_init.h index d702f0f325..f01852473b 100644 --- a/src/core/lib/surface/channel_init.h +++ b/src/core/lib/surface/channel_init.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H #define GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/surface/channel_stack_type.h" #include "src/core/lib/transport/transport.h" diff --git a/src/core/lib/surface/channel_ping.cc b/src/core/lib/surface/channel_ping.cc index 513519d991..bae945942f 100644 --- a/src/core/lib/surface/channel_ping.cc +++ b/src/core/lib/surface/channel_ping.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/channel.h" #include <string.h> diff --git a/src/core/lib/surface/channel_stack_type.cc b/src/core/lib/surface/channel_stack_type.cc index 366c452942..fcf96ddc9f 100644 --- a/src/core/lib/surface/channel_stack_type.cc +++ b/src/core/lib/surface/channel_stack_type.cc @@ -16,10 +16,11 @@ * */ -#include "src/core/lib/surface/channel_stack_type.h" -#include <grpc/support/log.h> #include <grpc/support/port_platform.h> +#include <grpc/support/log.h> +#include "src/core/lib/surface/channel_stack_type.h" + bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) { switch (type) { case GRPC_CLIENT_CHANNEL: diff --git a/src/core/lib/surface/channel_stack_type.h b/src/core/lib/surface/channel_stack_type.h index 52f85a6406..8a3c08e1cc 100644 --- a/src/core/lib/surface/channel_stack_type.h +++ b/src/core/lib/surface/channel_stack_type.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H #define GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> typedef enum { diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h index aea47afaf5..c9dc2d93c1 100644 --- a/src/core/lib/surface/completion_queue.h +++ b/src/core/lib/surface/completion_queue.h @@ -21,6 +21,8 @@ /* Internal API for completion queues */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/pollset.h" diff --git a/src/core/lib/surface/completion_queue_factory.cc b/src/core/lib/surface/completion_queue_factory.cc index d0bb065c8f..51c1183c5f 100644 --- a/src/core/lib/surface/completion_queue_factory.cc +++ b/src/core/lib/surface/completion_queue_factory.cc @@ -16,8 +16,10 @@ * */ -#include "src/core/lib/surface/completion_queue_factory.h" +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/completion_queue_factory.h" #include <grpc/support/log.h> diff --git a/src/core/lib/surface/completion_queue_factory.h b/src/core/lib/surface/completion_queue_factory.h index 89be8f8216..d2b30a9ce1 100644 --- a/src/core/lib/surface/completion_queue_factory.h +++ b/src/core/lib/surface/completion_queue_factory.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H #define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/surface/completion_queue.h" diff --git a/src/core/lib/surface/event_string.cc b/src/core/lib/surface/event_string.cc index 7f40bb2405..d639baec45 100644 --- a/src/core/lib/surface/event_string.cc +++ b/src/core/lib/surface/event_string.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/event_string.h" #include <stdio.h> diff --git a/src/core/lib/surface/event_string.h b/src/core/lib/surface/event_string.h index cbf96da6c5..e6095705e9 100644 --- a/src/core/lib/surface/event_string.h +++ b/src/core/lib/surface/event_string.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_EVENT_STRING_H #define GRPC_CORE_LIB_SURFACE_EVENT_STRING_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> /* Returns a string describing an event. Must be later freed with gpr_free() */ diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc index 7bc24a5049..ac9f9e6066 100644 --- a/src/core/lib/surface/init.cc +++ b/src/core/lib/surface/init.cc @@ -32,7 +32,7 @@ #include "src/core/lib/debug/stats.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/gpr/fork.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/http/parser.h" #include "src/core/lib/iomgr/call_combiner.h" #include "src/core/lib/iomgr/combiner.h" @@ -123,7 +123,7 @@ void grpc_init(void) { gpr_mu_lock(&g_init_mu); if (++g_initializations == 1) { gpr_time_init(); - gpr_thd_init(); + grpc_core::Thread::Init(); grpc_stats_init(); grpc_slice_intern_init(); grpc_mdctx_global_init(); diff --git a/src/core/lib/surface/init_unsecure.cc b/src/core/lib/surface/init_unsecure.cc index b852cab985..2b3bc64382 100644 --- a/src/core/lib/surface/init_unsecure.cc +++ b/src/core/lib/surface/init_unsecure.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/init.h" void grpc_security_pre_init(void) {} diff --git a/src/core/lib/surface/lame_client.cc b/src/core/lib/surface/lame_client.cc index a1f1cf1107..5a84428b0e 100644 --- a/src/core/lib/surface/lame_client.cc +++ b/src/core/lib/surface/lame_client.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <string.h> @@ -50,14 +52,14 @@ struct ChannelData { }; static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); bool expected = false; if (!calld->filled_metadata.compare_exchange_strong( expected, true, grpc_core::memory_order_relaxed, grpc_core::memory_order_relaxed)) { return; } - ChannelData* chand = reinterpret_cast<ChannelData*>(elem->channel_data); + ChannelData* chand = static_cast<ChannelData*>(elem->channel_data); char tmp[GPR_LTOA_MIN_BUFSIZE]; gpr_ltoa(chand->error_code, tmp); calld->status.md = grpc_mdelem_from_slices( @@ -76,7 +78,7 @@ static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) { static void lame_start_transport_stream_op_batch( grpc_call_element* elem, grpc_transport_stream_op_batch* op) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); if (op->recv_initial_metadata) { fill_metadata(elem, op->payload->recv_initial_metadata.recv_initial_metadata); @@ -117,7 +119,7 @@ static void lame_start_transport_op(grpc_channel_element* elem, static grpc_error* init_call_elem(grpc_call_element* elem, const grpc_call_element_args* args) { - CallData* calld = reinterpret_cast<CallData*>(elem->call_data); + CallData* calld = static_cast<CallData*>(elem->call_data); calld->call_combiner = args->call_combiner; return GRPC_ERROR_NONE; } @@ -170,7 +172,7 @@ grpc_channel* grpc_lame_client_channel_create(const char* target, "error_message=%s)", 3, (target, (int)error_code, error_message)); GPR_ASSERT(elem->filter == &grpc_lame_filter); - auto chand = reinterpret_cast<grpc_core::ChannelData*>(elem->channel_data); + auto chand = static_cast<grpc_core::ChannelData*>(elem->channel_data); chand->error_code = error_code; chand->error_message = error_message; diff --git a/src/core/lib/surface/lame_client.h b/src/core/lib/surface/lame_client.h index 3ce353f101..aefa67c24a 100644 --- a/src/core/lib/surface/lame_client.h +++ b/src/core/lib/surface/lame_client.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H #define GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/channel/channel_stack.h" extern const grpc_channel_filter grpc_lame_filter; diff --git a/src/core/lib/surface/metadata_array.cc b/src/core/lib/surface/metadata_array.cc index 0afb8b4b87..f794a2bb95 100644 --- a/src/core/lib/surface/metadata_array.cc +++ b/src/core/lib/surface/metadata_array.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/support/alloc.h> diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc index d71a23a5d7..f7505c888e 100644 --- a/src/core/lib/surface/server.cc +++ b/src/core/lib/surface/server.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/surface/server.h" #include <limits.h> diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h index 63b6dff16b..c617cc223e 100644 --- a/src/core/lib/surface/server.h +++ b/src/core/lib/surface/server.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_SERVER_H #define GRPC_CORE_LIB_SURFACE_SERVER_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/debug/trace.h" diff --git a/src/core/lib/surface/validate_metadata.cc b/src/core/lib/surface/validate_metadata.cc index fc94ea7dbe..2dd18f3dd3 100644 --- a/src/core/lib/surface/validate_metadata.cc +++ b/src/core/lib/surface/validate_metadata.cc @@ -16,12 +16,13 @@ * */ +#include <grpc/support/port_platform.h> + #include <stdlib.h> #include <string.h> #include <grpc/grpc.h> #include <grpc/support/alloc.h> -#include <grpc/support/port_platform.h> #include "src/core/lib/iomgr/error.h" #include "src/core/lib/slice/slice_internal.h" diff --git a/src/core/lib/surface/validate_metadata.h b/src/core/lib/surface/validate_metadata.h index ff074b00b2..e87fb7beed 100644 --- a/src/core/lib/surface/validate_metadata.h +++ b/src/core/lib/surface/validate_metadata.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H #define GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include "src/core/lib/iomgr/error.h" diff --git a/src/core/lib/surface/version.cc b/src/core/lib/surface/version.cc index 1710fd5afb..be196a78bc 100644 --- a/src/core/lib/surface/version.cc +++ b/src/core/lib/surface/version.cc @@ -19,6 +19,8 @@ /* This file is autogenerated from: templates/src/core/surface/version.c.template */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> const char* grpc_version_string(void) { return "6.0.0-dev"; } diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc index 70b308230b..8130535ddd 100644 --- a/src/core/lib/transport/bdp_estimator.cc +++ b/src/core/lib/transport/bdp_estimator.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/bdp_estimator.h" #include <inttypes.h> diff --git a/src/core/lib/transport/byte_stream.cc b/src/core/lib/transport/byte_stream.cc index b96598c393..cb15a71a91 100644 --- a/src/core/lib/transport/byte_stream.cc +++ b/src/core/lib/transport/byte_stream.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/byte_stream.h" #include <stdlib.h> @@ -23,160 +25,136 @@ #include <grpc/support/log.h> +#include "src/core/lib/gprpp/memory.h" #include "src/core/lib/slice/slice_internal.h" -bool grpc_byte_stream_next(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete) { - return byte_stream->vtable->next(byte_stream, max_size_hint, on_complete); -} +namespace grpc_core { -grpc_error* grpc_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - return byte_stream->vtable->pull(byte_stream, slice); -} +// +// SliceBufferByteStream +// -void grpc_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - byte_stream->vtable->shutdown(byte_stream, error); +SliceBufferByteStream::SliceBufferByteStream(grpc_slice_buffer* slice_buffer, + uint32_t flags) + : ByteStream(static_cast<uint32_t>(slice_buffer->length), flags) { + GPR_ASSERT(slice_buffer->length <= UINT32_MAX); + grpc_slice_buffer_init(&backing_buffer_); + grpc_slice_buffer_swap(slice_buffer, &backing_buffer_); } -void grpc_byte_stream_destroy(grpc_byte_stream* byte_stream) { - byte_stream->vtable->destroy(byte_stream); -} +SliceBufferByteStream::~SliceBufferByteStream() {} -// grpc_slice_buffer_stream +void SliceBufferByteStream::Orphan() { + grpc_slice_buffer_destroy(&backing_buffer_); + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // SliceBufferByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. +} -static bool slice_buffer_stream_next(grpc_byte_stream* byte_stream, - size_t max_size_hint, - grpc_closure* on_complete) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - GPR_ASSERT(stream->cursor < stream->backing_buffer.count); +bool SliceBufferByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + GPR_ASSERT(cursor_ < backing_buffer_.count); return true; } -static grpc_error* slice_buffer_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); +grpc_error* SliceBufferByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); } - GPR_ASSERT(stream->cursor < stream->backing_buffer.count); - *slice = - grpc_slice_ref_internal(stream->backing_buffer.slices[stream->cursor]); - stream->cursor++; + GPR_ASSERT(cursor_ < backing_buffer_.count); + *slice = grpc_slice_ref_internal(backing_buffer_.slices[cursor_]); + ++cursor_; return GRPC_ERROR_NONE; } -static void slice_buffer_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = error; +void SliceBufferByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = error; } -static void slice_buffer_stream_destroy(grpc_byte_stream* byte_stream) { - grpc_slice_buffer_stream* stream = - reinterpret_cast<grpc_slice_buffer_stream*>(byte_stream); - grpc_slice_buffer_destroy(&stream->backing_buffer); - GRPC_ERROR_UNREF(stream->shutdown_error); +// +// ByteStreamCache +// + +ByteStreamCache::ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream) + : underlying_stream_(std::move(underlying_stream)), + length_(underlying_stream_->length()), + flags_(underlying_stream_->flags()) { + grpc_slice_buffer_init(&cache_buffer_); } -static const grpc_byte_stream_vtable slice_buffer_stream_vtable = { - slice_buffer_stream_next, slice_buffer_stream_pull, - slice_buffer_stream_shutdown, slice_buffer_stream_destroy}; +ByteStreamCache::~ByteStreamCache() { Destroy(); } -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, - grpc_slice_buffer* slice_buffer, - uint32_t flags) { - GPR_ASSERT(slice_buffer->length <= UINT32_MAX); - stream->base.length = static_cast<uint32_t>(slice_buffer->length); - stream->base.flags = flags; - stream->base.vtable = &slice_buffer_stream_vtable; - grpc_slice_buffer_init(&stream->backing_buffer); - grpc_slice_buffer_swap(slice_buffer, &stream->backing_buffer); - stream->cursor = 0; - stream->shutdown_error = GRPC_ERROR_NONE; +void ByteStreamCache::Destroy() { + underlying_stream_.reset(); + if (cache_buffer_.length > 0) { + grpc_slice_buffer_destroy_internal(&cache_buffer_); + } } -// grpc_caching_byte_stream +// +// ByteStreamCache::CachingByteStream +// -void grpc_byte_stream_cache_init(grpc_byte_stream_cache* cache, - grpc_byte_stream* underlying_stream) { - cache->underlying_stream = underlying_stream; - grpc_slice_buffer_init(&cache->cache_buffer); -} +ByteStreamCache::CachingByteStream::CachingByteStream(ByteStreamCache* cache) + : ByteStream(cache->length_, cache->flags_), cache_(cache) {} -void grpc_byte_stream_cache_destroy(grpc_byte_stream_cache* cache) { - grpc_byte_stream_destroy(cache->underlying_stream); - grpc_slice_buffer_destroy_internal(&cache->cache_buffer); +ByteStreamCache::CachingByteStream::~CachingByteStream() {} + +void ByteStreamCache::CachingByteStream::Orphan() { + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // CachingByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. } -static bool caching_byte_stream_next(grpc_byte_stream* byte_stream, - size_t max_size_hint, - grpc_closure* on_complete) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) return true; - if (stream->cursor < stream->cache->cache_buffer.count) return true; - return grpc_byte_stream_next(stream->cache->underlying_stream, max_size_hint, - on_complete); +bool ByteStreamCache::CachingByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + if (shutdown_error_ != GRPC_ERROR_NONE) return true; + if (cursor_ < cache_->cache_buffer_.count) return true; + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + return cache_->underlying_stream_->Next(max_size_hint, on_complete); } -static grpc_error* caching_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); +grpc_error* ByteStreamCache::CachingByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); } - if (stream->cursor < stream->cache->cache_buffer.count) { - *slice = grpc_slice_ref_internal( - stream->cache->cache_buffer.slices[stream->cursor]); - ++stream->cursor; + if (cursor_ < cache_->cache_buffer_.count) { + *slice = grpc_slice_ref_internal(cache_->cache_buffer_.slices[cursor_]); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); return GRPC_ERROR_NONE; } - grpc_error* error = - grpc_byte_stream_pull(stream->cache->underlying_stream, slice); + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + grpc_error* error = cache_->underlying_stream_->Pull(slice); if (error == GRPC_ERROR_NONE) { - ++stream->cursor; - grpc_slice_buffer_add(&stream->cache->cache_buffer, + grpc_slice_buffer_add(&cache_->cache_buffer_, grpc_slice_ref_internal(*slice)); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); + // Orphan the underlying stream if it's been drained. + if (offset_ == cache_->underlying_stream_->length()) { + cache_->underlying_stream_.reset(); + } } return error; } -static void caching_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = GRPC_ERROR_REF(error); - grpc_byte_stream_shutdown(stream->cache->underlying_stream, error); -} - -static void caching_byte_stream_destroy(grpc_byte_stream* byte_stream) { - grpc_caching_byte_stream* stream = - reinterpret_cast<grpc_caching_byte_stream*>(byte_stream); - GRPC_ERROR_UNREF(stream->shutdown_error); +void ByteStreamCache::CachingByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = GRPC_ERROR_REF(error); + if (cache_->underlying_stream_ != nullptr) { + cache_->underlying_stream_->Shutdown(error); + } } -static const grpc_byte_stream_vtable caching_byte_stream_vtable = { - caching_byte_stream_next, caching_byte_stream_pull, - caching_byte_stream_shutdown, caching_byte_stream_destroy}; - -void grpc_caching_byte_stream_init(grpc_caching_byte_stream* stream, - grpc_byte_stream_cache* cache) { - memset(stream, 0, sizeof(*stream)); - stream->base.length = cache->underlying_stream->length; - stream->base.flags = cache->underlying_stream->flags; - stream->base.vtable = &caching_byte_stream_vtable; - stream->cache = cache; - stream->shutdown_error = GRPC_ERROR_NONE; +void ByteStreamCache::CachingByteStream::Reset() { + cursor_ = 0; + offset_ = 0; } -void grpc_caching_byte_stream_reset(grpc_caching_byte_stream* stream) { - stream->cursor = 0; -} +} // namespace grpc_core diff --git a/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h index fc12e5686d..95a756a5e4 100644 --- a/src/core/lib/transport/byte_stream.h +++ b/src/core/lib/transport/byte_stream.h @@ -19,7 +19,11 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H #define GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H +#include <grpc/support/port_platform.h> + #include <grpc/slice_buffer.h> +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" #include "src/core/lib/iomgr/exec_ctx.h" /** Internal bit flag for grpc_begin_message's \a flags signaling the use of @@ -28,71 +32,82 @@ /** Mask of all valid internal flags. */ #define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS) -typedef struct grpc_byte_stream grpc_byte_stream; - -typedef struct { - bool (*next)(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete); - grpc_error* (*pull)(grpc_byte_stream* byte_stream, grpc_slice* slice); - void (*shutdown)(grpc_byte_stream* byte_stream, grpc_error* error); - void (*destroy)(grpc_byte_stream* byte_stream); -} grpc_byte_stream_vtable; - -struct grpc_byte_stream { - uint32_t length; - uint32_t flags; - const grpc_byte_stream_vtable* vtable; +namespace grpc_core { + +class ByteStream : public Orphanable { + public: + virtual ~ByteStream() {} + + // Returns true if the bytes are available immediately (in which case + // on_complete will not be called), or false if the bytes will be available + // asynchronously (in which case on_complete will be called when they + // are available). + // + // max_size_hint can be set as a hint as to the maximum number + // of bytes that would be acceptable to read. + virtual bool Next(size_t max_size_hint, + grpc_closure* on_complete) GRPC_ABSTRACT; + + // Returns the next slice in the byte stream when it is available, as + // indicated by Next(). + // + // Once a slice is returned into *slice, it is owned by the caller. + virtual grpc_error* Pull(grpc_slice* slice) GRPC_ABSTRACT; + + // Shuts down the byte stream. + // + // If there is a pending call to on_complete from Next(), it will be + // invoked with the error passed to Shutdown(). + // + // The next call to Pull() (if any) will return the error passed to + // Shutdown(). + virtual void Shutdown(grpc_error* error) GRPC_ABSTRACT; + + uint32_t length() const { return length_; } + uint32_t flags() const { return flags_; } + + void set_flags(uint32_t flags) { flags_ = flags; } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + ByteStream(uint32_t length, uint32_t flags) + : length_(length), flags_(flags) {} + + private: + const uint32_t length_; + uint32_t flags_; }; -// Returns true if the bytes are available immediately (in which case -// on_complete will not be called), false if the bytes will be available -// asynchronously. // -// max_size_hint can be set as a hint as to the maximum number -// of bytes that would be acceptable to read. -bool grpc_byte_stream_next(grpc_byte_stream* byte_stream, size_t max_size_hint, - grpc_closure* on_complete); - -// Returns the next slice in the byte stream when it is ready (indicated by -// either grpc_byte_stream_next returning true or on_complete passed to -// grpc_byte_stream_next is called). -// -// Once a slice is returned into *slice, it is owned by the caller. -grpc_error* grpc_byte_stream_pull(grpc_byte_stream* byte_stream, - grpc_slice* slice); - -// Shuts down the byte stream. +// SliceBufferByteStream // -// If there is a pending call to on_complete from grpc_byte_stream_next(), -// it will be invoked with the error passed to grpc_byte_stream_shutdown(). +// A ByteStream that wraps a slice buffer. // -// The next call to grpc_byte_stream_pull() (if any) will return the error -// passed to grpc_byte_stream_shutdown(). -void grpc_byte_stream_shutdown(grpc_byte_stream* byte_stream, - grpc_error* error); -void grpc_byte_stream_destroy(grpc_byte_stream* byte_stream); +class SliceBufferByteStream : public ByteStream { + public: + // Removes all slices in slice_buffer, leaving it empty. + SliceBufferByteStream(grpc_slice_buffer* slice_buffer, uint32_t flags); + + ~SliceBufferByteStream(); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + private: + grpc_slice_buffer backing_buffer_; + size_t cursor_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; +}; -// grpc_slice_buffer_stream // -// A grpc_byte_stream that wraps a slice buffer. The stream takes -// ownership of the slices in the buffer, and on destruction will -// reset the contents of the buffer. - -typedef struct grpc_slice_buffer_stream { - grpc_byte_stream base; - grpc_slice_buffer backing_buffer; - size_t cursor; - grpc_error* shutdown_error; -} grpc_slice_buffer_stream; - -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, - grpc_slice_buffer* slice_buffer, - uint32_t flags); - -// grpc_caching_byte_stream +// CachingByteStream // -// A grpc_byte_stream that that wraps an underlying byte stream but caches +// A ByteStream that that wraps an underlying byte stream but caches // the resulting slices in a slice buffer. If an initial attempt fails // without fully draining the underlying stream, a new caching stream // can be created from the same underlying cache, in which case it will @@ -100,32 +115,50 @@ void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream* stream, // underlying stream. // // NOTE: No synchronization is done, so it is not safe to have multiple -// grpc_caching_byte_streams simultaneously drawing from the same underlying -// grpc_byte_stream_cache at the same time. +// CachingByteStreams simultaneously drawing from the same underlying +// ByteStreamCache at the same time. +// + +class ByteStreamCache { + public: + class CachingByteStream : public ByteStream { + public: + explicit CachingByteStream(ByteStreamCache* cache); + + ~CachingByteStream(); + + void Orphan() override; -typedef struct { - grpc_byte_stream* underlying_stream; - grpc_slice_buffer cache_buffer; -} grpc_byte_stream_cache; + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; -// Takes ownership of underlying_stream. -void grpc_byte_stream_cache_init(grpc_byte_stream_cache* cache, - grpc_byte_stream* underlying_stream); + // Resets the byte stream to the start of the underlying stream. + void Reset(); -// Must not be called while still in use by a grpc_caching_byte_stream. -void grpc_byte_stream_cache_destroy(grpc_byte_stream_cache* cache); + private: + ByteStreamCache* cache_; + size_t cursor_ = 0; + size_t offset_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; + }; -typedef struct { - grpc_byte_stream base; - grpc_byte_stream_cache* cache; - size_t cursor; - grpc_error* shutdown_error; -} grpc_caching_byte_stream; + explicit ByteStreamCache(OrphanablePtr<ByteStream> underlying_stream); -void grpc_caching_byte_stream_init(grpc_caching_byte_stream* stream, - grpc_byte_stream_cache* cache); + ~ByteStreamCache(); + + // Must not be destroyed while still in use by a CachingByteStream. + void Destroy(); + + grpc_slice_buffer* cache_buffer() { return &cache_buffer_; } + + private: + OrphanablePtr<ByteStream> underlying_stream_; + uint32_t length_; + uint32_t flags_; + grpc_slice_buffer cache_buffer_; +}; -// Resets the byte stream to the start of the underlying stream. -void grpc_caching_byte_stream_reset(grpc_caching_byte_stream* stream); +} // namespace grpc_core #endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */ diff --git a/src/core/lib/transport/connectivity_state.cc b/src/core/lib/transport/connectivity_state.cc index 17f3529a0e..0122e773ca 100644 --- a/src/core/lib/transport/connectivity_state.cc +++ b/src/core/lib/transport/connectivity_state.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/connectivity_state.h" #include <string.h> diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h index c3a50f3211..421db5aa39 100644 --- a/src/core/lib/transport/connectivity_state.h +++ b/src/core/lib/transport/connectivity_state.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H #define GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/exec_ctx.h" diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc index 79d904315e..2eff8b2916 100644 --- a/src/core/lib/transport/error_utils.cc +++ b/src/core/lib/transport/error_utils.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/error_utils.h" #include <grpc/support/string_util.h> diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h index 4100f65d6d..9a46267f38 100644 --- a/src/core/lib/transport/error_utils.h +++ b/src/core/lib/transport/error_utils.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H #define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/transport/http2_errors.h" diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc index e06e0b5313..d10194a2fe 100644 --- a/src/core/lib/transport/metadata.cc +++ b/src/core/lib/transport/metadata.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/metadata.h" #include <assert.h> diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h index 82142ebed8..5e0ecbdb96 100644 --- a/src/core/lib/transport/metadata.h +++ b/src/core/lib/transport/metadata.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H #define GRPC_CORE_LIB_TRANSPORT_METADATA_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/slice.h> diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc index 9c95339ba0..49740fcd1e 100644 --- a/src/core/lib/transport/metadata_batch.cc +++ b/src/core/lib/transport/metadata_batch.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/metadata_batch.h" #include <stdbool.h> @@ -301,3 +303,27 @@ grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch, } return error; } + +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage) { + grpc_metadata_batch_init(dst); + dst->deadline = src->deadline; + size_t i = 0; + for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr; + elem = elem->next) { + grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++], + GRPC_MDELEM_REF(elem->md)); + // The only way that grpc_metadata_batch_add_tail() can fail is if + // there's a duplicate entry for a callout. However, that can't be + // the case here, because we would not have been allowed to create + // a source batch that had that kind of conflict. + GPR_ASSERT(error == GRPC_ERROR_NONE); + } +} + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst) { + *dst = *src; + grpc_metadata_batch_init(src); +} diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index 8353a426f8..3876063b52 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -19,11 +19,12 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H #define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include <grpc/grpc.h> #include <grpc/slice.h> -#include <grpc/support/port_platform.h> #include <grpc/support/time.h> #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/static_metadata.h" @@ -136,4 +137,13 @@ void grpc_metadata_batch_assert_ok(grpc_metadata_batch* comd); } while (0) #endif +/// Copies \a src to \a dst. \a storage must point to an array of +/// \a grpc_linked_mdelem structs of at least the same size as \a src. +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage); + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst); + #endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */ diff --git a/src/core/lib/transport/pid_controller.cc b/src/core/lib/transport/pid_controller.cc index b33ea63df6..dbc98f4917 100644 --- a/src/core/lib/transport/pid_controller.cc +++ b/src/core/lib/transport/pid_controller.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/pid_controller.h" #include "src/core/lib/gpr/useful.h" diff --git a/src/core/lib/transport/pid_controller.h b/src/core/lib/transport/pid_controller.h index 87e59a1a90..e26205bf20 100644 --- a/src/core/lib/transport/pid_controller.h +++ b/src/core/lib/transport/pid_controller.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H #define GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H +#include <grpc/support/port_platform.h> + #include <limits> /* \file Simple PID controller. diff --git a/src/core/lib/transport/service_config.cc b/src/core/lib/transport/service_config.cc index 75196c5f88..e1a55d98ab 100644 --- a/src/core/lib/transport/service_config.cc +++ b/src/core/lib/transport/service_config.cc @@ -14,6 +14,8 @@ // limitations under the License. // +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/service_config.h" #include <string.h> @@ -29,74 +31,30 @@ #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/slice/slice_string_helpers.h" -// The main purpose of the code here is to parse the service config in -// JSON form, which will look like this: -// -// { -// "loadBalancingPolicy": "string", // optional -// "methodConfig": [ // array of one or more method_config objects -// { -// "name": [ // array of one or more name objects -// { -// "service": "string", // required -// "method": "string", // optional -// } -// ], -// // remaining fields are optional. -// // see https://developers.google.com/protocol-buffers/docs/proto3#json -// // for format details. -// "waitForReady": bool, -// "timeout": "duration_string", -// "maxRequestMessageBytes": "int64_string", -// "maxResponseMessageBytes": "int64_string", -// } -// ] -// } - -struct grpc_service_config { - char* json_string; // Underlying storage for json_tree. - grpc_json* json_tree; -}; +namespace grpc_core { -grpc_service_config* grpc_service_config_create(const char* json_string) { - grpc_service_config* service_config = - static_cast<grpc_service_config*>(gpr_malloc(sizeof(*service_config))); - service_config->json_string = gpr_strdup(json_string); - service_config->json_tree = - grpc_json_parse_string(service_config->json_string); - if (service_config->json_tree == nullptr) { +UniquePtr<ServiceConfig> ServiceConfig::Create(const char* json) { + UniquePtr<char> json_string(gpr_strdup(json)); + grpc_json* json_tree = grpc_json_parse_string(json_string.get()); + if (json_tree == nullptr) { gpr_log(GPR_INFO, "failed to parse JSON for service config"); - gpr_free(service_config->json_string); - gpr_free(service_config); return nullptr; } - return service_config; + return MakeUnique<ServiceConfig>(std::move(json_string), json_tree); } -void grpc_service_config_destroy(grpc_service_config* service_config) { - grpc_json_destroy(service_config->json_tree); - gpr_free(service_config->json_string); - gpr_free(service_config); -} +ServiceConfig::ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree) + : json_string_(std::move(json_string)), json_tree_(json_tree) {} -void grpc_service_config_parse_global_params( - const grpc_service_config* service_config, - void (*process_json)(const grpc_json* json, void* arg), void* arg) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { - if (field->key == nullptr) return; - if (strcmp(field->key, "methodConfig") == 0) continue; - process_json(field, arg); - } -} +ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); } -const char* grpc_service_config_get_lb_policy_name( - const grpc_service_config* service_config) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr; +const char* ServiceConfig::GetLoadBalancingPolicyName() const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } const char* lb_policy_name = nullptr; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { if (field->key == nullptr) return nullptr; if (strcmp(field->key, "loadBalancingPolicy") == 0) { if (lb_policy_name != nullptr) return nullptr; // Duplicate. @@ -107,8 +65,7 @@ const char* grpc_service_config_get_lb_policy_name( return lb_policy_name; } -// Returns the number of names specified in the method config \a json. -static size_t count_names_in_method_config_json(grpc_json* json) { +size_t ServiceConfig::CountNamesInMethodConfig(grpc_json* json) { size_t num_names = 0; for (grpc_json* field = json->child; field != nullptr; field = field->next) { if (field->key != nullptr && strcmp(field->key, "name") == 0) { @@ -122,9 +79,7 @@ static size_t count_names_in_method_config_json(grpc_json* json) { return num_names; } -// Returns a path string for the JSON name object specified by \a json. -// Returns NULL on error. Caller takes ownership of result. -static char* parse_json_method_name(grpc_json* json) { +UniquePtr<char> ServiceConfig::ParseJsonMethodName(grpc_json* json) { if (json->type != GRPC_JSON_OBJECT) return nullptr; const char* service_name = nullptr; const char* method_name = nullptr; @@ -145,116 +100,7 @@ static char* parse_json_method_name(grpc_json* json) { char* path; gpr_asprintf(&path, "/%s/%s", service_name, method_name == nullptr ? "*" : method_name); - return path; -} - -// Parses the method config from \a json. Adds an entry to \a entries for -// each name found, incrementing \a idx for each entry added. -// Returns false on error. -static bool parse_json_method_config( - grpc_json* json, void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value), - grpc_slice_hash_table_entry* entries, size_t* idx) { - // Construct value. - void* method_config = create_value(json); - if (method_config == nullptr) return false; - // Construct list of paths. - bool success = false; - gpr_strvec paths; - gpr_strvec_init(&paths); - for (grpc_json* child = json->child; child != nullptr; child = child->next) { - if (child->key == nullptr) continue; - if (strcmp(child->key, "name") == 0) { - if (child->type != GRPC_JSON_ARRAY) goto done; - for (grpc_json* name = child->child; name != nullptr; name = name->next) { - char* path = parse_json_method_name(name); - if (path == nullptr) goto done; - gpr_strvec_add(&paths, path); - } - } - } - if (paths.count == 0) goto done; // No names specified. - // Add entry for each path. - for (size_t i = 0; i < paths.count; ++i) { - entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]); - entries[*idx].value = ref_value(method_config); - ++*idx; - } - success = true; -done: - unref_value(method_config); - gpr_strvec_destroy(&paths); - return success; -} - -grpc_slice_hash_table* grpc_service_config_create_method_config_table( - const grpc_service_config* service_config, - void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value)) { - const grpc_json* json = service_config->json_tree; - // Traverse parsed JSON tree. - if (json->type != GRPC_JSON_OBJECT || json->key != nullptr) return nullptr; - size_t num_entries = 0; - grpc_slice_hash_table_entry* entries = nullptr; - for (grpc_json* field = json->child; field != nullptr; field = field->next) { - if (field->key == nullptr) return nullptr; - if (strcmp(field->key, "methodConfig") == 0) { - if (entries != nullptr) return nullptr; // Duplicate. - if (field->type != GRPC_JSON_ARRAY) return nullptr; - // Find number of entries. - for (grpc_json* method = field->child; method != nullptr; - method = method->next) { - size_t count = count_names_in_method_config_json(method); - if (count <= 0) return nullptr; - num_entries += count; - } - // Populate method config table entries. - entries = static_cast<grpc_slice_hash_table_entry*>( - gpr_malloc(num_entries * sizeof(grpc_slice_hash_table_entry))); - size_t idx = 0; - for (grpc_json* method = field->child; method != nullptr; - method = method->next) { - if (!parse_json_method_config(method, create_value, ref_value, - unref_value, entries, &idx)) { - for (size_t i = 0; i < idx; ++i) { - grpc_slice_unref_internal(entries[i].key); - unref_value(entries[i].value); - } - gpr_free(entries); - return nullptr; - } - } - GPR_ASSERT(idx == num_entries); - } - } - // Instantiate method config table. - grpc_slice_hash_table* method_config_table = nullptr; - if (entries != nullptr) { - method_config_table = grpc_slice_hash_table_create(num_entries, entries, - unref_value, nullptr); - gpr_free(entries); - } - return method_config_table; + return UniquePtr<char>(path); } -void* grpc_method_config_table_get(const grpc_slice_hash_table* table, - grpc_slice path) { - void* value = grpc_slice_hash_table_get(table, path); - // If we didn't find a match for the path, try looking for a wildcard - // entry (i.e., change "/service/method" to "/service/*"). - if (value == nullptr) { - char* path_str = grpc_slice_to_c_string(path); - const char* sep = strrchr(path_str, '/') + 1; - const size_t len = static_cast<size_t>(sep - path_str); - char* buf = static_cast<char*>(gpr_malloc(len + 2)); // '*' and NUL - memcpy(buf, path_str, len); - buf[len] = '*'; - buf[len + 1] = '\0'; - grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); - gpr_free(buf); - value = grpc_slice_hash_table_get(table, wildcard_path); - grpc_slice_unref_internal(wildcard_path); - gpr_free(path_str); - } - return value; -} +} // namespace grpc_core diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h index 98554b9f0f..a65b267d46 100644 --- a/src/core/lib/transport/service_config.h +++ b/src/core/lib/transport/service_config.h @@ -17,45 +17,233 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H #define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H +#include <grpc/support/port_platform.h> + #include <grpc/impl/codegen/grpc_types.h> +#include <grpc/support/string_util.h> +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/core/lib/json/json.h" #include "src/core/lib/slice/slice_hash_table.h" -typedef struct grpc_service_config grpc_service_config; - -grpc_service_config* grpc_service_config_create(const char* json_string); -void grpc_service_config_destroy(grpc_service_config* service_config); - -/// Invokes \a process_json() for each global parameter in the service -/// config. \a arg is passed as the second argument to \a process_json(). -void grpc_service_config_parse_global_params( - const grpc_service_config* service_config, - void (*process_json)(const grpc_json* json, void* arg), void* arg); - -/// Gets the LB policy name from \a service_config. -/// Returns NULL if no LB policy name was specified. -/// Caller does NOT take ownership. -const char* grpc_service_config_get_lb_policy_name( - const grpc_service_config* service_config); - -/// Creates a method config table based on the data in \a json. -/// The table's keys are request paths. The table's value type is -/// returned by \a create_value(), based on data parsed from the JSON tree. -/// \a ref_value() and \a unref_value() are used to ref and unref values. -/// Returns NULL on error. -grpc_slice_hash_table* grpc_service_config_create_method_config_table( - const grpc_service_config* service_config, - void* (*create_value)(const grpc_json* method_config_json), - void* (*ref_value)(void* value), void (*unref_value)(void* value)); - -/// A helper function for looking up values in the table returned by -/// \a grpc_service_config_create_method_config_table(). -/// Gets the method config for the specified \a path, which should be of -/// the form "/service/method". -/// Returns NULL if the method has no config. -/// Caller does NOT own a reference to the result. -void* grpc_method_config_table_get(const grpc_slice_hash_table* table, - grpc_slice path); +// The main purpose of the code here is to parse the service config in +// JSON form, which will look like this: +// +// { +// "loadBalancingPolicy": "string", // optional +// "methodConfig": [ // array of one or more method_config objects +// { +// "name": [ // array of one or more name objects +// { +// "service": "string", // required +// "method": "string", // optional +// } +// ], +// // remaining fields are optional. +// // see +// https://developers.google.com/protocol-buffers/docs/proto3#json +// // for format details. +// "waitForReady": bool, +// "timeout": "duration_string", +// "maxRequestMessageBytes": "int64_string", +// "maxResponseMessageBytes": "int64_string", +// } +// ] +// } + +namespace grpc_core { + +class ServiceConfig { + public: + /// Creates a new service config from parsing \a json_string. + /// Returns null on parse error. + static UniquePtr<ServiceConfig> Create(const char* json); + + ~ServiceConfig(); + + /// Invokes \a process_json() for each global parameter in the service + /// config. \a arg is passed as the second argument to \a process_json(). + template <typename T> + using ProcessJson = void (*)(const grpc_json*, T*); + template <typename T> + void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const; + + /// Gets the LB policy name from \a service_config. + /// Returns NULL if no LB policy name was specified. + /// Caller does NOT take ownership. + const char* GetLoadBalancingPolicyName() const; + + /// Creates a method config table based on the data in \a json. + /// The table's keys are request paths. The table's value type is + /// returned by \a create_value(), based on data parsed from the JSON tree. + /// Returns null on error. + template <typename T> + using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json); + template <typename T> + RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable( + CreateValue<T> create_value); + + /// A helper function for looking up values in the table returned by + /// \a CreateMethodConfigTable(). + /// Gets the method config for the specified \a path, which should be of + /// the form "/service/method". + /// Returns null if the method has no config. + /// Caller does NOT own a reference to the result. + template <typename T> + static RefCountedPtr<T> MethodConfigTableLookup( + const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path); + + private: + // So New() can call our private ctor. + template <typename T, typename... Args> + friend T* New(Args&&... args); + + // Takes ownership of \a json_tree. + ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree); + + // Returns the number of names specified in the method config \a json. + static size_t CountNamesInMethodConfig(grpc_json* json); + + // Returns a path string for the JSON name object specified by \a json. + // Returns null on error. + static UniquePtr<char> ParseJsonMethodName(grpc_json* json); + + // Parses the method config from \a json. Adds an entry to \a entries for + // each name found, incrementing \a idx for each entry added. + // Returns false on error. + template <typename T> + static bool ParseJsonMethodConfig( + grpc_json* json, CreateValue<T> create_value, + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx); + + UniquePtr<char> json_string_; // Underlying storage for json_tree. + grpc_json* json_tree_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template <typename T> +void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json, + T* arg) const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return; + } + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return; + if (strcmp(field->key, "methodConfig") == 0) continue; + process_json(field, arg); + } +} + +template <typename T> +bool ServiceConfig::ParseJsonMethodConfig( + grpc_json* json, CreateValue<T> create_value, + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) { + // Construct value. + RefCountedPtr<T> method_config = create_value(json); + if (method_config == nullptr) return false; + // Construct list of paths. + InlinedVector<UniquePtr<char>, 10> paths; + for (grpc_json* child = json->child; child != nullptr; child = child->next) { + if (child->key == nullptr) continue; + if (strcmp(child->key, "name") == 0) { + if (child->type != GRPC_JSON_ARRAY) return false; + for (grpc_json* name = child->child; name != nullptr; name = name->next) { + UniquePtr<char> path = ParseJsonMethodName(name); + if (path == nullptr) return false; + paths.push_back(std::move(path)); + } + } + } + if (paths.size() == 0) return false; // No names specified. + // Add entry for each path. + for (size_t i = 0; i < paths.size(); ++i) { + entries[*idx].key = grpc_slice_from_copied_string(paths[i].get()); + entries[*idx].value = method_config; // Takes a new ref. + ++*idx; + } + // Success. + return true; +} + +template <typename T> +RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> +ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) { + // Traverse parsed JSON tree. + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } + size_t num_entries = 0; + typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr; + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return nullptr; + if (strcmp(field->key, "methodConfig") == 0) { + if (entries != nullptr) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_ARRAY) return nullptr; + // Find number of entries. + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + size_t count = CountNamesInMethodConfig(method); + if (count <= 0) return nullptr; + num_entries += count; + } + // Populate method config table entries. + entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>( + gpr_zalloc(num_entries * + sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry))); + size_t idx = 0; + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) { + for (size_t i = 0; i < idx; ++i) { + grpc_slice_unref_internal(entries[i].key); + entries[i].value.reset(); + } + gpr_free(entries); + return nullptr; + } + } + GPR_ASSERT(idx == num_entries); + } + } + // Instantiate method config table. + RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table; + if (entries != nullptr) { + method_config_table = + SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr); + gpr_free(entries); + } + return method_config_table; +} + +template <typename T> +RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup( + const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path) { + const RefCountedPtr<T>* value = table.Get(path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (value == nullptr) { + char* path_str = grpc_slice_to_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char* buf = (char*)gpr_malloc(len + 2); // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); + gpr_free(buf); + value = table.Get(wildcard_path); + grpc_slice_unref_internal(wildcard_path); + gpr_free(path_str); + } + return RefCountedPtr<T>(*value); +} + +} // namespace grpc_core #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ diff --git a/src/core/lib/transport/static_metadata.cc b/src/core/lib/transport/static_metadata.cc index 5994cbc265..6a5144f21a 100644 --- a/src/core/lib/transport/static_metadata.cc +++ b/src/core/lib/transport/static_metadata.cc @@ -24,6 +24,8 @@ * an explanation of what's going on. */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/static_metadata.h" #include "src/core/lib/slice/slice_internal.h" @@ -48,61 +50,64 @@ static uint8_t g_bytes[] = { 114, 110, 97, 108, 45, 115, 116, 114, 101, 97, 109, 45, 101, 110, 99, 111, 100, 105, 110, 103, 45, 114, 101, 113, 117, 101, 115, 116, 117, 115, 101, 114, 45, 97, 103, 101, 110, 116, 104, 111, 115, 116, 108, 98, 45, - 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 116, 105, 109, 101, 111, - 117, 116, 103, 114, 112, 99, 46, 119, 97, 105, 116, 95, 102, 111, 114, - 95, 114, 101, 97, 100, 121, 103, 114, 112, 99, 46, 116, 105, 109, 101, - 111, 117, 116, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 113, - 117, 101, 115, 116, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, 121, - 116, 101, 115, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 115, - 112, 111, 110, 115, 101, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, - 121, 116, 101, 115, 47, 103, 114, 112, 99, 46, 108, 98, 46, 118, 49, - 46, 76, 111, 97, 100, 66, 97, 108, 97, 110, 99, 101, 114, 47, 66, - 97, 108, 97, 110, 99, 101, 76, 111, 97, 100, 100, 101, 102, 108, 97, - 116, 101, 103, 122, 105, 112, 115, 116, 114, 101, 97, 109, 47, 103, 122, - 105, 112, 48, 49, 50, 105, 100, 101, 110, 116, 105, 116, 121, 116, 114, - 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, 105, 99, 97, 116, 105, - 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, 84, 50, 48, 48, 52, - 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, 115, 103, 114, 112, 99, - 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, 100, 101, 120, 46, 104, - 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, 48, 52, 52, 48, 48, - 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, 99, 104, 97, 114, 115, - 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, 102, 108, 97, 116, 101, - 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, - 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, 103, 101, 115, 97, 99, - 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, 45, 99, 111, 110, 116, - 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, 111, 114, 105, 103, 105, - 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, 117, 116, 104, 111, 114, - 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, 104, 101, 45, 99, 111, - 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, 110, 116, 45, 100, 105, - 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, 111, 110, 116, 101, 110, - 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, 99, 111, 110, 116, 101, - 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, 111, 110, 116, 101, 110, - 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, 99, 111, 110, 116, 101, - 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, 111, 107, 105, 101, 100, - 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, 101, 99, 116, 101, 120, - 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, 102, 45, 109, 97, 116, - 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, - 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, 101, 45, 109, 97, 116, - 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, 105, 102, 45, 117, 110, - 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, 105, 110, 99, 101, 108, - 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, 101, 100, 108, 98, 45, - 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, 110, 107, 108, 111, 99, - 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, 111, 114, 119, 97, 114, - 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, 116, 104, 101, 110, 116, - 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, 45, 97, 117, 116, 104, - 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, 97, 110, 103, 101, 114, - 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, 115, 104, 114, 101, - 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, 101, 114, 118, 101, 114, - 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, 115, 116, 114, 105, 99, - 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, 116, 45, 115, 101, 99, - 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, 102, 101, 114, 45, 101, - 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, 121, 118, 105, 97, 119, - 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, 105, 99, 97, 116, 101, - 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, - 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 103, 122, 105, 112, 100, - 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, 112, 105, 100, 101, 110, - 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, - 105, 112}; + 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 112, 114, 101, 118, 105, + 111, 117, 115, 45, 114, 112, 99, 45, 97, 116, 116, 101, 109, 112, 116, + 115, 103, 114, 112, 99, 45, 114, 101, 116, 114, 121, 45, 112, 117, 115, + 104, 98, 97, 99, 107, 45, 109, 115, 103, 114, 112, 99, 45, 116, 105, + 109, 101, 111, 117, 116, 49, 50, 51, 52, 103, 114, 112, 99, 46, 119, + 97, 105, 116, 95, 102, 111, 114, 95, 114, 101, 97, 100, 121, 103, 114, + 112, 99, 46, 116, 105, 109, 101, 111, 117, 116, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 113, 117, 101, 115, 116, 95, 109, 101, 115, + 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 115, 112, 111, 110, 115, 101, 95, 109, 101, + 115, 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 47, 103, 114, 112, + 99, 46, 108, 98, 46, 118, 49, 46, 76, 111, 97, 100, 66, 97, 108, + 97, 110, 99, 101, 114, 47, 66, 97, 108, 97, 110, 99, 101, 76, 111, + 97, 100, 100, 101, 102, 108, 97, 116, 101, 103, 122, 105, 112, 115, 116, + 114, 101, 97, 109, 47, 103, 122, 105, 112, 48, 105, 100, 101, 110, 116, + 105, 116, 121, 116, 114, 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, + 105, 99, 97, 116, 105, 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, + 84, 50, 48, 48, 52, 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, + 115, 103, 114, 112, 99, 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, + 100, 101, 120, 46, 104, 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, + 48, 52, 52, 48, 48, 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, + 99, 104, 97, 114, 115, 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, + 102, 108, 97, 116, 101, 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, + 103, 117, 97, 103, 101, 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, + 103, 101, 115, 97, 99, 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, + 45, 99, 111, 110, 116, 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, + 111, 114, 105, 103, 105, 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, + 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, + 104, 101, 45, 99, 111, 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, + 110, 116, 45, 100, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, + 111, 110, 116, 101, 110, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, + 99, 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, + 111, 110, 116, 101, 110, 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, + 99, 111, 110, 116, 101, 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, + 111, 107, 105, 101, 100, 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, + 101, 99, 116, 101, 120, 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, + 102, 45, 109, 97, 116, 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, + 105, 101, 100, 45, 115, 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, + 101, 45, 109, 97, 116, 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, + 105, 102, 45, 117, 110, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, + 105, 110, 99, 101, 108, 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, + 101, 100, 108, 98, 45, 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, + 110, 107, 108, 111, 99, 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, + 111, 114, 119, 97, 114, 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, + 116, 104, 101, 110, 116, 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, + 45, 97, 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, + 97, 110, 103, 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, + 101, 115, 104, 114, 101, 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, + 101, 114, 118, 101, 114, 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, + 115, 116, 114, 105, 99, 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, + 116, 45, 115, 101, 99, 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, + 102, 101, 114, 45, 101, 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, + 121, 118, 105, 97, 119, 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, + 105, 99, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, + 101, 102, 108, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, + 103, 122, 105, 112, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, + 112, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, + 116, 101, 44, 103, 122, 105, 112}; static void static_ref(void* unused) {} static void static_unref(void* unused) {} @@ -215,6 +220,10 @@ grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = { {&grpc_static_metadata_vtable, &static_sub_refcnt}, {&grpc_static_metadata_vtable, &static_sub_refcnt}, {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, }; const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { @@ -240,85 +249,89 @@ const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}, - {&grpc_static_metadata_refcounts[24], {{g_bytes + 302, 19}}}, - {&grpc_static_metadata_refcounts[25], {{g_bytes + 321, 12}}}, - {&grpc_static_metadata_refcounts[26], {{g_bytes + 333, 30}}}, - {&grpc_static_metadata_refcounts[27], {{g_bytes + 363, 31}}}, - {&grpc_static_metadata_refcounts[28], {{g_bytes + 394, 36}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}, - {&grpc_static_metadata_refcounts[31], {{g_bytes + 441, 11}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}}, - {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}}, - {&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}}, - {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}}, - {&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}}, - {&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}}, - {&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}}, - {&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}}, - {&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}}, - {&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}}, - {&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}}, - {&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}}, - {&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}}, - {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}}, - {&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}}, - {&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}}, - {&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}}, - {&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}}, - {&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}}, - {&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}}, - {&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}}, - {&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}}, - {&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}}, - {&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}}, - {&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}}, - {&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}}, - {&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}}, - {&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}}, - {&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}}, - {&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}}, - {&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}}, - {&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}}, - {&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}}, - {&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}}, - {&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}}, - {&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}}, - {&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}}, - {&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}}, - {&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}}, - {&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}}, - {&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}}, - {&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}}, - {&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}}, - {&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}}, - {&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}}, - {&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}}, - {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}}, + {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 26}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 316, 22}}}, + {&grpc_static_metadata_refcounts[24], {{g_bytes + 338, 12}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}, + {&grpc_static_metadata_refcounts[27], {{g_bytes + 352, 1}}}, + {&grpc_static_metadata_refcounts[28], {{g_bytes + 353, 1}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}, + {&grpc_static_metadata_refcounts[30], {{g_bytes + 354, 19}}}, + {&grpc_static_metadata_refcounts[31], {{g_bytes + 373, 12}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 385, 30}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 415, 31}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 446, 36}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 493, 11}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}}, + {&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}}, + {&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}}, + {&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}}, + {&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}}, + {&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}}, + {&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}}, + {&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}}, + {&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}}, + {&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}}, + {&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}}, + {&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}}, + {&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}}, + {&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}}, + {&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}}, + {&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}}, + {&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}}, + {&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}}, + {&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}}, + {&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}}, + {&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}}, + {&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}}, + {&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}}, + {&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}}, + {&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}}, + {&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}}, + {&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}}, + {&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}}, + {&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}}, + {&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}}, + {&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}}, + {&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}}, + {&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}}, + {&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}}, + {&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}}, + {&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}}, + {&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}}, + {&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}}, + {&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}}, + {&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}}, }; uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { @@ -328,50 +341,51 @@ uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4}; static const int8_t elems_r[] = { - 13, 2, 1, 0, 15, 4, 0, 21, 0, 23, -3, 0, 0, 0, 10, 19, -4, - 0, 0, 1, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, -52, 0, -55, -36, -57, -58, -58, -58, 0, 40, 39, 38, 37, 36, 35, - 34, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, - 18, 17, 16, 15, 18, 17, 16, 15, 14, 13, 12, 11, 11, 0}; + 16, 11, -1, 0, 15, 2, -78, 24, 0, 18, -5, 0, 0, 0, 17, 14, -8, 0, + 0, 27, 8, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -64, 0, -44, -43, -70, 0, 34, 33, 33, 32, 31, 30, 29, 28, 27, + 27, 26, 25, 24, 23, 22, 21, 20, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, + 11, 14, 13, 12, 11, 10, 9, 9, 8, 7, 6, 5, 0}; static uint32_t elems_phash(uint32_t i) { - i -= 46; - uint32_t x = i % 99; - uint32_t y = i / 99; + i -= 50; + uint32_t x = i % 103; + uint32_t y = i / 103; uint32_t h = x; if (y < GPR_ARRAY_SIZE(elems_r)) { - uint32_t delta = static_cast<uint32_t>(elems_r[y]); + uint32_t delta = (uint32_t)elems_r[y]; h += delta; } return h; } static const uint16_t elem_keys[] = { - 1039, 1040, 145, 146, 541, 1639, 1045, 250, 251, 252, 253, 254, - 1646, 46, 47, 1437, 1942, 1651, 445, 446, 447, 739, 740, 741, - 938, 939, 1538, 2043, 2144, 1451, 944, 5376, 5578, 1545, 5780, 5881, - 1670, 5982, 1550, 6083, 6184, 6285, 6386, 6487, 6588, 6689, 6790, 6891, - 6992, 7093, 7194, 7295, 7396, 5679, 7497, 7598, 7699, 7800, 7901, 8002, - 8103, 8204, 8305, 8406, 8507, 8608, 8709, 8810, 1107, 1108, 1109, 1110, - 8911, 9012, 9113, 9214, 9315, 9416, 9517, 9618, 1714, 9719, 0, 326, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 241, 242, 0, 0, 0, 0, 0, 0, 139, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0}; + 1085, 1086, 565, 1709, 1089, 262, 263, 264, 265, 266, 1716, + 153, 154, 1719, 760, 761, 50, 51, 465, 466, 467, 980, + 981, 1604, 1499, 984, 773, 2129, 2234, 6014, 1611, 6434, 1738, + 1614, 6539, 6644, 1511, 6749, 6854, 6959, 7064, 7169, 7274, 7379, + 2024, 7484, 7589, 7694, 7799, 7904, 8009, 8114, 8219, 6224, 8324, + 8429, 6329, 8534, 8639, 8744, 8849, 8954, 9059, 9164, 9269, 9374, + 1151, 1152, 1153, 1154, 9479, 9584, 9689, 9794, 9899, 10004, 1782, + 10109, 10214, 10319, 10424, 10529, 0, 0, 0, 0, 0, 344, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 253, 254, 147, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0}; static const uint8_t elem_idxs[] = { - 77, 79, 15, 16, 6, 25, 76, 19, 20, 21, 22, 23, 84, 17, - 18, 43, 72, 83, 11, 12, 13, 0, 1, 2, 5, 4, 38, 50, - 57, 7, 3, 24, 27, 37, 29, 30, 26, 31, 36, 32, 33, 34, - 35, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 28, 51, 52, - 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 78, 80, - 81, 82, 66, 67, 68, 69, 70, 71, 73, 74, 85, 75, 255, 14, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 9, 10, 255, 255, 255, 255, 255, 255, 8}; + 77, 79, 6, 25, 76, 19, 20, 21, 22, 23, 84, 15, 16, 83, 1, + 2, 17, 18, 11, 12, 13, 5, 4, 38, 43, 3, 0, 50, 57, 24, + 37, 29, 26, 36, 30, 31, 7, 32, 33, 34, 35, 39, 40, 41, 72, + 42, 44, 45, 46, 47, 48, 49, 51, 27, 52, 53, 28, 54, 55, 56, + 58, 59, 60, 61, 62, 63, 78, 80, 81, 82, 64, 65, 66, 67, 68, + 69, 85, 70, 71, 73, 74, 75, 255, 255, 255, 255, 255, 14, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 9, 10, 8}; grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { if (a == -1 || b == -1) return GRPC_MDNULL; - uint32_t k = static_cast<uint32_t>(a * 101 + b); + uint32_t k = (uint32_t)(a * 105 + b); uint32_t h = elems_phash(k); return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && elem_idxs[h] != 255 @@ -382,177 +396,177 @@ grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 452, 1}}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 504, 1}}}}, {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 453, 1}}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}}, {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 454, 1}}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}}, {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 463, 8}}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 513, 8}}}}, {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 471, 16}}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 521, 16}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 487, 4}}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 537, 4}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 491, 3}}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 541, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 494, 3}}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 544, 3}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 497, 4}}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 547, 4}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 501, 5}}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 551, 5}}}}, {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 506, 4}}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 556, 4}}}}, {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 510, 3}}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 560, 3}}}}, {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 513, 3}}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 563, 3}}}}, {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 516, 1}}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 566, 1}}}}, {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 11}}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 567, 11}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 528, 3}}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 578, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 531, 3}}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 581, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 534, 3}}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 584, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 537, 3}}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 587, 3}}}}, {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[52], {{g_bytes + 540, 3}}}}, - {{&grpc_static_metadata_refcounts[53], {{g_bytes + 543, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 590, 3}}}}, + {{&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[54], {{g_bytes + 557, 13}}}}, - {{&grpc_static_metadata_refcounts[55], {{g_bytes + 570, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[56], {{g_bytes + 585, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[57], {{g_bytes + 598, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[58], {{g_bytes + 604, 27}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[59], {{g_bytes + 631, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[60], {{g_bytes + 634, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[61], {{g_bytes + 639, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[62], {{g_bytes + 652, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[63], {{g_bytes + 665, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 607, 13}}}}, + {{&grpc_static_metadata_refcounts[59], {{g_bytes + 620, 15}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[60], {{g_bytes + 635, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[61], {{g_bytes + 648, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 27}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[63], {{g_bytes + 681, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[65], {{g_bytes + 689, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[66], {{g_bytes + 702, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[67], {{g_bytes + 715, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[64], {{g_bytes + 684, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[65], {{g_bytes + 700, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[66], {{g_bytes + 714, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[67], {{g_bytes + 730, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[68], {{g_bytes + 734, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[69], {{g_bytes + 750, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[70], {{g_bytes + 764, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[71], {{g_bytes + 780, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[68], {{g_bytes + 743, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[69], {{g_bytes + 749, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[70], {{g_bytes + 753, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[71], {{g_bytes + 757, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[72], {{g_bytes + 763, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[73], {{g_bytes + 770, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[72], {{g_bytes + 793, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[73], {{g_bytes + 799, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[74], {{g_bytes + 803, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[75], {{g_bytes + 807, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[76], {{g_bytes + 813, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[77], {{g_bytes + 820, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[74], {{g_bytes + 774, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[75], {{g_bytes + 782, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[76], {{g_bytes + 799, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[77], {{g_bytes + 812, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[78], {{g_bytes + 820, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[79], {{g_bytes + 839, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[78], {{g_bytes + 824, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[79], {{g_bytes + 832, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[80], {{g_bytes + 849, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[81], {{g_bytes + 862, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[82], {{g_bytes + 870, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[83], {{g_bytes + 889, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[81], {{g_bytes + 863, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[82], {{g_bytes + 867, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[83], {{g_bytes + 875, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[84], {{g_bytes + 887, 18}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[85], {{g_bytes + 905, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[86], {{g_bytes + 924, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[87], {{g_bytes + 929, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[88], {{g_bytes + 936, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[90], {{g_bytes + 954, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[91], {{g_bytes + 960, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[92], {{g_bytes + 970, 25}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[93], {{g_bytes + 995, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[84], {{g_bytes + 902, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[86], {{g_bytes + 917, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 12}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[88], {{g_bytes + 937, 18}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[89], {{g_bytes + 955, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[90], {{g_bytes + 974, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[91], {{g_bytes + 979, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[92], {{g_bytes + 986, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[93], {{g_bytes + 993, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1004, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1010, 10}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1020, 25}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[97], {{g_bytes + 1045, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1012, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1016, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[96], {{g_bytes + 1019, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[98], {{g_bytes + 1062, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[99], {{g_bytes + 1066, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[100], {{g_bytes + 1069, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 7}}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 7}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1035, 16}}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1085, 16}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1064, 12}}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1114, 12}}}}, {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[100], {{g_bytes + 1076, 21}}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1126, 21}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 455, 8}}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 505, 8}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 437, 4}}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 489, 4}}}}, {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1051, 13}}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1101, 13}}}}, }; bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { true, // :path @@ -577,6 +591,8 @@ bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { true, // user-agent true, // host true, // lb-token + true, // grpc-previous-rpc-attempts + true, // grpc-retry-pushback-ms }; const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h index 8ce9b21bc1..b3a10f5873 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -27,9 +27,11 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H #define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/metadata.h" -#define GRPC_STATIC_MDSTR_COUNT 101 +#define GRPC_STATIC_MDSTR_COUNT 105 extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]; /* ":path" */ #define GRPC_MDSTR_PATH (grpc_static_slice_table[0]) @@ -76,168 +78,176 @@ extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]; #define GRPC_MDSTR_HOST (grpc_static_slice_table[20]) /* "lb-token" */ #define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[21]) +/* "grpc-previous-rpc-attempts" */ +#define GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS (grpc_static_slice_table[22]) +/* "grpc-retry-pushback-ms" */ +#define GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS (grpc_static_slice_table[23]) /* "grpc-timeout" */ -#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[22]) +#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[24]) +/* "1" */ +#define GRPC_MDSTR_1 (grpc_static_slice_table[25]) +/* "2" */ +#define GRPC_MDSTR_2 (grpc_static_slice_table[26]) +/* "3" */ +#define GRPC_MDSTR_3 (grpc_static_slice_table[27]) +/* "4" */ +#define GRPC_MDSTR_4 (grpc_static_slice_table[28]) /* "" */ -#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[23]) +#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[29]) /* "grpc.wait_for_ready" */ -#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[24]) +#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[30]) /* "grpc.timeout" */ -#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[25]) +#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[31]) /* "grpc.max_request_message_bytes" */ #define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \ - (grpc_static_slice_table[26]) + (grpc_static_slice_table[32]) /* "grpc.max_response_message_bytes" */ #define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \ - (grpc_static_slice_table[27]) + (grpc_static_slice_table[33]) /* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */ #define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \ - (grpc_static_slice_table[28]) + (grpc_static_slice_table[34]) /* "deflate" */ -#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[29]) +#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[35]) /* "gzip" */ -#define GRPC_MDSTR_GZIP (grpc_static_slice_table[30]) +#define GRPC_MDSTR_GZIP (grpc_static_slice_table[36]) /* "stream/gzip" */ -#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[31]) +#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[37]) /* "0" */ -#define GRPC_MDSTR_0 (grpc_static_slice_table[32]) -/* "1" */ -#define GRPC_MDSTR_1 (grpc_static_slice_table[33]) -/* "2" */ -#define GRPC_MDSTR_2 (grpc_static_slice_table[34]) +#define GRPC_MDSTR_0 (grpc_static_slice_table[38]) /* "identity" */ -#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[35]) +#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[39]) /* "trailers" */ -#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[36]) +#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[40]) /* "application/grpc" */ -#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[37]) +#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[41]) /* "POST" */ -#define GRPC_MDSTR_POST (grpc_static_slice_table[38]) +#define GRPC_MDSTR_POST (grpc_static_slice_table[42]) /* "200" */ -#define GRPC_MDSTR_200 (grpc_static_slice_table[39]) +#define GRPC_MDSTR_200 (grpc_static_slice_table[43]) /* "404" */ -#define GRPC_MDSTR_404 (grpc_static_slice_table[40]) +#define GRPC_MDSTR_404 (grpc_static_slice_table[44]) /* "http" */ -#define GRPC_MDSTR_HTTP (grpc_static_slice_table[41]) +#define GRPC_MDSTR_HTTP (grpc_static_slice_table[45]) /* "https" */ -#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[42]) +#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[46]) /* "grpc" */ -#define GRPC_MDSTR_GRPC (grpc_static_slice_table[43]) +#define GRPC_MDSTR_GRPC (grpc_static_slice_table[47]) /* "GET" */ -#define GRPC_MDSTR_GET (grpc_static_slice_table[44]) +#define GRPC_MDSTR_GET (grpc_static_slice_table[48]) /* "PUT" */ -#define GRPC_MDSTR_PUT (grpc_static_slice_table[45]) +#define GRPC_MDSTR_PUT (grpc_static_slice_table[49]) /* "/" */ -#define GRPC_MDSTR_SLASH (grpc_static_slice_table[46]) +#define GRPC_MDSTR_SLASH (grpc_static_slice_table[50]) /* "/index.html" */ -#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[47]) +#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[51]) /* "204" */ -#define GRPC_MDSTR_204 (grpc_static_slice_table[48]) +#define GRPC_MDSTR_204 (grpc_static_slice_table[52]) /* "206" */ -#define GRPC_MDSTR_206 (grpc_static_slice_table[49]) +#define GRPC_MDSTR_206 (grpc_static_slice_table[53]) /* "304" */ -#define GRPC_MDSTR_304 (grpc_static_slice_table[50]) +#define GRPC_MDSTR_304 (grpc_static_slice_table[54]) /* "400" */ -#define GRPC_MDSTR_400 (grpc_static_slice_table[51]) +#define GRPC_MDSTR_400 (grpc_static_slice_table[55]) /* "500" */ -#define GRPC_MDSTR_500 (grpc_static_slice_table[52]) +#define GRPC_MDSTR_500 (grpc_static_slice_table[56]) /* "accept-charset" */ -#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[53]) +#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[57]) /* "gzip, deflate" */ -#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[54]) +#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[58]) /* "accept-language" */ -#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[55]) +#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[59]) /* "accept-ranges" */ -#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[56]) +#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[60]) /* "accept" */ -#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[57]) +#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[61]) /* "access-control-allow-origin" */ -#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[58]) +#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[62]) /* "age" */ -#define GRPC_MDSTR_AGE (grpc_static_slice_table[59]) +#define GRPC_MDSTR_AGE (grpc_static_slice_table[63]) /* "allow" */ -#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[60]) +#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[64]) /* "authorization" */ -#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[61]) +#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[65]) /* "cache-control" */ -#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[62]) +#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[66]) /* "content-disposition" */ -#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[63]) +#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[67]) /* "content-language" */ -#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[64]) +#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[68]) /* "content-length" */ -#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[65]) +#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[69]) /* "content-location" */ -#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[66]) +#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[70]) /* "content-range" */ -#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[67]) +#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[71]) /* "cookie" */ -#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[68]) +#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[72]) /* "date" */ -#define GRPC_MDSTR_DATE (grpc_static_slice_table[69]) +#define GRPC_MDSTR_DATE (grpc_static_slice_table[73]) /* "etag" */ -#define GRPC_MDSTR_ETAG (grpc_static_slice_table[70]) +#define GRPC_MDSTR_ETAG (grpc_static_slice_table[74]) /* "expect" */ -#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[71]) +#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[75]) /* "expires" */ -#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[72]) +#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[76]) /* "from" */ -#define GRPC_MDSTR_FROM (grpc_static_slice_table[73]) +#define GRPC_MDSTR_FROM (grpc_static_slice_table[77]) /* "if-match" */ -#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[74]) +#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[78]) /* "if-modified-since" */ -#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[75]) +#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[79]) /* "if-none-match" */ -#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[76]) +#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[80]) /* "if-range" */ -#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[77]) +#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[81]) /* "if-unmodified-since" */ -#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[78]) +#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[82]) /* "last-modified" */ -#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[79]) +#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[83]) /* "lb-cost-bin" */ -#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[80]) +#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[84]) /* "link" */ -#define GRPC_MDSTR_LINK (grpc_static_slice_table[81]) +#define GRPC_MDSTR_LINK (grpc_static_slice_table[85]) /* "location" */ -#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[82]) +#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[86]) /* "max-forwards" */ -#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[83]) +#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[87]) /* "proxy-authenticate" */ -#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[84]) +#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[88]) /* "proxy-authorization" */ -#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[85]) +#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[89]) /* "range" */ -#define GRPC_MDSTR_RANGE (grpc_static_slice_table[86]) +#define GRPC_MDSTR_RANGE (grpc_static_slice_table[90]) /* "referer" */ -#define GRPC_MDSTR_REFERER (grpc_static_slice_table[87]) +#define GRPC_MDSTR_REFERER (grpc_static_slice_table[91]) /* "refresh" */ -#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[88]) +#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[92]) /* "retry-after" */ -#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[89]) +#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[93]) /* "server" */ -#define GRPC_MDSTR_SERVER (grpc_static_slice_table[90]) +#define GRPC_MDSTR_SERVER (grpc_static_slice_table[94]) /* "set-cookie" */ -#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[91]) +#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[95]) /* "strict-transport-security" */ -#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[92]) +#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[96]) /* "transfer-encoding" */ -#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[93]) +#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[97]) /* "vary" */ -#define GRPC_MDSTR_VARY (grpc_static_slice_table[94]) +#define GRPC_MDSTR_VARY (grpc_static_slice_table[98]) /* "via" */ -#define GRPC_MDSTR_VIA (grpc_static_slice_table[95]) +#define GRPC_MDSTR_VIA (grpc_static_slice_table[99]) /* "www-authenticate" */ -#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[96]) +#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[100]) /* "identity,deflate" */ -#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[97]) +#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[101]) /* "identity,gzip" */ -#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[98]) +#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[102]) /* "deflate,gzip" */ -#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[99]) +#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[103]) /* "identity,deflate,gzip" */ #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ - (grpc_static_slice_table[100]) + (grpc_static_slice_table[104]) extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable; extern grpc_slice_refcount @@ -535,6 +545,8 @@ typedef enum { GRPC_BATCH_USER_AGENT, GRPC_BATCH_HOST, GRPC_BATCH_LB_TOKEN, + GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS, + GRPC_BATCH_GRPC_RETRY_PUSHBACK_MS, GRPC_BATCH_CALLOUTS_COUNT } grpc_metadata_batch_callouts_index; @@ -563,6 +575,8 @@ typedef union { struct grpc_linked_mdelem* user_agent; struct grpc_linked_mdelem* host; struct grpc_linked_mdelem* lb_token; + struct grpc_linked_mdelem* grpc_previous_rpc_attempts; + struct grpc_linked_mdelem* grpc_retry_pushback_ms; } named; } grpc_metadata_batch_callouts; diff --git a/src/core/lib/transport/status_conversion.cc b/src/core/lib/transport/status_conversion.cc index 46cba4292b..e58bef5ba9 100644 --- a/src/core/lib/transport/status_conversion.cc +++ b/src/core/lib/transport/status_conversion.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/status_conversion.h" grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) { diff --git a/src/core/lib/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h index 107eb92a53..9f14e9bee0 100644 --- a/src/core/lib/transport/status_conversion.h +++ b/src/core/lib/transport/status_conversion.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H #define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/transport/http2_errors.h" diff --git a/src/core/lib/transport/status_metadata.cc b/src/core/lib/transport/status_metadata.cc new file mode 100644 index 0000000000..f896053e4d --- /dev/null +++ b/src/core/lib/transport/status_metadata.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/lib/transport/status_metadata.h" + +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +/* we offset status by a small amount when storing it into transport metadata + as metadata cannot store a 0 value (which is used as OK for grpc_status_codes + */ +#define STATUS_OFFSET 1 + +static void destroy_status(void* ignored) {} + +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) { + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + return GRPC_STATUS_OK; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) { + return GRPC_STATUS_CANCELLED; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) { + return GRPC_STATUS_UNKNOWN; + } + void* user_data = grpc_mdelem_get_user_data(md, destroy_status); + if (user_data != nullptr) { + return static_cast<grpc_status_code>((intptr_t)user_data - STATUS_OFFSET); + } + uint32_t status; + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { + status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ + } + grpc_mdelem_set_user_data( + md, destroy_status, (void*)static_cast<intptr_t>(status + STATUS_OFFSET)); + return static_cast<grpc_status_code>(status); +} diff --git a/src/core/lib/security/transport/lb_targets_info.h b/src/core/lib/transport/status_metadata.h index 7543d3c012..aed9c7ac20 100644 --- a/src/core/lib/security/transport/lb_targets_info.h +++ b/src/core/lib/transport/status_metadata.h @@ -16,17 +16,15 @@ * */ -#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H -#define GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H +#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H +#define GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H -#include "src/core/lib/slice/slice_hash_table.h" +#include <grpc/support/port_platform.h> -/** Return a channel argument containing \a targets_info. */ -grpc_arg grpc_lb_targets_info_create_channel_arg( - grpc_slice_hash_table* targets_info); +#include <grpc/status.h> -/** Return the instance of targets info in \a args or NULL */ -grpc_slice_hash_table* grpc_lb_targets_info_find_in_args( - const grpc_channel_args* args); +#include "src/core/lib/transport/metadata.h" -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H */ +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md); + +#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H */ diff --git a/src/core/lib/transport/timeout_encoding.cc b/src/core/lib/transport/timeout_encoding.cc index 6800255be2..c37249920b 100644 --- a/src/core/lib/transport/timeout_encoding.cc +++ b/src/core/lib/transport/timeout_encoding.cc @@ -16,12 +16,13 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/timeout_encoding.h" #include <stdio.h> #include <string.h> -#include <grpc/support/port_platform.h> #include "src/core/lib/gpr/string.h" static int64_t round_up(int64_t x, int64_t divisor) { diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h index 4e9268262e..8505e32ff0 100644 --- a/src/core/lib/transport/timeout_encoding.h +++ b/src/core/lib/transport/timeout_encoding.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H #define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H +#include <grpc/support/port_platform.h> + #include <grpc/slice.h> #include <grpc/support/time.h> diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc index d71d4fdd76..6b41e4b37e 100644 --- a/src/core/lib/transport/transport.cc +++ b/src/core/lib/transport/transport.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/transport.h" #include <string.h> @@ -207,7 +209,7 @@ void grpc_transport_stream_op_batch_finish_with_failure( grpc_transport_stream_op_batch* batch, grpc_error* error, grpc_call_combiner* call_combiner) { if (batch->send_message) { - grpc_byte_stream_destroy(batch->payload->send_message.send_message); + batch->payload->send_message.send_message.reset(); } if (batch->recv_message) { GRPC_CALL_COMBINER_START( diff --git a/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h index b392c696cd..10e9df0f7c 100644 --- a/src/core/lib/transport/transport.h +++ b/src/core/lib/transport/transport.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H #define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H +#include <grpc/support/port_platform.h> + #include <stddef.h> #include "src/core/lib/channel/context.h" @@ -96,6 +98,19 @@ void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from, void grpc_transport_move_stats(grpc_transport_stream_stats* from, grpc_transport_stream_stats* to); +// This struct (which is present in both grpc_transport_stream_op_batch +// and grpc_transport_op_batch) is a convenience to allow filters or +// transports to schedule a closure related to a particular batch without +// having to allocate memory. The general pattern is to initialize the +// closure with the callback arg set to the batch and extra_arg set to +// whatever state is associated with the handler (e.g., the call element +// or the transport stream object). +// +// Note that this can only be used by the current handler of a given +// batch on the way down the stack (i.e., whichever filter or transport is +// currently handling the batch). Once a filter or transport passes control +// of the batch to the next handler, it cannot depend on the contents of +// this struct anymore, because the next handler may reuse it. typedef struct { void* extra_arg; grpc_closure closure; @@ -155,6 +170,11 @@ struct grpc_transport_stream_op_batch_payload { uint32_t send_initial_metadata_flags; // If non-NULL, will be set by the transport to the peer string // (a char*, which the caller takes ownership of). + // Note: This pointer may be used by the transport after the + // send_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + // Note: When a transport sets this, it must free the previous + // value, if any. gpr_atm* peer_string; } send_initial_metadata; @@ -164,15 +184,17 @@ struct grpc_transport_stream_op_batch_payload { struct { // The transport (or a filter that decides to return a failure before - // the op gets down to the transport) is responsible for calling - // grpc_byte_stream_destroy() on this. + // the op gets down to the transport) takes ownership. // The batch's on_complete will not be called until after the byte - // stream is destroyed. - grpc_byte_stream* send_message; + // stream is orphaned. + grpc_core::OrphanablePtr<grpc_core::ByteStream> send_message; } send_message; struct { grpc_metadata_batch* recv_initial_metadata; + // Flags are used only on the server side. If non-null, will be set to + // a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to + // indicate if the call is idempotent). uint32_t* recv_flags; /** Should be enqueued when initial metadata is ready to be processed. */ grpc_closure* recv_initial_metadata_ready; @@ -182,15 +204,19 @@ struct grpc_transport_stream_op_batch_payload { bool* trailing_metadata_available; // If non-NULL, will be set by the transport to the peer string // (a char*, which the caller takes ownership of). + // Note: This pointer may be used by the transport after the + // recv_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + // Note: When a transport sets this, it must free the previous + // value, if any. gpr_atm* peer_string; } recv_initial_metadata; struct { // Will be set by the transport to point to the byte stream // containing a received message. - // The caller is responsible for calling grpc_byte_stream_destroy() - // on this byte stream. - grpc_byte_stream** recv_message; + // Will be NULL if trailing metadata is received instead of a message. + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message; /** Should be enqueued when one message is ready to be processed. */ grpc_closure* recv_message_ready; } recv_message; diff --git a/src/core/lib/transport/transport_impl.h b/src/core/lib/transport/transport_impl.h index 50b8a5f9b7..ba5e05df0a 100644 --- a/src/core/lib/transport/transport_impl.h +++ b/src/core/lib/transport/transport_impl.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H #define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/transport/transport.h" typedef struct grpc_transport_vtable { diff --git a/src/core/lib/transport/transport_op_string.cc b/src/core/lib/transport/transport_op_string.cc index 6898da17ed..99af7c1931 100644 --- a/src/core/lib/transport/transport_op_string.cc +++ b/src/core/lib/transport/transport_op_string.cc @@ -75,9 +75,16 @@ char* grpc_transport_stream_op_batch_string( if (op->send_message) { gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", - op->payload->send_message.send_message->flags, - op->payload->send_message.send_message->length); + if (op->payload->send_message.send_message != nullptr) { + gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", + op->payload->send_message.send_message->flags(), + op->payload->send_message.send_message->length()); + } else { + // This can happen when we check a batch after the transport has + // processed and cleared the send_message op. + tmp = + gpr_strdup("SEND_MESSAGE(flag and length unknown, already orphaned)"); + } gpr_strvec_add(&b, tmp); } diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc index fe5eb28f05..49b9c7d4fe 100644 --- a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc +++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> void grpc_http_filters_init(void); diff --git a/src/core/plugin_registry/grpc_plugin_registry.cc b/src/core/plugin_registry/grpc_plugin_registry.cc index fdf9acc09c..6f11e6bb5b 100644 --- a/src/core/plugin_registry/grpc_plugin_registry.cc +++ b/src/core/plugin_registry/grpc_plugin_registry.cc @@ -16,18 +16,20 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> void grpc_http_filters_init(void); void grpc_http_filters_shutdown(void); void grpc_chttp2_plugin_init(void); void grpc_chttp2_plugin_shutdown(void); -void grpc_tsi_alts_init(void); -void grpc_tsi_alts_shutdown(void); void grpc_deadline_filter_init(void); void grpc_deadline_filter_shutdown(void); void grpc_client_channel_init(void); void grpc_client_channel_shutdown(void); +void grpc_tsi_alts_init(void); +void grpc_tsi_alts_shutdown(void); void grpc_inproc_plugin_init(void); void grpc_inproc_plugin_shutdown(void); void grpc_resolver_fake_init(void); @@ -58,12 +60,12 @@ void grpc_register_built_in_plugins(void) { grpc_http_filters_shutdown); grpc_register_plugin(grpc_chttp2_plugin_init, grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_tsi_alts_init, - grpc_tsi_alts_shutdown); grpc_register_plugin(grpc_deadline_filter_init, grpc_deadline_filter_shutdown); grpc_register_plugin(grpc_client_channel_init, grpc_client_channel_shutdown); + grpc_register_plugin(grpc_tsi_alts_init, + grpc_tsi_alts_shutdown); grpc_register_plugin(grpc_inproc_plugin_init, grpc_inproc_plugin_shutdown); grpc_register_plugin(grpc_resolver_fake_init, diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc index d73f946241..b08c5ce3ae 100644 --- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc +++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> void grpc_http_filters_init(void); diff --git a/src/core/tsi/alts/crypt/aes_gcm.cc b/src/core/tsi/alts/crypt/aes_gcm.cc new file mode 100644 index 0000000000..02b1ac4492 --- /dev/null +++ b/src/core/tsi/alts/crypt/aes_gcm.cc @@ -0,0 +1,687 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/crypt/gsec.h" + +#include <openssl/bio.h> +#include <openssl/buffer.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <string.h> + +#include <grpc/support/alloc.h> + +constexpr size_t kKdfKeyLen = 32; +constexpr size_t kKdfCounterLen = 6; +constexpr size_t kKdfCounterOffset = 2; +constexpr size_t kRekeyAeadKeyLen = kAes128GcmKeyLength; + +/* Struct for additional data required if rekeying is enabled. */ +struct gsec_aes_gcm_aead_rekey_data { + uint8_t kdf_counter[kKdfCounterLen]; + uint8_t nonce_mask[kAesGcmNonceLength]; +}; + +/* Main struct for AES_GCM crypter interface. */ +struct gsec_aes_gcm_aead_crypter { + gsec_aead_crypter crypter; + size_t key_length; + size_t nonce_length; + size_t tag_length; + uint8_t* key; + gsec_aes_gcm_aead_rekey_data* rekey_data; + EVP_CIPHER_CTX* ctx; +}; + +static char* aes_gcm_get_openssl_errors() { + BIO* bio = BIO_new(BIO_s_mem()); + ERR_print_errors(bio); + BUF_MEM* mem = nullptr; + char* error_msg = nullptr; + BIO_get_mem_ptr(bio, &mem); + if (mem != nullptr) { + error_msg = static_cast<char*>(gpr_malloc(mem->length + 1)); + memcpy(error_msg, mem->data, mem->length); + error_msg[mem->length] = '\0'; + } + BIO_free_all(bio); + return error_msg; +} + +static void aes_gcm_format_errors(const char* error_msg, char** error_details) { + if (error_details == nullptr) { + return; + } + unsigned long error = ERR_get_error(); + if (error == 0 && error_msg != nullptr) { + *error_details = static_cast<char*>(gpr_malloc(strlen(error_msg) + 1)); + memcpy(*error_details, error_msg, strlen(error_msg) + 1); + return; + } + char* openssl_errors = aes_gcm_get_openssl_errors(); + if (openssl_errors != nullptr && error_msg != nullptr) { + size_t len = strlen(error_msg) + strlen(openssl_errors) + 2; /* ", " */ + *error_details = static_cast<char*>(gpr_malloc(len + 1)); + snprintf(*error_details, len + 1, "%s, %s", error_msg, openssl_errors); + gpr_free(openssl_errors); + } +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length, char** error_details) { + if (max_ciphertext_and_tag_length == nullptr) { + aes_gcm_format_errors("max_ciphertext_and_tag_length is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + *max_ciphertext_and_tag_length = + plaintext_length + aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length, char** error_details) { + if (max_plaintext_length == nullptr) { + aes_gcm_format_errors("max_plaintext_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + if (ciphertext_and_tag_length < aes_gcm_crypter->tag_length) { + *max_plaintext_length = 0; + aes_gcm_format_errors( + "ciphertext_and_tag_length is smaller than tag_length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *max_plaintext_length = + ciphertext_and_tag_length - aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length, + char** error_details) { + if (nonce_length == nullptr) { + aes_gcm_format_errors("nonce_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + *nonce_length = aes_gcm_crypter->nonce_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_key_length( + const gsec_aead_crypter* crypter, size_t* key_length, + char** error_details) { + if (key_length == nullptr) { + aes_gcm_format_errors("key_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + *key_length = aes_gcm_crypter->key_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_tag_length( + const gsec_aead_crypter* crypter, size_t* tag_length, + char** error_details) { + if (tag_length == nullptr) { + aes_gcm_format_errors("tag_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + *tag_length = aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static void aes_gcm_mask_nonce(uint8_t* dst, const uint8_t* nonce, + const uint8_t* mask) { + uint64_t mask1; + uint32_t mask2; + memcpy(&mask1, mask, sizeof(mask1)); + memcpy(&mask2, mask + sizeof(mask1), sizeof(mask2)); + uint64_t nonce1; + uint32_t nonce2; + memcpy(&nonce1, nonce, sizeof(nonce1)); + memcpy(&nonce2, nonce + sizeof(nonce1), sizeof(nonce2)); + nonce1 ^= mask1; + nonce2 ^= mask2; + memcpy(dst, &nonce1, sizeof(nonce1)); + memcpy(dst + sizeof(nonce1), &nonce2, sizeof(nonce2)); +} + +static grpc_status_code aes_gcm_derive_aead_key(uint8_t* dst, + const uint8_t* kdf_key, + const uint8_t* kdf_counter) { + unsigned char buf[EVP_MAX_MD_SIZE]; + unsigned char ctr = 1; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX hmac; + HMAC_CTX_init(&hmac); + if (!HMAC_Init_ex(&hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || + !HMAC_Update(&hmac, kdf_counter, kKdfCounterLen) || + !HMAC_Update(&hmac, &ctr, 1) || !HMAC_Final(&hmac, buf, nullptr)) { + HMAC_CTX_cleanup(&hmac); + return GRPC_STATUS_INTERNAL; + } + HMAC_CTX_cleanup(&hmac); +#else + HMAC_CTX* hmac = HMAC_CTX_new(); + if (hmac == nullptr) { + return GRPC_STATUS_INTERNAL; + } + if (!HMAC_Init_ex(hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || + !HMAC_Update(hmac, kdf_counter, kKdfCounterLen) || + !HMAC_Update(hmac, &ctr, 1) || !HMAC_Final(hmac, buf, nullptr)) { + HMAC_CTX_free(hmac); + return GRPC_STATUS_INTERNAL; + } + HMAC_CTX_free(hmac); +#endif + memcpy(dst, buf, kRekeyAeadKeyLen); + return GRPC_STATUS_OK; +} + +static grpc_status_code aes_gcm_rekey_if_required( + gsec_aes_gcm_aead_crypter* aes_gcm_crypter, const uint8_t* nonce, + char** error_details) { + // If rekey_data is nullptr, then rekeying is not supported and not required. + // If bytes 2-7 of kdf_counter differ from the (per message) nonce, then the + // encryption key is recomputed from a new kdf_counter to ensure that we don't + // encrypt more than 2^16 messages per encryption key (in each direction). + if (aes_gcm_crypter->rekey_data == nullptr || + memcmp(aes_gcm_crypter->rekey_data->kdf_counter, + nonce + kKdfCounterOffset, kKdfCounterLen) == 0) { + return GRPC_STATUS_OK; + } + memcpy(aes_gcm_crypter->rekey_data->kdf_counter, nonce + kKdfCounterOffset, + kKdfCounterLen); + uint8_t aead_key[kRekeyAeadKeyLen]; + if (aes_gcm_derive_aead_key(aead_key, aes_gcm_crypter->key, + aes_gcm_crypter->rekey_data->kdf_counter) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Rekeying failed in key derivation.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, aead_key, + nullptr)) { + aes_gcm_format_errors("Rekeying failed in context update.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>(crypter); + // Input checks + if (nonce == nullptr) { + aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (kAesGcmNonceLength != nonce_length) { + aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (aad_vec_length > 0 && aad_vec == nullptr) { + aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (plaintext_vec_length > 0 && plaintext_vec == nullptr) { + aes_gcm_format_errors( + "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_bytes_written == nullptr) { + aes_gcm_format_errors("bytes_written is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *ciphertext_bytes_written = 0; + // rekey if required + if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != + GRPC_STATUS_OK) { + return GRPC_STATUS_INTERNAL; + } + // mask nonce if required + const uint8_t* nonce_aead = nonce; + uint8_t nonce_masked[kAesGcmNonceLength]; + if (aes_gcm_crypter->rekey_data != nullptr) { + aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, + nonce); + nonce_aead = nonce_masked; + } + // init openssl context + if (!EVP_EncryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, + nonce_aead)) { + aes_gcm_format_errors("Initializing nonce failed", error_details); + return GRPC_STATUS_INTERNAL; + } + // process aad + size_t i; + for (i = 0; i < aad_vec_length; i++) { + const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base); + size_t aad_length = aad_vec[i].iov_len; + if (aad_length == 0) { + continue; + } + size_t aad_bytes_read = 0; + if (aad == nullptr) { + aes_gcm_format_errors("aad is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, nullptr, + reinterpret_cast<int*>(&aad_bytes_read), aad, + static_cast<int>(aad_length)) || + aad_bytes_read != aad_length) { + aes_gcm_format_errors("Setting authenticated associated data failed", + error_details); + return GRPC_STATUS_INTERNAL; + } + } + uint8_t* ciphertext = static_cast<uint8_t*>(ciphertext_vec.iov_base); + size_t ciphertext_length = ciphertext_vec.iov_len; + if (ciphertext == nullptr) { + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + // process plaintext + for (i = 0; i < plaintext_vec_length; i++) { + const uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec[i].iov_base); + size_t plaintext_length = plaintext_vec[i].iov_len; + if (plaintext == nullptr) { + if (plaintext_length == 0) { + continue; + } + aes_gcm_format_errors("plaintext is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_length < plaintext_length) { + aes_gcm_format_errors( + "ciphertext is not large enough to hold the result.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + int bytes_written = 0; + int bytes_to_write = static_cast<int>(plaintext_length); + if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, ciphertext, &bytes_written, + plaintext, bytes_to_write)) { + aes_gcm_format_errors("Encrypting plaintext failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written > bytes_to_write) { + aes_gcm_format_errors("More bytes written than expected.", error_details); + return GRPC_STATUS_INTERNAL; + } + ciphertext += bytes_written; + ciphertext_length -= bytes_written; + } + int bytes_written_temp = 0; + if (!EVP_EncryptFinal_ex(aes_gcm_crypter->ctx, nullptr, + &bytes_written_temp)) { + aes_gcm_format_errors("Finalizing encryption failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written_temp != 0) { + aes_gcm_format_errors("Openssl wrote some unexpected bytes.", + error_details); + return GRPC_STATUS_INTERNAL; + } + if (ciphertext_length < kAesGcmTagLength) { + aes_gcm_format_errors("ciphertext is too small to hold a tag.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_GET_TAG, + kAesGcmTagLength, ciphertext)) { + aes_gcm_format_errors("Writing tag failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + ciphertext += kAesGcmTagLength; + ciphertext_length -= kAesGcmTagLength; + *ciphertext_bytes_written = ciphertext_vec.iov_len - ciphertext_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + if (nonce == nullptr) { + aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (kAesGcmNonceLength != nonce_length) { + aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (aad_vec_length > 0 && aad_vec == nullptr) { + aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_vec_length > 0 && ciphertext_vec == nullptr) { + aes_gcm_format_errors( + "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + // Compute the total length so we can ensure we don't pass the tag into + // EVP_decrypt. + size_t total_ciphertext_length = 0; + size_t i; + for (i = 0; i < ciphertext_vec_length; i++) { + total_ciphertext_length += ciphertext_vec[i].iov_len; + } + if (total_ciphertext_length < kAesGcmTagLength) { + aes_gcm_format_errors("ciphertext is too small to hold a tag.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (plaintext_bytes_written == nullptr) { + aes_gcm_format_errors("bytes_written is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *plaintext_bytes_written = 0; + // rekey if required + if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Rekeying failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + // mask nonce if required + const uint8_t* nonce_aead = nonce; + uint8_t nonce_masked[kAesGcmNonceLength]; + if (aes_gcm_crypter->rekey_data != nullptr) { + aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, + nonce); + nonce_aead = nonce_masked; + } + // init openssl context + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, + nonce_aead)) { + aes_gcm_format_errors("Initializing nonce failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + // process aad + for (i = 0; i < aad_vec_length; i++) { + const uint8_t* aad = static_cast<uint8_t*>(aad_vec[i].iov_base); + size_t aad_length = aad_vec[i].iov_len; + if (aad_length == 0) { + continue; + } + size_t aad_bytes_read = 0; + if (aad == nullptr) { + aes_gcm_format_errors("aad is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, nullptr, + reinterpret_cast<int*>(&aad_bytes_read), aad, + static_cast<int>(aad_length)) || + aad_bytes_read != aad_length) { + aes_gcm_format_errors("Setting authenticated associated data failed.", + error_details); + return GRPC_STATUS_INTERNAL; + } + } + // process ciphertext + uint8_t* plaintext = static_cast<uint8_t*>(plaintext_vec.iov_base); + size_t plaintext_length = plaintext_vec.iov_len; + if (plaintext_length > 0 && plaintext == nullptr) { + aes_gcm_format_errors( + "plaintext is nullptr, but plaintext_length is positive.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + const uint8_t* ciphertext = nullptr; + size_t ciphertext_length = 0; + for (i = 0; + i < ciphertext_vec_length && total_ciphertext_length > kAesGcmTagLength; + i++) { + ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base); + ciphertext_length = ciphertext_vec[i].iov_len; + if (ciphertext == nullptr) { + if (ciphertext_length == 0) { + continue; + } + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + size_t bytes_written = 0; + size_t bytes_to_write = ciphertext_length; + // Don't include the tag + if (bytes_to_write > total_ciphertext_length - kAesGcmTagLength) { + bytes_to_write = total_ciphertext_length - kAesGcmTagLength; + } + if (plaintext_length < bytes_to_write) { + aes_gcm_format_errors( + "Not enough plaintext buffer to hold encrypted ciphertext.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, plaintext, + reinterpret_cast<int*>(&bytes_written), ciphertext, + static_cast<int>(bytes_to_write))) { + aes_gcm_format_errors("Decrypting ciphertext failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written > ciphertext_length) { + aes_gcm_format_errors("More bytes written than expected.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + ciphertext += bytes_written; + ciphertext_length -= bytes_written; + total_ciphertext_length -= bytes_written; + plaintext += bytes_written; + plaintext_length -= bytes_written; + } + if (total_ciphertext_length > kAesGcmTagLength) { + aes_gcm_format_errors( + "Not enough plaintext buffer to hold encrypted ciphertext.", + error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + uint8_t tag[kAesGcmTagLength]; + uint8_t* tag_tmp = tag; + if (ciphertext_length > 0) { + memcpy(tag_tmp, ciphertext, ciphertext_length); + tag_tmp += ciphertext_length; + total_ciphertext_length -= ciphertext_length; + } + for (; i < ciphertext_vec_length; i++) { + ciphertext = static_cast<uint8_t*>(ciphertext_vec[i].iov_base); + ciphertext_length = ciphertext_vec[i].iov_len; + if (ciphertext == nullptr) { + if (ciphertext_length == 0) { + continue; + } + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + memcpy(tag_tmp, ciphertext, ciphertext_length); + tag_tmp += ciphertext_length; + total_ciphertext_length -= ciphertext_length; + } + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_TAG, + kAesGcmTagLength, reinterpret_cast<void*>(tag))) { + aes_gcm_format_errors("Setting tag failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + int bytes_written_temp = 0; + if (!EVP_DecryptFinal_ex(aes_gcm_crypter->ctx, nullptr, + &bytes_written_temp)) { + aes_gcm_format_errors("Checking tag failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (bytes_written_temp != 0) { + aes_gcm_format_errors("Openssl wrote some unexpected bytes.", + error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + *plaintext_bytes_written = plaintext_vec.iov_len - plaintext_length; + return GRPC_STATUS_OK; +} + +static void gsec_aes_gcm_aead_crypter_destroy(gsec_aead_crypter* crypter) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast<gsec_aes_gcm_aead_crypter*>( + const_cast<gsec_aead_crypter*>(crypter)); + gpr_free(aes_gcm_crypter->key); + gpr_free(aes_gcm_crypter->rekey_data); + EVP_CIPHER_CTX_free(aes_gcm_crypter->ctx); +} + +static const gsec_aead_crypter_vtable vtable = { + gsec_aes_gcm_aead_crypter_encrypt_iovec, + gsec_aes_gcm_aead_crypter_decrypt_iovec, + gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length, + gsec_aes_gcm_aead_crypter_max_plaintext_length, + gsec_aes_gcm_aead_crypter_nonce_length, + gsec_aes_gcm_aead_crypter_key_length, + gsec_aes_gcm_aead_crypter_tag_length, + gsec_aes_gcm_aead_crypter_destroy}; + +static grpc_status_code aes_gcm_new_evp_cipher_ctx( + gsec_aes_gcm_aead_crypter* aes_gcm_crypter, char** error_details) { + const EVP_CIPHER* cipher = nullptr; + bool is_rekey = aes_gcm_crypter->rekey_data != nullptr; + switch (is_rekey ? kRekeyAeadKeyLen : aes_gcm_crypter->key_length) { + case kAes128GcmKeyLength: + cipher = EVP_aes_128_gcm(); + break; + case kAes256GcmKeyLength: + cipher = EVP_aes_256_gcm(); + break; + } + const uint8_t* aead_key = aes_gcm_crypter->key; + uint8_t aead_key_rekey[kRekeyAeadKeyLen]; + if (is_rekey) { + if (aes_gcm_derive_aead_key(aead_key_rekey, aes_gcm_crypter->key, + aes_gcm_crypter->rekey_data->kdf_counter) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Deriving key failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + aead_key = aead_key_rekey; + } + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, cipher, nullptr, aead_key, + nullptr)) { + aes_gcm_format_errors("Setting key failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_IVLEN, + static_cast<int>(aes_gcm_crypter->nonce_length), + nullptr)) { + aes_gcm_format_errors("Setting nonce length failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key, + size_t key_length, + size_t nonce_length, + size_t tag_length, bool rekey, + gsec_aead_crypter** crypter, + char** error_details) { + if (key == nullptr) { + aes_gcm_format_errors("key is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (crypter == nullptr) { + aes_gcm_format_errors("crypter is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + *crypter = nullptr; + if ((rekey && key_length != kAes128GcmRekeyKeyLength) || + (!rekey && key_length != kAes128GcmKeyLength && + key_length != kAes256GcmKeyLength) || + (tag_length != kAesGcmTagLength) || + (nonce_length != kAesGcmNonceLength)) { + aes_gcm_format_errors( + "Invalid key and/or nonce and/or tag length are provided at AEAD " + "crypter instance construction time.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + static_cast<gsec_aes_gcm_aead_crypter*>( + gpr_malloc(sizeof(gsec_aes_gcm_aead_crypter))); + aes_gcm_crypter->crypter.vtable = &vtable; + aes_gcm_crypter->nonce_length = nonce_length; + aes_gcm_crypter->tag_length = tag_length; + if (rekey) { + aes_gcm_crypter->key_length = kKdfKeyLen; + aes_gcm_crypter->rekey_data = static_cast<gsec_aes_gcm_aead_rekey_data*>( + gpr_malloc(sizeof(gsec_aes_gcm_aead_rekey_data))); + memcpy(aes_gcm_crypter->rekey_data->nonce_mask, key + kKdfKeyLen, + kAesGcmNonceLength); + // Set kdf_counter to all-zero for initial key derivation. + memset(aes_gcm_crypter->rekey_data->kdf_counter, 0, kKdfCounterLen); + } else { + aes_gcm_crypter->key_length = key_length; + aes_gcm_crypter->rekey_data = nullptr; + } + aes_gcm_crypter->key = + static_cast<uint8_t*>(gpr_malloc(aes_gcm_crypter->key_length)); + memcpy(aes_gcm_crypter->key, key, aes_gcm_crypter->key_length); + aes_gcm_crypter->ctx = EVP_CIPHER_CTX_new(); + grpc_status_code status = + aes_gcm_new_evp_cipher_ctx(aes_gcm_crypter, error_details); + if (status != GRPC_STATUS_OK) { + gsec_aes_gcm_aead_crypter_destroy(&aes_gcm_crypter->crypter); + gpr_free(aes_gcm_crypter); + return status; + } + *crypter = &aes_gcm_crypter->crypter; + return GRPC_STATUS_OK; +} diff --git a/src/core/tsi/alts/crypt/gsec.cc b/src/core/tsi/alts/crypt/gsec.cc new file mode 100644 index 0000000000..6236591a97 --- /dev/null +++ b/src/core/tsi/alts/crypt/gsec.cc @@ -0,0 +1,189 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/crypt/gsec.h" + +#include <stdio.h> +#include <string.h> + +#include <grpc/support/alloc.h> + +static const char vtable_error_msg[] = + "crypter or crypter->vtable has not been initialized properly"; + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code gsec_aead_crypter_encrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* plaintext, + size_t plaintext_length, uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, size_t* bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + struct iovec aad_vec = {(void*)aad, aad_length}; + struct iovec plaintext_vec = {(void*)plaintext, plaintext_length}; + struct iovec ciphertext_vec = {ciphertext_and_tag, + ciphertext_and_tag_length}; + return crypter->vtable->encrypt_iovec( + crypter, nonce, nonce_length, &aad_vec, 1, &plaintext_vec, 1, + ciphertext_vec, bytes_written, error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + return crypter->vtable->encrypt_iovec( + crypter, nonce, nonce_length, aad_vec, aad_vec_length, plaintext_vec, + plaintext_vec_length, ciphertext_vec, ciphertext_bytes_written, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_decrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, uint8_t* plaintext, + size_t plaintext_length, size_t* bytes_written, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->decrypt_iovec != nullptr) { + struct iovec aad_vec = {(void*)aad, aad_length}; + struct iovec ciphertext_vec = {(void*)ciphertext_and_tag, + ciphertext_and_tag_length}; + struct iovec plaintext_vec = {plaintext, plaintext_length}; + return crypter->vtable->decrypt_iovec( + crypter, nonce, nonce_length, &aad_vec, 1, &ciphertext_vec, 1, + plaintext_vec, bytes_written, error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + return crypter->vtable->decrypt_iovec( + crypter, nonce, nonce_length, aad_vec, aad_vec_length, ciphertext_vec, + ciphertext_vec_length, plaintext_vec, plaintext_bytes_written, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->max_ciphertext_and_tag_length != nullptr) { + return crypter->vtable->max_ciphertext_and_tag_length( + crypter, plaintext_length, max_ciphertext_and_tag_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->max_plaintext_length != nullptr) { + return crypter->vtable->max_plaintext_length( + crypter, ciphertext_and_tag_length, max_plaintext_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->nonce_length != nullptr) { + return crypter->vtable->nonce_length(crypter, nonce_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->key_length != nullptr) { + return crypter->vtable->key_length(crypter, key_length_to_return, + error_details); + } + /* An error occurred */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->tag_length != nullptr) { + return crypter->vtable->tag_length(crypter, tag_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter) { + if (crypter != nullptr) { + if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) { + crypter->vtable->destruct(crypter); + } + gpr_free(crypter); + } +} diff --git a/src/core/tsi/alts/crypt/gsec.h b/src/core/tsi/alts/crypt/gsec.h new file mode 100644 index 0000000000..4d65caa944 --- /dev/null +++ b/src/core/tsi/alts/crypt/gsec.h @@ -0,0 +1,454 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H +#define GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H + +#include <grpc/support/port_platform.h> + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> + +#include <grpc/grpc.h> + +struct iovec { + void* iov_base; + size_t iov_len; +}; + +/** + * A gsec interface for AEAD encryption schemes. The API is thread-compatible. + * Each implementation of this interface should specify supported values for + * key, nonce, and tag lengths. + */ + +/* Key, nonce, and tag length in bytes */ +const size_t kAesGcmNonceLength = 12; +const size_t kAesGcmTagLength = 16; +const size_t kAes128GcmKeyLength = 16; +const size_t kAes256GcmKeyLength = 32; + +// The first 32 bytes are used as a KDF key and the remaining 12 bytes are used +// to mask the nonce. +const size_t kAes128GcmRekeyKeyLength = 44; + +typedef struct gsec_aead_crypter gsec_aead_crypter; + +/** + * The gsec_aead_crypter is an API for different AEAD implementations such as + * AES_GCM. It encapsulates all AEAD-related operations in the format of + * V-table that stores pointers to functions implementing those operations. + * It also provides helper functions to wrap each of those function pointers. + * + * A typical usage of this object would be: + * + *------------------------------------------------------------------------------ + * // Declare a gsec_aead_crypter object, and create and assign an instance + * // of specific AEAD implementation e.g., AES_GCM to it. We assume both + * // key and nonce contain cryptographically secure random bytes, and the key + * // can be derived from an upper-layer application. + * gsec_aead_crypter* crypter; + * char* error_in_creation; + * // User can populate the message with any 100 bytes data. + * uint8_t* message = gpr_malloc(100); + * grpc_status_code creation_status = gsec_aes_gcm_aead_crypter_create(key, + * kAes128GcmKeyLength, + * kAesGcmNonceLength, + * kAesGcmTagLength, + * &crypter, + * false, + * 0 + * &error_in_creation); + * + * if (creation_status == GRPC_STATUS_OK) { + * // Allocate a correct amount of memory to hold a ciphertext. + * size_t clength = 0; + * gsec_aead_crypter_max_ciphertext_and_tag_length(crypter, 100, &clength, + * nullptr); + * uint8_t* ciphertext = gpr_malloc(clength); + * + * // Perform encryption + * size_t num_encrypted_bytes = 0; + * char* error_in_encryption = nullptr; + * grpc_status_code status = gsec_aead_crypter_encrypt(crypter, nonce, + * kAesGcmNonceLength, + * nullptr, 0, message, + * 100, ciphertext, + * clength, + * &num_encrypted_bytes, + * &error_in_encryption); + * if (status == GRPC_STATUS_OK) { + * // Allocate a correct amount of memory to hold a plaintext. + * size_t plength = 0; + * gsec_aead_crypter_max_plaintext_length(crypter, num_encrypted_bytes, + * &plength, nullptr); + * uint8_t* plaintext = gpr_malloc(plength); + * + * // Perform decryption. + * size_t num_decrypted_bytes = 0; + * char* error_in_decryption = nullptr; + * status = gsec_aead_crypter_decrypt(crypter, nonce, + * kAesGcmNonceLength, nullptr, 0, + * ciphertext, num_encrypted_bytes, + * plaintext, plength, + * &num_decrypted_bytes, + * &error_in_decryption); + * if (status != GRPC_STATUS_OK) { + * fprintf(stderr, "AEAD decrypt operation failed with error code:" + * "%d, message: %s\n", status, error_in_decryption); + * } + * ... + * gpr_free(plaintext); + * gpr_free(error_in_decryption); + * } else { + * fprintf(stderr, "AEAD encrypt operation failed with error code:" + * "%d, message: %s\n", status, error_in_encryption); + * } + * ... + * gpr_free(ciphertext); + * gpr_free(error_in_encryption); + * } else { + * fprintf(stderr, "Creation of AEAD crypter instance failed with error code:" + * "%d, message: %s\n", creation_status, error_in_creation); + * } + * + * // Destruct AEAD crypter instance. + * if (creation_status == GRPC_STATUS_OK) { + * gsec_aead_crypter_destroy(crypter); + * } + * gpr_free(error_in_creation); + * gpr_free(message); + * ----------------------------------------------------------------------------- + */ + +/* V-table for gsec AEAD operations */ +typedef struct gsec_aead_crypter_vtable { + grpc_status_code (*encrypt_iovec)( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details); + grpc_status_code (*decrypt_iovec)( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details); + grpc_status_code (*max_ciphertext_and_tag_length)( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details); + grpc_status_code (*max_plaintext_length)( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details); + grpc_status_code (*nonce_length)(const gsec_aead_crypter* crypter, + size_t* nonce_length_to_return, + char** error_details); + grpc_status_code (*key_length)(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details); + grpc_status_code (*tag_length)(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details); + void (*destruct)(gsec_aead_crypter* crypter); +} gsec_aead_crypter_vtable; + +/* Main struct for gsec interface */ +struct gsec_aead_crypter { + const struct gsec_aead_crypter_vtable* vtable; +}; + +/** + * This method performs an AEAD encrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad: buffer containing data that needs to be authenticated but not + * encrypted with its size equal to aad_length. + * - aad_length: size of aad buffer, which should be zero if the buffer is + * nullptr. + * - plaintext: buffer containing data that needs to be both encrypted and + * authenticated with its size equal to plaintext_length. + * - plaintext_length: size of plaintext buffer, which should be zero if + * plaintext is nullptr. + * - ciphertext_and_tag: buffer that will contain ciphertext and tags the method + * produced. The buffer should not overlap the plaintext buffer, and pointers + * to those buffers should not be equal. Also if the ciphertext+tag buffer is + * nullptr, the plaintext_length should be zero. + * - ciphertext_and_tag_length: size of ciphertext+tag buffer, which should be + * at least as long as the one returned from method + * gsec_aead_crypter_max_ciphertext_and_tag_length. + * - bytes_written: the actual number of bytes written to the ciphertext+tag + * buffer. If bytes_written is nullptr, the plaintext_length should be zero. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + * + */ +grpc_status_code gsec_aead_crypter_encrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* plaintext, + size_t plaintext_length, uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, size_t* bytes_written, + char** error_details); + +/** + * This method performs an AEAD encrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad_vec: an iovec array containing data that needs to be authenticated but + * not encrypted. + * - aad_vec_length: the array length of aad_vec. + * - plaintext_vec: an iovec array containing data that needs to be both + * encrypted and authenticated. + * - plaintext_vec_length: the array length of plaintext_vec. + * - ciphertext_vec: an iovec containing a ciphertext buffer. The buffer should + * not overlap the plaintext buffer. + * - ciphertext_bytes_written: the actual number of bytes written to + * ciphertext_vec. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + * + */ +grpc_status_code gsec_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details); + +/** + * This method performs an AEAD decrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad: buffer containing data that needs to be authenticated only. + * - aad_length: size of aad buffer, which should be zero if the buffer is + * nullptr. + * - ciphertext_and_tag: buffer containing ciphertext and tag. + * - ciphertext_and_tag_length: length of ciphertext and tag. It should be zero + * if any of plaintext, ciphertext_and_tag, or bytes_written is nullptr. Also, + * ciphertext_and_tag_length should be at least as large as the tag length set + * at AEAD crypter instance construction time. + * - plaintext: buffer containing decrypted and authenticated data the method + * produced. The buffer should not overlap with the ciphertext+tag buffer, and + * pointers to those buffers should not be equal. + * - plaintext_length: size of plaintext buffer, which should be at least as + * long as the one returned from gsec_aead_crypter_max_plaintext_length + * method. + * - bytes_written: the actual number of bytes written to the plaintext + * buffer. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_decrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, uint8_t* plaintext, + size_t plaintext_length, size_t* bytes_written, char** error_details); + +/** + * This method performs an AEAD decrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad_vec: an iovec array containing data that needs to be authenticated but + * not encrypted. + * - aad_vec_length: the array length of aad_vec. + * - ciphertext_vec: an iovec array containing the ciphertext and tag. + * - ciphertext_vec_length: the array length of ciphertext_vec. + * - plaintext_vec: an iovec containing a plaintext buffer. The buffer should + * not overlap the ciphertext buffer. + * - plaintext_bytes_written: the actual number of bytes written to + * plaintext_vec. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details); + +/** + * This method computes the size of ciphertext+tag buffer that must be passed to + * gsec_aead_crypter_encrypt function to ensure correct encryption of a + * plaintext. The actual size of ciphertext+tag written to the buffer could be + * smaller. + * + * - crypter: AEAD crypter instance. + * - plaintext_length: length of plaintext. + * - max_ciphertext_and_tag_length_to_return: the size of ciphertext+tag buffer + * the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details); + +/** + * This method computes the size of plaintext buffer that must be passed to + * gsec_aead_crypter_decrypt function to ensure correct decryption of a + * ciphertext. The actual size of plaintext written to the buffer could be + * smaller. + * + * - crypter: AEAD crypter instance. + * - ciphertext_and_tag_length: length of ciphertext and tag. + * - max_plaintext_length_to_return: the size of plaintext buffer the method + * returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details); + +/** + * This method returns a valid size of nonce array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - nonce_length_to_return: the length of nonce array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length_to_return, + char** error_details); + +/** + * This method returns a valid size of key array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - key_length_to_return: the length of key array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details); +/** + * This method returns a valid size of tag array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - tag_length_to_return: the length of tag array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details); + +/** + * This method destroys an AEAD crypter instance by de-allocating all of its + * occupied memory. + * + * - crypter: AEAD crypter instance that needs to be destroyed. + */ +void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter); + +/** + * This method creates an AEAD crypter instance of AES-GCM encryption scheme + * which supports 16 and 32 bytes long keys, 12 and 16 bytes long nonces, and + * 16 bytes long tags. It should be noted that once the lengths of key, nonce, + * and tag are determined at construction time, they cannot be modified later. + * + * - key: buffer containing a key which is binded with AEAD crypter instance. + * - key_length: length of a key in bytes, which should be 44 if rekeying is + * enabled and 16 or 32 otherwise. + * - nonce_length: length of a nonce in bytes, which should be either 12 or 16. + * - tag_length: length of a tag in bytes, which should be always 16. + * - rekey: enable nonce-based rekeying and nonce-masking. + * - crypter: address of AES_GCM crypter instance returned from the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of instance creation, it stores the address of instance at + * crypter. Otherwise, it returns an error status code together with its details + * specified in error_details. + */ +grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key, + size_t key_length, + size_t nonce_length, + size_t tag_length, bool rekey, + gsec_aead_crypter** crypter, + char** error_details); + +#endif /* GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H */ diff --git a/src/core/tsi/alts/frame_protector/alts_counter.cc b/src/core/tsi/alts/frame_protector/alts_counter.cc new file mode 100644 index 0000000000..de163e3e08 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_counter.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" + +#include <string.h> + +#include <grpc/support/alloc.h> + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code alts_counter_create(bool is_client, size_t counter_size, + size_t overflow_size, + alts_counter** crypter_counter, + char** error_details) { + /* Perform input sanity check. */ + if (counter_size == 0) { + const char error_msg[] = "counter_size is invalid."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (overflow_size == 0 || overflow_size >= counter_size) { + const char error_msg[] = "overflow_size is invalid."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (crypter_counter == nullptr) { + const char error_msg[] = "crypter_counter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *crypter_counter = + static_cast<alts_counter*>(gpr_malloc(sizeof(**crypter_counter))); + (*crypter_counter)->size = counter_size; + (*crypter_counter)->overflow_size = overflow_size; + (*crypter_counter)->counter = + static_cast<unsigned char*>(gpr_zalloc(counter_size)); + if (is_client) { + ((*crypter_counter)->counter)[counter_size - 1] = 0x80; + } + return GRPC_STATUS_OK; +} + +grpc_status_code alts_counter_increment(alts_counter* crypter_counter, + bool* is_overflow, + char** error_details) { + /* Perform input sanity check. */ + if (crypter_counter == nullptr) { + const char error_msg[] = "crypter_counter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (is_overflow == nullptr) { + const char error_msg[] = "is_overflow is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Increment the internal counter. */ + size_t i = 0; + for (; i < crypter_counter->overflow_size; i++) { + (crypter_counter->counter)[i]++; + if ((crypter_counter->counter)[i] != 0x00) { + break; + } + } + /** + * If the lower overflow_size bytes are all zero, the counter has overflowed. + */ + if (i == crypter_counter->overflow_size) { + *is_overflow = true; + return GRPC_STATUS_FAILED_PRECONDITION; + } + *is_overflow = false; + return GRPC_STATUS_OK; +} + +size_t alts_counter_get_size(alts_counter* crypter_counter) { + if (crypter_counter == nullptr) { + return 0; + } + return crypter_counter->size; +} + +unsigned char* alts_counter_get_counter(alts_counter* crypter_counter) { + if (crypter_counter == nullptr) { + return nullptr; + } + return crypter_counter->counter; +} + +void alts_counter_destroy(alts_counter* crypter_counter) { + if (crypter_counter != nullptr) { + gpr_free(crypter_counter->counter); + gpr_free(crypter_counter); + } +} diff --git a/src/core/tsi/alts/frame_protector/alts_counter.h b/src/core/tsi/alts/frame_protector/alts_counter.h new file mode 100644 index 0000000000..d705638fa8 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_counter.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> +#include <stdlib.h> + +#include <grpc/grpc.h> + +/* Main struct for a crypter counter managed within seal/unseal operations. */ +typedef struct alts_counter { + size_t size; + size_t overflow_size; + unsigned char* counter; +} alts_counter; + +/** + * This method creates and initializes an alts_counter instance. + * + * - is_client: a flag indicating if the alts_counter instance will be used + * at client (is_client = true) or server (is_client = false) side. + * - counter_size: size of buffer holding the counter value. + * - overflow_size: overflow size in bytes. The counter instance can be used + * to produce at most 2^(overflow_size*8) frames. + * - crypter_counter: an alts_counter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_counter_create(bool is_client, size_t counter_size, + size_t overflow_size, + alts_counter** crypter_counter, + char** error_details); + +/** + * This method increments the internal counter. + * + * - crypter_counter: an alts_counter instance. + * - is_overflow: after incrementing the internal counter, if an overflow + * occurs, is_overflow is set to true, and no further calls to + * alts_counter_increment() should be made. Otherwise, is_overflow is set to + * false. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_counter_increment(alts_counter* crypter_counter, + bool* is_overflow, + char** error_details); + +/** + * This method returns the size of counter buffer. + * + * - crypter_counter: an alts_counter instance. + */ +size_t alts_counter_get_size(alts_counter* crypter_counter); + +/** + * This method returns the counter buffer. + * + * - crypter_counter: an alts_counter instance. + */ +unsigned char* alts_counter_get_counter(alts_counter* crypter_counter); + +/** + * This method de-allocates all memory allocated to an alts_coutner instance. + * - crypter_counter: an alts_counter instance. + */ +void alts_counter_destroy(alts_counter* crypter_counter); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H */ diff --git a/src/core/tsi/alts/frame_protector/alts_crypter.cc b/src/core/tsi/alts/frame_protector/alts_crypter.cc new file mode 100644 index 0000000000..56f0512186 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_crypter.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" + +#include <string.h> + +#include <grpc/support/alloc.h> + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code alts_crypter_process_in_place( + alts_crypter* crypter, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->process_in_place != nullptr) { + return crypter->vtable->process_in_place(crypter, data, data_allocated_size, + data_size, output_size, + error_details); + } + /* An error occurred. */ + const char error_msg[] = + "crypter or crypter->vtable has not been initialized properly."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->num_overhead_bytes != nullptr) { + return crypter->vtable->num_overhead_bytes(crypter); + } + /* An error occurred. */ + return 0; +} + +void alts_crypter_destroy(alts_crypter* crypter) { + if (crypter != nullptr) { + if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) { + crypter->vtable->destruct(crypter); + } + gpr_free(crypter); + } +} diff --git a/src/core/tsi/alts/frame_protector/alts_crypter.h b/src/core/tsi/alts/frame_protector/alts_crypter.h new file mode 100644 index 0000000000..3140778f4f --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_crypter.h @@ -0,0 +1,255 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> +#include <string.h> + +#include <grpc/grpc.h> + +#include "src/core/tsi/alts/crypt/gsec.h" + +/** + * An alts_crypter interface for an ALTS record protocol providing + * seal/unseal functionality. The interface is thread-compatible. + */ + +typedef struct alts_crypter alts_crypter; + +/** + * A typical usage of the interface would be + *------------------------------------------------------------------------------ + * // Perform a seal operation. We assume the gsec_aead_crypter instance - + * // client_aead_crypter is created beforehand with a 16-byte key and 12-byte + * // nonce length. + * + * alts_crypter* client = nullptr; + * char* client_error_in_creation = nullptr; + * unsigned char* data = nullptr; + * grpc_status_code client_status = + * alts_seal_crypter_create(client_aead_crypter, 1, 5, &client, + * &client_error_in_creation); + * if (client_status == GRPC_STATUS_OK) { + * size_t data_size = 100; + * size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(client); + * size_t data_allocated_size = data_size + num_overhead_bytes; + * data = gpr_malloc(data_allocated_size); + * char* client_error_in_seal = nullptr; + * // Client performs a seal operation. + * client_status = alts_crypter_process_in_place(client, data, + * data_allocated_size, + * &data_size, + * &client_error_in_seal); + * if (client_status != GRPC_STATUS_OK) { + * fprintf(stderr, "seal operation failed with error code:" + * "%d, message: %s\n", client_status, + * client_error_in_seal); + * } + * gpr_free(client_error_in_seal); + * } else { + * fprintf(stderr, "alts_crypter instance creation failed with error" + * "code: %d, message: %s\n", client_status, + * client_error_in_creation); + * } + * + * ... + * + * gpr_free(client_error_in_creation); + * alts_crypter_destroy(client); + * + * ... + * + * // Perform an unseal operation. We assume the gsec_aead_crypter instance - + * // server_aead_crypter is created beforehand with a 16-byte key and 12-byte + * // nonce length. The key used in the creation of gsec_aead_crypter instances + * // at server and client sides should be identical. + * + * alts_crypter* server = nullptr; + * char* server_error_in_creation = nullptr; + * grpc_status_code server_status = + * alts_unseal_crypter_create(server_aead_crypter, 0, 5, &server, + * &server_error_in_creation); + * if (server_status == GRPC_STATUS_OK) { + * size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server); + * size_t data_size = 100 + num_overhead_bytes; + * size_t data_allocated_size = data_size; + * char* server_error_in_unseal = nullptr; + * // Server performs an unseal operation. + * server_status = alts_crypter_process_in_place(server, data, + * data_allocated_size, + * &data_size, + * &server_error_in_unseal); + * if (server_status != GRPC_STATUS_OK) { + * fprintf(stderr, "unseal operation failed with error code:" + * "%d, message: %s\n", server_status, + * server_error_in_unseal); + * } + * gpr_free(server_error_in_unseal); + * } else { + * fprintf(stderr, "alts_crypter instance creation failed with error" + * "code: %d, message: %s\n", server_status, + * server_error_in_creation); + * } + * + * ... + * + * gpr_free(data); + * gpr_free(server_error_in_creation); + * alts_crypter_destroy(server); + * + * ... + *------------------------------------------------------------------------------ + */ + +/* V-table for alts_crypter operations */ +typedef struct alts_crypter_vtable { + size_t (*num_overhead_bytes)(const alts_crypter* crypter); + grpc_status_code (*process_in_place)(alts_crypter* crypter, + unsigned char* data, + size_t data_allocated_size, + size_t data_size, size_t* output_size, + char** error_details); + void (*destruct)(alts_crypter* crypter); +} alts_crypter_vtable; + +/* Main struct for alts_crypter interface */ +struct alts_crypter { + const alts_crypter_vtable* vtable; +}; + +/** + * This method gets the number of overhead bytes needed for sealing data that + * is the difference in size between the protected and raw data. The counter + * value used in a seal or unseal operation is locally maintained (not sent or + * received from the other peer) and therefore, will not be counted as part of + * overhead bytes. + * + * - crypter: an alts_crypter instance. + * + * On success, the method returns the number of overhead bytes. Otherwise, it + * returns zero. + * + */ +size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter); + +/** + * This method performs either a seal or an unseal operation depending on the + * alts_crypter instance - crypter passed to the method. If the crypter is + * an instance implementing a seal operation, the method will perform a seal + * operation. That is, it seals raw data and stores the result in-place, and the + * memory allocated for data must be at least data_length + + * alts_crypter_num_overhead_bytes(). If the crypter is an instance + * implementing an unseal operation, the method will perform an unseal + * operation. That is, it unseals protected data and stores the result in-place. + * The size of unsealed data will be data_length - + * alts_crypter_num_overhead_bytes(). Integrity tag will be verified during + * the unseal operation, and if verification fails, the data will be wiped. + * The counters used in both seal and unseal operations are managed internally. + * + * - crypter: an alts_crypter instance. + * - data: if the method performs a seal operation, the data represents raw data + * that needs to be sealed. It also plays the role of buffer to hold the + * protected data as a result of seal. If the method performs an unseal + * operation, the data represents protected data that needs to be unsealed. It + * also plays the role of buffer to hold raw data as a result of unseal. + * - data_allocated_size: the size of data buffer. The parameter is used to + * check whether the result of either seal or unseal can be safely written to + * the data buffer. + * - data_size: if the method performs a seal operation, data_size + * represents the size of raw data that needs to be sealed, and if the method + * performs an unseal operation, data_size represents the size of protected + * data that needs to be unsealed. + * - output_size: size of data written to the data buffer after a seal or an + * unseal operation. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_crypter_process_in_place( + alts_crypter* crypter, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details); + +/** + * This method creates an alts_crypter instance to be used to perform a seal + * operation, given a gsec_aead_crypter instance and a flag indicating if the + * created instance will be used at the client or server side. It takes + * ownership of gsec_aead_crypter instance. + * + * - gc: a gsec_aead_crypter instance used to perform AEAD encryption. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - crypter: an alts_crypter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns GRPC_STATUS_OK. + * Otherwise, it returns an error status code along with its details specified + * in error_details (if error_details is not nullptr). + */ +grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details); + +/** + * This method creates an alts_crypter instance used to perform an unseal + * operation, given a gsec_aead_crypter instance and a flag indicating if the + * created instance will be used at the client or server side. It takes + * ownership of gsec_aead_crypter instance. + * + * - gc: a gsec_aead_crypter instance used to perform AEAD decryption. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - crypter: an alts_crypter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns GRPC_STATUS_OK. + * Otherwise, it returns an error status code along with its details specified + * in error_details (if error_details is not nullptr). + */ +grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc, + bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details); + +/** + * This method destroys an alts_crypter instance by de-allocating all of its + * occupied memory. A gsec_aead_crypter instance passed in at alts_crypter + * instance creation time will be destroyed in this method. + * + * - crypter: an alts_crypter instance. + */ +void alts_crypter_destroy(alts_crypter* crypter); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H */ diff --git a/src/core/tsi/alts/frame_protector/alts_frame_protector.cc b/src/core/tsi/alts/frame_protector/alts_frame_protector.cc new file mode 100644 index 0000000000..bfa0b7a720 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_frame_protector.cc @@ -0,0 +1,407 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h" + +#include <stdio.h> +#include <stdlib.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/frame_handler.h" +#include "src/core/tsi/transport_security.h" + +constexpr size_t kMinFrameLength = 1024; +constexpr size_t kDefaultFrameLength = 16 * 1024; +constexpr size_t kMaxFrameLength = 1024 * 1024; + +// Limit k on number of frames such that at most 2^(8 * k) frames can be sent. +constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8; +constexpr size_t kAltsRecordProtocolFrameLimit = 5; + +/* Main struct for alts_frame_protector. */ +struct alts_frame_protector { + tsi_frame_protector base; + alts_crypter* seal_crypter; + alts_crypter* unseal_crypter; + alts_frame_writer* writer; + alts_frame_reader* reader; + unsigned char* in_place_protect_buffer; + unsigned char* in_place_unprotect_buffer; + size_t in_place_protect_bytes_buffered; + size_t in_place_unprotect_bytes_processed; + size_t max_protected_frame_size; + size_t max_unprotected_frame_size; + size_t overhead_length; + size_t counter_overflow; +}; + +static tsi_result seal(alts_frame_protector* impl) { + char* error_details = nullptr; + size_t output_size = 0; + grpc_status_code status = alts_crypter_process_in_place( + impl->seal_crypter, impl->in_place_protect_buffer, + impl->max_protected_frame_size, impl->in_place_protect_bytes_buffered, + &output_size, &error_details); + impl->in_place_protect_bytes_buffered = output_size; + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "%s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +static size_t max_encrypted_payload_bytes(alts_frame_protector* impl) { + return impl->max_protected_frame_size - kFrameHeaderSize; +} + +static tsi_result alts_protect_flush(tsi_frame_protector* self, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size, + size_t* still_pending_size) { + if (self == nullptr || protected_output_frames == nullptr || + protected_output_frames_size == nullptr || + still_pending_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect_flush()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self); + /** + * If there's nothing to flush (i.e., in_place_protect_buffer is empty), + * we're done. + */ + if (impl->in_place_protect_bytes_buffered == 0) { + *protected_output_frames_size = 0; + *still_pending_size = 0; + return TSI_OK; + } + /** + * If a new frame can start being processed, we encrypt the payload and reset + * the frame writer to point to in_place_protect_buffer that holds the newly + * sealed frame. + */ + if (alts_is_frame_writer_done(impl->writer)) { + tsi_result result = seal(impl); + if (result != TSI_OK) { + return result; + } + if (!alts_reset_frame_writer(impl->writer, impl->in_place_protect_buffer, + impl->in_place_protect_bytes_buffered)) { + gpr_log(GPR_ERROR, "Couldn't reset frame writer."); + return TSI_INTERNAL_ERROR; + } + } + /** + * Write the sealed frame as much as possible to protected_output_frames. It's + * possible a frame will not be written out completely by a single flush + * (i.e., still_pending_size != 0), in which case the flush should be called + * iteratively until a complete frame has been written out. + */ + size_t written_frame_bytes = *protected_output_frames_size; + if (!alts_write_frame_bytes(impl->writer, protected_output_frames, + &written_frame_bytes)) { + gpr_log(GPR_ERROR, "Couldn't write frame bytes."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = written_frame_bytes; + *still_pending_size = alts_get_num_writer_bytes_remaining(impl->writer); + /** + * If the current frame has been finished processing (i.e., sealed and written + * out completely), we empty in_place_protect_buffer. + */ + if (alts_is_frame_writer_done(impl->writer)) { + impl->in_place_protect_bytes_buffered = 0; + } + return TSI_OK; +} + +static tsi_result alts_protect(tsi_frame_protector* self, + const unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size) { + if (self == nullptr || unprotected_bytes == nullptr || + unprotected_bytes_size == nullptr || protected_output_frames == nullptr || + protected_output_frames_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self); + + /** + * If more payload can be buffered, we buffer it as much as possible to + * in_place_protect_buffer. + */ + if (impl->in_place_protect_bytes_buffered + impl->overhead_length < + max_encrypted_payload_bytes(impl)) { + size_t bytes_to_buffer = GPR_MIN(*unprotected_bytes_size, + max_encrypted_payload_bytes(impl) - + impl->in_place_protect_bytes_buffered - + impl->overhead_length); + *unprotected_bytes_size = bytes_to_buffer; + if (bytes_to_buffer > 0) { + memcpy( + impl->in_place_protect_buffer + impl->in_place_protect_bytes_buffered, + unprotected_bytes, bytes_to_buffer); + impl->in_place_protect_bytes_buffered += bytes_to_buffer; + } + } else { + *unprotected_bytes_size = 0; + } + /** + * If a full frame has been buffered, we output it. If the first condition + * holds, then there exists an unencrypted full frame. If the second + * condition holds, then there exists a full frame that has already been + * encrypted. + */ + if (max_encrypted_payload_bytes(impl) == + impl->in_place_protect_bytes_buffered + impl->overhead_length || + max_encrypted_payload_bytes(impl) == + impl->in_place_protect_bytes_buffered) { + size_t still_pending_size = 0; + return alts_protect_flush(self, protected_output_frames, + protected_output_frames_size, + &still_pending_size); + } else { + *protected_output_frames_size = 0; + return TSI_OK; + } +} + +static tsi_result unseal(alts_frame_protector* impl) { + char* error_details = nullptr; + size_t output_size = 0; + grpc_status_code status = alts_crypter_process_in_place( + impl->unseal_crypter, impl->in_place_unprotect_buffer, + impl->max_unprotected_frame_size, + alts_get_output_bytes_read(impl->reader), &output_size, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "%s", error_details); + gpr_free(error_details); + return TSI_DATA_CORRUPTED; + } + return TSI_OK; +} + +static void ensure_buffer_size(alts_frame_protector* impl) { + if (!alts_has_read_frame_length(impl->reader)) { + return; + } + size_t buffer_space_remaining = impl->max_unprotected_frame_size - + alts_get_output_bytes_read(impl->reader); + /** + * Check if we need to resize in_place_unprotect_buffer in order to hold + * remaining bytes of a full frame. + */ + if (buffer_space_remaining < alts_get_reader_bytes_remaining(impl->reader)) { + size_t buffer_len = alts_get_output_bytes_read(impl->reader) + + alts_get_reader_bytes_remaining(impl->reader); + unsigned char* buffer = static_cast<unsigned char*>(gpr_malloc(buffer_len)); + memcpy(buffer, impl->in_place_unprotect_buffer, + alts_get_output_bytes_read(impl->reader)); + impl->max_unprotected_frame_size = buffer_len; + gpr_free(impl->in_place_unprotect_buffer); + impl->in_place_unprotect_buffer = buffer; + alts_reset_reader_output_buffer( + impl->reader, buffer + alts_get_output_bytes_read(impl->reader)); + } +} + +static tsi_result alts_unprotect(tsi_frame_protector* self, + const unsigned char* protected_frames_bytes, + size_t* protected_frames_bytes_size, + unsigned char* unprotected_bytes, + size_t* unprotected_bytes_size) { + if (self == nullptr || protected_frames_bytes == nullptr || + protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr || + unprotected_bytes_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_unprotect()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self); + /** + * If a new frame can start being processed, we reset the frame reader to + * point to in_place_unprotect_buffer that will be used to hold deframed + * result. + */ + if (alts_is_frame_reader_done(impl->reader) && + ((alts_get_output_buffer(impl->reader) == nullptr) || + (alts_get_output_bytes_read(impl->reader) == + impl->in_place_unprotect_bytes_processed + impl->overhead_length))) { + if (!alts_reset_frame_reader(impl->reader, + impl->in_place_unprotect_buffer)) { + gpr_log(GPR_ERROR, "Couldn't reset frame reader."); + return TSI_INTERNAL_ERROR; + } + impl->in_place_unprotect_bytes_processed = 0; + } + /** + * If a full frame has not yet been read, we read more bytes from + * protected_frames_bytes until a full frame has been read. We also need to + * make sure in_place_unprotect_buffer is large enough to hold a complete + * frame. + */ + if (!alts_is_frame_reader_done(impl->reader)) { + ensure_buffer_size(impl); + *protected_frames_bytes_size = + GPR_MIN(impl->max_unprotected_frame_size - + alts_get_output_bytes_read(impl->reader), + *protected_frames_bytes_size); + size_t read_frames_bytes_size = *protected_frames_bytes_size; + if (!alts_read_frame_bytes(impl->reader, protected_frames_bytes, + &read_frames_bytes_size)) { + gpr_log(GPR_ERROR, "Failed to process frame."); + return TSI_INTERNAL_ERROR; + } + *protected_frames_bytes_size = read_frames_bytes_size; + } else { + *protected_frames_bytes_size = 0; + } + /** + * If a full frame has been read, we unseal it, and write out the + * deframed result to unprotected_bytes. + */ + if (alts_is_frame_reader_done(impl->reader)) { + if (impl->in_place_unprotect_bytes_processed == 0) { + tsi_result result = unseal(impl); + if (result != TSI_OK) { + return result; + } + } + size_t bytes_to_write = GPR_MIN( + *unprotected_bytes_size, alts_get_output_bytes_read(impl->reader) - + impl->in_place_unprotect_bytes_processed - + impl->overhead_length); + if (bytes_to_write > 0) { + memcpy(unprotected_bytes, + impl->in_place_unprotect_buffer + + impl->in_place_unprotect_bytes_processed, + bytes_to_write); + } + *unprotected_bytes_size = bytes_to_write; + impl->in_place_unprotect_bytes_processed += bytes_to_write; + return TSI_OK; + } else { + *unprotected_bytes_size = 0; + return TSI_OK; + } +} + +static void alts_destroy(tsi_frame_protector* self) { + alts_frame_protector* impl = reinterpret_cast<alts_frame_protector*>(self); + if (impl != nullptr) { + alts_crypter_destroy(impl->seal_crypter); + alts_crypter_destroy(impl->unseal_crypter); + gpr_free(impl->in_place_protect_buffer); + gpr_free(impl->in_place_unprotect_buffer); + alts_destroy_frame_writer(impl->writer); + alts_destroy_frame_reader(impl->reader); + gpr_free(impl); + } +} + +static const tsi_frame_protector_vtable alts_frame_protector_vtable = { + alts_protect, alts_protect_flush, alts_unprotect, alts_destroy}; + +static grpc_status_code create_alts_crypters(const uint8_t* key, + size_t key_size, bool is_client, + bool is_rekey, + alts_frame_protector* impl, + char** error_details) { + grpc_status_code status; + gsec_aead_crypter* aead_crypter_seal = nullptr; + gsec_aead_crypter* aead_crypter_unseal = nullptr; + status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength, + kAesGcmTagLength, is_rekey, + &aead_crypter_seal, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + status = gsec_aes_gcm_aead_crypter_create( + key, key_size, kAesGcmNonceLength, kAesGcmTagLength, is_rekey, + &aead_crypter_unseal, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + size_t overflow_size = is_rekey ? kAltsRecordProtocolRekeyFrameLimit + : kAltsRecordProtocolFrameLimit; + status = alts_seal_crypter_create(aead_crypter_seal, is_client, overflow_size, + &impl->seal_crypter, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + status = + alts_unseal_crypter_create(aead_crypter_unseal, is_client, overflow_size, + &impl->unseal_crypter, error_details); + return status; +} + +tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size, + bool is_client, bool is_rekey, + size_t* max_protected_frame_size, + tsi_frame_protector** self) { + if (key == nullptr || self == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_create_frame_protector()."); + return TSI_INTERNAL_ERROR; + } + char* error_details = nullptr; + alts_frame_protector* impl = + static_cast<alts_frame_protector*>(gpr_zalloc(sizeof(*impl))); + grpc_status_code status = create_alts_crypters( + key, key_size, is_client, is_rekey, impl, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create ALTS crypters, %s.", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /** + * Set maximum frame size to be used by a frame protector. If it is nullptr, a + * default frame size will be used. Otherwise, the provided frame size will be + * adjusted (if not falling into a valid frame range) and used. + */ + size_t max_protected_frame_size_to_set = kDefaultFrameLength; + if (max_protected_frame_size != nullptr) { + *max_protected_frame_size = + GPR_MIN(*max_protected_frame_size, kMaxFrameLength); + *max_protected_frame_size = + GPR_MAX(*max_protected_frame_size, kMinFrameLength); + max_protected_frame_size_to_set = *max_protected_frame_size; + } + impl->max_protected_frame_size = max_protected_frame_size_to_set; + impl->max_unprotected_frame_size = max_protected_frame_size_to_set; + impl->in_place_protect_bytes_buffered = 0; + impl->in_place_unprotect_bytes_processed = 0; + impl->in_place_protect_buffer = static_cast<unsigned char*>( + gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set)); + impl->in_place_unprotect_buffer = static_cast<unsigned char*>( + gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set)); + impl->overhead_length = alts_crypter_num_overhead_bytes(impl->seal_crypter); + impl->writer = alts_create_frame_writer(); + impl->reader = alts_create_frame_reader(); + impl->base.vtable = &alts_frame_protector_vtable; + *self = &impl->base; + return TSI_OK; +} diff --git a/src/core/tsi/alts/frame_protector/alts_frame_protector.h b/src/core/tsi/alts/frame_protector/alts_frame_protector.h new file mode 100644 index 0000000000..321bffaed8 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_frame_protector.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/transport_security_interface.h" + +typedef struct alts_frame_protector alts_frame_protector; + +/** + * TODO: Add a parameter to the interface to support the use of + * different record protocols within a frame protector. + * + * This method creates a frame protector. + * + * - key: a symmetric key used to seal/unseal frames. + * - key_size: the size of symmetric key. + * - is_client: a flag indicating if the frame protector will be used at client + * (is_client = true) or server (is_client = false) side. + * - is_rekey: a flag indicating if the frame protector will use an AEAD with + * rekeying. + * - max_protected_frame_size: an in/out parameter indicating max frame size + * to be used by the frame protector. If it is nullptr, the default frame + * size will be used. Otherwise, the provided frame size will be adjusted (if + * not falling into a valid frame range) and used. + * - self: a pointer to the frame protector returned from the method. + * + * This method returns TSI_OK on success and TSI_INTERNAL_ERROR otherwise. + */ +tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size, + bool is_client, bool is_rekey, + size_t* max_protected_frame_size, + tsi_frame_protector** self); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H */ diff --git a/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc new file mode 100644 index 0000000000..0574ed5012 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +#include <grpc/support/alloc.h> + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code input_sanity_check( + const alts_record_protocol_crypter* rp_crypter, const unsigned char* data, + size_t* output_size, char** error_details) { + if (rp_crypter == nullptr) { + maybe_copy_error_msg("alts_crypter instance is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } else if (data == nullptr) { + maybe_copy_error_msg("data is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } else if (output_size == nullptr) { + maybe_copy_error_msg("output_size is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter, + char** error_details) { + bool is_overflow = false; + grpc_status_code status = + alts_counter_increment(rp_crypter->ctr, &is_overflow, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (is_overflow) { + const char error_msg[] = + "crypter counter is wrapped. The connection" + "should be closed and the key should be deleted."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c) { + if (c != nullptr) { + size_t num_overhead_bytes = 0; + char* error_details = nullptr; + const alts_record_protocol_crypter* rp_crypter = + reinterpret_cast<const alts_record_protocol_crypter*>(c); + grpc_status_code status = gsec_aead_crypter_tag_length( + rp_crypter->crypter, &num_overhead_bytes, &error_details); + if (status == GRPC_STATUS_OK) { + return num_overhead_bytes; + } + } + return 0; +} + +void alts_record_protocol_crypter_destruct(alts_crypter* c) { + if (c != nullptr) { + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast<alts_record_protocol_crypter*>(c); + alts_counter_destroy(rp_crypter->ctr); + gsec_aead_crypter_destroy(rp_crypter->crypter); + } +} + +alts_record_protocol_crypter* alts_crypter_create_common( + gsec_aead_crypter* crypter, bool is_client, size_t overflow_size, + char** error_details) { + if (crypter != nullptr) { + auto* rp_crypter = static_cast<alts_record_protocol_crypter*>( + gpr_malloc(sizeof(alts_record_protocol_crypter))); + size_t counter_size = 0; + grpc_status_code status = + gsec_aead_crypter_nonce_length(crypter, &counter_size, error_details); + if (status != GRPC_STATUS_OK) { + return nullptr; + } + /* Create a counter. */ + status = alts_counter_create(is_client, counter_size, overflow_size, + &rp_crypter->ctr, error_details); + if (status != GRPC_STATUS_OK) { + return nullptr; + } + rp_crypter->crypter = crypter; + return rp_crypter; + } + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return nullptr; +} diff --git a/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h new file mode 100644 index 0000000000..682a8f7e7a --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H + +#include <grpc/support/port_platform.h> + +#include <grpc/grpc.h> + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" + +/** + * This file contains common implementation that will be used in both seal and + * unseal operations. + */ + +/** + * Main struct for alts_record_protocol_crypter that will be used in both + * seal and unseal operations. + */ +typedef struct alts_record_protocol_crypter { + alts_crypter base; + gsec_aead_crypter* crypter; + alts_counter* ctr; +} alts_record_protocol_crypter; + +/** + * This method performs input sanity checks on a subset of inputs to + * alts_crypter_process_in_place() for both seal and unseal operations. + * + * - rp_crypter: an alts_record_protocol_crypter instance. + * - data: it represents raw data that needs to be sealed in a seal operation or + * protected data that needs to be unsealed in an unseal operation. + * - output_size: size of data written to the data buffer after a seal or + * unseal operation. + * - error_details: a buffer containing an error message if any of checked + * inputs is nullptr. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code input_sanity_check( + const alts_record_protocol_crypter* rp_crypter, const unsigned char* data, + size_t* output_size, char** error_details); + +/** + * This method increments the counter within an alts_record_protocol_crypter + * instance. + * + * - rp_crypter: an alts_record_protocol_crypter instance. + * - error_details: a buffer containing an error message if the method does not + * function correctly or the counter is wrapped. It is legal to pass nullptr + * into error_details and otherwise, the parameter should be freed with + * gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter, + char** error_details); + +/** + * This method creates an alts_crypter instance, and populates the fields + * that are common to both seal and unseal operations. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. The + * function does not take ownership of crypter. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns alts_record_protocol_crypter + * instance. Otherwise, it returns nullptr with its details specified in + * error_details (if error_details is not nullptr). + * + */ +alts_record_protocol_crypter* alts_crypter_create_common( + gsec_aead_crypter* crypter, bool is_client, size_t overflow_size, + char** error_details); + +/** + * For the following two methods, please refer to the corresponding API in + * alts_crypter.h for detailed specifications. + */ +size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c); + +void alts_record_protocol_crypter_destruct(alts_crypter* c); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H \ + */ diff --git a/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc b/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc new file mode 100644 index 0000000000..f407831613 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc @@ -0,0 +1,105 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/alloc.h> + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Perform input santity check for a seal operation. */ +static grpc_status_code seal_check(alts_crypter* c, const unsigned char* data, + size_t data_allocated_size, size_t data_size, + size_t* output_size, char** error_details) { + /* Do common input sanity check. */ + grpc_status_code status = input_sanity_check( + reinterpret_cast<const alts_record_protocol_crypter*>(c), data, + output_size, error_details); + if (status != GRPC_STATUS_OK) return status; + /* Do seal-specific check. */ + size_t num_overhead_bytes = + alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c)); + if (data_size == 0) { + const char error_msg[] = "data_size is zero."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (data_size + num_overhead_bytes > data_allocated_size) { + const char error_msg[] = + "data_allocated_size is smaller than sum of data_size and " + "num_overhead_bytes."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code alts_seal_crypter_process_in_place( + alts_crypter* c, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + grpc_status_code status = seal_check(c, data, data_allocated_size, data_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do AEAD encryption. */ + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast<alts_record_protocol_crypter*>(c); + status = gsec_aead_crypter_encrypt( + rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr), + alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */, + 0 /* aad_length */, data, data_size, data, data_allocated_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Increment the crypter counter. */ + return increment_counter(rp_crypter, error_details); +} + +static const alts_crypter_vtable vtable = { + alts_record_protocol_crypter_num_overhead_bytes, + alts_seal_crypter_process_in_place, alts_record_protocol_crypter_destruct}; + +grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details) { + if (crypter == nullptr) { + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + alts_record_protocol_crypter* rp_crypter = + alts_crypter_create_common(gc, !is_client, overflow_size, error_details); + if (rp_crypter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + rp_crypter->base.vtable = &vtable; + *crypter = &rp_crypter->base; + return GRPC_STATUS_OK; +} diff --git a/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc b/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc new file mode 100644 index 0000000000..51bea24f1f --- /dev/null +++ b/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc @@ -0,0 +1,103 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include <grpc/support/alloc.h> + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Perform input santity check. */ +static grpc_status_code unseal_check(alts_crypter* c, const unsigned char* data, + size_t data_allocated_size, + size_t data_size, size_t* output_size, + char** error_details) { + /* Do common input sanity check. */ + grpc_status_code status = input_sanity_check( + reinterpret_cast<const alts_record_protocol_crypter*>(c), data, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do unseal-specific input check. */ + size_t num_overhead_bytes = + alts_crypter_num_overhead_bytes(reinterpret_cast<const alts_crypter*>(c)); + if (num_overhead_bytes > data_size) { + const char error_msg[] = "data_size is smaller than num_overhead_bytes."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code alts_unseal_crypter_process_in_place( + alts_crypter* c, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + grpc_status_code status = unseal_check(c, data, data_allocated_size, + data_size, output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do AEAD decryption. */ + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast<alts_record_protocol_crypter*>(c); + status = gsec_aead_crypter_decrypt( + rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr), + alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */, + 0 /* aad_length */, data, data_size, data, data_allocated_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Increment the crypter counter. */ + return increment_counter(rp_crypter, error_details); +} + +static const alts_crypter_vtable vtable = { + alts_record_protocol_crypter_num_overhead_bytes, + alts_unseal_crypter_process_in_place, + alts_record_protocol_crypter_destruct}; + +grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc, + bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details) { + if (crypter == nullptr) { + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + alts_record_protocol_crypter* rp_crypter = + alts_crypter_create_common(gc, is_client, overflow_size, error_details); + if (rp_crypter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + rp_crypter->base.vtable = &vtable; + *crypter = &rp_crypter->base; + return GRPC_STATUS_OK; +} diff --git a/src/core/tsi/alts/frame_protector/frame_handler.cc b/src/core/tsi/alts/frame_protector/frame_handler.cc new file mode 100644 index 0000000000..d3fda63b3d --- /dev/null +++ b/src/core/tsi/alts/frame_protector/frame_handler.cc @@ -0,0 +1,218 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/frame_protector/frame_handler.h" + +#include <limits.h> +#include <stdint.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" + +/* Use little endian to interpret a string of bytes as uint32_t. */ +static uint32_t load_32_le(const unsigned char* buffer) { + return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) | + (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]); +} + +/* Store uint32_t as a string of little endian bytes. */ +static void store_32_le(uint32_t value, unsigned char* buffer) { + buffer[3] = (unsigned char)(value >> 24) & 0xFF; + buffer[2] = (unsigned char)(value >> 16) & 0xFF; + buffer[1] = (unsigned char)(value >> 8) & 0xFF; + buffer[0] = (unsigned char)(value)&0xFF; +} + +/* Frame writer implementation. */ +alts_frame_writer* alts_create_frame_writer() { + alts_frame_writer* writer = + static_cast<alts_frame_writer*>(gpr_zalloc(sizeof(*writer))); + return writer; +} + +bool alts_reset_frame_writer(alts_frame_writer* writer, + const unsigned char* buffer, size_t length) { + if (buffer == nullptr) return false; + size_t max_input_size = SIZE_MAX - kFrameLengthFieldSize; + if (length > max_input_size) { + gpr_log(GPR_ERROR, "length must be at most %zu", max_input_size); + return false; + } + writer->input_buffer = buffer; + writer->input_size = length; + writer->input_bytes_written = 0; + writer->header_bytes_written = 0; + store_32_le( + static_cast<uint32_t>(writer->input_size + kFrameMessageTypeFieldSize), + writer->header_buffer); + store_32_le(kFrameMessageType, writer->header_buffer + kFrameLengthFieldSize); + return true; +} + +bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output, + size_t* bytes_size) { + if (bytes_size == nullptr || output == nullptr) return false; + if (alts_is_frame_writer_done(writer)) { + *bytes_size = 0; + return true; + } + size_t bytes_written = 0; + /* Write some header bytes, if needed. */ + if (writer->header_bytes_written != sizeof(writer->header_buffer)) { + size_t bytes_to_write = + GPR_MIN(*bytes_size, + sizeof(writer->header_buffer) - writer->header_bytes_written); + memcpy(output, writer->header_buffer + writer->header_bytes_written, + bytes_to_write); + bytes_written += bytes_to_write; + *bytes_size -= bytes_to_write; + writer->header_bytes_written += bytes_to_write; + output += bytes_to_write; + if (writer->header_bytes_written != sizeof(writer->header_buffer)) { + *bytes_size = bytes_written; + return true; + } + } + /* Write some non-header bytes. */ + size_t bytes_to_write = + GPR_MIN(writer->input_size - writer->input_bytes_written, *bytes_size); + memcpy(output, writer->input_buffer, bytes_to_write); + writer->input_buffer += bytes_to_write; + bytes_written += bytes_to_write; + writer->input_bytes_written += bytes_to_write; + *bytes_size = bytes_written; + return true; +} + +bool alts_is_frame_writer_done(alts_frame_writer* writer) { + return writer->input_buffer == nullptr || + writer->input_size == writer->input_bytes_written; +} + +size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer) { + return (sizeof(writer->header_buffer) - writer->header_bytes_written) + + (writer->input_size - writer->input_bytes_written); +} + +void alts_destroy_frame_writer(alts_frame_writer* writer) { gpr_free(writer); } + +/* Frame reader implementation. */ +alts_frame_reader* alts_create_frame_reader() { + alts_frame_reader* reader = + static_cast<alts_frame_reader*>(gpr_zalloc(sizeof(*reader))); + return reader; +} + +bool alts_is_frame_reader_done(alts_frame_reader* reader) { + return reader->output_buffer == nullptr || + (reader->header_bytes_read == sizeof(reader->header_buffer) && + reader->bytes_remaining == 0); +} + +bool alts_has_read_frame_length(alts_frame_reader* reader) { + return sizeof(reader->header_buffer) == reader->header_bytes_read; +} + +size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader) { + return alts_has_read_frame_length(reader) ? reader->bytes_remaining : 0; +} + +void alts_reset_reader_output_buffer(alts_frame_reader* reader, + unsigned char* buffer) { + reader->output_buffer = buffer; +} + +bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer) { + if (buffer == nullptr) return false; + reader->output_buffer = buffer; + reader->bytes_remaining = 0; + reader->header_bytes_read = 0; + reader->output_bytes_read = 0; + return true; +} + +bool alts_read_frame_bytes(alts_frame_reader* reader, + const unsigned char* bytes, size_t* bytes_size) { + if (bytes_size == nullptr) return false; + if (bytes == nullptr) { + *bytes_size = 0; + return false; + } + if (alts_is_frame_reader_done(reader)) { + *bytes_size = 0; + return true; + } + size_t bytes_processed = 0; + /* Process the header, if needed. */ + if (reader->header_bytes_read != sizeof(reader->header_buffer)) { + size_t bytes_to_write = GPR_MIN( + *bytes_size, sizeof(reader->header_buffer) - reader->header_bytes_read); + memcpy(reader->header_buffer + reader->header_bytes_read, bytes, + bytes_to_write); + reader->header_bytes_read += bytes_to_write; + bytes_processed += bytes_to_write; + bytes += bytes_to_write; + *bytes_size -= bytes_to_write; + if (reader->header_bytes_read != sizeof(reader->header_buffer)) { + *bytes_size = bytes_processed; + return true; + } + size_t frame_length = load_32_le(reader->header_buffer); + if (frame_length < kFrameMessageTypeFieldSize || + frame_length > kFrameMaxSize) { + gpr_log(GPR_ERROR, + "Bad frame length (should be at least %zu, and at most %zu)", + kFrameMessageTypeFieldSize, kFrameMaxSize); + *bytes_size = 0; + return false; + } + size_t message_type = + load_32_le(reader->header_buffer + kFrameLengthFieldSize); + if (message_type != kFrameMessageType) { + gpr_log(GPR_ERROR, "Unsupported message type %zu (should be %zu)", + message_type, kFrameMessageType); + *bytes_size = 0; + return false; + } + reader->bytes_remaining = frame_length - kFrameMessageTypeFieldSize; + } + /* Process the non-header bytes. */ + size_t bytes_to_write = GPR_MIN(*bytes_size, reader->bytes_remaining); + memcpy(reader->output_buffer, bytes, bytes_to_write); + reader->output_buffer += bytes_to_write; + bytes_processed += bytes_to_write; + reader->bytes_remaining -= bytes_to_write; + reader->output_bytes_read += bytes_to_write; + *bytes_size = bytes_processed; + return true; +} + +size_t alts_get_output_bytes_read(alts_frame_reader* reader) { + return reader->output_bytes_read; +} + +unsigned char* alts_get_output_buffer(alts_frame_reader* reader) { + return reader->output_buffer; +} + +void alts_destroy_frame_reader(alts_frame_reader* reader) { gpr_free(reader); } diff --git a/src/core/tsi/alts/frame_protector/frame_handler.h b/src/core/tsi/alts/frame_protector/frame_handler.h new file mode 100644 index 0000000000..a703ff40d3 --- /dev/null +++ b/src/core/tsi/alts/frame_protector/frame_handler.h @@ -0,0 +1,236 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> +#include <stdlib.h> + +const size_t kFrameMessageType = 0x06; +const size_t kFrameLengthFieldSize = 4; +const size_t kFrameMessageTypeFieldSize = 4; +const size_t kFrameMaxSize = 1024 * 1024; +const size_t kFrameHeaderSize = + kFrameLengthFieldSize + kFrameMessageTypeFieldSize; + +/** + * Implementation of frame reader and frame writer. All APIs in the + * header are thread-compatible. + */ + +/** + * Main struct for a frame writer. It reads frames from an input buffer, and + * writes the contents as raw bytes. It does not own the input buffer. + */ +typedef struct alts_frame_writer { + const unsigned char* input_buffer; + unsigned char header_buffer[kFrameHeaderSize]; + size_t input_bytes_written; + size_t header_bytes_written; + size_t input_size; +} alts_frame_writer; + +/** + * Main struct for a frame reader. It reads raw bytes and puts the framed + * result into an output buffer. It does not own the output buffer. + */ +typedef struct alts_frame_reader { + unsigned char* output_buffer; + unsigned char header_buffer[kFrameHeaderSize]; + size_t header_bytes_read; + size_t output_bytes_read; + size_t bytes_remaining; +} alts_frame_reader; + +/** + * This method creates a frame writer instance and initializes its internal + * states. + */ +alts_frame_writer* alts_create_frame_writer(); + +/** + * This method resets internal states of a frame writer and prepares to write + * a single frame. It does not take ownership of payload_buffer. + * The payload_buffer must outlive the writer. + * + * - writer: a frame writer instance. + * - buffer: a buffer storing full payload data to be framed. + * - length: size of payload data. + * + * The method returns true on success and false otherwise. + */ +bool alts_reset_frame_writer(alts_frame_writer* writer, + const unsigned char* buffer, size_t length); + +/** + * This method writes up to bytes_size bytes of a frame to output. + * + * - writer: a frame writer instance. + * - output: an output buffer used to store the frame. + * - bytes_size: an in/out parameter that stores the size of output buffer + * before the call, and gets written the number of frame bytes written to the + * buffer. + * + * The method returns true on success and false otherwise. + */ +bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output, + size_t* bytes_size); + +/** + * This method checks if a reset can be called to write a new frame. It returns + * true if it's the first time to frame a payload, or the current frame has + * been finished processing. It returns false if it's not ready yet to start a + * new frame (e.g., more payload data needs to be accumulated to process the + * current frame). + * + * if (alts_is_frame_writer_done(writer)) { + * // a new frame can be written, call reset. + * alts_reset_frame_writer(writer, payload_buffer, payload_size); + * } else { + * // accumulate more payload data until a full frame can be written. + * } + * + * - writer: a frame writer instance. + */ +bool alts_is_frame_writer_done(alts_frame_writer* writer); + +/** + * This method returns the number of bytes left to write before a complete frame + * is formed. + * + * - writer: a frame writer instance. + */ +size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer); + +/** + * This method destroys a frame writer instance. + * + * - writer: a frame writer instance. + */ +void alts_destroy_frame_writer(alts_frame_writer* writer); + +/** + * This method creates a frame reader instance and initializes its internal + * states. + */ +alts_frame_reader* alts_create_frame_reader(); + +/** + * This method resets internal states of a frame reader (including setting its + * output_buffer with buffer), and prepares to write processed bytes to + * an output_buffer. It does not take ownership of buffer. The buffer must + * outlive reader. + * + * - reader: a frame reader instance. + * - buffer: an output buffer used to store deframed results. + * + * The method returns true on success and false otherwise. + */ +bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer); + +/** + * This method processes up to the number of bytes given in bytes_size. It may + * choose not to process all the bytes, if, for instance, more bytes are + * given to the method than required to complete the current frame. + * + * - reader: a frame reader instance. + * - bytes: a buffer that stores data to be processed. + * - bytes_size: an in/out parameter that stores the size of bytes before the + * call and gets written the number of bytes processed. + * + * The method returns true on success and false otherwise. + */ +bool alts_read_frame_bytes(alts_frame_reader* reader, + const unsigned char* bytes, size_t* bytes_size); + +/** + * This method checks if a frame length has been read. + * + * - reader: a frame reader instance. + * + * The method returns true if a frame length has been read and false otherwise. + */ +bool alts_has_read_frame_length(alts_frame_reader* reader); + +/** + * This method returns the number of bytes the frame reader intends to write. + * It may only be called if alts_has_read_frame_length() returns true. + * + * - reader: a frame reader instance. + */ +size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader); + +/** + * This method resets output_buffer but does not otherwise modify other internal + * states of a frame reader instance. After being set, the new output_buffer + * will hold the deframed payload held by the original output_buffer. It does + * not take ownership of buffer. The buffer must outlive the reader. + * To distinguish between two reset methods on a frame reader, + * + * if (alts_fh_is_frame_reader_done(reader)) { + * // if buffer contains a full payload to be deframed, call reset. + * alts_reset_frame_reader(reader, buffer); + * } + * + * // if remaining buffer space is not enough to hold a full payload + * if (buffer_space_remaining < alts_get_reader_bytes_remaining(reader)) { + * // allocate enough space for a new buffer, copy back data processed so far, + * // and call reset. + * alts_reset_reader_output_buffer(reader, new_buffer). + * } + * + * - reader: a frame reader instance. + * - buffer: a buffer used to set reader's output_buffer. + */ +void alts_reset_reader_output_buffer(alts_frame_reader* reader, + unsigned char* buffer); + +/** + * This method checks if reset can be called to start processing a new frame. + * If true and reset was previously called, a full frame has been processed and + * the content of the frame is available in output_buffer. + + * - reader: a frame reader instance. + */ +bool alts_is_frame_reader_done(alts_frame_reader* reader); + +/** + * This method returns output_bytes_read of a frame reader instance. + * + * - reader: a frame reader instance. + */ +size_t alts_get_output_bytes_read(alts_frame_reader* reader); + +/** + * This method returns output_buffer of a frame reader instance. + * + * - reader: a frame reader instance. + */ +unsigned char* alts_get_output_buffer(alts_frame_reader* reader); + +/** + * This method destroys a frame reader instance. + * + * - reader: a frame reader instance. + */ +void alts_destroy_frame_reader(alts_frame_reader* reader); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H */ diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.cc b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc new file mode 100644 index 0000000000..40f30e41ca --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.cc @@ -0,0 +1,316 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" + +#include <grpc/byte_buffer.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" + +const int kHandshakerClientOpNum = 4; + +typedef struct alts_grpc_handshaker_client { + alts_handshaker_client base; + grpc_call* call; + alts_grpc_caller grpc_caller; +} alts_grpc_handshaker_client; + +static grpc_call_error grpc_start_batch(grpc_call* call, const grpc_op* ops, + size_t nops, void* tag) { + return grpc_call_start_batch(call, ops, nops, tag, nullptr); +} + +/** + * Populate grpc operation data with the fields of ALTS TSI event and make a + * grpc call. + */ +static tsi_result make_grpc_call(alts_handshaker_client* client, + alts_tsi_event* event, bool is_start) { + GPR_ASSERT(client != nullptr && event != nullptr); + alts_grpc_handshaker_client* grpc_client = + reinterpret_cast<alts_grpc_handshaker_client*>(client); + grpc_op ops[kHandshakerClientOpNum]; + memset(ops, 0, sizeof(ops)); + grpc_op* op = ops; + if (is_start) { + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &event->initial_metadata; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + } + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = event->send_buffer; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &event->recv_buffer; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + GPR_ASSERT(grpc_client->grpc_caller != nullptr); + if (grpc_client->grpc_caller(grpc_client->call, ops, + static_cast<size_t>(op - ops), + (void*)event) != GRPC_CALL_OK) { + gpr_log(GPR_ERROR, "Start batch operation failed"); + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +/* Create and populate a client_start handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_start_client(alts_tsi_event* event) { + bool ok = true; + grpc_gcp_handshaker_req* req = + grpc_gcp_handshaker_req_create(CLIENT_START_REQ); + ok &= grpc_gcp_handshaker_req_set_handshake_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS); + ok &= grpc_gcp_handshaker_req_add_application_protocol( + req, ALTS_APPLICATION_PROTOCOL); + ok &= grpc_gcp_handshaker_req_add_record_protocol(req, ALTS_RECORD_PROTOCOL); + grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions; + ok &= grpc_gcp_handshaker_req_set_rpc_versions( + req, versions->max_rpc_version.major, versions->max_rpc_version.minor, + versions->min_rpc_version.major, versions->min_rpc_version.minor); + char* target_name = grpc_slice_to_c_string(event->target_name); + ok &= grpc_gcp_handshaker_req_set_target_name(req, target_name); + target_service_account* ptr = + (reinterpret_cast<grpc_alts_credentials_client_options*>(event->options)) + ->target_account_list_head; + while (ptr != nullptr) { + grpc_gcp_handshaker_req_add_target_identity_service_account(req, ptr->data); + ptr = ptr->next; + } + grpc_slice slice; + ok &= grpc_gcp_handshaker_req_encode(req, &slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */); + } + grpc_slice_unref(slice); + gpr_free(target_name); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_start_client(alts_handshaker_client* client, + alts_tsi_event* event) { + if (client == nullptr || event == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to alts_grpc_handshaker_client_start_client()"); + return TSI_INVALID_ARGUMENT; + } + grpc_byte_buffer* buffer = get_serialized_start_client(event); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_start_client() failed"); + return TSI_INTERNAL_ERROR; + } + event->send_buffer = buffer; + tsi_result result = make_grpc_call(client, event, true /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +/* Create and populate a start_server handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_start_server( + alts_tsi_event* event, grpc_slice* bytes_received) { + GPR_ASSERT(bytes_received != nullptr); + grpc_gcp_handshaker_req* req = + grpc_gcp_handshaker_req_create(SERVER_START_REQ); + bool ok = grpc_gcp_handshaker_req_add_application_protocol( + req, ALTS_APPLICATION_PROTOCOL); + ok &= grpc_gcp_handshaker_req_param_add_record_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS, ALTS_RECORD_PROTOCOL); + ok &= grpc_gcp_handshaker_req_set_in_bytes( + req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received), + GRPC_SLICE_LENGTH(*bytes_received)); + grpc_gcp_rpc_protocol_versions* versions = &event->options->rpc_versions; + ok &= grpc_gcp_handshaker_req_set_rpc_versions( + req, versions->max_rpc_version.major, versions->max_rpc_version.minor, + versions->min_rpc_version.major, versions->min_rpc_version.minor); + grpc_slice req_slice; + ok &= grpc_gcp_handshaker_req_encode(req, &req_slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */); + } + grpc_slice_unref(req_slice); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_start_server(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received) { + if (client == nullptr || event == nullptr || bytes_received == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to alts_grpc_handshaker_client_start_server()"); + return TSI_INVALID_ARGUMENT; + } + grpc_byte_buffer* buffer = get_serialized_start_server(event, bytes_received); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_start_server() failed"); + return TSI_INTERNAL_ERROR; + } + event->send_buffer = buffer; + tsi_result result = make_grpc_call(client, event, true /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +/* Create and populate a next handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_next(grpc_slice* bytes_received) { + GPR_ASSERT(bytes_received != nullptr); + grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_req_create(NEXT_REQ); + bool ok = grpc_gcp_handshaker_req_set_in_bytes( + req, reinterpret_cast<const char*> GRPC_SLICE_START_PTR(*bytes_received), + GRPC_SLICE_LENGTH(*bytes_received)); + grpc_slice req_slice; + ok &= grpc_gcp_handshaker_req_encode(req, &req_slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */); + } + grpc_slice_unref(req_slice); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_next(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received) { + if (client == nullptr || event == nullptr || bytes_received == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to alts_grpc_handshaker_client_next()"); + return TSI_INVALID_ARGUMENT; + } + grpc_byte_buffer* buffer = get_serialized_next(bytes_received); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_next() failed"); + return TSI_INTERNAL_ERROR; + } + event->send_buffer = buffer; + tsi_result result = make_grpc_call(client, event, false /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +static void handshaker_client_destruct(alts_handshaker_client* client) { + if (client == nullptr) { + return; + } + alts_grpc_handshaker_client* grpc_client = + reinterpret_cast<alts_grpc_handshaker_client*>(client); + grpc_call_unref(grpc_client->call); +} + +static const alts_handshaker_client_vtable vtable = { + handshaker_client_start_client, handshaker_client_start_server, + handshaker_client_next, handshaker_client_destruct}; + +alts_handshaker_client* alts_grpc_handshaker_client_create( + grpc_channel* channel, grpc_completion_queue* queue, + const char* handshaker_service_url) { + if (channel == nullptr || queue == nullptr || + handshaker_service_url == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to alts_handshaker_client_create()"); + return nullptr; + } + alts_grpc_handshaker_client* client = + static_cast<alts_grpc_handshaker_client*>(gpr_zalloc(sizeof(*client))); + client->grpc_caller = grpc_start_batch; + grpc_slice slice = grpc_slice_from_copied_string(handshaker_service_url); + client->call = grpc_channel_create_call( + channel, nullptr, GRPC_PROPAGATE_DEFAULTS, queue, + grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice, + gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + client->base.vtable = &vtable; + grpc_slice_unref(slice); + return &client->base; +} + +namespace grpc_core { +namespace internal { + +void alts_handshaker_client_set_grpc_caller_for_testing( + alts_handshaker_client* client, alts_grpc_caller caller) { + GPR_ASSERT(client != nullptr && caller != nullptr); + alts_grpc_handshaker_client* grpc_client = + reinterpret_cast<alts_grpc_handshaker_client*>(client); + grpc_client->grpc_caller = caller; +} + +} // namespace internal +} // namespace grpc_core + +tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client, + alts_tsi_event* event) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->client_start != nullptr) { + return client->vtable->client_start(client, event); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->server_start != nullptr) { + return client->vtable->server_start(client, event, bytes_received); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +tsi_result alts_handshaker_client_next(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->next != nullptr) { + return client->vtable->next(client, event, bytes_received); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +void alts_handshaker_client_destroy(alts_handshaker_client* client) { + if (client != nullptr) { + if (client->vtable != nullptr && client->vtable->destruct != nullptr) { + client->vtable->destruct(client); + } + gpr_free(client); + } +} diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_client.h b/src/core/tsi/alts/handshaker/alts_handshaker_client.h new file mode 100644 index 0000000000..fb2d2cf68e --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_client.h @@ -0,0 +1,137 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H + +#include <grpc/support/port_platform.h> + +#include <grpc/grpc.h> + +#include "src/core/tsi/alts/handshaker/alts_tsi_event.h" + +#define ALTS_SERVICE_METHOD "/grpc.gcp.HandshakerService/DoHandshake" +#define ALTS_APPLICATION_PROTOCOL "grpc" +#define ALTS_RECORD_PROTOCOL "ALTSRP_GCM_AES128_REKEY" + +const size_t kAltsAes128GcmRekeyKeyLength = 44; + +/** + * A ALTS handshaker client interface. It is used to communicate with + * ALTS handshaker service by scheduling a handshaker request that could be one + * of client_start, server_start, and next handshaker requests. All APIs in the + * header are thread-compatible. + */ +typedef struct alts_handshaker_client alts_handshaker_client; + +/* A function that makes the grpc call to the handshaker service. */ +typedef grpc_call_error (*alts_grpc_caller)(grpc_call* call, const grpc_op* ops, + size_t nops, void* tag); + +/* V-table for ALTS handshaker client operations. */ +typedef struct alts_handshaker_client_vtable { + tsi_result (*client_start)(alts_handshaker_client* client, + alts_tsi_event* event); + tsi_result (*server_start)(alts_handshaker_client* client, + alts_tsi_event* event, grpc_slice* bytes_received); + tsi_result (*next)(alts_handshaker_client* client, alts_tsi_event* event, + grpc_slice* bytes_received); + void (*destruct)(alts_handshaker_client* client); +} alts_handshaker_client_vtable; + +struct alts_handshaker_client { + const alts_handshaker_client_vtable* vtable; +}; + +/** + * This method schedules a client_start handshaker request to ALTS handshaker + * service. + * + * - client: ALTS handshaker client instance. + * - event: ALTS TSI event instance. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client, + alts_tsi_event* event); + +/** + * This method schedules a server_start handshaker request to ALTS handshaker + * service. + * + * - client: ALTS handshaker client instance. + * - event: ALTS TSI event instance. + * - bytes_received: bytes in out_frames returned from the peer's handshaker + * response. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received); + +/** + * This method schedules a next handshaker request to ALTS handshaker service. + * + * - client: ALTS handshaker client instance. + * - event: ALTS TSI event instance. + * - bytes_received: bytes in out_frames returned from the peer's handshaker + * response. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_next(alts_handshaker_client* client, + alts_tsi_event* event, + grpc_slice* bytes_received); + +/** + * This method destroys a ALTS handshaker client. + * + * - client: a ALTS handshaker client instance. + */ +void alts_handshaker_client_destroy(alts_handshaker_client* client); + +/** + * This method creates a ALTS handshaker client. + * + * - channel: grpc channel to ALTS handshaker service. + * - queue: grpc completion queue. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". + * + * It returns the created ALTS handshaker client on success, and NULL on + * failure. + */ +alts_handshaker_client* alts_grpc_handshaker_client_create( + grpc_channel* channel, grpc_completion_queue* queue, + const char* handshaker_service_url); + +namespace grpc_core { +namespace internal { + +/** + * Unsafe, use for testing only. It allows the caller to change the way that + * GRPC calls are made to the handshaker service. + */ +void alts_handshaker_client_set_grpc_caller_for_testing( + alts_handshaker_client* client, alts_grpc_caller caller); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H */ diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc new file mode 100644 index 0000000000..256e414ae4 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc @@ -0,0 +1,520 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +/* HandshakerReq */ +grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create( + grpc_gcp_handshaker_req_type type) { + grpc_gcp_handshaker_req* req = + static_cast<grpc_gcp_handshaker_req*>(gpr_zalloc(sizeof(*req))); + switch (type) { + case CLIENT_START_REQ: + req->has_client_start = true; + break; + case SERVER_START_REQ: + req->has_server_start = true; + break; + case NEXT_REQ: + req->has_next = true; + break; + } + return req; +} + +void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req) { + if (req == nullptr) { + return; + } + if (req->has_client_start) { + /* Destroy client_start request. */ + destroy_repeated_field_list_identity( + static_cast<repeated_field*>(req->client_start.target_identities.arg)); + destroy_repeated_field_list_string(static_cast<repeated_field*>( + req->client_start.application_protocols.arg)); + destroy_repeated_field_list_string( + static_cast<repeated_field*>(req->client_start.record_protocols.arg)); + if (req->client_start.has_local_identity) { + destroy_slice(static_cast<grpc_slice*>( + req->client_start.local_identity.hostname.arg)); + destroy_slice(static_cast<grpc_slice*>( + req->client_start.local_identity.service_account.arg)); + } + if (req->client_start.has_local_endpoint) { + destroy_slice(static_cast<grpc_slice*>( + req->client_start.local_endpoint.ip_address.arg)); + } + if (req->client_start.has_remote_endpoint) { + destroy_slice(static_cast<grpc_slice*>( + req->client_start.remote_endpoint.ip_address.arg)); + } + destroy_slice(static_cast<grpc_slice*>(req->client_start.target_name.arg)); + } else if (req->has_server_start) { + /* Destroy server_start request. */ + size_t i = 0; + for (i = 0; i < req->server_start.handshake_parameters_count; i++) { + destroy_repeated_field_list_identity( + static_cast<repeated_field*>(req->server_start.handshake_parameters[i] + .value.local_identities.arg)); + destroy_repeated_field_list_string( + static_cast<repeated_field*>(req->server_start.handshake_parameters[i] + .value.record_protocols.arg)); + } + destroy_repeated_field_list_string(static_cast<repeated_field*>( + req->server_start.application_protocols.arg)); + if (req->server_start.has_local_endpoint) { + destroy_slice(static_cast<grpc_slice*>( + req->server_start.local_endpoint.ip_address.arg)); + } + if (req->server_start.has_remote_endpoint) { + destroy_slice(static_cast<grpc_slice*>( + req->server_start.remote_endpoint.ip_address.arg)); + } + destroy_slice(static_cast<grpc_slice*>(req->server_start.in_bytes.arg)); + } else { + /* Destroy next request. */ + destroy_slice(static_cast<grpc_slice*>(req->next.in_bytes.arg)); + } + gpr_free(req); +} + +bool grpc_gcp_handshaker_req_set_handshake_protocol( + grpc_gcp_handshaker_req* req, + grpc_gcp_handshake_protocol handshake_protocol) { + if (req == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_handshake_protocol()."); + return false; + } + req->client_start.has_handshake_security_protocol = true; + req->client_start.handshake_security_protocol = handshake_protocol; + return true; +} + +bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req, + const char* target_name) { + if (req == nullptr || target_name == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_target_name()."); + return false; + } + grpc_slice* slice = create_slice(target_name, strlen(target_name)); + req->client_start.target_name.arg = slice; + req->client_start.target_name.funcs.encode = encode_string_or_bytes_cb; + return true; +} + +bool grpc_gcp_handshaker_req_add_application_protocol( + grpc_gcp_handshaker_req* req, const char* application_protocol) { + if (req == nullptr || application_protocol == nullptr || req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_add_application_protocol()."); + return false; + } + grpc_slice* slice = + create_slice(application_protocol, strlen(application_protocol)); + if (req->has_client_start) { + add_repeated_field(reinterpret_cast<repeated_field**>( + &req->client_start.application_protocols.arg), + slice); + req->client_start.application_protocols.funcs.encode = + encode_repeated_string_cb; + } else { + add_repeated_field(reinterpret_cast<repeated_field**>( + &req->server_start.application_protocols.arg), + slice); + req->server_start.application_protocols.funcs.encode = + encode_repeated_string_cb; + } + return true; +} + +bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req, + const char* record_protocol) { + if (req == nullptr || record_protocol == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_add_record_protocol()."); + return false; + } + grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol)); + add_repeated_field(reinterpret_cast<repeated_field**>( + &req->client_start.record_protocols.arg), + slice); + req->client_start.record_protocols.funcs.encode = encode_repeated_string_cb; + return true; +} + +static void set_identity_hostname(grpc_gcp_identity* identity, + const char* hostname) { + grpc_slice* slice = create_slice(hostname, strlen(hostname)); + identity->hostname.arg = slice; + identity->hostname.funcs.encode = encode_string_or_bytes_cb; +} + +static void set_identity_service_account(grpc_gcp_identity* identity, + const char* service_account) { + grpc_slice* slice = create_slice(service_account, strlen(service_account)); + identity->service_account.arg = slice; + identity->service_account.funcs.encode = encode_string_or_bytes_cb; +} + +bool grpc_gcp_handshaker_req_add_target_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_add_target_identity_hostname()."); + return false; + } + grpc_gcp_identity* target_identity = + static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity))); + set_identity_hostname(target_identity, hostname); + req->client_start.target_identities.funcs.encode = + encode_repeated_identity_cb; + add_repeated_field(reinterpret_cast<repeated_field**>( + &req->client_start.target_identities.arg), + target_identity); + return true; +} + +bool grpc_gcp_handshaker_req_add_target_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_add_target_identity_service_account()."); + return false; + } + grpc_gcp_identity* target_identity = + static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*target_identity))); + set_identity_service_account(target_identity, service_account); + req->client_start.target_identities.funcs.encode = + encode_repeated_identity_cb; + add_repeated_field(reinterpret_cast<repeated_field**>( + &req->client_start.target_identities.arg), + target_identity); + return true; +} + +bool grpc_gcp_handshaker_req_set_local_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_set_local_identity_hostname()."); + return false; + } + req->client_start.has_local_identity = true; + set_identity_hostname(&req->client_start.local_identity, hostname); + return true; +} + +bool grpc_gcp_handshaker_req_set_local_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_set_local_identity_service_account()."); + return false; + } + req->client_start.has_local_identity = true; + set_identity_service_account(&req->client_start.local_identity, + service_account); + return true; +} + +static void set_endpoint(grpc_gcp_endpoint* endpoint, const char* ip_address, + size_t port, grpc_gcp_network_protocol protocol) { + grpc_slice* slice = create_slice(ip_address, strlen(ip_address)); + endpoint->ip_address.arg = slice; + endpoint->ip_address.funcs.encode = encode_string_or_bytes_cb; + endpoint->has_port = true; + endpoint->port = static_cast<int32_t>(port); + endpoint->has_protocol = true; + endpoint->protocol = protocol; +} + +bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req, + uint32_t max_major, + uint32_t max_minor, + uint32_t min_major, + uint32_t min_minor) { + if (req == nullptr || req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_rpc_versions()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_rpc_versions = true; + grpc_gcp_rpc_protocol_versions_set_max(&req->client_start.rpc_versions, + max_major, max_minor); + grpc_gcp_rpc_protocol_versions_set_min(&req->client_start.rpc_versions, + min_major, min_minor); + } else { + req->server_start.has_rpc_versions = true; + grpc_gcp_rpc_protocol_versions_set_max(&req->server_start.rpc_versions, + max_major, max_minor); + grpc_gcp_rpc_protocol_versions_set_min(&req->server_start.rpc_versions, + min_major, min_minor); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_local_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol) { + if (req == nullptr || ip_address == nullptr || port > 65535 || + req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_local_endpoint()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_local_endpoint = true; + set_endpoint(&req->client_start.local_endpoint, ip_address, port, protocol); + } else { + req->server_start.has_local_endpoint = true; + set_endpoint(&req->server_start.local_endpoint, ip_address, port, protocol); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_remote_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol) { + if (req == nullptr || ip_address == nullptr || port > 65535 || + req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_remote_endpoint()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_remote_endpoint = true; + set_endpoint(&req->client_start.remote_endpoint, ip_address, port, + protocol); + } else { + req->server_start.has_remote_endpoint = true; + set_endpoint(&req->server_start.remote_endpoint, ip_address, port, + protocol); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req, + const char* in_bytes, size_t size) { + if (req == nullptr || in_bytes == nullptr || req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_in_bytes()."); + return false; + } + grpc_slice* slice = create_slice(in_bytes, size); + if (req->has_next) { + req->next.in_bytes.arg = slice; + req->next.in_bytes.funcs.encode = &encode_string_or_bytes_cb; + } else { + req->server_start.in_bytes.arg = slice; + req->server_start.in_bytes.funcs.encode = &encode_string_or_bytes_cb; + } + return true; +} + +static grpc_gcp_server_handshake_parameters* server_start_find_param( + grpc_gcp_handshaker_req* req, int32_t key) { + size_t i = 0; + for (i = 0; i < req->server_start.handshake_parameters_count; i++) { + if (req->server_start.handshake_parameters[i].key == key) { + return &req->server_start.handshake_parameters[i].value; + } + } + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count] + .has_key = true; + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count] + .has_value = true; + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count++] + .key = key; + return &req->server_start + .handshake_parameters + [req->server_start.handshake_parameters_count - 1] + .value; +} + +bool grpc_gcp_handshaker_req_param_add_record_protocol( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* record_protocol) { + if (req == nullptr || record_protocol == nullptr || !req->has_server_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_record_protocol()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol)); + add_repeated_field( + reinterpret_cast<repeated_field**>(¶m->record_protocols.arg), slice); + param->record_protocols.funcs.encode = &encode_repeated_string_cb; + return true; +} + +bool grpc_gcp_handshaker_req_param_add_local_identity_hostname( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_server_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_local_identity_hostname()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_gcp_identity* local_identity = + static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity))); + set_identity_hostname(local_identity, hostname); + add_repeated_field( + reinterpret_cast<repeated_field**>(¶m->local_identities.arg), + local_identity); + param->local_identities.funcs.encode = &encode_repeated_identity_cb; + return true; +} + +bool grpc_gcp_handshaker_req_param_add_local_identity_service_account( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_server_start) { + gpr_log( + GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_local_identity_service_account()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_gcp_identity* local_identity = + static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*local_identity))); + set_identity_service_account(local_identity, service_account); + add_repeated_field( + reinterpret_cast<repeated_field**>(¶m->local_identities.arg), + local_identity); + param->local_identities.funcs.encode = &encode_repeated_identity_cb; + return true; +} + +bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req, + grpc_slice* slice) { + if (req == nullptr || slice == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to grpc_gcp_handshaker_req_encode()."); + return false; + } + pb_ostream_t size_stream; + memset(&size_stream, 0, sizeof(pb_ostream_t)); + if (!pb_encode(&size_stream, grpc_gcp_HandshakerReq_fields, req)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream)); + return false; + } + size_t encoded_length = size_stream.bytes_written; + *slice = grpc_slice_malloc(encoded_length); + pb_ostream_t output_stream = + pb_ostream_from_buffer(GRPC_SLICE_START_PTR(*slice), encoded_length); + if (!pb_encode(&output_stream, grpc_gcp_HandshakerReq_fields, req) != 0) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream)); + return false; + } + return true; +} + +/* HandshakerResp. */ +grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void) { + grpc_gcp_handshaker_resp* resp = + static_cast<grpc_gcp_handshaker_resp*>(gpr_zalloc(sizeof(*resp))); + return resp; +} + +void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp) { + if (resp != nullptr) { + destroy_slice(static_cast<grpc_slice*>(resp->out_frames.arg)); + if (resp->has_status) { + destroy_slice(static_cast<grpc_slice*>(resp->status.details.arg)); + } + if (resp->has_result) { + destroy_slice( + static_cast<grpc_slice*>(resp->result.application_protocol.arg)); + destroy_slice(static_cast<grpc_slice*>(resp->result.record_protocol.arg)); + destroy_slice(static_cast<grpc_slice*>(resp->result.key_data.arg)); + if (resp->result.has_local_identity) { + destroy_slice( + static_cast<grpc_slice*>(resp->result.local_identity.hostname.arg)); + destroy_slice(static_cast<grpc_slice*>( + resp->result.local_identity.service_account.arg)); + } + if (resp->result.has_peer_identity) { + destroy_slice( + static_cast<grpc_slice*>(resp->result.peer_identity.hostname.arg)); + destroy_slice(static_cast<grpc_slice*>( + resp->result.peer_identity.service_account.arg)); + } + } + gpr_free(resp); + } +} + +bool grpc_gcp_handshaker_resp_decode(grpc_slice encoded_handshaker_resp, + grpc_gcp_handshaker_resp* resp) { + if (resp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr argument to grpc_gcp_handshaker_resp_decode()."); + return false; + } + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_handshaker_resp), + GRPC_SLICE_LENGTH(encoded_handshaker_resp)); + resp->out_frames.funcs.decode = decode_string_or_bytes_cb; + resp->status.details.funcs.decode = decode_string_or_bytes_cb; + resp->result.application_protocol.funcs.decode = decode_string_or_bytes_cb; + resp->result.record_protocol.funcs.decode = decode_string_or_bytes_cb; + resp->result.key_data.funcs.decode = decode_string_or_bytes_cb; + resp->result.peer_identity.hostname.funcs.decode = decode_string_or_bytes_cb; + resp->result.peer_identity.service_account.funcs.decode = + decode_string_or_bytes_cb; + resp->result.local_identity.hostname.funcs.decode = decode_string_or_bytes_cb; + resp->result.local_identity.service_account.funcs.decode = + decode_string_or_bytes_cb; + if (!pb_decode(&stream, grpc_gcp_HandshakerResp_fields, resp)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return false; + } + return true; +} diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h new file mode 100644 index 0000000000..5df56a86fa --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h @@ -0,0 +1,323 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" + +/** + * An implementation of nanopb thin wrapper used to set/get and + * serialize/de-serialize of ALTS handshake requests and responses. + * + * All APIs in the header are thread-compatible. A typical usage of this API at + * the client side is as follows: + * + * ----------------------------------------------------------------------------- + * // Create, populate, and serialize an ALTS client_start handshake request to + * // send to the server. + * grpc_gcp_handshaker_req* req = + * grpc_gcp_handshaker_req_create(CLIENT_START_REQ); + * grpc_gcp_handshaker_req_set_handshake_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS); + * grpc_gcp_handshaker_req_add_application_protocol(req, "grpc"); + * grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES128"); + * grpc_slice client_slice; + * if (!grpc_gcp_handshaker_req_encode(req, &client_slice)) { + * fprintf(stderr, "ALTS handshake request encoding failed."; + * } + * + * // De-serialize a data stream received from the server, and store the result + * // at ALTS handshake response. + * grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create(); + * if (!grpc_gcp_handshaker_resp_decode(server_slice, resp)) { + * fprintf(stderr, "ALTS handshake response decoding failed."); + * } + * // To access a variable-length datatype field (i.e., pb_callback_t), + * // access its "arg" subfield (if it has been set). + * if (resp->out_frames.arg != nullptr) { + * grpc_slice* slice = resp->out_frames.arg; + * } + * // To access a fixed-length datatype field (i.e., not pb_calback_t), + * // access the field directly (if it has been set). + * if (resp->has_status && resp->status->has_code) { + * uint32_t code = resp->status->code; + * } + *------------------------------------------------------------------------------ + */ + +/** + * This method creates an ALTS handshake request. + * + * - type: an enum type value that can be either CLIENT_START_REQ, + * SERVER_START_REQ, or NEXT_REQ to indicate the created instance will be + * client_start, server_start, and next handshake request message + * respectively. + * + * The method returns a pointer to the created instance. + */ +grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create( + grpc_gcp_handshaker_req_type type); + +/** + * This method sets the value for handshake_security_protocol field of ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - handshake_protocol: a enum type value representing the handshake security + * protocol. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_handshake_protocol( + grpc_gcp_handshaker_req* req, + grpc_gcp_handshake_protocol handshake_protocol); + +/** + * This method sets the value for target_name field of ALTS client_start + * handshake request. + * + * - req: an ALTS handshake request. + * - target_name: a target name to be set. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req, + const char* target_name); + +/** + * This method adds an application protocol supported by the server (or + * client) to ALTS server_start (or client_start) handshake request. + * + * - req: an ALTS handshake request. + * - application_protocol: an application protocol (e.g., grpc) to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_application_protocol( + grpc_gcp_handshaker_req* req, const char* application_protocol); + +/** + * This method adds a record protocol supported by the client to ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - record_protocol: a record protocol (e.g., ALTSRP_GCM_AES128) to be + * added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req, + const char* record_protocol); + +/** + * This method adds a target server identity represented as hostname and + * acceptable by a client to ALTS client_start handshake request. + * + * - req: an ALTS handshake request. + * - hostname: a string representation of hostname at the connection + * endpoint to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_target_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname); + +/** + * This method adds a target server identity represented as service account and + * acceptable by a client to ALTS client_start handshake request. + * + * - req: an ALTS handshake request. + * - service_account: a string representation of service account at the + * connection endpoint to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_target_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account); + +/** + * This method sets the hostname for local_identity field of ALTS client_start + * handshake request. + * + * - req: an ALTS handshake request. + * - hostname: a string representation of hostname. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname); + +/** + * This method sets the service account for local_identity field of ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - service_account: a string representation of service account. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account); + +/** + * This method sets the value for local_endpoint field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - ip_address: a string representation of ip address associated with the + * local endpoint, that could be either IPv4 or IPv6. + * - port: a port number associated with the local endpoint. + * - protocol: a network protocol (e.g., TCP or UDP) associated with the + * local endpoint. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol); + +/** + * This method sets the value for remote_endpoint field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - ip_address: a string representation of ip address associated with the + * remote endpoint, that could be either IPv4 or IPv6. + * - port: a port number associated with the remote endpoint. + * - protocol: a network protocol (e.g., TCP or UDP) associated with the + * remote endpoint. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_remote_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol); + +/** + * This method sets the value for in_bytes field of either ALTS server_start or + * next handshake request. + * + * - req: an ALTS handshake request. + * - in_bytes: a buffer containing bytes taken from out_frames of the peer's + * ALTS handshake response. It is possible that the peer's out_frames are + * split into multiple handshake request messages. + * - size: size of in_bytes buffer. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req, + const char* in_bytes, size_t size); + +/** + * This method adds a record protocol to handshake parameters mapped by the + * handshake protocol for ALTS server_start handshake request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - record_protocol: a record protocol to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_record_protocol( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* record_protocol); + +/** + * This method adds a local identity represented as hostname to handshake + * parameters mapped by the handshake protocol for ALTS server_start handshake + * request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - hostname: a string representation of hostname to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_local_identity_hostname( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* hostname); + +/** + * This method adds a local identity represented as service account to handshake + * parameters mapped by the handshake protocol for ALTS server_start handshake + * request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - service_account: a string representation of service account to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_local_identity_service_account( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* service_account); + +/** + * This method sets the value for rpc_versions field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - max_major: a major version of maximum supported RPC version. + * - max_minor: a minor version of maximum supported RPC version. + * - min_major: a major version of minimum supported RPC version. + * - min_minor: a minor version of minimum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req, + uint32_t max_major, + uint32_t max_minor, + uint32_t min_major, + uint32_t min_minor); + +/** + * This method serializes an ALTS handshake request and returns a data stream. + * + * - req: an ALTS handshake request. + * - slice: a data stream where the serialized result will be written. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req, + grpc_slice* slice); + +/* This method destroys an ALTS handshake request. */ +void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req); + +/* This method creates an ALTS handshake response. */ +grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void); + +/** + * This method de-serializes a data stream and stores the result + * in an ALTS handshake response. + * + * - slice: a data stream containing a serialized ALTS handshake response. + * - resp: an ALTS handshake response used to hold de-serialized result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_resp_decode(grpc_slice slice, + grpc_gcp_handshaker_resp* resp); + +/* This method destroys an ALTS handshake response. */ +void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H */ diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc new file mode 100644 index 0000000000..e0e4184686 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc @@ -0,0 +1,143 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" + +void add_repeated_field(repeated_field** head, const void* data) { + repeated_field* field = + static_cast<repeated_field*>(gpr_zalloc(sizeof(*field))); + field->data = data; + if (*head == nullptr) { + *head = field; + (*head)->next = nullptr; + } else { + field->next = *head; + *head = field; + } +} + +void destroy_repeated_field_list_identity(repeated_field* head) { + repeated_field* field = head; + while (field != nullptr) { + repeated_field* next_field = field->next; + const grpc_gcp_identity* identity = + static_cast<const grpc_gcp_identity*>(field->data); + destroy_slice(static_cast<grpc_slice*>(identity->hostname.arg)); + destroy_slice(static_cast<grpc_slice*>(identity->service_account.arg)); + gpr_free((void*)identity); + gpr_free(field); + field = next_field; + } +} + +void destroy_repeated_field_list_string(repeated_field* head) { + repeated_field* field = head; + while (field != nullptr) { + repeated_field* next_field = field->next; + destroy_slice((grpc_slice*)field->data); + gpr_free(field); + field = next_field; + } +} + +grpc_slice* create_slice(const char* data, size_t size) { + grpc_slice slice = grpc_slice_from_copied_buffer(data, size); + grpc_slice* cb_slice = + static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(*cb_slice)); + return cb_slice; +} + +void destroy_slice(grpc_slice* slice) { + if (slice != nullptr) { + grpc_slice_unref(*slice); + gpr_free(slice); + } +} + +bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + grpc_slice* slice = static_cast<grpc_slice*>(*arg); + if (!pb_encode_tag_for_field(stream, field)) return false; + return pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice), + GRPC_SLICE_LENGTH(*slice)); +} + +bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + repeated_field* var = static_cast<repeated_field*>(*arg); + while (var != nullptr) { + if (!pb_encode_tag_for_field(stream, field)) return false; + if (!pb_encode_submessage(stream, grpc_gcp_Identity_fields, + (grpc_gcp_identity*)var->data)) + return false; + var = var->next; + } + return true; +} + +bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + repeated_field* var = static_cast<repeated_field*>(*arg); + while (var != nullptr) { + if (!pb_encode_tag_for_field(stream, field)) return false; + const grpc_slice* slice = static_cast<const grpc_slice*>(var->data); + if (!pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice), + GRPC_SLICE_LENGTH(*slice))) + return false; + var = var->next; + } + return true; +} + +bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_slice slice = grpc_slice_malloc(stream->bytes_left); + grpc_slice* cb_slice = + static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(*cb_slice)); + if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left)) + return false; + *arg = cb_slice; + return true; +} + +bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_gcp_identity* identity = + static_cast<grpc_gcp_identity*>(gpr_zalloc(sizeof(*identity))); + identity->hostname.funcs.decode = decode_string_or_bytes_cb; + identity->service_account.funcs.decode = decode_string_or_bytes_cb; + add_repeated_field(reinterpret_cast<repeated_field**>(arg), identity); + if (!pb_decode(stream, grpc_gcp_Identity_fields, identity)) return false; + return true; +} + +bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_slice slice = grpc_slice_malloc(stream->bytes_left); + grpc_slice* cb_slice = + static_cast<grpc_slice*>(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(grpc_slice)); + if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left)) + return false; + add_repeated_field(reinterpret_cast<repeated_field**>(arg), cb_slice); + return true; +} diff --git a/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h new file mode 100644 index 0000000000..8fe8f73f8b --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h @@ -0,0 +1,149 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H + +#include <grpc/support/port_platform.h> + +#include "third_party/nanopb/pb_decode.h" +#include "third_party/nanopb/pb_encode.h" + +#include <grpc/slice.h> +#include <grpc/slice_buffer.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/tsi/alts/handshaker/handshaker.pb.h" + +/** + * An implementation of utility functions used to serialize/ + * de-serialize ALTS handshake requests/responses. All APIs in the header + * are thread-compatible. + */ + +/* Renaming of message/field structs generated by nanopb compiler. */ +typedef grpc_gcp_HandshakeProtocol grpc_gcp_handshake_protocol; +typedef grpc_gcp_NetworkProtocol grpc_gcp_network_protocol; +typedef grpc_gcp_Identity grpc_gcp_identity; +typedef grpc_gcp_NextHandshakeMessageReq grpc_gcp_next_handshake_message_req; +typedef grpc_gcp_ServerHandshakeParameters grpc_gcp_server_handshake_parameters; +typedef grpc_gcp_Endpoint grpc_gcp_endpoint; +typedef grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry + grpc_gcp_handshake_parameters_entry; +typedef grpc_gcp_StartClientHandshakeReq grpc_gcp_start_client_handshake_req; +typedef grpc_gcp_StartServerHandshakeReq grpc_gcp_start_server_handshake_req; +typedef grpc_gcp_HandshakerReq grpc_gcp_handshaker_req; +typedef grpc_gcp_HandshakerResult grpc_gcp_handshaker_result; +typedef grpc_gcp_HandshakerStatus grpc_gcp_handshaker_status; +typedef grpc_gcp_HandshakerResp grpc_gcp_handshaker_resp; + +typedef enum { + CLIENT_START_REQ = 0, /* StartClientHandshakeReq. */ + SERVER_START_REQ = 1, /* StartServerHandshakeReq. */ + NEXT_REQ = 2, /* NextHandshakeMessageReq. */ +} grpc_gcp_handshaker_req_type; + +/** + * A struct representing a repeated field. The struct is used to organize all + * instances of a specific repeated field into a linked list, which then will + * be used at encode/decode phase. For instance at the encode phase, the encode + * function will iterate through the list, encode each field, and then output + * the result to the stream. + */ +typedef struct repeated_field_ { + struct repeated_field_* next; + const void* data; +} repeated_field; + +/** + * This method adds a repeated field to the head of repeated field list. + * + * - head: a head of repeated field list. + * - field: a repeated field to be added to the list. + */ +void add_repeated_field(repeated_field** head, const void* field); + +/** + * This method destroys a repeated field list that consists of string type + * fields. + * + * - head: a head of repeated field list. + */ +void destroy_repeated_field_list_string(repeated_field* head); + +/** + * This method destroys a repeated field list that consists of + * grpc_gcp_identity type fields. + * + * - head: a head of repeated field list. + */ +void destroy_repeated_field_list_identity(repeated_field* head); + +/** + * This method creates a grpc_slice instance by copying a data buffer. It is + * similar to grpc_slice_from_copied_buffer() except that it returns an instance + * allocated from the heap. + * + * - data: a data buffer to be copied to grpc_slice instance. + * - size: size of data buffer. + */ +grpc_slice* create_slice(const char* data, size_t size); + +/* This method destroys a grpc_slice instance. */ +void destroy_slice(grpc_slice* slice); + +/** + * The following encode/decode functions will be assigned to encode/decode + * function pointers of pb_callback_t struct (defined in + * //third_party/nanopb/pb.h), that represent a repeated field with a dynamic + * length (e.g., a string type or repeated field). + */ + +/* This method is an encode callback function for a string or byte array. */ +bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/** + * This method is an encode callback function for a repeated grpc_gcp_identity + * field. + */ +bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/* This method is an encode callback function for a repeated string field. */ +bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/** + * This method is a decode callback function for a string or byte array field. + */ +bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); +/** + * This method is a decode callback function for a repeated grpc_gcp_identity + * field. + */ +bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); + +/* This method is a decode callback function for a repeated string field. */ +bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H */ diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.cc b/src/core/tsi/alts/handshaker/alts_tsi_event.cc new file mode 100644 index 0000000000..ec0bf12b95 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_event.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_tsi_event.h" + +#include <grpc/grpc.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker, + tsi_handshaker_on_next_done_cb cb, + void* user_data, + grpc_alts_credentials_options* options, + grpc_slice target_name, + alts_tsi_event** event) { + if (event == nullptr || handshaker == nullptr || cb == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_event_create()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_event* e = static_cast<alts_tsi_event*>(gpr_zalloc(sizeof(*e))); + e->handshaker = handshaker; + e->cb = cb; + e->user_data = user_data; + e->options = grpc_alts_credentials_options_copy(options); + e->target_name = grpc_slice_copy(target_name); + grpc_metadata_array_init(&e->initial_metadata); + grpc_metadata_array_init(&e->trailing_metadata); + *event = e; + return TSI_OK; +} + +void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok) { + if (event == nullptr) { + gpr_log( + GPR_ERROR, + "ALTS TSI event is nullptr in alts_tsi_event_dispatch_to_handshaker()"); + return; + } + alts_tsi_handshaker_handle_response(event->handshaker, event->recv_buffer, + event->status, &event->details, event->cb, + event->user_data, is_ok); +} + +void alts_tsi_event_destroy(alts_tsi_event* event) { + if (event == nullptr) { + return; + } + grpc_byte_buffer_destroy(event->send_buffer); + grpc_byte_buffer_destroy(event->recv_buffer); + grpc_metadata_array_destroy(&event->initial_metadata); + grpc_metadata_array_destroy(&event->trailing_metadata); + grpc_slice_unref(event->details); + grpc_slice_unref(event->target_name); + grpc_alts_credentials_options_destroy(event->options); + gpr_free(event); +} diff --git a/src/core/tsi/alts/handshaker/alts_tsi_event.h b/src/core/tsi/alts/handshaker/alts_tsi_event.h new file mode 100644 index 0000000000..043e75d4a9 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_event.h @@ -0,0 +1,93 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H + +#include <grpc/support/port_platform.h> + +#include <grpc/byte_buffer.h> +#include <grpc/byte_buffer_reader.h> + +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" +#include "src/core/tsi/transport_security_interface.h" + +/** + * A ALTS TSI event interface. In asynchronous implementation of + * tsi_handshaker_next(), the function will exit after scheduling a handshaker + * request to ALTS handshaker service without waiting for response to return. + * The event is used to link the scheduled handshaker request with the + * corresponding response so that enough context information can be inferred + * from it to handle the response. All APIs in the header are thread-compatible. + */ + +/** + * Main struct for ALTS TSI event. It retains ownership on send_buffer and + * recv_buffer, but not on handshaker. + */ +typedef struct alts_tsi_event { + alts_tsi_handshaker* handshaker; + grpc_byte_buffer* send_buffer; + grpc_byte_buffer* recv_buffer; + grpc_status_code status; + grpc_slice details; + grpc_metadata_array initial_metadata; + grpc_metadata_array trailing_metadata; + tsi_handshaker_on_next_done_cb cb; + void* user_data; + grpc_alts_credentials_options* options; + grpc_slice target_name; +} alts_tsi_event; + +/** + * This method creates a ALTS TSI event. + * + * - handshaker: ALTS TSI handshaker instance associated with the event to be + * created. The created event does not own the handshaker instance. + * - cb: callback function to be called when handling data received from ALTS + * handshaker service. + * - user_data: argument to callback function. + * - options: ALTS credentials options. + * - target_name: name of endpoint used for secure naming check. + * - event: address of ALTS TSI event instance to be returned from the method. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_tsi_event_create(alts_tsi_handshaker* handshaker, + tsi_handshaker_on_next_done_cb cb, + void* user_data, + grpc_alts_credentials_options* options, + grpc_slice target_name, + alts_tsi_event** event); + +/** + * This method dispatches a ALTS TSI event received from the handshaker service, + * and a boolean flag indicating if the event is valid to read to ALTS TSI + * handshaker to process. It is called by TSI thread. + * + * - event: ALTS TSI event instance. + * - is_ok: a boolean value indicating if the event is valid to read. + */ +void alts_tsi_event_dispatch_to_handshaker(alts_tsi_event* event, bool is_ok); + +/** + * This method destroys the ALTS TSI event. + */ +void alts_tsi_event_destroy(alts_tsi_event* event); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_EVENT_H */ diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc new file mode 100644 index 0000000000..529f2103c7 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc @@ -0,0 +1,483 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/sync.h> +#include <grpc/support/thd_id.h> + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h" +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h" +#include "src/core/tsi/alts_transport_security.h" + +#define TSI_ALTS_INITIAL_BUFFER_SIZE 256 + +static alts_shared_resource* kSharedResource = alts_get_shared_resource(); + +/* Main struct for ALTS TSI handshaker. */ +typedef struct alts_tsi_handshaker { + tsi_handshaker base; + alts_handshaker_client* client; + grpc_slice recv_bytes; + grpc_slice target_name; + unsigned char* buffer; + size_t buffer_size; + bool is_client; + bool has_sent_start_message; + grpc_alts_credentials_options* options; +} alts_tsi_handshaker; + +/* Main struct for ALTS TSI handshaker result. */ +typedef struct alts_tsi_handshaker_result { + tsi_handshaker_result base; + char* peer_identity; + char* key_data; + unsigned char* unused_bytes; + size_t unused_bytes_size; + grpc_slice rpc_versions; + bool is_client; +} alts_tsi_handshaker_result; + +static tsi_result handshaker_result_extract_peer( + const tsi_handshaker_result* self, tsi_peer* peer) { + if (self == nullptr || peer == nullptr) { + gpr_log(GPR_ERROR, "Invalid argument to handshaker_result_extract_peer()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>( + const_cast<tsi_handshaker_result*>(self)); + GPR_ASSERT(kTsiAltsNumOfPeerProperties == 3); + tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer); + int index = 0; + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to construct tsi peer"); + return ok; + } + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE, + &peer->properties[index]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + return ok; + } + index++; + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property_from_cstring( + TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, result->peer_identity, + &peer->properties[index]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + } + index++; + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property( + TSI_ALTS_RPC_VERSIONS, + reinterpret_cast<char*>(GRPC_SLICE_START_PTR(result->rpc_versions)), + GRPC_SLICE_LENGTH(result->rpc_versions), &peer->properties[2]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + } + GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties); + return ok; +} + +static tsi_result handshaker_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (self == nullptr || protector == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to create_zero_copy_grpc_protector()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>( + const_cast<tsi_handshaker_result*>(self)); + tsi_result ok = alts_zero_copy_grpc_protector_create( + reinterpret_cast<const uint8_t*>(result->key_data), + kAltsAes128GcmRekeyKeyLength, /*is_rekey=*/true, result->is_client, + /*is_integrity_only=*/false, max_output_protected_frame_size, protector); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector"); + } + return ok; +} + +static tsi_result handshaker_result_create_frame_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector) { + if (self == nullptr || protector == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to handshaker_result_create_frame_protector()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>( + const_cast<tsi_handshaker_result*>(self)); + tsi_result ok = alts_create_frame_protector( + reinterpret_cast<const uint8_t*>(result->key_data), + kAltsAes128GcmRekeyKeyLength, result->is_client, /*is_rekey=*/true, + max_output_protected_frame_size, protector); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create frame protector"); + } + return ok; +} + +static tsi_result handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* bytes_size) { + if (self == nullptr || bytes == nullptr || bytes_size == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to handshaker_result_get_unused_bytes()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>( + const_cast<tsi_handshaker_result*>(self)); + *bytes = result->unused_bytes; + *bytes_size = result->unused_bytes_size; + return TSI_OK; +} + +static void handshaker_result_destroy(tsi_handshaker_result* self) { + if (self == nullptr) { + return; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>( + const_cast<tsi_handshaker_result*>(self)); + gpr_free(result->peer_identity); + gpr_free(result->key_data); + gpr_free(result->unused_bytes); + grpc_slice_unref(result->rpc_versions); + gpr_free(result); +} + +static const tsi_handshaker_result_vtable result_vtable = { + handshaker_result_extract_peer, + handshaker_result_create_zero_copy_grpc_protector, + handshaker_result_create_frame_protector, + handshaker_result_get_unused_bytes, handshaker_result_destroy}; + +static tsi_result create_handshaker_result(grpc_gcp_handshaker_resp* resp, + bool is_client, + tsi_handshaker_result** self) { + if (self == nullptr || resp == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()"); + return TSI_INVALID_ARGUMENT; + } + grpc_slice* key = static_cast<grpc_slice*>(resp->result.key_data.arg); + GPR_ASSERT(key != nullptr); + grpc_slice* identity = + static_cast<grpc_slice*>(resp->result.peer_identity.service_account.arg); + if (identity == nullptr) { + gpr_log(GPR_ERROR, "Invalid service account"); + return TSI_FAILED_PRECONDITION; + } + if (GRPC_SLICE_LENGTH(*key) < kAltsAes128GcmRekeyKeyLength) { + gpr_log(GPR_ERROR, "Bad key length"); + return TSI_FAILED_PRECONDITION; + } + alts_tsi_handshaker_result* result = + static_cast<alts_tsi_handshaker_result*>(gpr_zalloc(sizeof(*result))); + result->key_data = + static_cast<char*>(gpr_zalloc(kAltsAes128GcmRekeyKeyLength)); + memcpy(result->key_data, GRPC_SLICE_START_PTR(*key), + kAltsAes128GcmRekeyKeyLength); + result->peer_identity = grpc_slice_to_c_string(*identity); + if (!resp->result.has_peer_rpc_versions) { + gpr_log(GPR_ERROR, "Peer does not set RPC protocol versions."); + return TSI_FAILED_PRECONDITION; + } + if (!grpc_gcp_rpc_protocol_versions_encode(&resp->result.peer_rpc_versions, + &result->rpc_versions)) { + gpr_log(GPR_ERROR, "Failed to serialize peer's RPC protocol versions."); + return TSI_FAILED_PRECONDITION; + } + result->is_client = is_client; + result->base.vtable = &result_vtable; + *self = &result->base; + return TSI_OK; +} + +static tsi_result handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + if (self == nullptr || cb == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker* handshaker = + reinterpret_cast<alts_tsi_handshaker*>(self); + tsi_result ok = TSI_OK; + alts_tsi_event* event = nullptr; + ok = alts_tsi_event_create(handshaker, cb, user_data, handshaker->options, + handshaker->target_name, &event); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create ALTS TSI event"); + return ok; + } + grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0) + ? grpc_empty_slice() + : grpc_slice_from_copied_buffer( + reinterpret_cast<const char*>(received_bytes), + received_bytes_size); + if (!handshaker->has_sent_start_message) { + ok = handshaker->is_client + ? alts_handshaker_client_start_client(handshaker->client, event) + : alts_handshaker_client_start_server(handshaker->client, event, + &slice); + handshaker->has_sent_start_message = true; + } else { + if (!GRPC_SLICE_IS_EMPTY(handshaker->recv_bytes)) { + grpc_slice_unref(handshaker->recv_bytes); + } + handshaker->recv_bytes = grpc_slice_ref(slice); + ok = alts_handshaker_client_next(handshaker->client, event, &slice); + } + grpc_slice_unref(slice); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to schedule ALTS handshaker requests"); + return ok; + } + return TSI_ASYNC; +} + +static void handshaker_destroy(tsi_handshaker* self) { + if (self == nullptr) { + return; + } + alts_tsi_handshaker* handshaker = + reinterpret_cast<alts_tsi_handshaker*>(self); + alts_handshaker_client_destroy(handshaker->client); + grpc_slice_unref(handshaker->recv_bytes); + grpc_slice_unref(handshaker->target_name); + grpc_alts_credentials_options_destroy(handshaker->options); + gpr_free(handshaker->buffer); + gpr_free(handshaker); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + nullptr, nullptr, nullptr, nullptr, nullptr, handshaker_destroy, + handshaker_next}; + +static void thread_worker(void* arg) { + while (true) { + grpc_event event = grpc_completion_queue_next( + kSharedResource->cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT); + if (event.type == GRPC_QUEUE_SHUTDOWN) { + /* signal alts_tsi_shutdown() to destroy completion queue. */ + grpc_tsi_alts_signal_for_cq_destroy(); + break; + } + /* event.type == GRPC_OP_COMPLETE. */ + alts_tsi_event* alts_event = static_cast<alts_tsi_event*>(event.tag); + alts_tsi_event_dispatch_to_handshaker(alts_event, event.success); + alts_tsi_event_destroy(alts_event); + } +} + +static void init_shared_resources(const char* handshaker_service_url) { + GPR_ASSERT(handshaker_service_url != nullptr); + gpr_mu_lock(&kSharedResource->mu); + if (kSharedResource->channel == nullptr) { + gpr_cv_init(&kSharedResource->cv); + kSharedResource->channel = + grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr); + kSharedResource->cq = grpc_completion_queue_create_for_next(nullptr); + kSharedResource->thread = + grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr); + kSharedResource->thread.Start(); + } + gpr_mu_unlock(&kSharedResource->mu); +} + +tsi_result alts_tsi_handshaker_create( + const grpc_alts_credentials_options* options, const char* target_name, + const char* handshaker_service_url, bool is_client, tsi_handshaker** self) { + if (handshaker_service_url == nullptr || self == nullptr || + options == nullptr || (is_client && target_name == nullptr)) { + gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()"); + return TSI_INVALID_ARGUMENT; + } + init_shared_resources(handshaker_service_url); + alts_handshaker_client* client = alts_grpc_handshaker_client_create( + kSharedResource->channel, kSharedResource->cq, handshaker_service_url); + if (client == nullptr) { + gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client"); + return TSI_FAILED_PRECONDITION; + } + alts_tsi_handshaker* handshaker = + static_cast<alts_tsi_handshaker*>(gpr_zalloc(sizeof(*handshaker))); + handshaker->client = client; + handshaker->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE; + handshaker->buffer = + static_cast<unsigned char*>(gpr_zalloc(handshaker->buffer_size)); + handshaker->is_client = is_client; + handshaker->has_sent_start_message = false; + handshaker->target_name = target_name == nullptr + ? grpc_empty_slice() + : grpc_slice_from_static_string(target_name); + handshaker->options = grpc_alts_credentials_options_copy(options); + handshaker->base.vtable = &handshaker_vtable; + *self = &handshaker->base; + return TSI_OK; +} + +static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) { + GPR_ASSERT(resp != nullptr); + if (resp->has_result) { + return true; + } + return false; +} + +static void set_unused_bytes(tsi_handshaker_result* self, + grpc_slice* recv_bytes, size_t bytes_consumed) { + GPR_ASSERT(recv_bytes != nullptr && self != nullptr); + if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) { + return; + } + alts_tsi_handshaker_result* result = + reinterpret_cast<alts_tsi_handshaker_result*>(self); + result->unused_bytes_size = GRPC_SLICE_LENGTH(*recv_bytes) - bytes_consumed; + result->unused_bytes = + static_cast<unsigned char*>(gpr_zalloc(result->unused_bytes_size)); + memcpy(result->unused_bytes, + GRPC_SLICE_START_PTR(*recv_bytes) + bytes_consumed, + result->unused_bytes_size); +} + +void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker, + grpc_byte_buffer* recv_buffer, + grpc_status_code status, + grpc_slice* details, + tsi_handshaker_on_next_done_cb cb, + void* user_data, bool is_ok) { + /* Invalid input check. */ + if (cb == nullptr) { + gpr_log(GPR_ERROR, + "cb is nullptr in alts_tsi_handshaker_handle_response()"); + return; + } + if (handshaker == nullptr || recv_buffer == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to alts_tsi_handshaker_handle_response()"); + cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr); + return; + } + /* Failed grpc call check. */ + if (!is_ok || status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "grpc call made to handshaker service failed"); + if (details != nullptr) { + char* error_details = grpc_slice_to_c_string(*details); + gpr_log(GPR_ERROR, "error details:%s", error_details); + gpr_free(error_details); + } + cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr); + return; + } + grpc_gcp_handshaker_resp* resp = + alts_tsi_utils_deserialize_response(recv_buffer); + /* Invalid handshaker response check. */ + if (resp == nullptr) { + gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed"); + cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr); + return; + } + grpc_slice* slice = static_cast<grpc_slice*>(resp->out_frames.arg); + unsigned char* bytes_to_send = nullptr; + size_t bytes_to_send_size = 0; + if (slice != nullptr) { + bytes_to_send_size = GRPC_SLICE_LENGTH(*slice); + while (bytes_to_send_size > handshaker->buffer_size) { + handshaker->buffer_size *= 2; + handshaker->buffer = static_cast<unsigned char*>( + gpr_realloc(handshaker->buffer, handshaker->buffer_size)); + } + memcpy(handshaker->buffer, GRPC_SLICE_START_PTR(*slice), + bytes_to_send_size); + bytes_to_send = handshaker->buffer; + } + tsi_handshaker_result* result = nullptr; + if (is_handshake_finished_properly(resp)) { + create_handshaker_result(resp, handshaker->is_client, &result); + set_unused_bytes(result, &handshaker->recv_bytes, resp->bytes_consumed); + } + grpc_status_code code = static_cast<grpc_status_code>(resp->status.code); + grpc_gcp_handshaker_resp_destroy(resp); + cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send, + bytes_to_send_size, result); +} + +namespace grpc_core { +namespace internal { + +bool alts_tsi_handshaker_get_has_sent_start_message_for_testing( + alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->has_sent_start_message; +} + +bool alts_tsi_handshaker_get_is_client_for_testing( + alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->is_client; +} + +void alts_tsi_handshaker_set_recv_bytes_for_testing( + alts_tsi_handshaker* handshaker, grpc_slice* slice) { + GPR_ASSERT(handshaker != nullptr && slice != nullptr); + handshaker->recv_bytes = grpc_slice_ref(*slice); +} + +grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing( + alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->recv_bytes; +} + +void alts_tsi_handshaker_set_client_for_testing( + alts_tsi_handshaker* handshaker, alts_handshaker_client* client) { + GPR_ASSERT(handshaker != nullptr && client != nullptr); + alts_handshaker_client_destroy(handshaker->client); + handshaker->client = client; +} + +} // namespace internal +} // namespace grpc_core diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h new file mode 100644 index 0000000000..227b30ce53 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H + +#include <grpc/support/port_platform.h> + +#include <grpc/grpc.h> + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts_transport_security.h" +#include "src/core/tsi/transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +#define TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY "service_accont" +#define TSI_ALTS_CERTIFICATE_TYPE "ALTS" +#define TSI_ALTS_RPC_VERSIONS "rpc_versions" + +const size_t kTsiAltsNumOfPeerProperties = 3; + +/** + * Main struct for ALTS TSI handshaker. All APIs in the header are + * thread-comptabile. + */ +typedef struct alts_tsi_handshaker alts_tsi_handshaker; + +/** + * This method creates a ALTS TSI handshaker instance. + * + * - options: ALTS credentials options containing information passed from TSI + * caller (e.g., rpc protocol versions). + * - target_name: the name of the endpoint that the channel is connecting to, + * and will be used for secure naming check. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". + * - is_client: boolean value indicating if the handshaker is used at the client + * (is_client = true) or server (is_client = false) side. + * - self: address of ALTS TSI handshaker instance to be returned from the + * method. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_tsi_handshaker_create( + const grpc_alts_credentials_options* options, const char* target_name, + const char* handshaker_service_url, bool is_client, tsi_handshaker** self); + +/** + * This method handles handshaker response returned from ALTS handshaker + * service. + * + * - handshaker: ALTS TSI handshaker instance. + * - recv_buffer: buffer holding data received from the handshaker service. + * - status: status of the grpc call made to the handshaker service. + * - details: error details of the grpc call made to the handshaker service. + * - cb: callback function of ALTS TSI event. + * - user_data: argument of callback function. + * - is_ok: a boolean value indicating if the handshaker response is ok to read. + * + */ +void alts_tsi_handshaker_handle_response(alts_tsi_handshaker* handshaker, + grpc_byte_buffer* recv_buffer, + grpc_status_code status, + grpc_slice* details, + tsi_handshaker_on_next_done_cb cb, + void* user_data, bool is_ok); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H */ diff --git a/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h new file mode 100644 index 0000000000..9b7b9bb6b1 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" + +namespace grpc_core { +namespace internal { + +/** + * Unsafe, use for testing only. It allows the caller to change the way the + * ALTS TSI handshaker schedules handshaker requests. + */ +void alts_tsi_handshaker_set_client_for_testing(alts_tsi_handshaker* handshaker, + alts_handshaker_client* client); + +/* For testing only. */ +bool alts_tsi_handshaker_get_has_sent_start_message_for_testing( + alts_tsi_handshaker* handshaker); + +bool alts_tsi_handshaker_get_is_client_for_testing( + alts_tsi_handshaker* handshaker); + +void alts_tsi_handshaker_set_recv_bytes_for_testing( + alts_tsi_handshaker* handshaker, grpc_slice* slice); + +grpc_slice alts_tsi_handshaker_get_recv_bytes_for_testing( + alts_tsi_handshaker* handshaker); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H */ diff --git a/src/core/tsi/alts/handshaker/alts_tsi_utils.cc b/src/core/tsi/alts/handshaker/alts_tsi_utils.cc new file mode 100644 index 0000000000..d9b5e6c945 --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_utils.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h" + +#include <grpc/byte_buffer_reader.h> + +tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code) { + switch (code) { + case GRPC_STATUS_OK: + return TSI_OK; + case GRPC_STATUS_UNKNOWN: + return TSI_UNKNOWN_ERROR; + case GRPC_STATUS_INVALID_ARGUMENT: + return TSI_INVALID_ARGUMENT; + case GRPC_STATUS_NOT_FOUND: + return TSI_NOT_FOUND; + case GRPC_STATUS_INTERNAL: + return TSI_INTERNAL_ERROR; + default: + return TSI_UNKNOWN_ERROR; + } +} + +grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response( + grpc_byte_buffer* resp_buffer) { + GPR_ASSERT(resp_buffer != nullptr); + grpc_byte_buffer_reader bbr; + grpc_byte_buffer_reader_init(&bbr, resp_buffer); + grpc_slice slice = grpc_byte_buffer_reader_readall(&bbr); + grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create(); + bool ok = grpc_gcp_handshaker_resp_decode(slice, resp); + grpc_slice_unref(slice); + grpc_byte_buffer_reader_destroy(&bbr); + if (!ok) { + grpc_gcp_handshaker_resp_destroy(resp); + gpr_log(GPR_ERROR, "grpc_gcp_handshaker_resp_decode() failed"); + return nullptr; + } + return resp; +} diff --git a/src/core/tsi/alts/handshaker/alts_tsi_utils.h b/src/core/tsi/alts/handshaker/alts_tsi_utils.h new file mode 100644 index 0000000000..9ef649de2b --- /dev/null +++ b/src/core/tsi/alts/handshaker/alts_tsi_utils.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H + +#include <grpc/support/port_platform.h> + +#include <grpc/byte_buffer.h> +#include <grpc/grpc.h> + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" +#include "src/core/tsi/transport_security_interface.h" + +/** + * This method converts grpc_status_code code to the corresponding tsi_result + * code. + * + * - code: grpc_status_code code. + * + * It returns the converted tsi_result code. + */ +tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code); + +/** + * This method deserializes a handshaker response returned from ALTS handshaker + * service. + * + * - bytes_received: data returned from ALTS handshaker service. + * + * It returns a deserialized handshaker response on success and nullptr on + * failure. + */ +grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response( + grpc_byte_buffer* resp_buffer); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H */ diff --git a/src/core/tsi/alts/handshaker/altscontext.pb.c b/src/core/tsi/alts/handshaker/altscontext.pb.c new file mode 100644 index 0000000000..81a82f5992 --- /dev/null +++ b/src/core/tsi/alts/handshaker/altscontext.pb.c @@ -0,0 +1,48 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/altscontext.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_AltsContext_fields[7] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_AltsContext, application_protocol, application_protocol, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, record_protocol, application_protocol, 0), + PB_FIELD( 3, UENUM , OPTIONAL, STATIC , OTHER, grpc_gcp_AltsContext, security_level, record_protocol, 0), + PB_FIELD( 4, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, peer_service_account, security_level, 0), + PB_FIELD( 5, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, local_service_account, peer_service_account, 0), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_AltsContext, peer_rpc_versions, local_service_account, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_AltsContext) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_AltsContext) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/core/tsi/alts/handshaker/altscontext.pb.h b/src/core/tsi/alts/handshaker/altscontext.pb.h new file mode 100644 index 0000000000..3e72d7f678 --- /dev/null +++ b/src/core/tsi/alts/handshaker/altscontext.pb.h @@ -0,0 +1,64 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED +#define PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED +#include "third_party/nanopb/pb.h" +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _grpc_gcp_AltsContext { + pb_callback_t application_protocol; + pb_callback_t record_protocol; + bool has_security_level; + grpc_gcp_SecurityLevel security_level; + pb_callback_t peer_service_account; + pb_callback_t local_service_account; + bool has_peer_rpc_versions; + grpc_gcp_RpcProtocolVersions peer_rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_AltsContext) */ +} grpc_gcp_AltsContext; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_AltsContext_init_default {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_AltsContext_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_AltsContext_application_protocol_tag 1 +#define grpc_gcp_AltsContext_record_protocol_tag 2 +#define grpc_gcp_AltsContext_security_level_tag 3 +#define grpc_gcp_AltsContext_peer_service_account_tag 4 +#define grpc_gcp_AltsContext_local_service_account_tag 5 +#define grpc_gcp_AltsContext_peer_rpc_versions_tag 6 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_AltsContext_fields[7]; + +/* Maximum encoded size of messages (where known) */ +/* grpc_gcp_AltsContext_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define ALTSCONTEXT_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/src/core/tsi/alts/handshaker/handshaker.pb.c b/src/core/tsi/alts/handshaker/handshaker.pb.c new file mode 100644 index 0000000000..bd992dfa4a --- /dev/null +++ b/src/core/tsi/alts/handshaker/handshaker.pb.c @@ -0,0 +1,123 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/handshaker.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_Endpoint_fields[4] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Endpoint, ip_address, ip_address, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_Endpoint, port, ip_address, 0), + PB_FIELD( 3, UENUM , OPTIONAL, STATIC , OTHER, grpc_gcp_Endpoint, protocol, port, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_Identity_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Identity, service_account, service_account, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_Identity, hostname, service_account, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, grpc_gcp_StartClientHandshakeReq, handshake_security_protocol, handshake_security_protocol, 0), + PB_FIELD( 2, STRING , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, application_protocols, handshake_security_protocol, 0), + PB_FIELD( 3, STRING , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, record_protocols, application_protocols, 0), + PB_FIELD( 4, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_identities, record_protocols, &grpc_gcp_Identity_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, local_identity, target_identities, &grpc_gcp_Identity_fields), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, local_endpoint, local_identity, &grpc_gcp_Endpoint_fields), + PB_FIELD( 7, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields), + PB_FIELD( 8, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_name, remote_endpoint, 0), + PB_FIELD( 9, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, rpc_versions, target_name, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, grpc_gcp_ServerHandshakeParameters, record_protocols, record_protocols, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_ServerHandshakeParameters, local_identities, record_protocols, &grpc_gcp_Identity_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, grpc_gcp_StartServerHandshakeReq, application_protocols, application_protocols, 0), + PB_FIELD( 2, MESSAGE , REPEATED, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, handshake_parameters, application_protocols, &grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields), + PB_FIELD( 3, BYTES , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartServerHandshakeReq, in_bytes, handshake_parameters, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, local_endpoint, in_bytes, &grpc_gcp_Endpoint_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, rpc_versions, remote_endpoint, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3] = { + PB_FIELD( 1, INT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, key, key, 0), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value, key, &grpc_gcp_ServerHandshakeParameters_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2] = { + PB_FIELD( 1, BYTES , OPTIONAL, CALLBACK, FIRST, grpc_gcp_NextHandshakeMessageReq, in_bytes, in_bytes, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerReq_fields[4] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_gcp_HandshakerReq, client_start, client_start, &grpc_gcp_StartClientHandshakeReq_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerReq, server_start, client_start, &grpc_gcp_StartServerHandshakeReq_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerReq, next, server_start, &grpc_gcp_NextHandshakeMessageReq_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerResult_fields[8] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResult, application_protocol, application_protocol, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, record_protocol, application_protocol, 0), + PB_FIELD( 3, BYTES , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, key_data, record_protocol, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, peer_identity, key_data, &grpc_gcp_Identity_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, local_identity, peer_identity, &grpc_gcp_Identity_fields), + PB_FIELD( 6, BOOL , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, keep_channel_open, local_identity, 0), + PB_FIELD( 7, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, peer_rpc_versions, keep_channel_open, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerStatus_fields[3] = { + PB_FIELD( 1, UINT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_HandshakerStatus, code, code, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerStatus, details, code, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerResp_fields[5] = { + PB_FIELD( 1, BYTES , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResp, out_frames, out_frames, 0), + PB_FIELD( 2, UINT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, bytes_consumed, out_frames, 0), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, result, bytes_consumed, &grpc_gcp_HandshakerResult_fields), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, status, result, &grpc_gcp_HandshakerStatus_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, next) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, result) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, status) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 256 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, next) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 256 && pb_membersize(grpc_gcp_HandshakerResp, result) < 256 && pb_membersize(grpc_gcp_HandshakerResp, status) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/core/tsi/alts/handshaker/handshaker.pb.h b/src/core/tsi/alts/handshaker/handshaker.pb.h new file mode 100644 index 0000000000..0805a144de --- /dev/null +++ b/src/core/tsi/alts/handshaker/handshaker.pb.h @@ -0,0 +1,255 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED +#define PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED +#include "third_party/nanopb/pb.h" +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _grpc_gcp_HandshakeProtocol { + grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED = 0, + grpc_gcp_HandshakeProtocol_TLS = 1, + grpc_gcp_HandshakeProtocol_ALTS = 2 +} grpc_gcp_HandshakeProtocol; +#define _grpc_gcp_HandshakeProtocol_MIN grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED +#define _grpc_gcp_HandshakeProtocol_MAX grpc_gcp_HandshakeProtocol_ALTS +#define _grpc_gcp_HandshakeProtocol_ARRAYSIZE ((grpc_gcp_HandshakeProtocol)(grpc_gcp_HandshakeProtocol_ALTS+1)) + +typedef enum _grpc_gcp_NetworkProtocol { + grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED = 0, + grpc_gcp_NetworkProtocol_TCP = 1, + grpc_gcp_NetworkProtocol_UDP = 2 +} grpc_gcp_NetworkProtocol; +#define _grpc_gcp_NetworkProtocol_MIN grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED +#define _grpc_gcp_NetworkProtocol_MAX grpc_gcp_NetworkProtocol_UDP +#define _grpc_gcp_NetworkProtocol_ARRAYSIZE ((grpc_gcp_NetworkProtocol)(grpc_gcp_NetworkProtocol_UDP+1)) + +/* Struct definitions */ +typedef struct _grpc_gcp_Identity { + pb_callback_t service_account; + pb_callback_t hostname; +/* @@protoc_insertion_point(struct:grpc_gcp_Identity) */ +} grpc_gcp_Identity; + +typedef struct _grpc_gcp_NextHandshakeMessageReq { + pb_callback_t in_bytes; +/* @@protoc_insertion_point(struct:grpc_gcp_NextHandshakeMessageReq) */ +} grpc_gcp_NextHandshakeMessageReq; + +typedef struct _grpc_gcp_ServerHandshakeParameters { + pb_callback_t record_protocols; + pb_callback_t local_identities; +/* @@protoc_insertion_point(struct:grpc_gcp_ServerHandshakeParameters) */ +} grpc_gcp_ServerHandshakeParameters; + +typedef struct _grpc_gcp_Endpoint { + pb_callback_t ip_address; + bool has_port; + int32_t port; + bool has_protocol; + grpc_gcp_NetworkProtocol protocol; +/* @@protoc_insertion_point(struct:grpc_gcp_Endpoint) */ +} grpc_gcp_Endpoint; + +typedef struct _grpc_gcp_HandshakerResult { + pb_callback_t application_protocol; + pb_callback_t record_protocol; + pb_callback_t key_data; + bool has_peer_identity; + grpc_gcp_Identity peer_identity; + bool has_local_identity; + grpc_gcp_Identity local_identity; + bool has_keep_channel_open; + bool keep_channel_open; + bool has_peer_rpc_versions; + grpc_gcp_RpcProtocolVersions peer_rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResult) */ +} grpc_gcp_HandshakerResult; + +typedef struct _grpc_gcp_HandshakerStatus { + bool has_code; + uint32_t code; + pb_callback_t details; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerStatus) */ +} grpc_gcp_HandshakerStatus; + +typedef struct _grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry { + bool has_key; + int32_t key; + bool has_value; + grpc_gcp_ServerHandshakeParameters value; +/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry) */ +} grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry; + +typedef struct _grpc_gcp_HandshakerResp { + pb_callback_t out_frames; + bool has_bytes_consumed; + uint32_t bytes_consumed; + bool has_result; + grpc_gcp_HandshakerResult result; + bool has_status; + grpc_gcp_HandshakerStatus status; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResp) */ +} grpc_gcp_HandshakerResp; + +typedef struct _grpc_gcp_StartClientHandshakeReq { + bool has_handshake_security_protocol; + grpc_gcp_HandshakeProtocol handshake_security_protocol; + pb_callback_t application_protocols; + pb_callback_t record_protocols; + pb_callback_t target_identities; + bool has_local_identity; + grpc_gcp_Identity local_identity; + bool has_local_endpoint; + grpc_gcp_Endpoint local_endpoint; + bool has_remote_endpoint; + grpc_gcp_Endpoint remote_endpoint; + pb_callback_t target_name; + bool has_rpc_versions; + grpc_gcp_RpcProtocolVersions rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_StartClientHandshakeReq) */ +} grpc_gcp_StartClientHandshakeReq; + +typedef struct _grpc_gcp_StartServerHandshakeReq { + pb_callback_t application_protocols; + pb_size_t handshake_parameters_count; + grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry handshake_parameters[3]; + pb_callback_t in_bytes; + bool has_local_endpoint; + grpc_gcp_Endpoint local_endpoint; + bool has_remote_endpoint; + grpc_gcp_Endpoint remote_endpoint; + bool has_rpc_versions; + grpc_gcp_RpcProtocolVersions rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq) */ +} grpc_gcp_StartServerHandshakeReq; + +typedef struct _grpc_gcp_HandshakerReq { + bool has_client_start; + grpc_gcp_StartClientHandshakeReq client_start; + bool has_server_start; + grpc_gcp_StartServerHandshakeReq server_start; + bool has_next; + grpc_gcp_NextHandshakeMessageReq next; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerReq) */ +} grpc_gcp_HandshakerReq; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_Endpoint_init_default {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0} +#define grpc_gcp_Identity_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartClientHandshakeReq_init_default {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_ServerHandshakeParameters_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartServerHandshakeReq_init_default {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_default} +#define grpc_gcp_NextHandshakeMessageReq_init_default {{{NULL}, NULL}} +#define grpc_gcp_HandshakerReq_init_default {false, grpc_gcp_StartClientHandshakeReq_init_default, false, grpc_gcp_StartServerHandshakeReq_init_default, false, grpc_gcp_NextHandshakeMessageReq_init_default} +#define grpc_gcp_HandshakerResult_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Identity_init_default, false, 0, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_HandshakerStatus_init_default {false, 0, {{NULL}, NULL}} +#define grpc_gcp_HandshakerResp_init_default {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_default, false, grpc_gcp_HandshakerStatus_init_default} +#define grpc_gcp_Endpoint_init_zero {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0} +#define grpc_gcp_Identity_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartClientHandshakeReq_init_zero {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_ServerHandshakeParameters_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartServerHandshakeReq_init_zero {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_zero} +#define grpc_gcp_NextHandshakeMessageReq_init_zero {{{NULL}, NULL}} +#define grpc_gcp_HandshakerReq_init_zero {false, grpc_gcp_StartClientHandshakeReq_init_zero, false, grpc_gcp_StartServerHandshakeReq_init_zero, false, grpc_gcp_NextHandshakeMessageReq_init_zero} +#define grpc_gcp_HandshakerResult_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Identity_init_zero, false, 0, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_HandshakerStatus_init_zero {false, 0, {{NULL}, NULL}} +#define grpc_gcp_HandshakerResp_init_zero {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_zero, false, grpc_gcp_HandshakerStatus_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_Identity_service_account_tag 1 +#define grpc_gcp_Identity_hostname_tag 2 +#define grpc_gcp_NextHandshakeMessageReq_in_bytes_tag 1 +#define grpc_gcp_ServerHandshakeParameters_record_protocols_tag 1 +#define grpc_gcp_ServerHandshakeParameters_local_identities_tag 2 +#define grpc_gcp_Endpoint_ip_address_tag 1 +#define grpc_gcp_Endpoint_port_tag 2 +#define grpc_gcp_Endpoint_protocol_tag 3 +#define grpc_gcp_HandshakerResult_application_protocol_tag 1 +#define grpc_gcp_HandshakerResult_record_protocol_tag 2 +#define grpc_gcp_HandshakerResult_key_data_tag 3 +#define grpc_gcp_HandshakerResult_peer_identity_tag 4 +#define grpc_gcp_HandshakerResult_local_identity_tag 5 +#define grpc_gcp_HandshakerResult_keep_channel_open_tag 6 +#define grpc_gcp_HandshakerResult_peer_rpc_versions_tag 7 +#define grpc_gcp_HandshakerStatus_code_tag 1 +#define grpc_gcp_HandshakerStatus_details_tag 2 +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_key_tag 1 +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_value_tag 2 +#define grpc_gcp_HandshakerResp_out_frames_tag 1 +#define grpc_gcp_HandshakerResp_bytes_consumed_tag 2 +#define grpc_gcp_HandshakerResp_result_tag 3 +#define grpc_gcp_HandshakerResp_status_tag 4 +#define grpc_gcp_StartClientHandshakeReq_handshake_security_protocol_tag 1 +#define grpc_gcp_StartClientHandshakeReq_application_protocols_tag 2 +#define grpc_gcp_StartClientHandshakeReq_record_protocols_tag 3 +#define grpc_gcp_StartClientHandshakeReq_target_identities_tag 4 +#define grpc_gcp_StartClientHandshakeReq_local_identity_tag 5 +#define grpc_gcp_StartClientHandshakeReq_local_endpoint_tag 6 +#define grpc_gcp_StartClientHandshakeReq_remote_endpoint_tag 7 +#define grpc_gcp_StartClientHandshakeReq_target_name_tag 8 +#define grpc_gcp_StartClientHandshakeReq_rpc_versions_tag 9 +#define grpc_gcp_StartServerHandshakeReq_application_protocols_tag 1 +#define grpc_gcp_StartServerHandshakeReq_handshake_parameters_tag 2 +#define grpc_gcp_StartServerHandshakeReq_in_bytes_tag 3 +#define grpc_gcp_StartServerHandshakeReq_local_endpoint_tag 4 +#define grpc_gcp_StartServerHandshakeReq_remote_endpoint_tag 5 +#define grpc_gcp_StartServerHandshakeReq_rpc_versions_tag 6 +#define grpc_gcp_HandshakerReq_client_start_tag 1 +#define grpc_gcp_HandshakerReq_server_start_tag 2 +#define grpc_gcp_HandshakerReq_next_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_Endpoint_fields[4]; +extern const pb_field_t grpc_gcp_Identity_fields[3]; +extern const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10]; +extern const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3]; +extern const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7]; +extern const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3]; +extern const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2]; +extern const pb_field_t grpc_gcp_HandshakerReq_fields[4]; +extern const pb_field_t grpc_gcp_HandshakerResult_fields[8]; +extern const pb_field_t grpc_gcp_HandshakerStatus_fields[3]; +extern const pb_field_t grpc_gcp_HandshakerResp_fields[5]; + +/* Maximum encoded size of messages (where known) */ +/* grpc_gcp_Endpoint_size depends on runtime parameters */ +/* grpc_gcp_Identity_size depends on runtime parameters */ +/* grpc_gcp_StartClientHandshakeReq_size depends on runtime parameters */ +/* grpc_gcp_ServerHandshakeParameters_size depends on runtime parameters */ +/* grpc_gcp_StartServerHandshakeReq_size depends on runtime parameters */ +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_size (17 + grpc_gcp_ServerHandshakeParameters_size) +/* grpc_gcp_NextHandshakeMessageReq_size depends on runtime parameters */ +#define grpc_gcp_HandshakerReq_size (18 + grpc_gcp_StartClientHandshakeReq_size + grpc_gcp_StartServerHandshakeReq_size + grpc_gcp_NextHandshakeMessageReq_size) +/* grpc_gcp_HandshakerResult_size depends on runtime parameters */ +/* grpc_gcp_HandshakerStatus_size depends on runtime parameters */ +/* grpc_gcp_HandshakerResp_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define HANDSHAKER_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/src/core/tsi/alts/handshaker/proto/altscontext.proto b/src/core/tsi/alts/handshaker/proto/altscontext.proto new file mode 100644 index 0000000000..d195b37e08 --- /dev/null +++ b/src/core/tsi/alts/handshaker/proto/altscontext.proto @@ -0,0 +1,41 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "transport_security_common.proto"; + +package grpc.gcp; + +option java_package = "io.grpc.alts"; + +message AltsContext { + // The application protocol negotiated for this connection. + string application_protocol = 1; + + // The record protocol negotiated for this connection. + string record_protocol = 2; + + // The security level of the created secure channel. + SecurityLevel security_level = 3; + + // The peer service account. + string peer_service_account = 4; + + // The local service account. + string local_service_account = 5; + + // The RPC protocol versions supported by the peer. + RpcProtocolVersions peer_rpc_versions = 6; +} diff --git a/src/core/tsi/alts/handshaker/proto/handshaker.options b/src/core/tsi/alts/handshaker/proto/handshaker.options new file mode 100644 index 0000000000..702ba3802a --- /dev/null +++ b/src/core/tsi/alts/handshaker/proto/handshaker.options @@ -0,0 +1,2 @@ +handshaker.proto no_unions:true +grpc.gcp.StartServerHandshakeReq.handshake_parameters max_count:3 diff --git a/src/core/tsi/alts/handshaker/proto/handshaker.proto b/src/core/tsi/alts/handshaker/proto/handshaker.proto new file mode 100644 index 0000000000..46b8b09eb0 --- /dev/null +++ b/src/core/tsi/alts/handshaker/proto/handshaker.proto @@ -0,0 +1,220 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "transport_security_common.proto"; + +package grpc.gcp; + +option java_package = "io.grpc.alts"; + +enum HandshakeProtocol { + // Default value. + HANDSHAKE_PROTOCOL_UNSPECIFIED = 0; + + // TLS handshake protocol. + TLS = 1; + + // Application Layer Transport Security handshake protocol. + ALTS = 2; +} + +enum NetworkProtocol { + NETWORK_PROTOCOL_UNSPECIFIED = 0; + TCP = 1; + UDP = 2; +} + +message Endpoint { + // IP address. It should contain an IPv4 or IPv6 string literal, e.g. + // "192.168.0.1" or "2001:db8::1". + string ip_address = 1; + + // Port number. + int32 port = 2; + + // Network protocol (e.g., TCP, UDP) associated with this endpoint. + NetworkProtocol protocol = 3; +} + +message Identity { + oneof identity_oneof { + // Service account of a connection endpoint. + string service_account = 1; + + // Hostname of a connection endpoint. + string hostname = 2; + } +} + +message StartClientHandshakeReq { + // Handshake security protocol requested by the client. + HandshakeProtocol handshake_security_protocol = 1; + + // The application protocols supported by the client, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 2; + + // The record protocols supported by the client, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 3; + + // (Optional) Describes which server identities are acceptable by the client. + // If target identities are provided and none of them matches the peer + // identity of the server, handshake will fail. + repeated Identity target_identities = 4; + + // (Optional) Application may specify a local identity. Otherwise, the + // handshaker chooses a default local identity. + Identity local_identity = 5; + + // (Optional) Local endpoint information of the connection to the server, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 6; + + // (Optional) Endpoint information of the remote server, such as IP address, + // port number, and network protocool. + Endpoint remote_endpoint = 7; + + // (Optional) If target name is provided, a secure naming check is performed + // to verify that the peer authenticated identity is indeed authorized to run + // the target name. + string target_name = 8; + + // (Optional) RPC protocol versions supported by the client. + RpcProtocolVersions rpc_versions = 9; +} + +message ServerHandshakeParameters { + // The record protocols supported by the server, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 1; + + // (Optional) A list of local identities supported by the server, if + // specified. Otherwise, the handshaker chooses a default local identity. + repeated Identity local_identities = 2; +} + +message StartServerHandshakeReq { + // The application protocols supported by the server, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 1; + + // Handshake parameters (record protocols and local identities supported by + // the server) mapped by the handshake protocol. Each handshake security + // protocol (e.g., TLS or ALTS) has its own set of record protocols and local + // identities. Since protobuf does not support enum as key to the map, the key + // to handshake_parameters is the integer value of HandshakeProtocol enum. + map<int32, ServerHandshakeParameters> handshake_parameters = 2; + + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple HandshakReq messages. + bytes in_bytes = 3; + + // (Optional) Local endpoint information of the connection to the client, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 4; + + // (Optional) Endpoint information of the remote client, such as IP address, + // port number, and network protocool. + Endpoint remote_endpoint = 5; + + // (Optional) RPC protocol versions supported by the server. + RpcProtocolVersions rpc_versions = 6; +} + +message NextHandshakeMessageReq { + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple NextHandshakerMessageReq + // messages. + bytes in_bytes = 1; +} + +message HandshakerReq { + oneof req_oneof { + // The start client handshake request message. + StartClientHandshakeReq client_start = 1; + + // The start server handshake request message. + StartServerHandshakeReq server_start = 2; + + // The next handshake request message. + NextHandshakeMessageReq next = 3; + } +} + +message HandshakerResult { + // The application protocol negotiated for this connection. + string application_protocol = 1; + + // The record protocol negotiated for this connection. + string record_protocol = 2; + + // Cryptographic key data. The key data may be more than the key length + // required for the record protocol, thus the client of the handshaker + // service needs to truncate the key data into the right key length. + bytes key_data = 3; + + // The authenticated identity of the peer. + Identity peer_identity = 4; + + // The local identity used in the handshake. + Identity local_identity = 5; + + // Indicate whether the handshaker service client should keep the channel + // between the handshaker service open, e.g., in order to handle + // post-handshake messages in the future. + bool keep_channel_open = 6; + + // The RPC protocol versions supported by the peer. + RpcProtocolVersions peer_rpc_versions = 7; +} + +message HandshakerStatus { + // The status code. This could be the gRPC status code. + uint32 code = 1; + + // The status details. + string details = 2; +} + +message HandshakerResp { + // Frames to be given to the peer for the NextHandshakeMessageReq. May be + // empty if no out_frames have to be sent to the peer or if in_bytes in the + // HandshakerReq are incomplete. All the non-empty out frames must be sent to + // the peer even if the handshaker status is not OK as these frames may + // contain the alert frames. + bytes out_frames = 1; + + // Number of bytes in the in_bytes consumed by the handshaker. It is possible + // that part of in_bytes in HandshakerReq was unrelated to the handshake + // process. + uint32 bytes_consumed = 2; + + // This is set iff the handshake was successful. out_frames may still be set + // to frames that needs to be forwarded to the peer. + HandshakerResult result = 3; + + // Status of the handshaker. + HandshakerStatus status = 4; +} + +service HandshakerService { + // Accepts a stream of handshaker request, returning a stream of handshaker + // response. + rpc DoHandshake(stream HandshakerReq) + returns (stream HandshakerResp) { + } +} diff --git a/src/core/tsi/alts/handshaker/proto/transport_security_common.proto b/src/core/tsi/alts/handshaker/proto/transport_security_common.proto new file mode 100644 index 0000000000..41983ab9f9 --- /dev/null +++ b/src/core/tsi/alts/handshaker/proto/transport_security_common.proto @@ -0,0 +1,40 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package grpc.gcp; + +option java_package = "io.grpc.alts"; + +// The security level of the created channel. The list is sorted in increasing +// level of security. This order must always be maintained. +enum SecurityLevel { + SECURITY_NONE = 0; + INTEGRITY_ONLY = 1; + INTEGRITY_AND_PRIVACY = 2; +} + +// Max and min supported RPC protocol versions. +message RpcProtocolVersions { + // RPC version contains a major version and a minor version. + message Version { + uint32 major = 1; + uint32 minor = 2; + } + // Maximum supported RPC version. + Version max_rpc_version = 1; + // Minimum supported RPC version. + Version min_rpc_version = 2; +} diff --git a/src/core/tsi/alts/handshaker/transport_security_common.pb.c b/src/core/tsi/alts/handshaker/transport_security_common.pb.c new file mode 100644 index 0000000000..6063c7625e --- /dev/null +++ b/src/core/tsi/alts/handshaker/transport_security_common.pb.c @@ -0,0 +1,50 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_gcp_RpcProtocolVersions, max_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_RpcProtocolVersions, min_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3] = { + PB_FIELD( 1, UINT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_RpcProtocolVersions_Version, major, major, 0), + PB_FIELD( 2, UINT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_RpcProtocolVersions_Version, minor, major, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 65536 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 256 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/core/tsi/alts/handshaker/transport_security_common.pb.h b/src/core/tsi/alts/handshaker/transport_security_common.pb.h new file mode 100644 index 0000000000..49096dffa3 --- /dev/null +++ b/src/core/tsi/alts/handshaker/transport_security_common.pb.h @@ -0,0 +1,78 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED +#define PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED +#include "third_party/nanopb/pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _grpc_gcp_SecurityLevel { + grpc_gcp_SecurityLevel_SECURITY_NONE = 0, + grpc_gcp_SecurityLevel_INTEGRITY_ONLY = 1, + grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY = 2 +} grpc_gcp_SecurityLevel; +#define _grpc_gcp_SecurityLevel_MIN grpc_gcp_SecurityLevel_SECURITY_NONE +#define _grpc_gcp_SecurityLevel_MAX grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY +#define _grpc_gcp_SecurityLevel_ARRAYSIZE ((grpc_gcp_SecurityLevel)(grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY+1)) + +/* Struct definitions */ +typedef struct _grpc_gcp_RpcProtocolVersions_Version { + bool has_major; + uint32_t major; + bool has_minor; + uint32_t minor; +/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions_Version) */ +} grpc_gcp_RpcProtocolVersions_Version; + +typedef struct _grpc_gcp_RpcProtocolVersions { + bool has_max_rpc_version; + grpc_gcp_RpcProtocolVersions_Version max_rpc_version; + bool has_min_rpc_version; + grpc_gcp_RpcProtocolVersions_Version min_rpc_version; +/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions) */ +} grpc_gcp_RpcProtocolVersions; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_RpcProtocolVersions_init_default {false, grpc_gcp_RpcProtocolVersions_Version_init_default, false, grpc_gcp_RpcProtocolVersions_Version_init_default} +#define grpc_gcp_RpcProtocolVersions_Version_init_default {false, 0, false, 0} +#define grpc_gcp_RpcProtocolVersions_init_zero {false, grpc_gcp_RpcProtocolVersions_Version_init_zero, false, grpc_gcp_RpcProtocolVersions_Version_init_zero} +#define grpc_gcp_RpcProtocolVersions_Version_init_zero {false, 0, false, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_RpcProtocolVersions_Version_major_tag 1 +#define grpc_gcp_RpcProtocolVersions_Version_minor_tag 2 +#define grpc_gcp_RpcProtocolVersions_max_rpc_version_tag 1 +#define grpc_gcp_RpcProtocolVersions_min_rpc_version_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3]; +extern const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define grpc_gcp_RpcProtocolVersions_size 28 +#define grpc_gcp_RpcProtocolVersions_Version_size 12 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define TRANSPORT_SECURITY_COMMON_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/src/core/tsi/alts/handshaker/transport_security_common_api.cc b/src/core/tsi/alts/handshaker/transport_security_common_api.cc new file mode 100644 index 0000000000..8a7edb53d4 --- /dev/null +++ b/src/core/tsi/alts/handshaker/transport_security_common_api.cc @@ -0,0 +1,196 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +bool grpc_gcp_rpc_protocol_versions_set_max( + grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major, + uint32_t max_minor) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "versions is nullptr in " + "grpc_gcp_rpc_protocol_versions_set_max()."); + return false; + } + versions->has_max_rpc_version = true; + versions->max_rpc_version.has_major = true; + versions->max_rpc_version.has_minor = true; + versions->max_rpc_version.major = max_major; + versions->max_rpc_version.minor = max_minor; + return true; +} + +bool grpc_gcp_rpc_protocol_versions_set_min( + grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major, + uint32_t min_minor) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "versions is nullptr in " + "grpc_gcp_rpc_protocol_versions_set_min()."); + return false; + } + versions->has_min_rpc_version = true; + versions->min_rpc_version.has_major = true; + versions->min_rpc_version.has_minor = true; + versions->min_rpc_version.major = min_major; + versions->min_rpc_version.minor = min_minor; + return true; +} + +size_t grpc_gcp_rpc_protocol_versions_encode_length( + const grpc_gcp_rpc_protocol_versions* versions) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode_length()."); + return 0; + } + pb_ostream_t size_stream; + memset(&size_stream, 0, sizeof(pb_ostream_t)); + if (!pb_encode(&size_stream, grpc_gcp_RpcProtocolVersions_fields, versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream)); + return 0; + } + return size_stream.bytes_written; +} + +bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes, + size_t bytes_length) { + if (versions == nullptr || bytes == nullptr || bytes_length == 0) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes()."); + return false; + } + pb_ostream_t output_stream = pb_ostream_from_buffer(bytes, bytes_length); + if (!pb_encode(&output_stream, grpc_gcp_RpcProtocolVersions_fields, + versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream)); + return false; + } + return true; +} + +bool grpc_gcp_rpc_protocol_versions_encode( + const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice) { + if (versions == nullptr || slice == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode()."); + return false; + } + size_t encoded_length = + grpc_gcp_rpc_protocol_versions_encode_length(versions); + if (encoded_length == 0) return false; + *slice = grpc_slice_malloc(encoded_length); + return grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + versions, GRPC_SLICE_START_PTR(*slice), encoded_length); +} + +bool grpc_gcp_rpc_protocol_versions_decode( + grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "version is nullptr in " + "grpc_gcp_rpc_protocol_versions_decode()."); + return false; + } + pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(slice), + GRPC_SLICE_LENGTH(slice)); + if (!pb_decode(&stream, grpc_gcp_RpcProtocolVersions_fields, versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return false; + } + return true; +} + +bool grpc_gcp_rpc_protocol_versions_copy( + const grpc_gcp_rpc_protocol_versions* src, + grpc_gcp_rpc_protocol_versions* dst) { + if ((src == nullptr && dst != nullptr) || + (src != nullptr && dst == nullptr)) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_rpc_protocol_versions_copy()."); + return false; + } + if (src == nullptr) { + return true; + } + grpc_gcp_rpc_protocol_versions_set_max(dst, src->max_rpc_version.major, + src->max_rpc_version.minor); + grpc_gcp_rpc_protocol_versions_set_min(dst, src->min_rpc_version.major, + src->min_rpc_version.minor); + return true; +} + +namespace grpc_core { +namespace internal { + +int grpc_gcp_rpc_protocol_version_compare( + const grpc_gcp_rpc_protocol_versions_version* v1, + const grpc_gcp_rpc_protocol_versions_version* v2) { + if ((v1->major > v2->major) || + (v1->major == v2->major && v1->minor > v2->minor)) { + return 1; + } + if ((v1->major < v2->major) || + (v1->major == v2->major && v1->minor < v2->minor)) { + return -1; + } + return 0; +} + +} // namespace internal +} // namespace grpc_core + +bool grpc_gcp_rpc_protocol_versions_check( + const grpc_gcp_rpc_protocol_versions* local_versions, + const grpc_gcp_rpc_protocol_versions* peer_versions, + grpc_gcp_rpc_protocol_versions_version* highest_common_version) { + if (local_versions == nullptr || peer_versions == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_rpc_protocol_versions_check()."); + return false; + } + /* max_common_version is MIN(local.max, peer.max) */ + const grpc_gcp_rpc_protocol_versions_version* max_common_version = + grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + &local_versions->max_rpc_version, &peer_versions->max_rpc_version) > 0 + ? &peer_versions->max_rpc_version + : &local_versions->max_rpc_version; + /* min_common_version is MAX(local.min, peer.min) */ + const grpc_gcp_rpc_protocol_versions_version* min_common_version = + grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + &local_versions->min_rpc_version, &peer_versions->min_rpc_version) > 0 + ? &local_versions->min_rpc_version + : &peer_versions->min_rpc_version; + bool result = grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + max_common_version, min_common_version) >= 0 + ? true + : false; + if (result && highest_common_version != nullptr) { + memcpy(highest_common_version, max_common_version, + sizeof(grpc_gcp_rpc_protocol_versions_version)); + } + return result; +} diff --git a/src/core/tsi/alts/handshaker/transport_security_common_api.h b/src/core/tsi/alts/handshaker/transport_security_common_api.h new file mode 100644 index 0000000000..68228cb3b5 --- /dev/null +++ b/src/core/tsi/alts/handshaker/transport_security_common_api.h @@ -0,0 +1,163 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H + +#include <grpc/support/port_platform.h> + +#include "third_party/nanopb/pb_decode.h" +#include "third_party/nanopb/pb_encode.h" + +#include <grpc/slice.h> +#include <grpc/slice_buffer.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" + +typedef grpc_gcp_RpcProtocolVersions grpc_gcp_rpc_protocol_versions; + +typedef grpc_gcp_RpcProtocolVersions_Version + grpc_gcp_rpc_protocol_versions_version; + +/** + * This method sets the value for max_rpc_versions field of rpc protocol + * versions. + * + * - versions: an rpc protocol version instance. + * - max_major: a major version of maximum supported RPC version. + * - max_minor: a minor version of maximum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_set_max( + grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major, + uint32_t max_minor); + +/** + * This method sets the value for min_rpc_versions field of rpc protocol + * versions. + * + * - versions: an rpc protocol version instance. + * - min_major: a major version of minimum supported RPC version. + * - min_minor: a minor version of minimum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_set_min( + grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major, + uint32_t min_minor); + +/** + * This method computes serialized byte length of rpc protocol versions. + * + * - versions: an rpc protocol versions instance. + * + * The method returns serialized byte length. It returns 0 on failure. + */ +size_t grpc_gcp_rpc_protocol_versions_encode_length( + const grpc_gcp_rpc_protocol_versions* versions); + +/** + * This method serializes rpc protocol versions and writes the result to + * the memory buffer provided by the caller. Caller is responsible for + * allocating sufficient memory to store the serialized data. + * + * - versions: an rpc protocol versions instance. + * - bytes: bytes buffer where the result will be written to. + * - bytes_length: length of the bytes buffer. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes, + size_t bytes_length); + +/** + * This method serializes an rpc protocol version and returns serialized rpc + * versions in grpc slice. + * + * - versions: an rpc protocol versions instance. + * - slice: grpc slice where the serialized result will be written. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_encode( + const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice); + +/** + * This method de-serializes input in grpc slice form and stores the result + * in rpc protocol versions. + * + * - slice: a data stream containing a serialized rpc protocol version. + * - versions: an rpc protocol version instance used to hold de-serialized + * result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_decode( + grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions); + +/** + * This method performs a deep copy operation on rpc protocol versions + * instance. + * + * - src: rpc protocol versions instance that needs to be copied. + * - dst: rpc protocol versions instance that stores the copied result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_copy( + const grpc_gcp_rpc_protocol_versions* src, + grpc_gcp_rpc_protocol_versions* dst); + +/** + * This method performs a version check between local and peer rpc protocol + * versions. + * + * - local_versions: local rpc protocol versions instance. + * - peer_versions: peer rpc protocol versions instance. + * - highest_common_version: an output parameter that will store the highest + * common rpc protocol version both parties agreed on. + * + * The method returns true if the check passes which means both parties agreed + * on a common rpc protocol to use, and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_check( + const grpc_gcp_rpc_protocol_versions* local_versions, + const grpc_gcp_rpc_protocol_versions* peer_versions, + grpc_gcp_rpc_protocol_versions_version* highest_common_version); + +namespace grpc_core { +namespace internal { + +/** + * Exposed for testing only. + * The method returns 0 if v1 = v2, + * returns 1 if v1 > v2, + * returns -1 if v1 < v2. + */ +int grpc_gcp_rpc_protocol_version_compare( + const grpc_gcp_rpc_protocol_versions_version* v1, + const grpc_gcp_rpc_protocol_versions_version* v2); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc new file mode 100644 index 0000000000..7ba03eb7f0 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc @@ -0,0 +1,180 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* Main struct for alts_grpc_integrity_only_record_protocol. */ +typedef struct alts_grpc_integrity_only_record_protocol { + alts_grpc_record_protocol base; + grpc_slice_buffer data_sb; + unsigned char* tag_buf; +} alts_grpc_integrity_only_record_protocol; + +/* --- alts_grpc_record_protocol methods implementation. --- */ + +static tsi_result alts_grpc_integrity_only_protect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + /* Input sanity check. */ + if (rp == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol protect."); + return TSI_INVALID_ARGUMENT; + } + /* Allocates memory for header and tag slices. */ + grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length); + grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length); + /* Calls alts_iovec_record_protocol protect. */ + char* error_details = nullptr; + iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice), + GRPC_SLICE_LENGTH(header_slice)}; + iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice), + GRPC_SLICE_LENGTH(tag_slice)}; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, + unprotected_slices); + grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect( + rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec, + tag_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to protect, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /* Appends result to protected_slices. */ + grpc_slice_buffer_add(protected_slices, header_slice); + grpc_slice_buffer_move_into(unprotected_slices, protected_slices); + grpc_slice_buffer_add(protected_slices, tag_slice); + return TSI_OK; +} + +static tsi_result alts_grpc_integrity_only_unprotect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + /* Input sanity check. */ + if (rp == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol unprotect."); + return TSI_INVALID_ARGUMENT; + } + if (protected_slices->length < rp->header_length + rp->tag_length) { + gpr_log(GPR_ERROR, "Protected slices do not have sufficient data."); + return TSI_INVALID_ARGUMENT; + } + /* In this method, rp points to alts_grpc_record_protocol struct + * and integrity_only_record_protocol points to + * alts_grpc_integrity_only_record_protocol struct. */ + alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol = + reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp); + /* Strips frame header from protected slices. */ + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_move_first(protected_slices, rp->header_length, + &rp->header_sb); + GPR_ASSERT(rp->header_sb.length == rp->header_length); + iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp); + /* Moves protected slices data to data_sb and leaves the remaining tag. */ + grpc_slice_buffer_reset_and_unref_internal( + &integrity_only_record_protocol->data_sb); + grpc_slice_buffer_move_first(protected_slices, + protected_slices->length - rp->tag_length, + &integrity_only_record_protocol->data_sb); + GPR_ASSERT(protected_slices->length == rp->tag_length); + iovec_t tag_iovec = {nullptr, rp->tag_length}; + if (protected_slices->count == 1) { + tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]); + } else { + /* Frame tag is in multiple slices, copies the tag bytes from slice + * buffer to a single flat buffer. */ + alts_grpc_record_protocol_copy_slice_buffer( + protected_slices, integrity_only_record_protocol->tag_buf); + tag_iovec.iov_base = integrity_only_record_protocol->tag_buf; + } + /* Calls alts_iovec_record_protocol unprotect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + rp, &integrity_only_record_protocol->data_sb); + grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect( + rp->iovec_rp, rp->iovec_buf, + integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec, + &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_reset_and_unref_internal(protected_slices); + grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb, + unprotected_slices); + return TSI_OK; +} + +static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) { + if (rp == nullptr) { + return; + } + alts_grpc_integrity_only_record_protocol* integrity_only_rp = + reinterpret_cast<alts_grpc_integrity_only_record_protocol*>(rp); + grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb); + gpr_free(integrity_only_rp->tag_buf); +} + +static const alts_grpc_record_protocol_vtable + alts_grpc_integrity_only_record_protocol_vtable = { + alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect, + alts_grpc_integrity_only_destruct}; + +tsi_result alts_grpc_integrity_only_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp) { + if (crypter == nullptr || rp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol create."); + return TSI_INVALID_ARGUMENT; + } + alts_grpc_integrity_only_record_protocol* impl = + static_cast<alts_grpc_integrity_only_record_protocol*>( + gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol))); + /* Calls alts_grpc_record_protocol init. */ + tsi_result result = alts_grpc_record_protocol_init( + &impl->base, crypter, overflow_size, is_client, + /*is_integrity_only=*/true, is_protect); + if (result != TSI_OK) { + gpr_free(impl); + return result; + } + /* Initializes slice buffer for data_sb. */ + grpc_slice_buffer_init(&impl->data_sb); + /* Allocates tag buffer. */ + impl->tag_buf = + static_cast<unsigned char*>(gpr_malloc(impl->base.tag_length)); + impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable; + *rp = &impl->base; + return TSI_OK; +} diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h new file mode 100644 index 0000000000..8d68b27e07 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" + +/** + * This method creates an integrity-only alts_grpc_record_protocol instance, + * given a gsec_aead_crypter instance and a flag indicating if the created + * instance will be used at the client or server side. The ownership of + * gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - overflow_size: overflow size of counter in bytes. + * - is_client: a flag indicating if the alts_grpc_record_protocol instance will + * be used at the client or server side. + * - is_protect: a flag indicating if the alts_grpc_record_protocol instance + * will be used for protect or unprotect. + * - rp: an alts_grpc_record_protocol instance to be returned from + * the method. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_integrity_only_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H \ + */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc new file mode 100644 index 0000000000..d4fd88d1e2 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* Privacy-integrity alts_grpc_record_protocol object uses the same struct + * defined in alts_grpc_record_protocol_common.h. */ + +/* --- alts_grpc_record_protocol methods implementation. --- */ + +static tsi_result alts_grpc_privacy_integrity_protect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + /* Input sanity check. */ + if (rp == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol protect."); + return TSI_INVALID_ARGUMENT; + } + /* Allocates memory for output frame. In privacy-integrity protect, the + * protected frame is stored in a newly allocated buffer. */ + size_t protected_frame_size = + unprotected_slices->length + rp->header_length + + alts_iovec_record_protocol_get_tag_length(rp->iovec_rp); + grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size); + iovec_t protected_iovec = {GRPC_SLICE_START_PTR(protected_slice), + GRPC_SLICE_LENGTH(protected_slice)}; + /* Calls alts_iovec_record_protocol protect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, + unprotected_slices); + grpc_status_code status = + alts_iovec_record_protocol_privacy_integrity_protect( + rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, + protected_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to protect, %s", error_details); + gpr_free(error_details); + grpc_slice_unref(protected_slice); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_add(protected_slices, protected_slice); + grpc_slice_buffer_reset_and_unref_internal(unprotected_slices); + return TSI_OK; +} + +static tsi_result alts_grpc_privacy_integrity_unprotect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + /* Input sanity check. */ + if (rp == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol unprotect."); + return TSI_INVALID_ARGUMENT; + } + /* Allocates memory for output frame. In privacy-integrity unprotect, the + * unprotected data are stored in a newly allocated buffer. */ + if (protected_slices->length < rp->header_length + rp->tag_length) { + gpr_log(GPR_ERROR, "Protected slices do not have sufficient data."); + return TSI_INVALID_ARGUMENT; + } + size_t unprotected_frame_size = + protected_slices->length - rp->header_length - rp->tag_length; + grpc_slice unprotected_slice = GRPC_SLICE_MALLOC(unprotected_frame_size); + iovec_t unprotected_iovec = {GRPC_SLICE_START_PTR(unprotected_slice), + GRPC_SLICE_LENGTH(unprotected_slice)}; + /* Strips frame header from protected slices. */ + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_move_first(protected_slices, rp->header_length, + &rp->header_sb); + iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp); + /* Calls alts_iovec_record_protocol unprotect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, protected_slices); + grpc_status_code status = + alts_iovec_record_protocol_privacy_integrity_unprotect( + rp->iovec_rp, header_iovec, rp->iovec_buf, protected_slices->count, + unprotected_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details); + gpr_free(error_details); + grpc_slice_unref(unprotected_slice); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_reset_and_unref_internal(protected_slices); + grpc_slice_buffer_add(unprotected_slices, unprotected_slice); + return TSI_OK; +} + +static const alts_grpc_record_protocol_vtable + alts_grpc_privacy_integrity_record_protocol_vtable = { + alts_grpc_privacy_integrity_protect, + alts_grpc_privacy_integrity_unprotect, nullptr}; + +tsi_result alts_grpc_privacy_integrity_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp) { + if (crypter == nullptr || rp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol create."); + return TSI_INVALID_ARGUMENT; + } + auto* impl = static_cast<alts_grpc_record_protocol*>( + gpr_zalloc(sizeof(alts_grpc_record_protocol))); + /* Calls alts_grpc_record_protocol init. */ + tsi_result result = + alts_grpc_record_protocol_init(impl, crypter, overflow_size, is_client, + /*is_integrity_only=*/false, is_protect); + if (result != TSI_OK) { + gpr_free(impl); + return result; + } + impl->vtable = &alts_grpc_privacy_integrity_record_protocol_vtable; + *rp = impl; + return TSI_OK; +} diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h new file mode 100644 index 0000000000..1e34aef2d8 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" + +/** + * This method creates a privacy-integrity alts_grpc_record_protocol instance, + * given a gsec_aead_crypter instance and a flag indicating if the created + * instance will be used at the client or server side. The ownership of + * gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - is_client: a flag indicating if the alts_grpc_record_protocol instance will + * be used at the client or server side. + * - rp: an alts_grpc_record_protocol instance to be returned from + * the method. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_privacy_integrity_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H \ + */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h new file mode 100644 index 0000000000..d1e433dac4 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h @@ -0,0 +1,91 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H + +#include <grpc/support/port_platform.h> + +#include <grpc/slice_buffer.h> + +#include "src/core/tsi/transport_security_interface.h" + +/** + * This alts_grpc_record_protocol object protects and unprotects a single frame + * stored in grpc slice buffer with zero or minimized memory copy. + * Implementations of this object must be thread compatible. + */ +typedef struct alts_grpc_record_protocol alts_grpc_record_protocol; + +/** + * This methods performs protect operation on unprotected data and appends the + * protected frame to protected_slices. The caller needs to ensure the length + * of unprotected data plus the frame overhead is less than or equal to the + * maximum frame length. The input unprotected data slice buffer will be + * cleared, although the actual unprotected data bytes are not modified. + * + * - self: an alts_grpc_record_protocol instance. + * - unprotected_slices: the unprotected data to be protected. + * - protected_slices: slice buffer where the protected frame is appended. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_record_protocol_protect( + alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + +/** + * This methods performs unprotect operation on a full frame of protected data + * and appends unprotected data to unprotected_slices. It is the caller's + * responsibility to prepare a full frame of data before calling this method. + * The input protected frame slice buffer will be cleared, although the actual + * protected data bytes are not modified. + * + * - self: an alts_grpc_record_protocol instance. + * - protected_slices: a full frame of protected data in grpc slices. + * - unprotected_slices: slice buffer where unprotected data is appended. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_record_protocol_unprotect( + alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + +/** + * This method returns maximum allowed unprotected data size, given maximum + * protected frame size. + * + * - self: an alts_grpc_record_protocol instance. + * - max_protected_frame_size: maximum protected frame size. + * + * On success, the method returns the maximum allowed unprotected data size. + * Otherwise, it returns zero. + */ +size_t alts_grpc_record_protocol_max_unprotected_data_size( + const alts_grpc_record_protocol* self, size_t max_protected_frame_size); + +/** + * This method destroys an alts_grpc_record_protocol instance by de-allocating + * all of its occupied memory. + */ +void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H \ + */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc new file mode 100644 index 0000000000..ff91aea350 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc @@ -0,0 +1,173 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/slice/slice_internal.h" + +const size_t kInitialIovecBufferSize = 8; + +/* Makes sure iovec_buf in alts_grpc_record_protocol is large enough. */ +static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp, + const grpc_slice_buffer* sb) { + GPR_ASSERT(rp != nullptr && sb != nullptr); + if (sb->count <= rp->iovec_buf_length) { + return; + } + /* At least double the iovec buffer size. */ + rp->iovec_buf_length = GPR_MAX(sb->count, 2 * rp->iovec_buf_length); + rp->iovec_buf = static_cast<iovec_t*>( + gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t))); +} + +/* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h. + * --- */ + +void alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) { + GPR_ASSERT(rp != nullptr && sb != nullptr); + ensure_iovec_buf_size(rp, sb); + for (size_t i = 0; i < sb->count; i++) { + rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]); + rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]); + } +} + +void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src, + unsigned char* dst) { + GPR_ASSERT(src != nullptr && dst != nullptr); + for (size_t i = 0; i < src->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]); + memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length); + dst += slice_length; + } +} + +iovec_t alts_grpc_record_protocol_get_header_iovec( + alts_grpc_record_protocol* rp) { + iovec_t header_iovec = {nullptr, 0}; + if (rp == nullptr) { + return header_iovec; + } + header_iovec.iov_len = rp->header_length; + if (rp->header_sb.count == 1) { + header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]); + } else { + /* Frame header is in multiple slices, copies the header bytes from slice + * buffer to a single flat buffer. */ + alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf); + header_iovec.iov_base = rp->header_buf; + } + return header_iovec; +} + +tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp, + gsec_aead_crypter* crypter, + size_t overflow_size, bool is_client, + bool is_integrity_only, + bool is_protect) { + if (rp == nullptr || crypter == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol init."); + return TSI_INVALID_ARGUMENT; + } + /* Creates alts_iovec_record_protocol. */ + char* error_details = nullptr; + grpc_status_code status = alts_iovec_record_protocol_create( + crypter, overflow_size, is_client, is_integrity_only, is_protect, + &rp->iovec_rp, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.", + error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /* Allocates header slice buffer. */ + grpc_slice_buffer_init(&rp->header_sb); + /* Allocates header buffer. */ + rp->header_length = alts_iovec_record_protocol_get_header_length(); + rp->header_buf = static_cast<unsigned char*>(gpr_malloc(rp->header_length)); + rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp); + /* Allocates iovec buffer. */ + rp->iovec_buf_length = kInitialIovecBufferSize; + rp->iovec_buf = + static_cast<iovec_t*>(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t))); + return TSI_OK; +} + +/* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */ +tsi_result alts_grpc_record_protocol_protect( + alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr || + self->vtable == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == nullptr) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->protect(self, unprotected_slices, protected_slices); +} + +tsi_result alts_grpc_record_protocol_unprotect( + alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr || + self->vtable == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == nullptr) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->unprotect(self, protected_slices, unprotected_slices); +} + +void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) { + if (self == nullptr) { + return; + } + if (self->vtable->destruct != nullptr) { + self->vtable->destruct(self); + } + alts_iovec_record_protocol_destroy(self->iovec_rp); + grpc_slice_buffer_destroy_internal(&self->header_sb); + gpr_free(self->header_buf); + gpr_free(self->iovec_buf); + gpr_free(self); +} + +/* Integrity-only and privacy-integrity share the same implementation. No need + * to call vtable. */ +size_t alts_grpc_record_protocol_max_unprotected_data_size( + const alts_grpc_record_protocol* self, size_t max_protected_frame_size) { + if (self == nullptr) { + return 0; + } + return alts_iovec_record_protocol_max_unprotected_data_size( + self->iovec_rp, max_protected_frame_size); +} diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h new file mode 100644 index 0000000000..43b8a4a2b8 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h @@ -0,0 +1,100 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H + +/** + * this file contains alts_grpc_record_protocol internals and internal-only + * helper functions. The public functions of alts_grpc_record_protocol are + * defined in the alts_grpc_record_protocol.h. + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* V-table for alts_grpc_record_protocol implementations. */ +typedef struct { + tsi_result (*protect)(alts_grpc_record_protocol* self, + grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + tsi_result (*unprotect)(alts_grpc_record_protocol* self, + grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + void (*destruct)(alts_grpc_record_protocol* self); +} alts_grpc_record_protocol_vtable; + +/* Main struct for alts_grpc_record_protocol implementation, shared by both + * integrity-only record protocol and privacy-integrity record protocol. + * Integrity-only record protocol has additional data elements. + * Privacy-integrity record protocol uses this struct directly. */ +struct alts_grpc_record_protocol { + const alts_grpc_record_protocol_vtable* vtable; + alts_iovec_record_protocol* iovec_rp; + grpc_slice_buffer header_sb; + unsigned char* header_buf; + size_t header_length; + size_t tag_length; + iovec_t* iovec_buf; + size_t iovec_buf_length; +}; + +/** + * Converts the slices of input sb into iovec_t's and puts the result into + * rp->iovec_buf. Note that the actual data are not copied, only + * pointers and lengths are copied. + */ +void alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb); + +/** + * Copies bytes from slice buffer to destination buffer. Caller is responsible + * for allocating enough memory of destination buffer. This method is used for + * copying frame header and tag in case they are stored in multiple slices. + */ +void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src, + unsigned char* dst); + +/** + * This method returns an iovec object pointing to the frame header stored in + * rp->header_sb. If the frame header is stored in multiple slices, + * this method will copy the bytes in rp->header_sb to + * rp->header_buf, and return an iovec object pointing to + * rp->header_buf. + */ +iovec_t alts_grpc_record_protocol_get_header_iovec( + alts_grpc_record_protocol* rp); + +/** + * Initializes an alts_grpc_record_protocol object, given a gsec_aead_crypter + * instance, the overflow size of the counter in bytes, a flag indicating if the + * object is used for client or server side, a flag indicating if it is used for + * integrity-only or privacy-integrity mode, and a flag indicating if it is for + * protect or unprotect. The ownership of gsec_aead_crypter object is + * transferred to the alts_grpc_record_protocol object. + */ +tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp, + gsec_aead_crypter* crypter, + size_t overflow_size, bool is_client, + bool is_integrity_only, + bool is_protect); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H \ + */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc new file mode 100644 index 0000000000..6a548e50dd --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc @@ -0,0 +1,476 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +#include <stdlib.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" + +struct alts_iovec_record_protocol { + alts_counter* ctr; + gsec_aead_crypter* crypter; + size_t tag_length; + bool is_integrity_only; + bool is_protect; +}; + +/* Copies error message to destination. */ +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast<char*>(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Appends error message to destination. */ +static void maybe_append_error_msg(const char* appendix, char** dst) { + if (dst != nullptr && appendix != nullptr) { + int dst_len = static_cast<int>(strlen(*dst)); + *dst = static_cast<char*>(realloc(*dst, dst_len + strlen(appendix) + 1)); + assert(*dst != nullptr); + memcpy(*dst + dst_len, appendix, strlen(appendix) + 1); + } +} + +/* Use little endian to interpret a string of bytes as uint32_t. */ +static uint32_t load_32_le(const unsigned char* buffer) { + return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) | + (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]); +} + +/* Store uint32_t as a string of little endian bytes. */ +static void store_32_le(uint32_t value, unsigned char* buffer) { + buffer[3] = (unsigned char)(value >> 24) & 0xFF; + buffer[2] = (unsigned char)(value >> 16) & 0xFF; + buffer[1] = (unsigned char)(value >> 8) & 0xFF; + buffer[0] = (unsigned char)(value)&0xFF; +} + +/* Ensures header and tag iovec have sufficient length. */ +static grpc_status_code ensure_header_and_tag_length( + const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag, + char** error_details) { + if (rp == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (header.iov_base == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (header.iov_len != alts_iovec_record_protocol_get_header_length()) { + maybe_copy_error_msg("Header length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (tag.iov_base == nullptr) { + maybe_copy_error_msg("Tag is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (tag.iov_len != rp->tag_length) { + maybe_copy_error_msg("Tag length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +/* Increments crypter counter and checks overflow. */ +static grpc_status_code increment_counter(alts_counter* counter, + char** error_details) { + if (counter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + bool is_overflow = false; + grpc_status_code status = + alts_counter_increment(counter, &is_overflow, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (is_overflow) { + maybe_copy_error_msg("Crypter counter is overflowed.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +/* Given an array of iovec, computes the total length of buffer. */ +static size_t get_total_length(const iovec_t* vec, size_t vec_length) { + size_t total_length = 0; + for (size_t i = 0; i < vec_length; ++i) { + total_length += vec[i].iov_len; + } + return total_length; +} + +/* Writes frame header given data and tag length. */ +static grpc_status_code write_frame_header(size_t data_length, + unsigned char* header, + char** error_details) { + if (header == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length; + store_32_le(static_cast<uint32_t>(frame_length), header); + store_32_le(kZeroCopyFrameMessageType, + header + kZeroCopyFrameLengthFieldSize); + return GRPC_STATUS_OK; +} + +/* Verifies frame header given protected data length. */ +static grpc_status_code verify_frame_header(size_t data_length, + unsigned char* header, + char** error_details) { + if (header == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + size_t frame_length = load_32_le(header); + if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) { + maybe_copy_error_msg("Bad frame length.", error_details); + return GRPC_STATUS_INTERNAL; + } + size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize); + if (message_type != kZeroCopyFrameMessageType) { + maybe_copy_error_msg("Unsupported message type.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +/* --- alts_iovec_record_protocol methods implementation. --- */ + +size_t alts_iovec_record_protocol_get_header_length() { + return kZeroCopyFrameHeaderSize; +} + +size_t alts_iovec_record_protocol_get_tag_length( + const alts_iovec_record_protocol* rp) { + if (rp != nullptr) { + return rp->tag_length; + } + return 0; +} + +size_t alts_iovec_record_protocol_max_unprotected_data_size( + const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) { + if (rp == nullptr) { + return 0; + } + size_t overhead_bytes_size = + kZeroCopyFrameMessageTypeFieldSize + rp->tag_length; + if (max_protected_frame_size <= overhead_bytes_size) return 0; + return max_protected_frame_size - overhead_bytes_size; +} + +grpc_status_code alts_iovec_record_protocol_integrity_only_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t header, iovec_t tag, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!rp->is_integrity_only) { + maybe_copy_error_msg( + "Integrity-only operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (!rp->is_protect) { + maybe_copy_error_msg("Protect operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + grpc_status_code status = + ensure_header_and_tag_length(rp, header, tag, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Unprotected data should not be zero length. */ + size_t data_length = + get_total_length(unprotected_vec, unprotected_vec_length); + /* Sets frame header. */ + status = write_frame_header(data_length + rp->tag_length, + static_cast<unsigned char*>(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Computes frame tag by calling AEAD crypter. */ + size_t bytes_written = 0; + status = gsec_aead_crypter_encrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length, + /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag, + &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (bytes_written != rp->tag_length) { + maybe_copy_error_msg("Bytes written expects to be the same as tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect( + alts_iovec_record_protocol* rp, const iovec_t* protected_vec, + size_t protected_vec_length, iovec_t header, iovec_t tag, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!rp->is_integrity_only) { + maybe_copy_error_msg( + "Integrity-only operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (rp->is_protect) { + maybe_copy_error_msg( + "Unprotect operations are not allowed for this object.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + grpc_status_code status = + ensure_header_and_tag_length(rp, header, tag, error_details); + if (status != GRPC_STATUS_OK) return status; + /* Protected data should not be zero length. */ + size_t data_length = get_total_length(protected_vec, protected_vec_length); + /* Verifies frame header. */ + status = verify_frame_header(data_length + rp->tag_length, + static_cast<unsigned char*>(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Verifies frame tag by calling AEAD crypter. */ + iovec_t plaintext = {nullptr, 0}; + size_t bytes_written = 0; + status = gsec_aead_crypter_decrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag, + 1, plaintext, &bytes_written, error_details); + if (status != GRPC_STATUS_OK || bytes_written != 0) { + maybe_append_error_msg(" Frame tag verification failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t protected_frame, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (rp->is_integrity_only) { + maybe_copy_error_msg( + "Privacy-integrity operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (!rp->is_protect) { + maybe_copy_error_msg("Protect operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + /* Unprotected data should not be zero length. */ + size_t data_length = + get_total_length(unprotected_vec, unprotected_vec_length); + /* Ensures protected frame iovec has sufficient size. */ + if (protected_frame.iov_base == nullptr) { + maybe_copy_error_msg("Protected frame is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (protected_frame.iov_len != + alts_iovec_record_protocol_get_header_length() + data_length + + rp->tag_length) { + maybe_copy_error_msg("Protected frame size is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Writer frame header. */ + grpc_status_code status = write_frame_header( + data_length + rp->tag_length, + static_cast<unsigned char*>(protected_frame.iov_base), error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Encrypt unprotected data by calling AEAD crypter. */ + unsigned char* ciphertext_buffer = + static_cast<unsigned char*>(protected_frame.iov_base) + + alts_iovec_record_protocol_get_header_length(); + iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length}; + size_t bytes_written = 0; + status = gsec_aead_crypter_encrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr, + /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length, + ciphertext, &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (bytes_written != data_length + rp->tag_length) { + maybe_copy_error_msg( + "Bytes written expects to be data length plus tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect( + alts_iovec_record_protocol* rp, iovec_t header, + const iovec_t* protected_vec, size_t protected_vec_length, + iovec_t unprotected_data, char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (rp->is_integrity_only) { + maybe_copy_error_msg( + "Privacy-integrity operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (rp->is_protect) { + maybe_copy_error_msg( + "Unprotect operations are not allowed for this object.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + /* Protected data size should be no less than tag size. */ + size_t protected_data_length = + get_total_length(protected_vec, protected_vec_length); + if (protected_data_length < rp->tag_length) { + maybe_copy_error_msg( + "Protected data length should be more than the tag length.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Ensures header has sufficient size. */ + if (header.iov_base == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (header.iov_len != alts_iovec_record_protocol_get_header_length()) { + maybe_copy_error_msg("Header length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Ensures unprotected data iovec has sufficient size. */ + if (unprotected_data.iov_len != protected_data_length - rp->tag_length) { + maybe_copy_error_msg("Unprotected data size is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Verify frame header. */ + grpc_status_code status = verify_frame_header( + protected_data_length, static_cast<unsigned char*>(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Decrypt protected data by calling AEAD crypter. */ + size_t bytes_written = 0; + status = gsec_aead_crypter_decrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr, + /* aad_vec_length = */ 0, protected_vec, protected_vec_length, + unprotected_data, &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + maybe_append_error_msg(" Frame decryption failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written != protected_data_length - rp->tag_length) { + maybe_copy_error_msg( + "Bytes written expects to be protected data length minus tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp, + char** error_details) { + if (crypter == nullptr || rp == nullptr) { + maybe_copy_error_msg( + "Invalid nullptr arguments to alts_iovec_record_protocol create.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + alts_iovec_record_protocol* impl = static_cast<alts_iovec_record_protocol*>( + gpr_zalloc(sizeof(alts_iovec_record_protocol))); + /* Gets counter length. */ + size_t counter_length = 0; + grpc_status_code status = + gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + /* Creates counters. */ + status = + alts_counter_create(is_protect ? !is_client : is_client, counter_length, + overflow_size, &impl->ctr, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + /* Gets tag length. */ + status = + gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + impl->crypter = crypter; + impl->is_integrity_only = is_integrity_only; + impl->is_protect = is_protect; + *rp = impl; + return GRPC_STATUS_OK; +cleanup: + alts_counter_destroy(impl->ctr); + gpr_free(impl); + return GRPC_STATUS_FAILED_PRECONDITION; +} + +void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) { + if (rp != nullptr) { + alts_counter_destroy(rp->ctr); + gsec_aead_crypter_destroy(rp->crypter); + gpr_free(rp); + } +} diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h new file mode 100644 index 0000000000..0b7d1bf5bf --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h @@ -0,0 +1,199 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/alts/crypt/gsec.h" + +constexpr size_t kZeroCopyFrameMessageType = 0x06; +constexpr size_t kZeroCopyFrameLengthFieldSize = 4; +constexpr size_t kZeroCopyFrameMessageTypeFieldSize = 4; +constexpr size_t kZeroCopyFrameHeaderSize = + kZeroCopyFrameLengthFieldSize + kZeroCopyFrameMessageTypeFieldSize; + +// Limit k on number of frames such that at most 2^(8 * k) frames can be sent. +constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8; +constexpr size_t kAltsRecordProtocolFrameLimit = 5; + +/* An implementation of alts record protocol. The API is thread-compatible. */ + +typedef struct iovec iovec_t; + +typedef struct alts_iovec_record_protocol alts_iovec_record_protocol; + +/** + * This method gets the length of record protocol frame header. + */ +size_t alts_iovec_record_protocol_get_header_length(); + +/** + * This method gets the length of record protocol frame tag. + * + * - rp: an alts_iovec_record_protocol instance. + * + * On success, the method returns the length of record protocol frame tag. + * Otherwise, it returns zero. + */ +size_t alts_iovec_record_protocol_get_tag_length( + const alts_iovec_record_protocol* rp); + +/** + * This method returns maximum allowed unprotected data size, given maximum + * protected frame size. + * + * - rp: an alts_iovec_record_protocol instance. + * - max_protected_frame_size: maximum protected frame size. + * + * On success, the method returns the maximum allowed unprotected data size. + * Otherwise, it returns zero. + */ +size_t alts_iovec_record_protocol_max_unprotected_data_size( + const alts_iovec_record_protocol* rp, size_t max_protected_frame_size); + +/** + * This method performs integrity-only protect operation on a + * alts_iovec_record_protocol instance, i.e., compute frame header and tag. The + * caller needs to allocate the memory for header and tag prior to calling this + * method. + * + * - rp: an alts_iovec_record_protocol instance. + * - unprotected_vec: an iovec array containing unprotected data. + * - unprotected_vec_length: the array length of unprotected_vec. + * - header: an iovec containing the output frame header. + * - tag: an iovec containing the output frame tag. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_integrity_only_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t header, iovec_t tag, + char** error_details); + +/** + * This method performs integrity-only unprotect operation on a + * alts_iovec_record_protocol instance, i.e., verify frame header and tag. + * + * - rp: an alts_iovec_record_protocol instance. + * - protected_vec: an iovec array containing protected data. + * - protected_vec_length: the array length of protected_vec. + * - header: an iovec containing the frame header. + * - tag: an iovec containing the frame tag. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect( + alts_iovec_record_protocol* rp, const iovec_t* protected_vec, + size_t protected_vec_length, iovec_t header, iovec_t tag, + char** error_details); + +/** + * This method performs privacy-integrity protect operation on a + * alts_iovec_record_protocol instance, i.e., compute a protected frame. The + * caller needs to allocate the memory for the protected frame prior to calling + * this method. + * + * - rp: an alts_iovec_record_protocol instance. + * - unprotected_vec: an iovec array containing unprotected data. + * - unprotected_vec_length: the array length of unprotected_vec. + * - protected_frame: an iovec containing the output protected frame. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t protected_frame, + char** error_details); + +/** + * This method performs privacy-integrity unprotect operation on a + * alts_iovec_record_protocol instance given a full protected frame, i.e., + * compute the unprotected data. The caller needs to allocated the memory for + * the unprotected data prior to calling this method. + * + * - rp: an alts_iovec_record_protocol instance. + * - header: an iovec containing the frame header. + * - protected_vec: an iovec array containing protected data including the tag. + * - protected_vec_length: the array length of protected_vec. + * - unprotected_data: an iovec containing the output unprotected data. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect( + alts_iovec_record_protocol* rp, iovec_t header, + const iovec_t* protected_vec, size_t protected_vec_length, + iovec_t unprotected_data, char** error_details); + +/** + * This method creates an alts_iovec_record_protocol instance, given a + * gsec_aead_crypter instance, a flag indicating if the created instance will be + * used at the client or server side, and a flag indicating if the created + * instance will be used for integrity-only mode or privacy-integrity mode. The + * ownership of gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - overflow_size: overflow size of counter in bytes. + * - is_client: a flag indicating if the alts_iovec_record_protocol instance + * will be used at the client or server side. + * - is_integrity_only: a flag indicating if the alts_iovec_record_protocol + * instance will be used for integrity-only or privacy-integrity mode. + * - is_protect: a flag indicating if the alts_grpc_record_protocol instance + * will be used for protect or unprotect. + * - rp: an alts_iovec_record_protocol instance to be returned from + * the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp, + char** error_details); + +/** + * This method destroys an alts_iovec_record_protocol instance by de-allocating + * all of its occupied memory. A gsec_aead_crypter instance passed in at + * gsec_alts_crypter instance creation time will be destroyed in this method. + */ +void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H \ + */ diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc new file mode 100644 index 0000000000..8c764961b3 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc @@ -0,0 +1,295 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" +#include "src/core/tsi/transport_security_grpc.h" + +constexpr size_t kMinFrameLength = 1024; +constexpr size_t kDefaultFrameLength = 16 * 1024; +constexpr size_t kMaxFrameLength = 1024 * 1024; + +/** + * Main struct for alts_zero_copy_grpc_protector. + * We choose to have two alts_grpc_record_protocol objects and two sets of slice + * buffers: one for protect and the other for unprotect, so that protect and + * unprotect can be executed in parallel. Implementations of this object must be + * thread compatible. + */ +typedef struct alts_zero_copy_grpc_protector { + tsi_zero_copy_grpc_protector base; + alts_grpc_record_protocol* record_protocol; + alts_grpc_record_protocol* unrecord_protocol; + size_t max_protected_frame_size; + size_t max_unprotected_data_size; + grpc_slice_buffer unprotected_staging_sb; + grpc_slice_buffer protected_sb; + grpc_slice_buffer protected_staging_sb; + uint32_t parsed_frame_size; +} alts_zero_copy_grpc_protector; + +/** + * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame + * size and returns the total frame size including the frame field. Caller + * needs to make sure the input slice buffer has at least 4 bytes. Returns true + * on success and false on failure. + */ +static bool read_frame_size(const grpc_slice_buffer* sb, + uint32_t* total_frame_size) { + if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) { + return false; + } + uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize]; + uint8_t* buf = frame_size_buffer; + /* Copies the first 4 bytes to a temporary buffer. */ + size_t remaining = kZeroCopyFrameLengthFieldSize; + for (size_t i = 0; i < sb->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]); + if (remaining <= slice_length) { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining); + remaining = 0; + break; + } else { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length); + buf += slice_length; + remaining -= slice_length; + } + } + GPR_ASSERT(remaining == 0); + /* Gets little-endian frame size. */ + uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) | + (((uint32_t)frame_size_buffer[2]) << 16) | + (((uint32_t)frame_size_buffer[1]) << 8) | + ((uint32_t)frame_size_buffer[0]); + if (frame_size > kMaxFrameLength) { + gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size"); + return false; + } + /* Returns frame size including frame length field. */ + *total_frame_size = + static_cast<uint32_t>(frame_size + kZeroCopyFrameLengthFieldSize); + return true; +} + +/** + * Creates an alts_grpc_record_protocol object, given key, key size, and flags + * to indicate whether the record_protocol object uses the rekeying AEAD, + * whether the object is for client or server, whether the object is for + * integrity-only or privacy-integrity mode, and whether the object is is used + * for protect or unprotect. + */ +static tsi_result create_alts_grpc_record_protocol( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, bool is_protect, + alts_grpc_record_protocol** record_protocol) { + if (key == nullptr || record_protocol == nullptr) { + return TSI_INVALID_ARGUMENT; + } + grpc_status_code status; + gsec_aead_crypter* crypter = nullptr; + char* error_details = nullptr; + status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength, + kAesGcmTagLength, is_rekey, + &crypter, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit + : kAltsRecordProtocolFrameLimit; + /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred. + */ + tsi_result result = + is_integrity_only + ? alts_grpc_integrity_only_record_protocol_create( + crypter, overflow_limit, is_client, is_protect, record_protocol) + : alts_grpc_privacy_integrity_record_protocol_create( + crypter, overflow_limit, is_client, is_protect, + record_protocol); + if (result != TSI_OK) { + gsec_aead_crypter_destroy(crypter); + return result; + } + return TSI_OK; +} + +/* --- tsi_zero_copy_grpc_protector methods implementation. --- */ + +static tsi_result alts_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect."); + return TSI_INVALID_ARGUMENT; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast<alts_zero_copy_grpc_protector*>(self); + /* Calls alts_grpc_record_protocol protect repeatly. */ + while (unprotected_slices->length > protector->max_unprotected_data_size) { + grpc_slice_buffer_move_first(unprotected_slices, + protector->max_unprotected_data_size, + &protector->unprotected_staging_sb); + tsi_result status = alts_grpc_record_protocol_protect( + protector->record_protocol, &protector->unprotected_staging_sb, + protected_slices); + if (status != TSI_OK) { + return status; + } + } + return alts_grpc_record_protocol_protect( + protector->record_protocol, unprotected_slices, protected_slices); +} + +static tsi_result alts_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to zero-copy grpc unprotect."); + return TSI_INVALID_ARGUMENT; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast<alts_zero_copy_grpc_protector*>(self); + grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb); + /* Keep unprotecting each frame if possible. */ + while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) { + if (protector->parsed_frame_size == 0) { + /* We have not parsed frame size yet. Parses frame size. */ + if (!read_frame_size(&protector->protected_sb, + &protector->parsed_frame_size)) { + grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb); + return TSI_DATA_CORRUPTED; + } + } + if (protector->protected_sb.length < protector->parsed_frame_size) break; + /* At this point, protected_sb contains at least one frame of data. */ + tsi_result status; + if (protector->protected_sb.length == protector->parsed_frame_size) { + status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol, + &protector->protected_sb, + unprotected_slices); + } else { + grpc_slice_buffer_move_first(&protector->protected_sb, + protector->parsed_frame_size, + &protector->protected_staging_sb); + status = alts_grpc_record_protocol_unprotect( + protector->unrecord_protocol, &protector->protected_staging_sb, + unprotected_slices); + } + protector->parsed_frame_size = 0; + if (status != TSI_OK) { + grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb); + return status; + } + } + return TSI_OK; +} + +static void alts_zero_copy_grpc_protector_destroy( + tsi_zero_copy_grpc_protector* self) { + if (self == nullptr) { + return; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast<alts_zero_copy_grpc_protector*>(self); + alts_grpc_record_protocol_destroy(protector->record_protocol); + alts_grpc_record_protocol_destroy(protector->unrecord_protocol); + grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb); + grpc_slice_buffer_destroy_internal(&protector->protected_sb); + grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb); + gpr_free(protector); +} + +static const tsi_zero_copy_grpc_protector_vtable + alts_zero_copy_grpc_protector_vtable = { + alts_zero_copy_grpc_protector_protect, + alts_zero_copy_grpc_protector_unprotect, + alts_zero_copy_grpc_protector_destroy}; + +tsi_result alts_zero_copy_grpc_protector_create( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, size_t* max_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr || + protector == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_zero_copy_grpc_protector create."); + return TSI_INVALID_ARGUMENT; + } + /* Creates alts_zero_copy_protector. */ + alts_zero_copy_grpc_protector* impl = + static_cast<alts_zero_copy_grpc_protector*>( + gpr_zalloc(sizeof(alts_zero_copy_grpc_protector))); + /* Creates alts_grpc_record_protocol objects. */ + tsi_result status = create_alts_grpc_record_protocol( + key, key_size, is_rekey, is_client, is_integrity_only, + /*is_protect=*/true, &impl->record_protocol); + if (status == TSI_OK) { + status = create_alts_grpc_record_protocol( + key, key_size, is_rekey, is_client, is_integrity_only, + /*is_protect=*/false, &impl->unrecord_protocol); + if (status == TSI_OK) { + /* Sets maximum frame size. */ + size_t max_protected_frame_size_to_set = kDefaultFrameLength; + if (max_protected_frame_size != nullptr) { + *max_protected_frame_size = + GPR_MIN(*max_protected_frame_size, kMaxFrameLength); + *max_protected_frame_size = + GPR_MAX(*max_protected_frame_size, kMinFrameLength); + max_protected_frame_size_to_set = *max_protected_frame_size; + } + impl->max_protected_frame_size = max_protected_frame_size_to_set; + impl->max_unprotected_data_size = + alts_grpc_record_protocol_max_unprotected_data_size( + impl->record_protocol, max_protected_frame_size_to_set); + GPR_ASSERT(impl->max_unprotected_data_size > 0); + /* Allocates internal slice buffers. */ + grpc_slice_buffer_init(&impl->unprotected_staging_sb); + grpc_slice_buffer_init(&impl->protected_sb); + grpc_slice_buffer_init(&impl->protected_staging_sb); + impl->parsed_frame_size = 0; + impl->base.vtable = &alts_zero_copy_grpc_protector_vtable; + *protector = &impl->base; + return TSI_OK; + } + } + + /* Cleanup if create failed. */ + alts_grpc_record_protocol_destroy(impl->record_protocol); + alts_grpc_record_protocol_destroy(impl->unrecord_protocol); + gpr_free(impl); + return TSI_INTERNAL_ERROR; +} diff --git a/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h new file mode 100644 index 0000000000..71e953cfc1 --- /dev/null +++ b/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H + +#include <grpc/support/port_platform.h> + +#include <stdbool.h> + +#include "src/core/tsi/transport_security_grpc.h" + +/** + * This method creates an ALTS zero-copy grpc protector. + * + * - key: a symmetric key used to seal/unseal frames. + * - key_size: the size of symmetric key. + * - is_rekey: use rekeying AEAD crypter. + * - is_client: a flag indicating if the protector will be used at client or + * server side. + * - is_integrity_only: a flag indicating if the protector instance will be + * used for integrity-only or privacy-integrity mode. + * - max_protected_frame_size: an in/out parameter indicating max frame size + * to be used by the protector. If it is nullptr, the default frame size will + * be used. Otherwise, the provided frame size will be adjusted (if not + * falling into a valid frame range) and used. + * - protector: a pointer to the zero-copy protector returned from the method. + * + * This method returns TSI_OK on success or a specific error code otherwise. + */ +tsi_result alts_zero_copy_grpc_protector_create( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, size_t* max_protected_frame_size, + tsi_zero_copy_grpc_protector** protector); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H \ + */ diff --git a/src/core/tsi/alts_transport_security.cc b/src/core/tsi/alts_transport_security.cc index 1654d893d0..2fd408103b 100644 --- a/src/core/tsi/alts_transport_security.cc +++ b/src/core/tsi/alts_transport_security.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/tsi/alts_transport_security.h" #include <string.h> @@ -54,7 +56,7 @@ void grpc_tsi_alts_shutdown() { grpc_tsi_alts_wait_for_cq_drain(); grpc_completion_queue_destroy(g_alts_resource.cq); grpc_channel_destroy(g_alts_resource.channel); - gpr_thd_join(g_alts_resource.thread_id); + g_alts_resource.thread.Join(); } gpr_cv_destroy(&g_alts_resource.cv); gpr_mu_destroy(&g_alts_resource.mu); diff --git a/src/core/tsi/alts_transport_security.h b/src/core/tsi/alts_transport_security.h index 37febd1e28..d6b8e11137 100644 --- a/src/core/tsi/alts_transport_security.h +++ b/src/core/tsi/alts_transport_security.h @@ -19,13 +19,15 @@ #ifndef GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H #define GRPC_CORE_TSI_ALTS_TRANSPORT_SECURITY_H +#include <grpc/support/port_platform.h> + #include <grpc/grpc.h> #include <grpc/support/sync.h> -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" typedef struct alts_shared_resource { - gpr_thd_id thread_id; + grpc_core::Thread thread; grpc_channel* channel; grpc_completion_queue* cq; gpr_mu mu; diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc index b5b7203d20..ad08b50ede 100644 --- a/src/core/tsi/fake_transport_security.cc +++ b/src/core/tsi/fake_transport_security.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/tsi/fake_transport_security.h" #include <stdlib.h> @@ -23,7 +25,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/port_platform.h> #include "src/core/lib/gpr/useful.h" #include "src/core/lib/slice/slice_internal.h" diff --git a/src/core/tsi/fake_transport_security.h b/src/core/tsi/fake_transport_security.h index 3848e7c6bf..37791827e1 100644 --- a/src/core/tsi/fake_transport_security.h +++ b/src/core/tsi/fake_transport_security.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H #define GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security_interface.h" /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */ diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index 29fd59a0e9..971170b7c5 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -16,10 +16,10 @@ * */ -#include "src/core/tsi/ssl_transport_security.h" - #include <grpc/support/port_platform.h> +#include "src/core/tsi/ssl_transport_security.h" + #include <limits.h> #include <string.h> diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h index bf211e110a..edebadc1be 100644 --- a/src/core/tsi/ssl_transport_security.h +++ b/src/core/tsi/ssl_transport_security.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H #define GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security_interface.h" /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */ diff --git a/src/core/tsi/ssl_types.h b/src/core/tsi/ssl_types.h index 3788643355..b15d02be39 100644 --- a/src/core/tsi/ssl_types.h +++ b/src/core/tsi/ssl_types.h @@ -27,6 +27,8 @@ * function */ +#include <grpc/support/port_platform.h> + #include <openssl/ssl.h> #ifdef OPENSSL_IS_BORINGSSL diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc index 0c8e3e9dcc..129533f779 100644 --- a/src/core/tsi/transport_security.cc +++ b/src/core/tsi/transport_security.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security.h" #include <grpc/support/alloc.h> diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h index ed662d48af..b1ec82d3f7 100644 --- a/src/core/tsi/transport_security.h +++ b/src/core/tsi/transport_security.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_H #define GRPC_CORE_TSI_TRANSPORT_SECURITY_H +#include <grpc/support/port_platform.h> + #include <stdbool.h> #include "src/core/lib/debug/trace.h" diff --git a/src/core/tsi/transport_security_adapter.cc b/src/core/tsi/transport_security_adapter.cc index 5f094b3201..25608f065a 100644 --- a/src/core/tsi/transport_security_adapter.cc +++ b/src/core/tsi/transport_security_adapter.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security_adapter.h" #include <string.h> diff --git a/src/core/tsi/transport_security_adapter.h b/src/core/tsi/transport_security_adapter.h index 9818fceb86..f83ecc53e5 100644 --- a/src/core/tsi/transport_security_adapter.h +++ b/src/core/tsi/transport_security_adapter.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H #define GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security_interface.h" /* Create a tsi handshaker that takes an implementation of old interface and diff --git a/src/core/tsi/transport_security_grpc.cc b/src/core/tsi/transport_security_grpc.cc index 76f7ae782f..c73a6e303e 100644 --- a/src/core/tsi/transport_security_grpc.cc +++ b/src/core/tsi/transport_security_grpc.cc @@ -16,6 +16,8 @@ * */ +#include <grpc/support/port_platform.h> + #include "src/core/tsi/transport_security_grpc.h" /* This method creates a tsi_zero_copy_grpc_protector object. */ diff --git a/src/core/tsi/transport_security_grpc.h b/src/core/tsi/transport_security_grpc.h index 0156ff1c68..d3bb04d07f 100644 --- a/src/core/tsi/transport_security_grpc.h +++ b/src/core/tsi/transport_security_grpc.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H #define GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H +#include <grpc/support/port_platform.h> + #include <grpc/slice_buffer.h> #include "src/core/tsi/transport_security.h" diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h index e925598463..8c10866934 100644 --- a/src/core/tsi/transport_security_interface.h +++ b/src/core/tsi/transport_security_interface.h @@ -19,6 +19,8 @@ #ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H #define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H +#include <grpc/support/port_platform.h> + #include <stdint.h> #include <stdlib.h> diff --git a/src/cpp/client/channel_cc.cc b/src/cpp/client/channel_cc.cc index cba5984f4b..867f31f025 100644 --- a/src/cpp/client/channel_cc.cc +++ b/src/cpp/client/channel_cc.cc @@ -42,7 +42,7 @@ #include <grpcpp/support/time.h> #include "src/core/lib/gpr/env.h" #include "src/core/lib/gpr/string.h" -#include "src/core/lib/gpr/thd.h" +#include "src/core/lib/gprpp/thd.h" #include "src/core/lib/profiling/timers.h" namespace grpc { diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 539db84db3..19d67c2e06 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -168,7 +168,7 @@ std::shared_ptr<CallCredentials> CompositeCallCredentials( void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) { if (wrapper == nullptr) return; MetadataCredentialsPluginWrapper* w = - reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper); + static_cast<MetadataCredentialsPluginWrapper*>(wrapper); delete w; } @@ -180,7 +180,7 @@ int MetadataCredentialsPluginWrapper::GetMetadata( const char** error_details) { GPR_ASSERT(wrapper); MetadataCredentialsPluginWrapper* w = - reinterpret_cast<MetadataCredentialsPluginWrapper*>(wrapper); + static_cast<MetadataCredentialsPluginWrapper*>(wrapper); if (!w->plugin_) { *num_creds_md = 0; *status = GRPC_STATUS_OK; diff --git a/src/cpp/common/channel_filter.h b/src/cpp/common/channel_filter.h index 642372be78..bd0ec969b4 100644 --- a/src/cpp/common/channel_filter.h +++ b/src/cpp/common/channel_filter.h @@ -182,20 +182,22 @@ class TransportStreamOpBatch { op_->payload->recv_initial_metadata.recv_initial_metadata_ready = closure; } - grpc_byte_stream* send_message() const { - return op_->send_message ? op_->payload->send_message.send_message + grpc_core::OrphanablePtr<grpc_core::ByteStream>* send_message() const { + return op_->send_message ? &op_->payload->send_message.send_message : nullptr; } - void set_send_message(grpc_byte_stream* send_message) { + void set_send_message( + grpc_core::OrphanablePtr<grpc_core::ByteStream> send_message) { op_->send_message = true; - op_->payload->send_message.send_message = send_message; + op_->payload->send_message.send_message = std::move(send_message); } - grpc_byte_stream** recv_message() const { + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message() const { return op_->recv_message ? op_->payload->recv_message.recv_message : nullptr; } - void set_recv_message(grpc_byte_stream** recv_message) { + void set_recv_message( + grpc_core::OrphanablePtr<grpc_core::ByteStream>* recv_message) { op_->recv_message = true; op_->payload->recv_message.recv_message = recv_message; } @@ -283,7 +285,7 @@ class ChannelFilter final { static void DestroyChannelElement(grpc_channel_element* elem) { ChannelDataType* channel_data = - reinterpret_cast<ChannelDataType*>(elem->channel_data); + static_cast<ChannelDataType*>(elem->channel_data); channel_data->Destroy(elem); channel_data->~ChannelDataType(); } @@ -291,7 +293,7 @@ class ChannelFilter final { static void StartTransportOp(grpc_channel_element* elem, grpc_transport_op* op) { ChannelDataType* channel_data = - reinterpret_cast<ChannelDataType*>(elem->channel_data); + static_cast<ChannelDataType*>(elem->channel_data); TransportOp op_wrapper(op); channel_data->StartTransportOp(elem, &op_wrapper); } @@ -299,7 +301,7 @@ class ChannelFilter final { static void GetChannelInfo(grpc_channel_element* elem, const grpc_channel_info* channel_info) { ChannelDataType* channel_data = - reinterpret_cast<ChannelDataType*>(elem->channel_data); + static_cast<ChannelDataType*>(elem->channel_data); channel_data->GetInfo(elem, channel_info); } @@ -315,21 +317,21 @@ class ChannelFilter final { static void DestroyCallElement(grpc_call_element* elem, const grpc_call_final_info* final_info, grpc_closure* then_call_closure) { - CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data); + CallDataType* call_data = static_cast<CallDataType*>(elem->call_data); call_data->Destroy(elem, final_info, then_call_closure); call_data->~CallDataType(); } static void StartTransportStreamOpBatch(grpc_call_element* elem, grpc_transport_stream_op_batch* op) { - CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data); + CallDataType* call_data = static_cast<CallDataType*>(elem->call_data); TransportStreamOpBatch op_wrapper(op); call_data->StartTransportStreamOpBatch(elem, &op_wrapper); } static void SetPollsetOrPollsetSet(grpc_call_element* elem, grpc_polling_entity* pollent) { - CallDataType* call_data = reinterpret_cast<CallDataType*>(elem->call_data); + CallDataType* call_data = static_cast<CallDataType*>(elem->call_data); call_data->SetPollsetOrPollsetSet(elem, pollent); } }; diff --git a/src/cpp/server/dynamic_thread_pool.cc b/src/cpp/server/dynamic_thread_pool.cc index 81c78fe739..ef99d6459f 100644 --- a/src/cpp/server/dynamic_thread_pool.cc +++ b/src/cpp/server/dynamic_thread_pool.cc @@ -19,20 +19,23 @@ #include "src/cpp/server/dynamic_thread_pool.h" #include <mutex> -#include <thread> #include <grpc/support/log.h> +#include "src/core/lib/gprpp/thd.h" + namespace grpc { DynamicThreadPool::DynamicThread::DynamicThread(DynamicThreadPool* pool) : pool_(pool), - thd_(new std::thread(&DynamicThreadPool::DynamicThread::ThreadFunc, - this)) {} -DynamicThreadPool::DynamicThread::~DynamicThread() { - thd_->join(); - thd_.reset(); + thd_("grpcpp_dynamic_pool", + [](void* th) { + static_cast<DynamicThreadPool::DynamicThread*>(th)->ThreadFunc(); + }, + this) { + thd_.Start(); } +DynamicThreadPool::DynamicThread::~DynamicThread() { thd_.Join(); } void DynamicThreadPool::DynamicThread::ThreadFunc() { pool_->ThreadFunc(); diff --git a/src/cpp/server/dynamic_thread_pool.h b/src/cpp/server/dynamic_thread_pool.h index 880a03d0f0..5df8cf2b04 100644 --- a/src/cpp/server/dynamic_thread_pool.h +++ b/src/cpp/server/dynamic_thread_pool.h @@ -24,10 +24,10 @@ #include <memory> #include <mutex> #include <queue> -#include <thread> #include <grpcpp/support/config.h> +#include "src/core/lib/gprpp/thd.h" #include "src/cpp/server/thread_pool_interface.h" namespace grpc { @@ -47,7 +47,7 @@ class DynamicThreadPool final : public ThreadPoolInterface { private: DynamicThreadPool* pool_; - std::unique_ptr<std::thread> thd_; + grpc_core::Thread thd_; void ThreadFunc(); }; std::mutex mu_; diff --git a/src/cpp/server/secure_server_credentials.cc b/src/cpp/server/secure_server_credentials.cc index 49ab2ea1bf..1887109086 100644 --- a/src/cpp/server/secure_server_credentials.cc +++ b/src/cpp/server/secure_server_credentials.cc @@ -29,14 +29,14 @@ namespace grpc { void AuthMetadataProcessorAyncWrapper::Destroy(void* wrapper) { - auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); + auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); delete w; } void AuthMetadataProcessorAyncWrapper::Process( void* wrapper, grpc_auth_context* context, const grpc_metadata* md, size_t num_md, grpc_process_auth_metadata_done_cb cb, void* user_data) { - auto* w = reinterpret_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); + auto* w = static_cast<AuthMetadataProcessorAyncWrapper*>(wrapper); if (!w->processor_) { // Early exit. cb(user_data, nullptr, 0, nullptr, 0, GRPC_STATUS_OK, nullptr); diff --git a/src/cpp/server/server_cc.cc b/src/cpp/server/server_cc.cc index 5638636e67..760aaa4b4d 100644 --- a/src/cpp/server/server_cc.cc +++ b/src/cpp/server/server_cc.cc @@ -382,11 +382,12 @@ Server::Server( global_callbacks_ = g_callbacks; global_callbacks_->UpdateArguments(args); - for (auto it = sync_server_cqs_->begin(); it != sync_server_cqs_->end(); - it++) { - sync_req_mgrs_.emplace_back(new SyncRequestThreadManager( - this, (*it).get(), global_callbacks_, min_pollers, max_pollers, - sync_cq_timeout_msec)); + if (sync_server_cqs_ != nullptr) { + for (const auto& it : *sync_server_cqs_) { + sync_req_mgrs_.emplace_back(new SyncRequestThreadManager( + this, it.get(), global_callbacks_, min_pollers, max_pollers, + sync_cq_timeout_msec)); + } } grpc_channel_args channel_args; @@ -525,7 +526,7 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) { // explicit one. if (health_check_service_ == nullptr && !health_check_service_disabled_ && DefaultHealthCheckServiceEnabled()) { - if (sync_server_cqs_->empty()) { + if (sync_server_cqs_ == nullptr || sync_server_cqs_->empty()) { gpr_log(GPR_INFO, "Default health check service disabled at async-only server."); } else { diff --git a/src/cpp/thread_manager/thread_manager.cc b/src/cpp/thread_manager/thread_manager.cc index 23264f1b5b..02ac56a3fd 100644 --- a/src/cpp/thread_manager/thread_manager.cc +++ b/src/cpp/thread_manager/thread_manager.cc @@ -20,18 +20,22 @@ #include <climits> #include <mutex> -#include <thread> #include <grpc/support/log.h> +#include "src/core/lib/gprpp/thd.h" + namespace grpc { ThreadManager::WorkerThread::WorkerThread(ThreadManager* thd_mgr) : thd_mgr_(thd_mgr) { // Make thread creation exclusive with respect to its join happening in // ~WorkerThread(). - std::lock_guard<std::mutex> lock(wt_mu_); - thd_ = std::thread(&ThreadManager::WorkerThread::Run, this); + thd_ = grpc_core::Thread( + "grpcpp_sync_server", + [](void* th) { static_cast<ThreadManager::WorkerThread*>(th)->Run(); }, + this); + thd_.Start(); } void ThreadManager::WorkerThread::Run() { @@ -41,8 +45,7 @@ void ThreadManager::WorkerThread::Run() { ThreadManager::WorkerThread::~WorkerThread() { // Don't join until the thread is fully constructed. - std::lock_guard<std::mutex> lock(wt_mu_); - thd_.join(); + thd_.Join(); } ThreadManager::ThreadManager(int min_pollers, int max_pollers) diff --git a/src/cpp/thread_manager/thread_manager.h b/src/cpp/thread_manager/thread_manager.h index 1113031695..5a40f2de47 100644 --- a/src/cpp/thread_manager/thread_manager.h +++ b/src/cpp/thread_manager/thread_manager.h @@ -23,10 +23,11 @@ #include <list> #include <memory> #include <mutex> -#include <thread> #include <grpcpp/support/config.h> +#include "src/core/lib/gprpp/thd.h" + namespace grpc { class ThreadManager { @@ -84,8 +85,8 @@ class ThreadManager { virtual void Wait(); private: - // Helper wrapper class around std::thread. This takes a ThreadManager object - // and starts a new std::thread to calls the Run() function. + // Helper wrapper class around grpc_core::Thread. Takes a ThreadManager object + // and starts a new grpc_core::Thread to calls the Run() function. // // The Run() function calls ThreadManager::MainWorkLoop() function and once // that completes, it marks the WorkerThread completed by calling @@ -101,8 +102,7 @@ class ThreadManager { void Run(); ThreadManager* const thd_mgr_; - std::mutex wt_mu_; - std::thread thd_; + grpc_core::Thread thd_; }; // The main funtion in ThreadManager diff --git a/src/cpp/util/slice_cc.cc b/src/cpp/util/slice_cc.cc index 9b50d10d17..c72dbdbfc0 100644 --- a/src/cpp/util/slice_cc.cc +++ b/src/cpp/util/slice_cc.cc @@ -32,15 +32,15 @@ Slice::Slice(grpc_slice slice, StealRef) : slice_(slice) {} Slice::Slice(size_t len) : slice_(grpc_slice_malloc(len)) {} Slice::Slice(const void* buf, size_t len) - : slice_(grpc_slice_from_copied_buffer(reinterpret_cast<const char*>(buf), - len)) {} + : slice_( + grpc_slice_from_copied_buffer(static_cast<const char*>(buf), len)) {} Slice::Slice(const grpc::string& str) : slice_(grpc_slice_from_copied_buffer(str.c_str(), str.length())) {} Slice::Slice(const void* buf, size_t len, StaticSlice) - : slice_(grpc_slice_from_static_buffer(reinterpret_cast<const char*>(buf), - len)) {} + : slice_( + grpc_slice_from_static_buffer(static_cast<const char*>(buf), len)) {} Slice::Slice(const Slice& other) : slice_(grpc_slice_ref(other.slice_)) {} diff --git a/src/csharp/Grpc.Core/Internal/NativeExtension.cs b/src/csharp/Grpc.Core/Internal/NativeExtension.cs index e514608c28..d5ec998bbd 100644 --- a/src/csharp/Grpc.Core/Internal/NativeExtension.cs +++ b/src/csharp/Grpc.Core/Internal/NativeExtension.cs @@ -37,7 +37,7 @@ namespace Grpc.Core.Internal private NativeExtension() { - this.nativeMethods = new NativeMethods(Load()); + this.nativeMethods = LoadNativeMethods(); // Redirect the native logs as the very first thing after loading the native extension // to make sure we don't lose any logs. @@ -77,7 +77,7 @@ namespace Grpc.Core.Internal /// <summary> /// Detects which configuration of native extension to load and load it. /// </summary> - private static UnmanagedLibrary Load() + private static UnmanagedLibrary LoadUnmanagedLibrary() { // TODO: allow customizing path to native extension (possibly through exposing a GrpcEnvironment property). // See https://github.com/grpc/grpc/pull/7303 for one option. @@ -96,11 +96,38 @@ namespace Grpc.Core.Internal var netCorePublishedAppStylePath = Path.Combine(assemblyDirectory, runtimesDirectory, GetNativeLibraryFilename()); var netCoreAppStylePath = Path.Combine(assemblyDirectory, "../..", runtimesDirectory, GetNativeLibraryFilename()); - // Look for all native library in all possible locations in given order. + // Look for the native library in all possible locations in given order. string[] paths = new[] { classicPath, netCorePublishedAppStylePath, netCoreAppStylePath}; return new UnmanagedLibrary(paths); } + /// <summary> + /// Loads native extension and return native methods delegates. + /// </summary> + private static NativeMethods LoadNativeMethods() + { + return PlatformApis.IsUnity ? LoadNativeMethodsUnity() : new NativeMethods(LoadUnmanagedLibrary()); + } + + /// <summary> + /// Return native method delegates when running on Unity platform. + /// Unity does not use standard NuGet packages and the native library is treated + /// there as a "native plugin" which is (provided it has the right metadata) + /// automatically made available to <c>[DllImport]</c> loading logic. + /// WARNING: Unity support is experimental and work-in-progress. Don't expect it to work. + /// </summary> + private static NativeMethods LoadNativeMethodsUnity() + { + switch (PlatformApis.GetUnityRuntimePlatform()) + { + case "IPhonePlayer": + return new NativeMethods(new NativeMethods.DllImportsFromStaticLib()); + default: + // most other platforms load unity plugins as a shared library + return new NativeMethods(new NativeMethods.DllImportsFromSharedLib()); + } + } + private static string GetAssemblyPath() { var assembly = typeof(NativeExtension).GetTypeInfo().Assembly; diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs new file mode 100644 index 0000000000..153a52f947 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs @@ -0,0 +1,1120 @@ + +#region Copyright notice and license + +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; + +using Grpc.Core.Logging; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + internal partial class NativeMethods + { + #region Native methods + + public readonly Delegates.grpcsharp_init_delegate grpcsharp_init; + public readonly Delegates.grpcsharp_shutdown_delegate grpcsharp_shutdown; + public readonly Delegates.grpcsharp_version_string_delegate grpcsharp_version_string; + public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create; + public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata; + public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length; + public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer; + public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status; + public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details; + public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata; + public readonly Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate grpcsharp_batch_context_recv_close_on_server_cancelled; + public readonly Delegates.grpcsharp_batch_context_reset_delegate grpcsharp_batch_context_reset; + public readonly Delegates.grpcsharp_batch_context_destroy_delegate grpcsharp_batch_context_destroy; + public readonly Delegates.grpcsharp_request_call_context_create_delegate grpcsharp_request_call_context_create; + public readonly Delegates.grpcsharp_request_call_context_call_delegate grpcsharp_request_call_context_call; + public readonly Delegates.grpcsharp_request_call_context_method_delegate grpcsharp_request_call_context_method; + public readonly Delegates.grpcsharp_request_call_context_host_delegate grpcsharp_request_call_context_host; + public readonly Delegates.grpcsharp_request_call_context_deadline_delegate grpcsharp_request_call_context_deadline; + public readonly Delegates.grpcsharp_request_call_context_request_metadata_delegate grpcsharp_request_call_context_request_metadata; + public readonly Delegates.grpcsharp_request_call_context_reset_delegate grpcsharp_request_call_context_reset; + public readonly Delegates.grpcsharp_request_call_context_destroy_delegate grpcsharp_request_call_context_destroy; + public readonly Delegates.grpcsharp_composite_call_credentials_create_delegate grpcsharp_composite_call_credentials_create; + public readonly Delegates.grpcsharp_call_credentials_release_delegate grpcsharp_call_credentials_release; + public readonly Delegates.grpcsharp_call_cancel_delegate grpcsharp_call_cancel; + public readonly Delegates.grpcsharp_call_cancel_with_status_delegate grpcsharp_call_cancel_with_status; + public readonly Delegates.grpcsharp_call_start_unary_delegate grpcsharp_call_start_unary; + public readonly Delegates.grpcsharp_call_start_client_streaming_delegate grpcsharp_call_start_client_streaming; + public readonly Delegates.grpcsharp_call_start_server_streaming_delegate grpcsharp_call_start_server_streaming; + public readonly Delegates.grpcsharp_call_start_duplex_streaming_delegate grpcsharp_call_start_duplex_streaming; + public readonly Delegates.grpcsharp_call_send_message_delegate grpcsharp_call_send_message; + public readonly Delegates.grpcsharp_call_send_close_from_client_delegate grpcsharp_call_send_close_from_client; + public readonly Delegates.grpcsharp_call_send_status_from_server_delegate grpcsharp_call_send_status_from_server; + public readonly Delegates.grpcsharp_call_recv_message_delegate grpcsharp_call_recv_message; + public readonly Delegates.grpcsharp_call_recv_initial_metadata_delegate grpcsharp_call_recv_initial_metadata; + public readonly Delegates.grpcsharp_call_start_serverside_delegate grpcsharp_call_start_serverside; + public readonly Delegates.grpcsharp_call_send_initial_metadata_delegate grpcsharp_call_send_initial_metadata; + public readonly Delegates.grpcsharp_call_set_credentials_delegate grpcsharp_call_set_credentials; + public readonly Delegates.grpcsharp_call_get_peer_delegate grpcsharp_call_get_peer; + public readonly Delegates.grpcsharp_call_destroy_delegate grpcsharp_call_destroy; + public readonly Delegates.grpcsharp_channel_args_create_delegate grpcsharp_channel_args_create; + public readonly Delegates.grpcsharp_channel_args_set_string_delegate grpcsharp_channel_args_set_string; + public readonly Delegates.grpcsharp_channel_args_set_integer_delegate grpcsharp_channel_args_set_integer; + public readonly Delegates.grpcsharp_channel_args_destroy_delegate grpcsharp_channel_args_destroy; + public readonly Delegates.grpcsharp_override_default_ssl_roots_delegate grpcsharp_override_default_ssl_roots; + public readonly Delegates.grpcsharp_ssl_credentials_create_delegate grpcsharp_ssl_credentials_create; + public readonly Delegates.grpcsharp_composite_channel_credentials_create_delegate grpcsharp_composite_channel_credentials_create; + public readonly Delegates.grpcsharp_channel_credentials_release_delegate grpcsharp_channel_credentials_release; + public readonly Delegates.grpcsharp_insecure_channel_create_delegate grpcsharp_insecure_channel_create; + public readonly Delegates.grpcsharp_secure_channel_create_delegate grpcsharp_secure_channel_create; + public readonly Delegates.grpcsharp_channel_create_call_delegate grpcsharp_channel_create_call; + public readonly Delegates.grpcsharp_channel_check_connectivity_state_delegate grpcsharp_channel_check_connectivity_state; + public readonly Delegates.grpcsharp_channel_watch_connectivity_state_delegate grpcsharp_channel_watch_connectivity_state; + public readonly Delegates.grpcsharp_channel_get_target_delegate grpcsharp_channel_get_target; + public readonly Delegates.grpcsharp_channel_destroy_delegate grpcsharp_channel_destroy; + public readonly Delegates.grpcsharp_sizeof_grpc_event_delegate grpcsharp_sizeof_grpc_event; + public readonly Delegates.grpcsharp_completion_queue_create_async_delegate grpcsharp_completion_queue_create_async; + public readonly Delegates.grpcsharp_completion_queue_create_sync_delegate grpcsharp_completion_queue_create_sync; + public readonly Delegates.grpcsharp_completion_queue_shutdown_delegate grpcsharp_completion_queue_shutdown; + public readonly Delegates.grpcsharp_completion_queue_next_delegate grpcsharp_completion_queue_next; + public readonly Delegates.grpcsharp_completion_queue_pluck_delegate grpcsharp_completion_queue_pluck; + public readonly Delegates.grpcsharp_completion_queue_destroy_delegate grpcsharp_completion_queue_destroy; + public readonly Delegates.gprsharp_free_delegate gprsharp_free; + public readonly Delegates.grpcsharp_metadata_array_create_delegate grpcsharp_metadata_array_create; + public readonly Delegates.grpcsharp_metadata_array_add_delegate grpcsharp_metadata_array_add; + public readonly Delegates.grpcsharp_metadata_array_count_delegate grpcsharp_metadata_array_count; + public readonly Delegates.grpcsharp_metadata_array_get_key_delegate grpcsharp_metadata_array_get_key; + public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value; + public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full; + public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log; + public readonly Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate grpcsharp_metadata_credentials_create_from_plugin; + public readonly Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate grpcsharp_metadata_credentials_notify_from_plugin; + public readonly Delegates.grpcsharp_ssl_server_credentials_create_delegate grpcsharp_ssl_server_credentials_create; + public readonly Delegates.grpcsharp_server_credentials_release_delegate grpcsharp_server_credentials_release; + public readonly Delegates.grpcsharp_server_create_delegate grpcsharp_server_create; + public readonly Delegates.grpcsharp_server_register_completion_queue_delegate grpcsharp_server_register_completion_queue; + public readonly Delegates.grpcsharp_server_add_insecure_http2_port_delegate grpcsharp_server_add_insecure_http2_port; + public readonly Delegates.grpcsharp_server_add_secure_http2_port_delegate grpcsharp_server_add_secure_http2_port; + public readonly Delegates.grpcsharp_server_start_delegate grpcsharp_server_start; + public readonly Delegates.grpcsharp_server_request_call_delegate grpcsharp_server_request_call; + public readonly Delegates.grpcsharp_server_cancel_all_calls_delegate grpcsharp_server_cancel_all_calls; + public readonly Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate grpcsharp_server_shutdown_and_notify_callback; + public readonly Delegates.grpcsharp_server_destroy_delegate grpcsharp_server_destroy; + public readonly Delegates.grpcsharp_call_auth_context_delegate grpcsharp_call_auth_context; + public readonly Delegates.grpcsharp_auth_context_peer_identity_property_name_delegate grpcsharp_auth_context_peer_identity_property_name; + public readonly Delegates.grpcsharp_auth_context_property_iterator_delegate grpcsharp_auth_context_property_iterator; + public readonly Delegates.grpcsharp_auth_property_iterator_next_delegate grpcsharp_auth_property_iterator_next; + public readonly Delegates.grpcsharp_auth_context_release_delegate grpcsharp_auth_context_release; + public readonly Delegates.gprsharp_now_delegate gprsharp_now; + public readonly Delegates.gprsharp_inf_future_delegate gprsharp_inf_future; + public readonly Delegates.gprsharp_inf_past_delegate gprsharp_inf_past; + public readonly Delegates.gprsharp_convert_clock_type_delegate gprsharp_convert_clock_type; + public readonly Delegates.gprsharp_sizeof_timespec_delegate gprsharp_sizeof_timespec; + public readonly Delegates.grpcsharp_test_callback_delegate grpcsharp_test_callback; + public readonly Delegates.grpcsharp_test_nop_delegate grpcsharp_test_nop; + public readonly Delegates.grpcsharp_test_override_method_delegate grpcsharp_test_override_method; + + #endregion + + public NativeMethods(UnmanagedLibrary library) + { + this.grpcsharp_init = GetMethodDelegate<Delegates.grpcsharp_init_delegate>(library); + this.grpcsharp_shutdown = GetMethodDelegate<Delegates.grpcsharp_shutdown_delegate>(library); + this.grpcsharp_version_string = GetMethodDelegate<Delegates.grpcsharp_version_string_delegate>(library); + this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library); + this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library); + this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library); + this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library); + this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library); + this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library); + this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library); + this.grpcsharp_batch_context_recv_close_on_server_cancelled = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate>(library); + this.grpcsharp_batch_context_reset = GetMethodDelegate<Delegates.grpcsharp_batch_context_reset_delegate>(library); + this.grpcsharp_batch_context_destroy = GetMethodDelegate<Delegates.grpcsharp_batch_context_destroy_delegate>(library); + this.grpcsharp_request_call_context_create = GetMethodDelegate<Delegates.grpcsharp_request_call_context_create_delegate>(library); + this.grpcsharp_request_call_context_call = GetMethodDelegate<Delegates.grpcsharp_request_call_context_call_delegate>(library); + this.grpcsharp_request_call_context_method = GetMethodDelegate<Delegates.grpcsharp_request_call_context_method_delegate>(library); + this.grpcsharp_request_call_context_host = GetMethodDelegate<Delegates.grpcsharp_request_call_context_host_delegate>(library); + this.grpcsharp_request_call_context_deadline = GetMethodDelegate<Delegates.grpcsharp_request_call_context_deadline_delegate>(library); + this.grpcsharp_request_call_context_request_metadata = GetMethodDelegate<Delegates.grpcsharp_request_call_context_request_metadata_delegate>(library); + this.grpcsharp_request_call_context_reset = GetMethodDelegate<Delegates.grpcsharp_request_call_context_reset_delegate>(library); + this.grpcsharp_request_call_context_destroy = GetMethodDelegate<Delegates.grpcsharp_request_call_context_destroy_delegate>(library); + this.grpcsharp_composite_call_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_call_credentials_create_delegate>(library); + this.grpcsharp_call_credentials_release = GetMethodDelegate<Delegates.grpcsharp_call_credentials_release_delegate>(library); + this.grpcsharp_call_cancel = GetMethodDelegate<Delegates.grpcsharp_call_cancel_delegate>(library); + this.grpcsharp_call_cancel_with_status = GetMethodDelegate<Delegates.grpcsharp_call_cancel_with_status_delegate>(library); + this.grpcsharp_call_start_unary = GetMethodDelegate<Delegates.grpcsharp_call_start_unary_delegate>(library); + this.grpcsharp_call_start_client_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_client_streaming_delegate>(library); + this.grpcsharp_call_start_server_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_server_streaming_delegate>(library); + this.grpcsharp_call_start_duplex_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_duplex_streaming_delegate>(library); + this.grpcsharp_call_send_message = GetMethodDelegate<Delegates.grpcsharp_call_send_message_delegate>(library); + this.grpcsharp_call_send_close_from_client = GetMethodDelegate<Delegates.grpcsharp_call_send_close_from_client_delegate>(library); + this.grpcsharp_call_send_status_from_server = GetMethodDelegate<Delegates.grpcsharp_call_send_status_from_server_delegate>(library); + this.grpcsharp_call_recv_message = GetMethodDelegate<Delegates.grpcsharp_call_recv_message_delegate>(library); + this.grpcsharp_call_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_recv_initial_metadata_delegate>(library); + this.grpcsharp_call_start_serverside = GetMethodDelegate<Delegates.grpcsharp_call_start_serverside_delegate>(library); + this.grpcsharp_call_send_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_send_initial_metadata_delegate>(library); + this.grpcsharp_call_set_credentials = GetMethodDelegate<Delegates.grpcsharp_call_set_credentials_delegate>(library); + this.grpcsharp_call_get_peer = GetMethodDelegate<Delegates.grpcsharp_call_get_peer_delegate>(library); + this.grpcsharp_call_destroy = GetMethodDelegate<Delegates.grpcsharp_call_destroy_delegate>(library); + this.grpcsharp_channel_args_create = GetMethodDelegate<Delegates.grpcsharp_channel_args_create_delegate>(library); + this.grpcsharp_channel_args_set_string = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_string_delegate>(library); + this.grpcsharp_channel_args_set_integer = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_integer_delegate>(library); + this.grpcsharp_channel_args_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_args_destroy_delegate>(library); + this.grpcsharp_override_default_ssl_roots = GetMethodDelegate<Delegates.grpcsharp_override_default_ssl_roots_delegate>(library); + this.grpcsharp_ssl_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_credentials_create_delegate>(library); + this.grpcsharp_composite_channel_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_channel_credentials_create_delegate>(library); + this.grpcsharp_channel_credentials_release = GetMethodDelegate<Delegates.grpcsharp_channel_credentials_release_delegate>(library); + this.grpcsharp_insecure_channel_create = GetMethodDelegate<Delegates.grpcsharp_insecure_channel_create_delegate>(library); + this.grpcsharp_secure_channel_create = GetMethodDelegate<Delegates.grpcsharp_secure_channel_create_delegate>(library); + this.grpcsharp_channel_create_call = GetMethodDelegate<Delegates.grpcsharp_channel_create_call_delegate>(library); + this.grpcsharp_channel_check_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_check_connectivity_state_delegate>(library); + this.grpcsharp_channel_watch_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_watch_connectivity_state_delegate>(library); + this.grpcsharp_channel_get_target = GetMethodDelegate<Delegates.grpcsharp_channel_get_target_delegate>(library); + this.grpcsharp_channel_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_destroy_delegate>(library); + this.grpcsharp_sizeof_grpc_event = GetMethodDelegate<Delegates.grpcsharp_sizeof_grpc_event_delegate>(library); + this.grpcsharp_completion_queue_create_async = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_async_delegate>(library); + this.grpcsharp_completion_queue_create_sync = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_sync_delegate>(library); + this.grpcsharp_completion_queue_shutdown = GetMethodDelegate<Delegates.grpcsharp_completion_queue_shutdown_delegate>(library); + this.grpcsharp_completion_queue_next = GetMethodDelegate<Delegates.grpcsharp_completion_queue_next_delegate>(library); + this.grpcsharp_completion_queue_pluck = GetMethodDelegate<Delegates.grpcsharp_completion_queue_pluck_delegate>(library); + this.grpcsharp_completion_queue_destroy = GetMethodDelegate<Delegates.grpcsharp_completion_queue_destroy_delegate>(library); + this.gprsharp_free = GetMethodDelegate<Delegates.gprsharp_free_delegate>(library); + this.grpcsharp_metadata_array_create = GetMethodDelegate<Delegates.grpcsharp_metadata_array_create_delegate>(library); + this.grpcsharp_metadata_array_add = GetMethodDelegate<Delegates.grpcsharp_metadata_array_add_delegate>(library); + this.grpcsharp_metadata_array_count = GetMethodDelegate<Delegates.grpcsharp_metadata_array_count_delegate>(library); + this.grpcsharp_metadata_array_get_key = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_key_delegate>(library); + this.grpcsharp_metadata_array_get_value = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_delegate>(library); + this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate<Delegates.grpcsharp_metadata_array_destroy_full_delegate>(library); + this.grpcsharp_redirect_log = GetMethodDelegate<Delegates.grpcsharp_redirect_log_delegate>(library); + this.grpcsharp_metadata_credentials_create_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate>(library); + this.grpcsharp_metadata_credentials_notify_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate>(library); + this.grpcsharp_ssl_server_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_server_credentials_create_delegate>(library); + this.grpcsharp_server_credentials_release = GetMethodDelegate<Delegates.grpcsharp_server_credentials_release_delegate>(library); + this.grpcsharp_server_create = GetMethodDelegate<Delegates.grpcsharp_server_create_delegate>(library); + this.grpcsharp_server_register_completion_queue = GetMethodDelegate<Delegates.grpcsharp_server_register_completion_queue_delegate>(library); + this.grpcsharp_server_add_insecure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_insecure_http2_port_delegate>(library); + this.grpcsharp_server_add_secure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_secure_http2_port_delegate>(library); + this.grpcsharp_server_start = GetMethodDelegate<Delegates.grpcsharp_server_start_delegate>(library); + this.grpcsharp_server_request_call = GetMethodDelegate<Delegates.grpcsharp_server_request_call_delegate>(library); + this.grpcsharp_server_cancel_all_calls = GetMethodDelegate<Delegates.grpcsharp_server_cancel_all_calls_delegate>(library); + this.grpcsharp_server_shutdown_and_notify_callback = GetMethodDelegate<Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate>(library); + this.grpcsharp_server_destroy = GetMethodDelegate<Delegates.grpcsharp_server_destroy_delegate>(library); + this.grpcsharp_call_auth_context = GetMethodDelegate<Delegates.grpcsharp_call_auth_context_delegate>(library); + this.grpcsharp_auth_context_peer_identity_property_name = GetMethodDelegate<Delegates.grpcsharp_auth_context_peer_identity_property_name_delegate>(library); + this.grpcsharp_auth_context_property_iterator = GetMethodDelegate<Delegates.grpcsharp_auth_context_property_iterator_delegate>(library); + this.grpcsharp_auth_property_iterator_next = GetMethodDelegate<Delegates.grpcsharp_auth_property_iterator_next_delegate>(library); + this.grpcsharp_auth_context_release = GetMethodDelegate<Delegates.grpcsharp_auth_context_release_delegate>(library); + this.gprsharp_now = GetMethodDelegate<Delegates.gprsharp_now_delegate>(library); + this.gprsharp_inf_future = GetMethodDelegate<Delegates.gprsharp_inf_future_delegate>(library); + this.gprsharp_inf_past = GetMethodDelegate<Delegates.gprsharp_inf_past_delegate>(library); + this.gprsharp_convert_clock_type = GetMethodDelegate<Delegates.gprsharp_convert_clock_type_delegate>(library); + this.gprsharp_sizeof_timespec = GetMethodDelegate<Delegates.gprsharp_sizeof_timespec_delegate>(library); + this.grpcsharp_test_callback = GetMethodDelegate<Delegates.grpcsharp_test_callback_delegate>(library); + this.grpcsharp_test_nop = GetMethodDelegate<Delegates.grpcsharp_test_nop_delegate>(library); + this.grpcsharp_test_override_method = GetMethodDelegate<Delegates.grpcsharp_test_override_method_delegate>(library); + } + + public NativeMethods(DllImportsFromStaticLib unusedInstance) + { + this.grpcsharp_init = DllImportsFromStaticLib.grpcsharp_init; + this.grpcsharp_shutdown = DllImportsFromStaticLib.grpcsharp_shutdown; + this.grpcsharp_version_string = DllImportsFromStaticLib.grpcsharp_version_string; + this.grpcsharp_batch_context_create = DllImportsFromStaticLib.grpcsharp_batch_context_create; + this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_initial_metadata; + this.grpcsharp_batch_context_recv_message_length = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_length; + this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromStaticLib.grpcsharp_batch_context_recv_message_to_buffer; + this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_status; + this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_details; + this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromStaticLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata; + this.grpcsharp_batch_context_recv_close_on_server_cancelled = DllImportsFromStaticLib.grpcsharp_batch_context_recv_close_on_server_cancelled; + this.grpcsharp_batch_context_reset = DllImportsFromStaticLib.grpcsharp_batch_context_reset; + this.grpcsharp_batch_context_destroy = DllImportsFromStaticLib.grpcsharp_batch_context_destroy; + this.grpcsharp_request_call_context_create = DllImportsFromStaticLib.grpcsharp_request_call_context_create; + this.grpcsharp_request_call_context_call = DllImportsFromStaticLib.grpcsharp_request_call_context_call; + this.grpcsharp_request_call_context_method = DllImportsFromStaticLib.grpcsharp_request_call_context_method; + this.grpcsharp_request_call_context_host = DllImportsFromStaticLib.grpcsharp_request_call_context_host; + this.grpcsharp_request_call_context_deadline = DllImportsFromStaticLib.grpcsharp_request_call_context_deadline; + this.grpcsharp_request_call_context_request_metadata = DllImportsFromStaticLib.grpcsharp_request_call_context_request_metadata; + this.grpcsharp_request_call_context_reset = DllImportsFromStaticLib.grpcsharp_request_call_context_reset; + this.grpcsharp_request_call_context_destroy = DllImportsFromStaticLib.grpcsharp_request_call_context_destroy; + this.grpcsharp_composite_call_credentials_create = DllImportsFromStaticLib.grpcsharp_composite_call_credentials_create; + this.grpcsharp_call_credentials_release = DllImportsFromStaticLib.grpcsharp_call_credentials_release; + this.grpcsharp_call_cancel = DllImportsFromStaticLib.grpcsharp_call_cancel; + this.grpcsharp_call_cancel_with_status = DllImportsFromStaticLib.grpcsharp_call_cancel_with_status; + this.grpcsharp_call_start_unary = DllImportsFromStaticLib.grpcsharp_call_start_unary; + this.grpcsharp_call_start_client_streaming = DllImportsFromStaticLib.grpcsharp_call_start_client_streaming; + this.grpcsharp_call_start_server_streaming = DllImportsFromStaticLib.grpcsharp_call_start_server_streaming; + this.grpcsharp_call_start_duplex_streaming = DllImportsFromStaticLib.grpcsharp_call_start_duplex_streaming; + this.grpcsharp_call_send_message = DllImportsFromStaticLib.grpcsharp_call_send_message; + this.grpcsharp_call_send_close_from_client = DllImportsFromStaticLib.grpcsharp_call_send_close_from_client; + this.grpcsharp_call_send_status_from_server = DllImportsFromStaticLib.grpcsharp_call_send_status_from_server; + this.grpcsharp_call_recv_message = DllImportsFromStaticLib.grpcsharp_call_recv_message; + this.grpcsharp_call_recv_initial_metadata = DllImportsFromStaticLib.grpcsharp_call_recv_initial_metadata; + this.grpcsharp_call_start_serverside = DllImportsFromStaticLib.grpcsharp_call_start_serverside; + this.grpcsharp_call_send_initial_metadata = DllImportsFromStaticLib.grpcsharp_call_send_initial_metadata; + this.grpcsharp_call_set_credentials = DllImportsFromStaticLib.grpcsharp_call_set_credentials; + this.grpcsharp_call_get_peer = DllImportsFromStaticLib.grpcsharp_call_get_peer; + this.grpcsharp_call_destroy = DllImportsFromStaticLib.grpcsharp_call_destroy; + this.grpcsharp_channel_args_create = DllImportsFromStaticLib.grpcsharp_channel_args_create; + this.grpcsharp_channel_args_set_string = DllImportsFromStaticLib.grpcsharp_channel_args_set_string; + this.grpcsharp_channel_args_set_integer = DllImportsFromStaticLib.grpcsharp_channel_args_set_integer; + this.grpcsharp_channel_args_destroy = DllImportsFromStaticLib.grpcsharp_channel_args_destroy; + this.grpcsharp_override_default_ssl_roots = DllImportsFromStaticLib.grpcsharp_override_default_ssl_roots; + this.grpcsharp_ssl_credentials_create = DllImportsFromStaticLib.grpcsharp_ssl_credentials_create; + this.grpcsharp_composite_channel_credentials_create = DllImportsFromStaticLib.grpcsharp_composite_channel_credentials_create; + this.grpcsharp_channel_credentials_release = DllImportsFromStaticLib.grpcsharp_channel_credentials_release; + this.grpcsharp_insecure_channel_create = DllImportsFromStaticLib.grpcsharp_insecure_channel_create; + this.grpcsharp_secure_channel_create = DllImportsFromStaticLib.grpcsharp_secure_channel_create; + this.grpcsharp_channel_create_call = DllImportsFromStaticLib.grpcsharp_channel_create_call; + this.grpcsharp_channel_check_connectivity_state = DllImportsFromStaticLib.grpcsharp_channel_check_connectivity_state; + this.grpcsharp_channel_watch_connectivity_state = DllImportsFromStaticLib.grpcsharp_channel_watch_connectivity_state; + this.grpcsharp_channel_get_target = DllImportsFromStaticLib.grpcsharp_channel_get_target; + this.grpcsharp_channel_destroy = DllImportsFromStaticLib.grpcsharp_channel_destroy; + this.grpcsharp_sizeof_grpc_event = DllImportsFromStaticLib.grpcsharp_sizeof_grpc_event; + this.grpcsharp_completion_queue_create_async = DllImportsFromStaticLib.grpcsharp_completion_queue_create_async; + this.grpcsharp_completion_queue_create_sync = DllImportsFromStaticLib.grpcsharp_completion_queue_create_sync; + this.grpcsharp_completion_queue_shutdown = DllImportsFromStaticLib.grpcsharp_completion_queue_shutdown; + this.grpcsharp_completion_queue_next = DllImportsFromStaticLib.grpcsharp_completion_queue_next; + this.grpcsharp_completion_queue_pluck = DllImportsFromStaticLib.grpcsharp_completion_queue_pluck; + this.grpcsharp_completion_queue_destroy = DllImportsFromStaticLib.grpcsharp_completion_queue_destroy; + this.gprsharp_free = DllImportsFromStaticLib.gprsharp_free; + this.grpcsharp_metadata_array_create = DllImportsFromStaticLib.grpcsharp_metadata_array_create; + this.grpcsharp_metadata_array_add = DllImportsFromStaticLib.grpcsharp_metadata_array_add; + this.grpcsharp_metadata_array_count = DllImportsFromStaticLib.grpcsharp_metadata_array_count; + this.grpcsharp_metadata_array_get_key = DllImportsFromStaticLib.grpcsharp_metadata_array_get_key; + this.grpcsharp_metadata_array_get_value = DllImportsFromStaticLib.grpcsharp_metadata_array_get_value; + this.grpcsharp_metadata_array_destroy_full = DllImportsFromStaticLib.grpcsharp_metadata_array_destroy_full; + this.grpcsharp_redirect_log = DllImportsFromStaticLib.grpcsharp_redirect_log; + this.grpcsharp_metadata_credentials_create_from_plugin = DllImportsFromStaticLib.grpcsharp_metadata_credentials_create_from_plugin; + this.grpcsharp_metadata_credentials_notify_from_plugin = DllImportsFromStaticLib.grpcsharp_metadata_credentials_notify_from_plugin; + this.grpcsharp_ssl_server_credentials_create = DllImportsFromStaticLib.grpcsharp_ssl_server_credentials_create; + this.grpcsharp_server_credentials_release = DllImportsFromStaticLib.grpcsharp_server_credentials_release; + this.grpcsharp_server_create = DllImportsFromStaticLib.grpcsharp_server_create; + this.grpcsharp_server_register_completion_queue = DllImportsFromStaticLib.grpcsharp_server_register_completion_queue; + this.grpcsharp_server_add_insecure_http2_port = DllImportsFromStaticLib.grpcsharp_server_add_insecure_http2_port; + this.grpcsharp_server_add_secure_http2_port = DllImportsFromStaticLib.grpcsharp_server_add_secure_http2_port; + this.grpcsharp_server_start = DllImportsFromStaticLib.grpcsharp_server_start; + this.grpcsharp_server_request_call = DllImportsFromStaticLib.grpcsharp_server_request_call; + this.grpcsharp_server_cancel_all_calls = DllImportsFromStaticLib.grpcsharp_server_cancel_all_calls; + this.grpcsharp_server_shutdown_and_notify_callback = DllImportsFromStaticLib.grpcsharp_server_shutdown_and_notify_callback; + this.grpcsharp_server_destroy = DllImportsFromStaticLib.grpcsharp_server_destroy; + this.grpcsharp_call_auth_context = DllImportsFromStaticLib.grpcsharp_call_auth_context; + this.grpcsharp_auth_context_peer_identity_property_name = DllImportsFromStaticLib.grpcsharp_auth_context_peer_identity_property_name; + this.grpcsharp_auth_context_property_iterator = DllImportsFromStaticLib.grpcsharp_auth_context_property_iterator; + this.grpcsharp_auth_property_iterator_next = DllImportsFromStaticLib.grpcsharp_auth_property_iterator_next; + this.grpcsharp_auth_context_release = DllImportsFromStaticLib.grpcsharp_auth_context_release; + this.gprsharp_now = DllImportsFromStaticLib.gprsharp_now; + this.gprsharp_inf_future = DllImportsFromStaticLib.gprsharp_inf_future; + this.gprsharp_inf_past = DllImportsFromStaticLib.gprsharp_inf_past; + this.gprsharp_convert_clock_type = DllImportsFromStaticLib.gprsharp_convert_clock_type; + this.gprsharp_sizeof_timespec = DllImportsFromStaticLib.gprsharp_sizeof_timespec; + this.grpcsharp_test_callback = DllImportsFromStaticLib.grpcsharp_test_callback; + this.grpcsharp_test_nop = DllImportsFromStaticLib.grpcsharp_test_nop; + this.grpcsharp_test_override_method = DllImportsFromStaticLib.grpcsharp_test_override_method; + } + + public NativeMethods(DllImportsFromSharedLib unusedInstance) + { + this.grpcsharp_init = DllImportsFromSharedLib.grpcsharp_init; + this.grpcsharp_shutdown = DllImportsFromSharedLib.grpcsharp_shutdown; + this.grpcsharp_version_string = DllImportsFromSharedLib.grpcsharp_version_string; + this.grpcsharp_batch_context_create = DllImportsFromSharedLib.grpcsharp_batch_context_create; + this.grpcsharp_batch_context_recv_initial_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_initial_metadata; + this.grpcsharp_batch_context_recv_message_length = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_length; + this.grpcsharp_batch_context_recv_message_to_buffer = DllImportsFromSharedLib.grpcsharp_batch_context_recv_message_to_buffer; + this.grpcsharp_batch_context_recv_status_on_client_status = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_status; + this.grpcsharp_batch_context_recv_status_on_client_details = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_details; + this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = DllImportsFromSharedLib.grpcsharp_batch_context_recv_status_on_client_trailing_metadata; + this.grpcsharp_batch_context_recv_close_on_server_cancelled = DllImportsFromSharedLib.grpcsharp_batch_context_recv_close_on_server_cancelled; + this.grpcsharp_batch_context_reset = DllImportsFromSharedLib.grpcsharp_batch_context_reset; + this.grpcsharp_batch_context_destroy = DllImportsFromSharedLib.grpcsharp_batch_context_destroy; + this.grpcsharp_request_call_context_create = DllImportsFromSharedLib.grpcsharp_request_call_context_create; + this.grpcsharp_request_call_context_call = DllImportsFromSharedLib.grpcsharp_request_call_context_call; + this.grpcsharp_request_call_context_method = DllImportsFromSharedLib.grpcsharp_request_call_context_method; + this.grpcsharp_request_call_context_host = DllImportsFromSharedLib.grpcsharp_request_call_context_host; + this.grpcsharp_request_call_context_deadline = DllImportsFromSharedLib.grpcsharp_request_call_context_deadline; + this.grpcsharp_request_call_context_request_metadata = DllImportsFromSharedLib.grpcsharp_request_call_context_request_metadata; + this.grpcsharp_request_call_context_reset = DllImportsFromSharedLib.grpcsharp_request_call_context_reset; + this.grpcsharp_request_call_context_destroy = DllImportsFromSharedLib.grpcsharp_request_call_context_destroy; + this.grpcsharp_composite_call_credentials_create = DllImportsFromSharedLib.grpcsharp_composite_call_credentials_create; + this.grpcsharp_call_credentials_release = DllImportsFromSharedLib.grpcsharp_call_credentials_release; + this.grpcsharp_call_cancel = DllImportsFromSharedLib.grpcsharp_call_cancel; + this.grpcsharp_call_cancel_with_status = DllImportsFromSharedLib.grpcsharp_call_cancel_with_status; + this.grpcsharp_call_start_unary = DllImportsFromSharedLib.grpcsharp_call_start_unary; + this.grpcsharp_call_start_client_streaming = DllImportsFromSharedLib.grpcsharp_call_start_client_streaming; + this.grpcsharp_call_start_server_streaming = DllImportsFromSharedLib.grpcsharp_call_start_server_streaming; + this.grpcsharp_call_start_duplex_streaming = DllImportsFromSharedLib.grpcsharp_call_start_duplex_streaming; + this.grpcsharp_call_send_message = DllImportsFromSharedLib.grpcsharp_call_send_message; + this.grpcsharp_call_send_close_from_client = DllImportsFromSharedLib.grpcsharp_call_send_close_from_client; + this.grpcsharp_call_send_status_from_server = DllImportsFromSharedLib.grpcsharp_call_send_status_from_server; + this.grpcsharp_call_recv_message = DllImportsFromSharedLib.grpcsharp_call_recv_message; + this.grpcsharp_call_recv_initial_metadata = DllImportsFromSharedLib.grpcsharp_call_recv_initial_metadata; + this.grpcsharp_call_start_serverside = DllImportsFromSharedLib.grpcsharp_call_start_serverside; + this.grpcsharp_call_send_initial_metadata = DllImportsFromSharedLib.grpcsharp_call_send_initial_metadata; + this.grpcsharp_call_set_credentials = DllImportsFromSharedLib.grpcsharp_call_set_credentials; + this.grpcsharp_call_get_peer = DllImportsFromSharedLib.grpcsharp_call_get_peer; + this.grpcsharp_call_destroy = DllImportsFromSharedLib.grpcsharp_call_destroy; + this.grpcsharp_channel_args_create = DllImportsFromSharedLib.grpcsharp_channel_args_create; + this.grpcsharp_channel_args_set_string = DllImportsFromSharedLib.grpcsharp_channel_args_set_string; + this.grpcsharp_channel_args_set_integer = DllImportsFromSharedLib.grpcsharp_channel_args_set_integer; + this.grpcsharp_channel_args_destroy = DllImportsFromSharedLib.grpcsharp_channel_args_destroy; + this.grpcsharp_override_default_ssl_roots = DllImportsFromSharedLib.grpcsharp_override_default_ssl_roots; + this.grpcsharp_ssl_credentials_create = DllImportsFromSharedLib.grpcsharp_ssl_credentials_create; + this.grpcsharp_composite_channel_credentials_create = DllImportsFromSharedLib.grpcsharp_composite_channel_credentials_create; + this.grpcsharp_channel_credentials_release = DllImportsFromSharedLib.grpcsharp_channel_credentials_release; + this.grpcsharp_insecure_channel_create = DllImportsFromSharedLib.grpcsharp_insecure_channel_create; + this.grpcsharp_secure_channel_create = DllImportsFromSharedLib.grpcsharp_secure_channel_create; + this.grpcsharp_channel_create_call = DllImportsFromSharedLib.grpcsharp_channel_create_call; + this.grpcsharp_channel_check_connectivity_state = DllImportsFromSharedLib.grpcsharp_channel_check_connectivity_state; + this.grpcsharp_channel_watch_connectivity_state = DllImportsFromSharedLib.grpcsharp_channel_watch_connectivity_state; + this.grpcsharp_channel_get_target = DllImportsFromSharedLib.grpcsharp_channel_get_target; + this.grpcsharp_channel_destroy = DllImportsFromSharedLib.grpcsharp_channel_destroy; + this.grpcsharp_sizeof_grpc_event = DllImportsFromSharedLib.grpcsharp_sizeof_grpc_event; + this.grpcsharp_completion_queue_create_async = DllImportsFromSharedLib.grpcsharp_completion_queue_create_async; + this.grpcsharp_completion_queue_create_sync = DllImportsFromSharedLib.grpcsharp_completion_queue_create_sync; + this.grpcsharp_completion_queue_shutdown = DllImportsFromSharedLib.grpcsharp_completion_queue_shutdown; + this.grpcsharp_completion_queue_next = DllImportsFromSharedLib.grpcsharp_completion_queue_next; + this.grpcsharp_completion_queue_pluck = DllImportsFromSharedLib.grpcsharp_completion_queue_pluck; + this.grpcsharp_completion_queue_destroy = DllImportsFromSharedLib.grpcsharp_completion_queue_destroy; + this.gprsharp_free = DllImportsFromSharedLib.gprsharp_free; + this.grpcsharp_metadata_array_create = DllImportsFromSharedLib.grpcsharp_metadata_array_create; + this.grpcsharp_metadata_array_add = DllImportsFromSharedLib.grpcsharp_metadata_array_add; + this.grpcsharp_metadata_array_count = DllImportsFromSharedLib.grpcsharp_metadata_array_count; + this.grpcsharp_metadata_array_get_key = DllImportsFromSharedLib.grpcsharp_metadata_array_get_key; + this.grpcsharp_metadata_array_get_value = DllImportsFromSharedLib.grpcsharp_metadata_array_get_value; + this.grpcsharp_metadata_array_destroy_full = DllImportsFromSharedLib.grpcsharp_metadata_array_destroy_full; + this.grpcsharp_redirect_log = DllImportsFromSharedLib.grpcsharp_redirect_log; + this.grpcsharp_metadata_credentials_create_from_plugin = DllImportsFromSharedLib.grpcsharp_metadata_credentials_create_from_plugin; + this.grpcsharp_metadata_credentials_notify_from_plugin = DllImportsFromSharedLib.grpcsharp_metadata_credentials_notify_from_plugin; + this.grpcsharp_ssl_server_credentials_create = DllImportsFromSharedLib.grpcsharp_ssl_server_credentials_create; + this.grpcsharp_server_credentials_release = DllImportsFromSharedLib.grpcsharp_server_credentials_release; + this.grpcsharp_server_create = DllImportsFromSharedLib.grpcsharp_server_create; + this.grpcsharp_server_register_completion_queue = DllImportsFromSharedLib.grpcsharp_server_register_completion_queue; + this.grpcsharp_server_add_insecure_http2_port = DllImportsFromSharedLib.grpcsharp_server_add_insecure_http2_port; + this.grpcsharp_server_add_secure_http2_port = DllImportsFromSharedLib.grpcsharp_server_add_secure_http2_port; + this.grpcsharp_server_start = DllImportsFromSharedLib.grpcsharp_server_start; + this.grpcsharp_server_request_call = DllImportsFromSharedLib.grpcsharp_server_request_call; + this.grpcsharp_server_cancel_all_calls = DllImportsFromSharedLib.grpcsharp_server_cancel_all_calls; + this.grpcsharp_server_shutdown_and_notify_callback = DllImportsFromSharedLib.grpcsharp_server_shutdown_and_notify_callback; + this.grpcsharp_server_destroy = DllImportsFromSharedLib.grpcsharp_server_destroy; + this.grpcsharp_call_auth_context = DllImportsFromSharedLib.grpcsharp_call_auth_context; + this.grpcsharp_auth_context_peer_identity_property_name = DllImportsFromSharedLib.grpcsharp_auth_context_peer_identity_property_name; + this.grpcsharp_auth_context_property_iterator = DllImportsFromSharedLib.grpcsharp_auth_context_property_iterator; + this.grpcsharp_auth_property_iterator_next = DllImportsFromSharedLib.grpcsharp_auth_property_iterator_next; + this.grpcsharp_auth_context_release = DllImportsFromSharedLib.grpcsharp_auth_context_release; + this.gprsharp_now = DllImportsFromSharedLib.gprsharp_now; + this.gprsharp_inf_future = DllImportsFromSharedLib.gprsharp_inf_future; + this.gprsharp_inf_past = DllImportsFromSharedLib.gprsharp_inf_past; + this.gprsharp_convert_clock_type = DllImportsFromSharedLib.gprsharp_convert_clock_type; + this.gprsharp_sizeof_timespec = DllImportsFromSharedLib.gprsharp_sizeof_timespec; + this.grpcsharp_test_callback = DllImportsFromSharedLib.grpcsharp_test_callback; + this.grpcsharp_test_nop = DllImportsFromSharedLib.grpcsharp_test_nop; + this.grpcsharp_test_override_method = DllImportsFromSharedLib.grpcsharp_test_override_method; + } + + /// <summary> + /// Delegate types for all published native methods. Declared under inner class to prevent scope pollution. + /// </summary> + public class Delegates + { + public delegate void grpcsharp_init_delegate(); + public delegate void grpcsharp_shutdown_delegate(); + public delegate IntPtr grpcsharp_version_string_delegate(); // returns not-owned const char* + public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate(); + public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx); + public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx); + public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); + public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx); + public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength); + public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx); + public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx); + public delegate void grpcsharp_batch_context_reset_delegate(BatchContextSafeHandle ctx); + public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx); + public delegate RequestCallContextSafeHandle grpcsharp_request_call_context_create_delegate(); + public delegate CallSafeHandle grpcsharp_request_call_context_call_delegate(RequestCallContextSafeHandle ctx); + public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx, out UIntPtr methodLength); + public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx, out UIntPtr hostLength); + public delegate Timespec grpcsharp_request_call_context_deadline_delegate(RequestCallContextSafeHandle ctx); + public delegate IntPtr grpcsharp_request_call_context_request_metadata_delegate(RequestCallContextSafeHandle ctx); + public delegate void grpcsharp_request_call_context_reset_delegate(RequestCallContextSafeHandle ctx); + public delegate void grpcsharp_request_call_context_destroy_delegate(IntPtr ctx); + public delegate CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create_delegate(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2); + public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials); + public delegate CallError grpcsharp_call_cancel_delegate(CallSafeHandle call); + public delegate CallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description); + public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + public delegate CallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); + public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); + public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); + public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); + public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call, BatchContextSafeHandle ctx); + public delegate CallError grpcsharp_call_send_initial_metadata_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + public delegate CallError grpcsharp_call_set_credentials_delegate(CallSafeHandle call, CallCredentialsSafeHandle credentials); + public delegate CStringSafeHandle grpcsharp_call_get_peer_delegate(CallSafeHandle call); + public delegate void grpcsharp_call_destroy_delegate(IntPtr call); + public delegate ChannelArgsSafeHandle grpcsharp_channel_args_create_delegate(UIntPtr numArgs); + public delegate void grpcsharp_channel_args_set_string_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); + public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); + public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args); + public delegate void grpcsharp_override_default_ssl_roots_delegate(string pemRootCerts); + public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); + public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials); + public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs); + public delegate ChannelSafeHandle grpcsharp_secure_channel_create_delegate(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); + public delegate CallSafeHandle grpcsharp_channel_create_call_delegate(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + public delegate ChannelState grpcsharp_channel_check_connectivity_state_delegate(ChannelSafeHandle channel, int tryToConnect); + public delegate void grpcsharp_channel_watch_connectivity_state_delegate(ChannelSafeHandle channel, ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + public delegate CStringSafeHandle grpcsharp_channel_get_target_delegate(ChannelSafeHandle call); + public delegate void grpcsharp_channel_destroy_delegate(IntPtr channel); + public delegate int grpcsharp_sizeof_grpc_event_delegate(); + public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_async_delegate(); + public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_sync_delegate(); + public delegate void grpcsharp_completion_queue_shutdown_delegate(CompletionQueueSafeHandle cq); + public delegate CompletionQueueEvent grpcsharp_completion_queue_next_delegate(CompletionQueueSafeHandle cq); + public delegate CompletionQueueEvent grpcsharp_completion_queue_pluck_delegate(CompletionQueueSafeHandle cq, IntPtr tag); + public delegate void grpcsharp_completion_queue_destroy_delegate(IntPtr cq); + public delegate void gprsharp_free_delegate(IntPtr ptr); + public delegate MetadataArraySafeHandle grpcsharp_metadata_array_create_delegate(UIntPtr capacity); + public delegate void grpcsharp_metadata_array_add_delegate(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); + public delegate UIntPtr grpcsharp_metadata_array_count_delegate(IntPtr metadataArray); + public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength); + public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength); + public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array); + public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback); + public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor); + public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); + public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth); + public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials); + public delegate ServerSafeHandle grpcsharp_server_create_delegate(ChannelArgsSafeHandle args); + public delegate void grpcsharp_server_register_completion_queue_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq); + public delegate int grpcsharp_server_add_insecure_http2_port_delegate(ServerSafeHandle server, string addr); + public delegate int grpcsharp_server_add_secure_http2_port_delegate(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); + public delegate void grpcsharp_server_start_delegate(ServerSafeHandle server); + public delegate CallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx); + public delegate void grpcsharp_server_cancel_all_calls_delegate(ServerSafeHandle server); + public delegate void grpcsharp_server_shutdown_and_notify_callback_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + public delegate void grpcsharp_server_destroy_delegate(IntPtr server); + public delegate AuthContextSafeHandle grpcsharp_call_auth_context_delegate(CallSafeHandle call); + public delegate IntPtr grpcsharp_auth_context_peer_identity_property_name_delegate(AuthContextSafeHandle authContext); // returns const char* + public delegate AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator_delegate(AuthContextSafeHandle authContext); + public delegate IntPtr grpcsharp_auth_property_iterator_next_delegate(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator); // returns const auth_property* + public delegate void grpcsharp_auth_context_release_delegate(IntPtr authContext); + public delegate Timespec gprsharp_now_delegate(ClockType clockType); + public delegate Timespec gprsharp_inf_future_delegate(ClockType clockType); + public delegate Timespec gprsharp_inf_past_delegate(ClockType clockType); + public delegate Timespec gprsharp_convert_clock_type_delegate(Timespec t, ClockType targetClock); + public delegate int gprsharp_sizeof_timespec_delegate(); + public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback); + public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr); + public delegate void grpcsharp_test_override_method_delegate(string methodName, string variant); + } + + /// <summary> + /// grpc_csharp_ext used as a static library (e.g Unity iOS). + /// </summary> + internal class DllImportsFromStaticLib + { + private const string ImportName = "__Internal"; + + [DllImport(ImportName)] + public static extern void grpcsharp_init(); + + [DllImport(ImportName)] + public static extern void grpcsharp_shutdown(); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_version_string(); + + [DllImport(ImportName)] + public static extern BatchContextSafeHandle grpcsharp_batch_context_create(); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); + + [DllImport(ImportName)] + public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx, out UIntPtr detailsLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_reset(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_destroy(IntPtr ctx); + + [DllImport(ImportName)] + public static extern RequestCallContextSafeHandle grpcsharp_request_call_context_create(); + + [DllImport(ImportName)] + public static extern CallSafeHandle grpcsharp_request_call_context_call(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_method(RequestCallContextSafeHandle ctx, out UIntPtr methodLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_host(RequestCallContextSafeHandle ctx, out UIntPtr hostLength); + + [DllImport(ImportName)] + public static extern Timespec grpcsharp_request_call_context_deadline(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_request_metadata(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_request_call_context_reset(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_request_call_context_destroy(IntPtr ctx); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2); + + [DllImport(ImportName)] + public static extern void grpcsharp_call_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_cancel(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_client_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_serverside(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_set_credentials(CallSafeHandle call, CallCredentialsSafeHandle credentials); + + [DllImport(ImportName)] + public static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern void grpcsharp_call_destroy(IntPtr call); + + [DllImport(ImportName)] + public static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_destroy(IntPtr args); + + [DllImport(ImportName)] + public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); + + [DllImport(ImportName)] + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + + [DllImport(ImportName)] + public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs); + + [DllImport(ImportName)] + public static extern ChannelSafeHandle grpcsharp_secure_channel_create(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); + + [DllImport(ImportName)] + public static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + + [DllImport(ImportName)] + public static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_destroy(IntPtr channel); + + [DllImport(ImportName)] + public static extern int grpcsharp_sizeof_grpc_event(); + + [DllImport(ImportName)] + public static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create_async(); + + [DllImport(ImportName)] + public static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create_sync(); + + [DllImport(ImportName)] + public static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag); + + [DllImport(ImportName)] + public static extern void grpcsharp_completion_queue_destroy(IntPtr cq); + + [DllImport(ImportName)] + public static extern void gprsharp_free(IntPtr ptr); + + [DllImport(ImportName)] + public static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); + + [DllImport(ImportName)] + public static extern UIntPtr grpcsharp_metadata_array_count(IntPtr metadataArray); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_metadata_array_get_key(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_array_destroy_full(IntPtr array); + + [DllImport(ImportName)] + public static extern void grpcsharp_redirect_log(GprLogDelegate callback); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); + + [DllImport(ImportName)] + public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern ServerSafeHandle grpcsharp_server_create(ChannelArgsSafeHandle args); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_register_completion_queue(ServerSafeHandle server, CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr); + + [DllImport(ImportName)] + public static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_start(ServerSafeHandle server); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_destroy(IntPtr server); + + [DllImport(ImportName)] + public static extern AuthContextSafeHandle grpcsharp_call_auth_context(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_auth_context_peer_identity_property_name(AuthContextSafeHandle authContext); + + [DllImport(ImportName)] + public static extern AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator(AuthContextSafeHandle authContext); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_auth_property_iterator_next(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator); + + [DllImport(ImportName)] + public static extern void grpcsharp_auth_context_release(IntPtr authContext); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_now(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_inf_future(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_inf_past(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_convert_clock_type(Timespec t, ClockType targetClock); + + [DllImport(ImportName)] + public static extern int gprsharp_sizeof_timespec(); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_test_nop(IntPtr ptr); + + [DllImport(ImportName)] + public static extern void grpcsharp_test_override_method(string methodName, string variant); + } + + /// <summary> + /// grpc_csharp_ext used a shared library (e.g on Unity Standalone and Android). + /// </summary> + internal class DllImportsFromSharedLib + { + private const string ImportName = "grpc_csharp_ext"; + + [DllImport(ImportName)] + public static extern void grpcsharp_init(); + + [DllImport(ImportName)] + public static extern void grpcsharp_shutdown(); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_version_string(); + + [DllImport(ImportName)] + public static extern BatchContextSafeHandle grpcsharp_batch_context_create(); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); + + [DllImport(ImportName)] + public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx, out UIntPtr detailsLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_reset(BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_batch_context_destroy(IntPtr ctx); + + [DllImport(ImportName)] + public static extern RequestCallContextSafeHandle grpcsharp_request_call_context_create(); + + [DllImport(ImportName)] + public static extern CallSafeHandle grpcsharp_request_call_context_call(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_method(RequestCallContextSafeHandle ctx, out UIntPtr methodLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_host(RequestCallContextSafeHandle ctx, out UIntPtr hostLength); + + [DllImport(ImportName)] + public static extern Timespec grpcsharp_request_call_context_deadline(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_request_call_context_request_metadata(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_request_call_context_reset(RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_request_call_context_destroy(IntPtr ctx); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2); + + [DllImport(ImportName)] + public static extern void grpcsharp_call_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_cancel(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_client_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_start_serverside(CallSafeHandle call, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_send_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_call_set_credentials(CallSafeHandle call, CallCredentialsSafeHandle credentials); + + [DllImport(ImportName)] + public static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern void grpcsharp_call_destroy(IntPtr call); + + [DllImport(ImportName)] + public static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_args_destroy(IntPtr args); + + [DllImport(ImportName)] + public static extern void grpcsharp_override_default_ssl_roots(string pemRootCerts); + + [DllImport(ImportName)] + public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); + + [DllImport(ImportName)] + public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs); + + [DllImport(ImportName)] + public static extern ChannelSafeHandle grpcsharp_secure_channel_create(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); + + [DllImport(ImportName)] + public static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); + + [DllImport(ImportName)] + public static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call); + + [DllImport(ImportName)] + public static extern void grpcsharp_channel_destroy(IntPtr channel); + + [DllImport(ImportName)] + public static extern int grpcsharp_sizeof_grpc_event(); + + [DllImport(ImportName)] + public static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create_async(); + + [DllImport(ImportName)] + public static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create_sync(); + + [DllImport(ImportName)] + public static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag); + + [DllImport(ImportName)] + public static extern void grpcsharp_completion_queue_destroy(IntPtr cq); + + [DllImport(ImportName)] + public static extern void gprsharp_free(IntPtr ptr); + + [DllImport(ImportName)] + public static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); + + [DllImport(ImportName)] + public static extern UIntPtr grpcsharp_metadata_array_count(IntPtr metadataArray); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_metadata_array_get_key(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_array_destroy_full(IntPtr array); + + [DllImport(ImportName)] + public static extern void grpcsharp_redirect_log(GprLogDelegate callback); + + [DllImport(ImportName)] + public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor); + + [DllImport(ImportName)] + public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); + + [DllImport(ImportName)] + public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_credentials_release(IntPtr credentials); + + [DllImport(ImportName)] + public static extern ServerSafeHandle grpcsharp_server_create(ChannelArgsSafeHandle args); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_register_completion_queue(ServerSafeHandle server, CompletionQueueSafeHandle cq); + + [DllImport(ImportName)] + public static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr); + + [DllImport(ImportName)] + public static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_start(ServerSafeHandle server); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + + [DllImport(ImportName)] + public static extern void grpcsharp_server_destroy(IntPtr server); + + [DllImport(ImportName)] + public static extern AuthContextSafeHandle grpcsharp_call_auth_context(CallSafeHandle call); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_auth_context_peer_identity_property_name(AuthContextSafeHandle authContext); + + [DllImport(ImportName)] + public static extern AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator(AuthContextSafeHandle authContext); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_auth_property_iterator_next(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator); + + [DllImport(ImportName)] + public static extern void grpcsharp_auth_context_release(IntPtr authContext); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_now(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_inf_future(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_inf_past(ClockType clockType); + + [DllImport(ImportName)] + public static extern Timespec gprsharp_convert_clock_type(Timespec t, ClockType targetClock); + + [DllImport(ImportName)] + public static extern int gprsharp_sizeof_timespec(); + + [DllImport(ImportName)] + public static extern CallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback); + + [DllImport(ImportName)] + public static extern IntPtr grpcsharp_test_nop(IntPtr ptr); + + [DllImport(ImportName)] + public static extern void grpcsharp_test_override_method(string methodName, string variant); + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs index 8b15c2690f..4e512442c8 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs @@ -36,243 +36,10 @@ namespace Grpc.Core.Internal /// An extra level of indirection is added to P/Invoke calls to allow intelligent loading /// of the right configuration of the native extension based on current platform, architecture etc. /// </summary> - internal class NativeMethods + internal partial class NativeMethods { - #region Native methods - - public readonly Delegates.grpcsharp_init_delegate grpcsharp_init; - public readonly Delegates.grpcsharp_shutdown_delegate grpcsharp_shutdown; - public readonly Delegates.grpcsharp_version_string_delegate grpcsharp_version_string; - - public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create; - public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata; - public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length; - public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer; - public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status; - public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details; - public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata; - public readonly Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate grpcsharp_batch_context_recv_close_on_server_cancelled; - public readonly Delegates.grpcsharp_batch_context_reset_delegate grpcsharp_batch_context_reset; - public readonly Delegates.grpcsharp_batch_context_destroy_delegate grpcsharp_batch_context_destroy; - - public readonly Delegates.grpcsharp_request_call_context_create_delegate grpcsharp_request_call_context_create; - public readonly Delegates.grpcsharp_request_call_context_call_delegate grpcsharp_request_call_context_call; - public readonly Delegates.grpcsharp_request_call_context_method_delegate grpcsharp_request_call_context_method; - public readonly Delegates.grpcsharp_request_call_context_host_delegate grpcsharp_request_call_context_host; - public readonly Delegates.grpcsharp_request_call_context_deadline_delegate grpcsharp_request_call_context_deadline; - public readonly Delegates.grpcsharp_request_call_context_request_metadata_delegate grpcsharp_request_call_context_request_metadata; - public readonly Delegates.grpcsharp_request_call_context_reset_delegate grpcsharp_request_call_context_reset; - public readonly Delegates.grpcsharp_request_call_context_destroy_delegate grpcsharp_request_call_context_destroy; - - public readonly Delegates.grpcsharp_composite_call_credentials_create_delegate grpcsharp_composite_call_credentials_create; - public readonly Delegates.grpcsharp_call_credentials_release_delegate grpcsharp_call_credentials_release; - - public readonly Delegates.grpcsharp_call_cancel_delegate grpcsharp_call_cancel; - public readonly Delegates.grpcsharp_call_cancel_with_status_delegate grpcsharp_call_cancel_with_status; - public readonly Delegates.grpcsharp_call_start_unary_delegate grpcsharp_call_start_unary; - public readonly Delegates.grpcsharp_call_start_client_streaming_delegate grpcsharp_call_start_client_streaming; - public readonly Delegates.grpcsharp_call_start_server_streaming_delegate grpcsharp_call_start_server_streaming; - public readonly Delegates.grpcsharp_call_start_duplex_streaming_delegate grpcsharp_call_start_duplex_streaming; - public readonly Delegates.grpcsharp_call_send_message_delegate grpcsharp_call_send_message; - public readonly Delegates.grpcsharp_call_send_close_from_client_delegate grpcsharp_call_send_close_from_client; - public readonly Delegates.grpcsharp_call_send_status_from_server_delegate grpcsharp_call_send_status_from_server; - public readonly Delegates.grpcsharp_call_recv_message_delegate grpcsharp_call_recv_message; - public readonly Delegates.grpcsharp_call_recv_initial_metadata_delegate grpcsharp_call_recv_initial_metadata; - public readonly Delegates.grpcsharp_call_start_serverside_delegate grpcsharp_call_start_serverside; - public readonly Delegates.grpcsharp_call_send_initial_metadata_delegate grpcsharp_call_send_initial_metadata; - public readonly Delegates.grpcsharp_call_set_credentials_delegate grpcsharp_call_set_credentials; - public readonly Delegates.grpcsharp_call_get_peer_delegate grpcsharp_call_get_peer; - public readonly Delegates.grpcsharp_call_destroy_delegate grpcsharp_call_destroy; - - public readonly Delegates.grpcsharp_channel_args_create_delegate grpcsharp_channel_args_create; - public readonly Delegates.grpcsharp_channel_args_set_string_delegate grpcsharp_channel_args_set_string; - public readonly Delegates.grpcsharp_channel_args_set_integer_delegate grpcsharp_channel_args_set_integer; - public readonly Delegates.grpcsharp_channel_args_destroy_delegate grpcsharp_channel_args_destroy; - - public readonly Delegates.grpcsharp_override_default_ssl_roots grpcsharp_override_default_ssl_roots; - public readonly Delegates.grpcsharp_ssl_credentials_create_delegate grpcsharp_ssl_credentials_create; - public readonly Delegates.grpcsharp_composite_channel_credentials_create_delegate grpcsharp_composite_channel_credentials_create; - public readonly Delegates.grpcsharp_channel_credentials_release_delegate grpcsharp_channel_credentials_release; - - public readonly Delegates.grpcsharp_insecure_channel_create_delegate grpcsharp_insecure_channel_create; - public readonly Delegates.grpcsharp_secure_channel_create_delegate grpcsharp_secure_channel_create; - public readonly Delegates.grpcsharp_channel_create_call_delegate grpcsharp_channel_create_call; - public readonly Delegates.grpcsharp_channel_check_connectivity_state_delegate grpcsharp_channel_check_connectivity_state; - public readonly Delegates.grpcsharp_channel_watch_connectivity_state_delegate grpcsharp_channel_watch_connectivity_state; - public readonly Delegates.grpcsharp_channel_get_target_delegate grpcsharp_channel_get_target; - public readonly Delegates.grpcsharp_channel_destroy_delegate grpcsharp_channel_destroy; - - public readonly Delegates.grpcsharp_sizeof_grpc_event_delegate grpcsharp_sizeof_grpc_event; - - public readonly Delegates.grpcsharp_completion_queue_create_async_delegate grpcsharp_completion_queue_create_async; - public readonly Delegates.grpcsharp_completion_queue_create_sync_delegate grpcsharp_completion_queue_create_sync; - public readonly Delegates.grpcsharp_completion_queue_shutdown_delegate grpcsharp_completion_queue_shutdown; - public readonly Delegates.grpcsharp_completion_queue_next_delegate grpcsharp_completion_queue_next; - public readonly Delegates.grpcsharp_completion_queue_pluck_delegate grpcsharp_completion_queue_pluck; - public readonly Delegates.grpcsharp_completion_queue_destroy_delegate grpcsharp_completion_queue_destroy; - - public readonly Delegates.gprsharp_free_delegate gprsharp_free; - - public readonly Delegates.grpcsharp_metadata_array_create_delegate grpcsharp_metadata_array_create; - public readonly Delegates.grpcsharp_metadata_array_add_delegate grpcsharp_metadata_array_add; - public readonly Delegates.grpcsharp_metadata_array_count_delegate grpcsharp_metadata_array_count; - public readonly Delegates.grpcsharp_metadata_array_get_key_delegate grpcsharp_metadata_array_get_key; - public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value; - public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full; - - public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log; - - public readonly Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate grpcsharp_metadata_credentials_create_from_plugin; - public readonly Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate grpcsharp_metadata_credentials_notify_from_plugin; - - public readonly Delegates.grpcsharp_ssl_server_credentials_create_delegate grpcsharp_ssl_server_credentials_create; - public readonly Delegates.grpcsharp_server_credentials_release_delegate grpcsharp_server_credentials_release; - - public readonly Delegates.grpcsharp_server_create_delegate grpcsharp_server_create; - public readonly Delegates.grpcsharp_server_register_completion_queue_delegate grpcsharp_server_register_completion_queue; - public readonly Delegates.grpcsharp_server_add_insecure_http2_port_delegate grpcsharp_server_add_insecure_http2_port; - public readonly Delegates.grpcsharp_server_add_secure_http2_port_delegate grpcsharp_server_add_secure_http2_port; - public readonly Delegates.grpcsharp_server_start_delegate grpcsharp_server_start; - public readonly Delegates.grpcsharp_server_request_call_delegate grpcsharp_server_request_call; - public readonly Delegates.grpcsharp_server_cancel_all_calls_delegate grpcsharp_server_cancel_all_calls; - public readonly Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate grpcsharp_server_shutdown_and_notify_callback; - public readonly Delegates.grpcsharp_server_destroy_delegate grpcsharp_server_destroy; - - public readonly Delegates.grpcsharp_call_auth_context_delegate grpcsharp_call_auth_context; - public readonly Delegates.grpcsharp_auth_context_peer_identity_property_name_delegate grpcsharp_auth_context_peer_identity_property_name; - public readonly Delegates.grpcsharp_auth_context_property_iterator_delegate grpcsharp_auth_context_property_iterator; - public readonly Delegates.grpcsharp_auth_property_iterator_next_delegate grpcsharp_auth_property_iterator_next; - public readonly Delegates.grpcsharp_auth_context_release_delegate grpcsharp_auth_context_release; - - public readonly Delegates.gprsharp_now_delegate gprsharp_now; - public readonly Delegates.gprsharp_inf_future_delegate gprsharp_inf_future; - public readonly Delegates.gprsharp_inf_past_delegate gprsharp_inf_past; - public readonly Delegates.gprsharp_convert_clock_type_delegate gprsharp_convert_clock_type; - public readonly Delegates.gprsharp_sizeof_timespec_delegate gprsharp_sizeof_timespec; - - public readonly Delegates.grpcsharp_test_callback_delegate grpcsharp_test_callback; - public readonly Delegates.grpcsharp_test_nop_delegate grpcsharp_test_nop; - - public readonly Delegates.grpcsharp_test_override_method_delegate grpcsharp_test_override_method; - - #endregion - - public NativeMethods(UnmanagedLibrary library) - { - this.grpcsharp_init = GetMethodDelegate<Delegates.grpcsharp_init_delegate>(library); - this.grpcsharp_shutdown = GetMethodDelegate<Delegates.grpcsharp_shutdown_delegate>(library); - this.grpcsharp_version_string = GetMethodDelegate<Delegates.grpcsharp_version_string_delegate>(library); - - this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library); - this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library); - this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library); - this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library); - this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library); - this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library); - this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library); - this.grpcsharp_batch_context_recv_close_on_server_cancelled = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate>(library); - this.grpcsharp_batch_context_reset = GetMethodDelegate<Delegates.grpcsharp_batch_context_reset_delegate>(library); - this.grpcsharp_batch_context_destroy = GetMethodDelegate<Delegates.grpcsharp_batch_context_destroy_delegate>(library); - - this.grpcsharp_request_call_context_create = GetMethodDelegate<Delegates.grpcsharp_request_call_context_create_delegate>(library); - this.grpcsharp_request_call_context_call = GetMethodDelegate<Delegates.grpcsharp_request_call_context_call_delegate>(library); - this.grpcsharp_request_call_context_method = GetMethodDelegate<Delegates.grpcsharp_request_call_context_method_delegate>(library); - this.grpcsharp_request_call_context_host = GetMethodDelegate<Delegates.grpcsharp_request_call_context_host_delegate>(library); - this.grpcsharp_request_call_context_deadline = GetMethodDelegate<Delegates.grpcsharp_request_call_context_deadline_delegate>(library); - this.grpcsharp_request_call_context_request_metadata = GetMethodDelegate<Delegates.grpcsharp_request_call_context_request_metadata_delegate>(library); - this.grpcsharp_request_call_context_reset = GetMethodDelegate<Delegates.grpcsharp_request_call_context_reset_delegate>(library); - this.grpcsharp_request_call_context_destroy = GetMethodDelegate<Delegates.grpcsharp_request_call_context_destroy_delegate>(library); - - this.grpcsharp_composite_call_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_call_credentials_create_delegate>(library); - this.grpcsharp_call_credentials_release = GetMethodDelegate<Delegates.grpcsharp_call_credentials_release_delegate>(library); - - this.grpcsharp_call_cancel = GetMethodDelegate<Delegates.grpcsharp_call_cancel_delegate>(library); - this.grpcsharp_call_cancel_with_status = GetMethodDelegate<Delegates.grpcsharp_call_cancel_with_status_delegate>(library); - this.grpcsharp_call_start_unary = GetMethodDelegate<Delegates.grpcsharp_call_start_unary_delegate>(library); - this.grpcsharp_call_start_client_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_client_streaming_delegate>(library); - this.grpcsharp_call_start_server_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_server_streaming_delegate>(library); - this.grpcsharp_call_start_duplex_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_duplex_streaming_delegate>(library); - this.grpcsharp_call_send_message = GetMethodDelegate<Delegates.grpcsharp_call_send_message_delegate>(library); - this.grpcsharp_call_send_close_from_client = GetMethodDelegate<Delegates.grpcsharp_call_send_close_from_client_delegate>(library); - this.grpcsharp_call_send_status_from_server = GetMethodDelegate<Delegates.grpcsharp_call_send_status_from_server_delegate>(library); - this.grpcsharp_call_recv_message = GetMethodDelegate<Delegates.grpcsharp_call_recv_message_delegate>(library); - this.grpcsharp_call_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_recv_initial_metadata_delegate>(library); - this.grpcsharp_call_start_serverside = GetMethodDelegate<Delegates.grpcsharp_call_start_serverside_delegate>(library); - this.grpcsharp_call_send_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_send_initial_metadata_delegate>(library); - this.grpcsharp_call_set_credentials = GetMethodDelegate<Delegates.grpcsharp_call_set_credentials_delegate>(library); - this.grpcsharp_call_get_peer = GetMethodDelegate<Delegates.grpcsharp_call_get_peer_delegate>(library); - this.grpcsharp_call_destroy = GetMethodDelegate<Delegates.grpcsharp_call_destroy_delegate>(library); - - this.grpcsharp_channel_args_create = GetMethodDelegate<Delegates.grpcsharp_channel_args_create_delegate>(library); - this.grpcsharp_channel_args_set_string = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_string_delegate>(library); - this.grpcsharp_channel_args_set_integer = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_integer_delegate>(library); - this.grpcsharp_channel_args_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_args_destroy_delegate>(library); - - this.grpcsharp_override_default_ssl_roots = GetMethodDelegate<Delegates.grpcsharp_override_default_ssl_roots>(library); - this.grpcsharp_ssl_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_credentials_create_delegate>(library); - this.grpcsharp_composite_channel_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_channel_credentials_create_delegate>(library); - this.grpcsharp_channel_credentials_release = GetMethodDelegate<Delegates.grpcsharp_channel_credentials_release_delegate>(library); - - this.grpcsharp_insecure_channel_create = GetMethodDelegate<Delegates.grpcsharp_insecure_channel_create_delegate>(library); - this.grpcsharp_secure_channel_create = GetMethodDelegate<Delegates.grpcsharp_secure_channel_create_delegate>(library); - this.grpcsharp_channel_create_call = GetMethodDelegate<Delegates.grpcsharp_channel_create_call_delegate>(library); - this.grpcsharp_channel_check_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_check_connectivity_state_delegate>(library); - this.grpcsharp_channel_watch_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_watch_connectivity_state_delegate>(library); - this.grpcsharp_channel_get_target = GetMethodDelegate<Delegates.grpcsharp_channel_get_target_delegate>(library); - this.grpcsharp_channel_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_destroy_delegate>(library); - - this.grpcsharp_sizeof_grpc_event = GetMethodDelegate<Delegates.grpcsharp_sizeof_grpc_event_delegate>(library); - - this.grpcsharp_completion_queue_create_async = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_async_delegate>(library); - this.grpcsharp_completion_queue_create_sync = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_sync_delegate>(library); - this.grpcsharp_completion_queue_shutdown = GetMethodDelegate<Delegates.grpcsharp_completion_queue_shutdown_delegate>(library); - this.grpcsharp_completion_queue_next = GetMethodDelegate<Delegates.grpcsharp_completion_queue_next_delegate>(library); - this.grpcsharp_completion_queue_pluck = GetMethodDelegate<Delegates.grpcsharp_completion_queue_pluck_delegate>(library); - this.grpcsharp_completion_queue_destroy = GetMethodDelegate<Delegates.grpcsharp_completion_queue_destroy_delegate>(library); - - this.gprsharp_free = GetMethodDelegate<Delegates.gprsharp_free_delegate>(library); - - this.grpcsharp_metadata_array_create = GetMethodDelegate<Delegates.grpcsharp_metadata_array_create_delegate>(library); - this.grpcsharp_metadata_array_add = GetMethodDelegate<Delegates.grpcsharp_metadata_array_add_delegate>(library); - this.grpcsharp_metadata_array_count = GetMethodDelegate<Delegates.grpcsharp_metadata_array_count_delegate>(library); - this.grpcsharp_metadata_array_get_key = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_key_delegate>(library); - this.grpcsharp_metadata_array_get_value = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_delegate>(library); - this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate<Delegates.grpcsharp_metadata_array_destroy_full_delegate>(library); - - this.grpcsharp_redirect_log = GetMethodDelegate<Delegates.grpcsharp_redirect_log_delegate>(library); - - this.grpcsharp_metadata_credentials_create_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate>(library); - this.grpcsharp_metadata_credentials_notify_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate>(library); - - this.grpcsharp_ssl_server_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_server_credentials_create_delegate>(library); - this.grpcsharp_server_credentials_release = GetMethodDelegate<Delegates.grpcsharp_server_credentials_release_delegate>(library); - - this.grpcsharp_server_create = GetMethodDelegate<Delegates.grpcsharp_server_create_delegate>(library); - this.grpcsharp_server_register_completion_queue = GetMethodDelegate<Delegates.grpcsharp_server_register_completion_queue_delegate>(library); - this.grpcsharp_server_add_insecure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_insecure_http2_port_delegate>(library); - this.grpcsharp_server_add_secure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_secure_http2_port_delegate>(library); - this.grpcsharp_server_start = GetMethodDelegate<Delegates.grpcsharp_server_start_delegate>(library); - this.grpcsharp_server_request_call = GetMethodDelegate<Delegates.grpcsharp_server_request_call_delegate>(library); - this.grpcsharp_server_cancel_all_calls = GetMethodDelegate<Delegates.grpcsharp_server_cancel_all_calls_delegate>(library); - this.grpcsharp_server_shutdown_and_notify_callback = GetMethodDelegate<Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate>(library); - this.grpcsharp_server_destroy = GetMethodDelegate<Delegates.grpcsharp_server_destroy_delegate>(library); - - this.grpcsharp_call_auth_context = GetMethodDelegate<Delegates.grpcsharp_call_auth_context_delegate>(library); - this.grpcsharp_auth_context_peer_identity_property_name = GetMethodDelegate<Delegates.grpcsharp_auth_context_peer_identity_property_name_delegate>(library); - this.grpcsharp_auth_context_property_iterator = GetMethodDelegate<Delegates.grpcsharp_auth_context_property_iterator_delegate>(library); - this.grpcsharp_auth_property_iterator_next = GetMethodDelegate<Delegates.grpcsharp_auth_property_iterator_next_delegate>(library); - this.grpcsharp_auth_context_release = GetMethodDelegate<Delegates.grpcsharp_auth_context_release_delegate>(library); - - this.gprsharp_now = GetMethodDelegate<Delegates.gprsharp_now_delegate>(library); - this.gprsharp_inf_future = GetMethodDelegate<Delegates.gprsharp_inf_future_delegate>(library); - this.gprsharp_inf_past = GetMethodDelegate<Delegates.gprsharp_inf_past_delegate>(library); - this.gprsharp_convert_clock_type = GetMethodDelegate<Delegates.gprsharp_convert_clock_type_delegate>(library); - this.gprsharp_sizeof_timespec = GetMethodDelegate<Delegates.gprsharp_sizeof_timespec_delegate>(library); - - this.grpcsharp_test_callback = GetMethodDelegate<Delegates.grpcsharp_test_callback_delegate>(library); - this.grpcsharp_test_nop = GetMethodDelegate<Delegates.grpcsharp_test_nop_delegate>(library); - this.grpcsharp_test_override_method = GetMethodDelegate<Delegates.grpcsharp_test_override_method_delegate>(library); - } + // Signatures of native methods are generated from a template + // and can be found in NativeMethods.Generated.cs /// <summary> /// Gets singleton instance of this class. @@ -297,140 +64,5 @@ namespace Grpc.Core.Internal } return str.Substring(0, str.Length - toRemove.Length); } - - /// <summary> - /// Delegate types for all published native methods. Declared under inner class to prevent scope pollution. - /// </summary> - public class Delegates - { - public delegate void grpcsharp_init_delegate(); - public delegate void grpcsharp_shutdown_delegate(); - public delegate IntPtr grpcsharp_version_string_delegate(); // returns not-owned const char* - - public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate(); - public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx); - public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx); - public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen); - public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx); - public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx, out UIntPtr detailsLength); - public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx); - public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx); - public delegate void grpcsharp_batch_context_reset_delegate(BatchContextSafeHandle ctx); - public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx); - - public delegate RequestCallContextSafeHandle grpcsharp_request_call_context_create_delegate(); - public delegate CallSafeHandle grpcsharp_request_call_context_call_delegate(RequestCallContextSafeHandle ctx); - public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx, out UIntPtr methodLength); - public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx, out UIntPtr hostLength); - public delegate Timespec grpcsharp_request_call_context_deadline_delegate(RequestCallContextSafeHandle ctx); - public delegate IntPtr grpcsharp_request_call_context_request_metadata_delegate(RequestCallContextSafeHandle ctx); - public delegate void grpcsharp_request_call_context_reset_delegate(RequestCallContextSafeHandle ctx); - public delegate void grpcsharp_request_call_context_destroy_delegate(IntPtr ctx); - - public delegate CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create_delegate(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2); - public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials); - - public delegate CallError grpcsharp_call_cancel_delegate(CallSafeHandle call); - public delegate CallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description); - public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); - public delegate CallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); - public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, - MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); - public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags); - public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata); - public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, StatusCode statusCode, byte[] statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, - byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags); - public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx); - public delegate CallError grpcsharp_call_send_initial_metadata_delegate(CallSafeHandle call, - BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray); - public delegate CallError grpcsharp_call_set_credentials_delegate(CallSafeHandle call, CallCredentialsSafeHandle credentials); - public delegate CStringSafeHandle grpcsharp_call_get_peer_delegate(CallSafeHandle call); - public delegate void grpcsharp_call_destroy_delegate(IntPtr call); - - public delegate ChannelArgsSafeHandle grpcsharp_channel_args_create_delegate(UIntPtr numArgs); - public delegate void grpcsharp_channel_args_set_string_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, string value); - public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value); - public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args); - - public delegate void grpcsharp_override_default_ssl_roots(string pemRootCerts); - public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey); - public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds); - public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials); - - public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs); - public delegate ChannelSafeHandle grpcsharp_secure_channel_create_delegate(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs); - public delegate CallSafeHandle grpcsharp_channel_create_call_delegate(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline); - public delegate ChannelState grpcsharp_channel_check_connectivity_state_delegate(ChannelSafeHandle channel, int tryToConnect); - public delegate void grpcsharp_channel_watch_connectivity_state_delegate(ChannelSafeHandle channel, ChannelState lastObservedState, - Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); - public delegate CStringSafeHandle grpcsharp_channel_get_target_delegate(ChannelSafeHandle call); - public delegate void grpcsharp_channel_destroy_delegate(IntPtr channel); - - public delegate int grpcsharp_sizeof_grpc_event_delegate(); - - public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_async_delegate(); - public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_sync_delegate(); - public delegate void grpcsharp_completion_queue_shutdown_delegate(CompletionQueueSafeHandle cq); - public delegate CompletionQueueEvent grpcsharp_completion_queue_next_delegate(CompletionQueueSafeHandle cq); - public delegate CompletionQueueEvent grpcsharp_completion_queue_pluck_delegate(CompletionQueueSafeHandle cq, IntPtr tag); - public delegate void grpcsharp_completion_queue_destroy_delegate(IntPtr cq); - - public delegate void gprsharp_free_delegate(IntPtr ptr); - - public delegate MetadataArraySafeHandle grpcsharp_metadata_array_create_delegate(UIntPtr capacity); - public delegate void grpcsharp_metadata_array_add_delegate(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength); - public delegate UIntPtr grpcsharp_metadata_array_count_delegate(IntPtr metadataArray); - public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr keyLength); - public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index, out UIntPtr valueLength); - public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array); - - public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback); - - public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor); - public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails); - - public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth); - public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials); - - public delegate ServerSafeHandle grpcsharp_server_create_delegate(ChannelArgsSafeHandle args); - public delegate void grpcsharp_server_register_completion_queue_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq); - public delegate int grpcsharp_server_add_insecure_http2_port_delegate(ServerSafeHandle server, string addr); - public delegate int grpcsharp_server_add_secure_http2_port_delegate(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); - public delegate void grpcsharp_server_start_delegate(ServerSafeHandle server); - public delegate CallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx); - public delegate void grpcsharp_server_cancel_all_calls_delegate(ServerSafeHandle server); - public delegate void grpcsharp_server_shutdown_and_notify_callback_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); - public delegate void grpcsharp_server_destroy_delegate(IntPtr server); - - public delegate AuthContextSafeHandle grpcsharp_call_auth_context_delegate(CallSafeHandle call); - public delegate IntPtr grpcsharp_auth_context_peer_identity_property_name_delegate(AuthContextSafeHandle authContext); // returns const char* - public delegate AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator_delegate(AuthContextSafeHandle authContext); - public delegate IntPtr grpcsharp_auth_property_iterator_next_delegate(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator); // returns const auth_property* - public delegate void grpcsharp_auth_context_release_delegate(IntPtr authContext); - - public delegate Timespec gprsharp_now_delegate(ClockType clockType); - public delegate Timespec gprsharp_inf_future_delegate(ClockType clockType); - public delegate Timespec gprsharp_inf_past_delegate(ClockType clockType); - - public delegate Timespec gprsharp_convert_clock_type_delegate(Timespec t, ClockType targetClock); - public delegate int gprsharp_sizeof_timespec_delegate(); - - public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback); - public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr); - public delegate void grpcsharp_test_override_method_delegate(string methodName, string variant); - } } } diff --git a/src/csharp/Grpc.Core/Internal/PlatformApis.cs b/src/csharp/Grpc.Core/Internal/PlatformApis.cs index 6bb4242479..b90fbccb2b 100644 --- a/src/csharp/Grpc.Core/Internal/PlatformApis.cs +++ b/src/csharp/Grpc.Core/Internal/PlatformApis.cs @@ -23,6 +23,7 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; +using Grpc.Core.Utils; namespace Grpc.Core.Internal { @@ -31,11 +32,13 @@ namespace Grpc.Core.Internal /// </summary> internal static class PlatformApis { + const string UnityEngineApplicationClassName = "UnityEngine.Application, UnityEngine"; static readonly bool isLinux; static readonly bool isMacOSX; static readonly bool isWindows; static readonly bool isMono; static readonly bool isNetCore; + static readonly bool isUnity; static PlatformApis() { @@ -54,6 +57,7 @@ namespace Grpc.Core.Internal isNetCore = false; #endif isMono = Type.GetType("Mono.Runtime") != null; + isUnity = Type.GetType(UnityEngineApplicationClassName) != null; } public static bool IsLinux @@ -77,6 +81,14 @@ namespace Grpc.Core.Internal } /// <summary> + /// true if running on Unity platform. + /// </summary> + public static bool IsUnity + { + get { return isUnity; } + } + + /// <summary> /// true if running on .NET Core (CoreCLR), false otherwise. /// </summary> public static bool IsNetCore @@ -89,6 +101,22 @@ namespace Grpc.Core.Internal get { return IntPtr.Size == 8; } } + /// <summary> + /// Returns <c>UnityEngine.Application.platform</c> as a string. + /// See https://docs.unity3d.com/ScriptReference/Application-platform.html for possible values. + /// Value is obtained via reflection to avoid compile-time dependency on Unity. + /// This method should only be called if <c>IsUnity</c> is <c>true</c>. + /// </summary> + public static string GetUnityRuntimePlatform() + { + GrpcPreconditions.CheckState(IsUnity, "Not running on Unity."); +#if NETSTANDARD1_5 + return Type.GetType(UnityEngineApplicationClassName).GetTypeInfo().GetProperty("platform").GetValue(null).ToString(); +#else + return Type.GetType(UnityEngineApplicationClassName).GetProperty("platform").GetValue(null).ToString(); +#endif + } + [DllImport("libc")] static extern int uname(IntPtr buf); diff --git a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs index aa5f81e3a8..7185d68efe 100644 --- a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs +++ b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs @@ -64,7 +64,7 @@ namespace Grpc.Core.Internal /// </summary> /// <param name="symbolName"></param> /// <returns></returns> - public IntPtr LoadSymbol(string symbolName) + private IntPtr LoadSymbol(string symbolName) { if (PlatformApis.IsWindows) { diff --git a/src/csharp/Grpc.Examples/MathGrpc.cs b/src/csharp/Grpc.Examples/MathGrpc.cs index e29b1087e4..045708b947 100644 --- a/src/csharp/Grpc.Examples/MathGrpc.cs +++ b/src/csharp/Grpc.Examples/MathGrpc.cs @@ -20,9 +20,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Math { @@ -159,7 +156,7 @@ namespace Math { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Math.DivReply Div(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Div(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -183,7 +180,7 @@ namespace Math { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Math.DivReply> DivAsync(global::Math.DivArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return DivAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -208,7 +205,7 @@ namespace Math { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Math.DivArgs, global::Math.DivReply> DivMany(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return DivMany(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -234,7 +231,7 @@ namespace Math { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncServerStreamingCall<global::Math.Num> Fib(global::Math.FibArgs request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Fib(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -258,7 +255,7 @@ namespace Math { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncClientStreamingCall<global::Math.Num, global::Math.Num> Sum(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Sum(new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs index 24a7259979..1d80bcd59e 100644 --- a/src/csharp/Grpc.HealthCheck/HealthGrpc.cs +++ b/src/csharp/Grpc.HealthCheck/HealthGrpc.cs @@ -20,9 +20,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Grpc.Health.V1 { @@ -79,7 +76,7 @@ namespace Grpc.Health.V1 { { } - public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Health.V1.HealthCheckResponse Check(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Check(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -87,7 +84,7 @@ namespace Grpc.Health.V1 { { return CallInvoker.BlockingUnaryCall(__Method_Check, null, options, request); } - public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Health.V1.HealthCheckResponse> CheckAsync(global::Grpc.Health.V1.HealthCheckRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return CheckAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/Grpc.IntegrationTesting/Control.cs b/src/csharp/Grpc.IntegrationTesting/Control.cs index 8e5da7b9f2..8795728906 100644 --- a/src/csharp/Grpc.IntegrationTesting/Control.cs +++ b/src/csharp/Grpc.IntegrationTesting/Control.cs @@ -32,7 +32,7 @@ namespace Grpc.Testing { "U2VjdXJpdHlQYXJhbXMSEwoLdXNlX3Rlc3RfY2EYASABKAgSHAoUc2VydmVy", "X2hvc3Rfb3ZlcnJpZGUYAiABKAkSEQoJY3JlZF90eXBlGAMgASgJIk0KCkNo", "YW5uZWxBcmcSDAoEbmFtZRgBIAEoCRITCglzdHJfdmFsdWUYAiABKAlIABIT", - "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSLVBAoMQ2xpZW50Q29uZmln", + "CglpbnRfdmFsdWUYAyABKAVIAEIHCgV2YWx1ZSLvBAoMQ2xpZW50Q29uZmln", "EhYKDnNlcnZlcl90YXJnZXRzGAEgAygJEi0KC2NsaWVudF90eXBlGAIgASgO", "MhguZ3JwYy50ZXN0aW5nLkNsaWVudFR5cGUSNQoPc2VjdXJpdHlfcGFyYW1z", "GAMgASgLMhwuZ3JwYy50ZXN0aW5nLlNlY3VyaXR5UGFyYW1zEiQKHG91dHN0", @@ -45,59 +45,60 @@ namespace Grpc.Testing { "dG9ncmFtUGFyYW1zEhEKCWNvcmVfbGlzdBgNIAMoBRISCgpjb3JlX2xpbWl0", "GA4gASgFEhgKEG90aGVyX2NsaWVudF9hcGkYDyABKAkSLgoMY2hhbm5lbF9h", "cmdzGBAgAygLMhguZ3JwYy50ZXN0aW5nLkNoYW5uZWxBcmcSFgoOdGhyZWFk", - "c19wZXJfY3EYESABKAUSGwoTbWVzc2FnZXNfcGVyX3N0cmVhbRgSIAEoBSI4", - "CgxDbGllbnRTdGF0dXMSKAoFc3RhdHMYASABKAsyGS5ncnBjLnRlc3Rpbmcu", - "Q2xpZW50U3RhdHMiFQoETWFyaxINCgVyZXNldBgBIAEoCCJoCgpDbGllbnRB", - "cmdzEisKBXNldHVwGAEgASgLMhouZ3JwYy50ZXN0aW5nLkNsaWVudENvbmZp", - "Z0gAEiIKBG1hcmsYAiABKAsyEi5ncnBjLnRlc3RpbmcuTWFya0gAQgkKB2Fy", - "Z3R5cGUi/QIKDFNlcnZlckNvbmZpZxItCgtzZXJ2ZXJfdHlwZRgBIAEoDjIY", - "LmdycGMudGVzdGluZy5TZXJ2ZXJUeXBlEjUKD3NlY3VyaXR5X3BhcmFtcxgC", - "IAEoCzIcLmdycGMudGVzdGluZy5TZWN1cml0eVBhcmFtcxIMCgRwb3J0GAQg", - "ASgFEhwKFGFzeW5jX3NlcnZlcl90aHJlYWRzGAcgASgFEhIKCmNvcmVfbGlt", - "aXQYCCABKAUSMwoOcGF5bG9hZF9jb25maWcYCSABKAsyGy5ncnBjLnRlc3Rp", - "bmcuUGF5bG9hZENvbmZpZxIRCgljb3JlX2xpc3QYCiADKAUSGAoQb3RoZXJf", - "c2VydmVyX2FwaRgLIAEoCRIWCg50aHJlYWRzX3Blcl9jcRgMIAEoBRIcChNy", - "ZXNvdXJjZV9xdW90YV9zaXplGOkHIAEoBRIvCgxjaGFubmVsX2FyZ3MY6gcg", - "AygLMhguZ3JwYy50ZXN0aW5nLkNoYW5uZWxBcmciaAoKU2VydmVyQXJncxIr", - "CgVzZXR1cBgBIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25maWdIABIi", - "CgRtYXJrGAIgASgLMhIuZ3JwYy50ZXN0aW5nLk1hcmtIAEIJCgdhcmd0eXBl", - "IlUKDFNlcnZlclN0YXR1cxIoCgVzdGF0cxgBIAEoCzIZLmdycGMudGVzdGlu", - "Zy5TZXJ2ZXJTdGF0cxIMCgRwb3J0GAIgASgFEg0KBWNvcmVzGAMgASgFIg0K", - "C0NvcmVSZXF1ZXN0Ih0KDENvcmVSZXNwb25zZRINCgVjb3JlcxgBIAEoBSIG", - "CgRWb2lkIv0BCghTY2VuYXJpbxIMCgRuYW1lGAEgASgJEjEKDWNsaWVudF9j", - "b25maWcYAiABKAsyGi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnEhMKC251", - "bV9jbGllbnRzGAMgASgFEjEKDXNlcnZlcl9jb25maWcYBCABKAsyGi5ncnBj", - "LnRlc3RpbmcuU2VydmVyQ29uZmlnEhMKC251bV9zZXJ2ZXJzGAUgASgFEhYK", - "Dndhcm11cF9zZWNvbmRzGAYgASgFEhkKEWJlbmNobWFya19zZWNvbmRzGAcg", - "ASgFEiAKGHNwYXduX2xvY2FsX3dvcmtlcl9jb3VudBgIIAEoBSI2CglTY2Vu", - "YXJpb3MSKQoJc2NlbmFyaW9zGAEgAygLMhYuZ3JwYy50ZXN0aW5nLlNjZW5h", - "cmlvIoQEChVTY2VuYXJpb1Jlc3VsdFN1bW1hcnkSCwoDcXBzGAEgASgBEhsK", - "E3Fwc19wZXJfc2VydmVyX2NvcmUYAiABKAESGgoSc2VydmVyX3N5c3RlbV90", - "aW1lGAMgASgBEhgKEHNlcnZlcl91c2VyX3RpbWUYBCABKAESGgoSY2xpZW50", - "X3N5c3RlbV90aW1lGAUgASgBEhgKEGNsaWVudF91c2VyX3RpbWUYBiABKAES", - "EgoKbGF0ZW5jeV81MBgHIAEoARISCgpsYXRlbmN5XzkwGAggASgBEhIKCmxh", - "dGVuY3lfOTUYCSABKAESEgoKbGF0ZW5jeV85ORgKIAEoARITCgtsYXRlbmN5", - "Xzk5ORgLIAEoARIYChBzZXJ2ZXJfY3B1X3VzYWdlGAwgASgBEiYKHnN1Y2Nl", - "c3NmdWxfcmVxdWVzdHNfcGVyX3NlY29uZBgNIAEoARIiChpmYWlsZWRfcmVx", - "dWVzdHNfcGVyX3NlY29uZBgOIAEoARIgChhjbGllbnRfcG9sbHNfcGVyX3Jl", - "cXVlc3QYDyABKAESIAoYc2VydmVyX3BvbGxzX3Blcl9yZXF1ZXN0GBAgASgB", - "EiIKGnNlcnZlcl9xdWVyaWVzX3Blcl9jcHVfc2VjGBEgASgBEiIKGmNsaWVu", - "dF9xdWVyaWVzX3Blcl9jcHVfc2VjGBIgASgBIoMDCg5TY2VuYXJpb1Jlc3Vs", - "dBIoCghzY2VuYXJpbxgBIAEoCzIWLmdycGMudGVzdGluZy5TY2VuYXJpbxIu", - "CglsYXRlbmNpZXMYAiABKAsyGy5ncnBjLnRlc3RpbmcuSGlzdG9ncmFtRGF0", - "YRIvCgxjbGllbnRfc3RhdHMYAyADKAsyGS5ncnBjLnRlc3RpbmcuQ2xpZW50", - "U3RhdHMSLwoMc2VydmVyX3N0YXRzGAQgAygLMhkuZ3JwYy50ZXN0aW5nLlNl", - "cnZlclN0YXRzEhQKDHNlcnZlcl9jb3JlcxgFIAMoBRI0CgdzdW1tYXJ5GAYg", - "ASgLMiMuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvUmVzdWx0U3VtbWFyeRIWCg5j", - "bGllbnRfc3VjY2VzcxgHIAMoCBIWCg5zZXJ2ZXJfc3VjY2VzcxgIIAMoCBI5", - "Cg9yZXF1ZXN0X3Jlc3VsdHMYCSADKAsyIC5ncnBjLnRlc3RpbmcuUmVxdWVz", - "dFJlc3VsdENvdW50KkEKCkNsaWVudFR5cGUSDwoLU1lOQ19DTElFTlQQABIQ", - "CgxBU1lOQ19DTElFTlQQARIQCgxPVEhFUl9DTElFTlQQAipbCgpTZXJ2ZXJU", - "eXBlEg8KC1NZTkNfU0VSVkVSEAASEAoMQVNZTkNfU0VSVkVSEAESGAoUQVNZ", - "TkNfR0VORVJJQ19TRVJWRVIQAhIQCgxPVEhFUl9TRVJWRVIQAypyCgdScGNU", - "eXBlEgkKBVVOQVJZEAASDQoJU1RSRUFNSU5HEAESGQoVU1RSRUFNSU5HX0ZS", - "T01fQ0xJRU5UEAISGQoVU1RSRUFNSU5HX0ZST01fU0VSVkVSEAMSFwoTU1RS", - "RUFNSU5HX0JPVEhfV0FZUxAEYgZwcm90bzM=")); + "c19wZXJfY3EYESABKAUSGwoTbWVzc2FnZXNfcGVyX3N0cmVhbRgSIAEoBRIY", + "ChB1c2VfY29hbGVzY2VfYXBpGBMgASgIIjgKDENsaWVudFN0YXR1cxIoCgVz", + "dGF0cxgBIAEoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cyIVCgRNYXJr", + "Eg0KBXJlc2V0GAEgASgIImgKCkNsaWVudEFyZ3MSKwoFc2V0dXAYASABKAsy", + "Gi5ncnBjLnRlc3RpbmcuQ2xpZW50Q29uZmlnSAASIgoEbWFyaxgCIAEoCzIS", + "LmdycGMudGVzdGluZy5NYXJrSABCCQoHYXJndHlwZSL9AgoMU2VydmVyQ29u", + "ZmlnEi0KC3NlcnZlcl90eXBlGAEgASgOMhguZ3JwYy50ZXN0aW5nLlNlcnZl", + "clR5cGUSNQoPc2VjdXJpdHlfcGFyYW1zGAIgASgLMhwuZ3JwYy50ZXN0aW5n", + "LlNlY3VyaXR5UGFyYW1zEgwKBHBvcnQYBCABKAUSHAoUYXN5bmNfc2VydmVy", + "X3RocmVhZHMYByABKAUSEgoKY29yZV9saW1pdBgIIAEoBRIzCg5wYXlsb2Fk", + "X2NvbmZpZxgJIAEoCzIbLmdycGMudGVzdGluZy5QYXlsb2FkQ29uZmlnEhEK", + "CWNvcmVfbGlzdBgKIAMoBRIYChBvdGhlcl9zZXJ2ZXJfYXBpGAsgASgJEhYK", + "DnRocmVhZHNfcGVyX2NxGAwgASgFEhwKE3Jlc291cmNlX3F1b3RhX3NpemUY", + "6QcgASgFEi8KDGNoYW5uZWxfYXJncxjqByADKAsyGC5ncnBjLnRlc3Rpbmcu", + "Q2hhbm5lbEFyZyJoCgpTZXJ2ZXJBcmdzEisKBXNldHVwGAEgASgLMhouZ3Jw", + "Yy50ZXN0aW5nLlNlcnZlckNvbmZpZ0gAEiIKBG1hcmsYAiABKAsyEi5ncnBj", + "LnRlc3RpbmcuTWFya0gAQgkKB2FyZ3R5cGUiVQoMU2VydmVyU3RhdHVzEigK", + "BXN0YXRzGAEgASgLMhkuZ3JwYy50ZXN0aW5nLlNlcnZlclN0YXRzEgwKBHBv", + "cnQYAiABKAUSDQoFY29yZXMYAyABKAUiDQoLQ29yZVJlcXVlc3QiHQoMQ29y", + "ZVJlc3BvbnNlEg0KBWNvcmVzGAEgASgFIgYKBFZvaWQi/QEKCFNjZW5hcmlv", + "EgwKBG5hbWUYASABKAkSMQoNY2xpZW50X2NvbmZpZxgCIAEoCzIaLmdycGMu", + "dGVzdGluZy5DbGllbnRDb25maWcSEwoLbnVtX2NsaWVudHMYAyABKAUSMQoN", + "c2VydmVyX2NvbmZpZxgEIAEoCzIaLmdycGMudGVzdGluZy5TZXJ2ZXJDb25m", + "aWcSEwoLbnVtX3NlcnZlcnMYBSABKAUSFgoOd2FybXVwX3NlY29uZHMYBiAB", + "KAUSGQoRYmVuY2htYXJrX3NlY29uZHMYByABKAUSIAoYc3Bhd25fbG9jYWxf", + "d29ya2VyX2NvdW50GAggASgFIjYKCVNjZW5hcmlvcxIpCglzY2VuYXJpb3MY", + "ASADKAsyFi5ncnBjLnRlc3RpbmcuU2NlbmFyaW8ihAQKFVNjZW5hcmlvUmVz", + "dWx0U3VtbWFyeRILCgNxcHMYASABKAESGwoTcXBzX3Blcl9zZXJ2ZXJfY29y", + "ZRgCIAEoARIaChJzZXJ2ZXJfc3lzdGVtX3RpbWUYAyABKAESGAoQc2VydmVy", + "X3VzZXJfdGltZRgEIAEoARIaChJjbGllbnRfc3lzdGVtX3RpbWUYBSABKAES", + "GAoQY2xpZW50X3VzZXJfdGltZRgGIAEoARISCgpsYXRlbmN5XzUwGAcgASgB", + "EhIKCmxhdGVuY3lfOTAYCCABKAESEgoKbGF0ZW5jeV85NRgJIAEoARISCgps", + "YXRlbmN5Xzk5GAogASgBEhMKC2xhdGVuY3lfOTk5GAsgASgBEhgKEHNlcnZl", + "cl9jcHVfdXNhZ2UYDCABKAESJgoec3VjY2Vzc2Z1bF9yZXF1ZXN0c19wZXJf", + "c2Vjb25kGA0gASgBEiIKGmZhaWxlZF9yZXF1ZXN0c19wZXJfc2Vjb25kGA4g", + "ASgBEiAKGGNsaWVudF9wb2xsc19wZXJfcmVxdWVzdBgPIAEoARIgChhzZXJ2", + "ZXJfcG9sbHNfcGVyX3JlcXVlc3QYECABKAESIgoac2VydmVyX3F1ZXJpZXNf", + "cGVyX2NwdV9zZWMYESABKAESIgoaY2xpZW50X3F1ZXJpZXNfcGVyX2NwdV9z", + "ZWMYEiABKAEigwMKDlNjZW5hcmlvUmVzdWx0EigKCHNjZW5hcmlvGAEgASgL", + "MhYuZ3JwYy50ZXN0aW5nLlNjZW5hcmlvEi4KCWxhdGVuY2llcxgCIAEoCzIb", + "LmdycGMudGVzdGluZy5IaXN0b2dyYW1EYXRhEi8KDGNsaWVudF9zdGF0cxgD", + "IAMoCzIZLmdycGMudGVzdGluZy5DbGllbnRTdGF0cxIvCgxzZXJ2ZXJfc3Rh", + "dHMYBCADKAsyGS5ncnBjLnRlc3RpbmcuU2VydmVyU3RhdHMSFAoMc2VydmVy", + "X2NvcmVzGAUgAygFEjQKB3N1bW1hcnkYBiABKAsyIy5ncnBjLnRlc3Rpbmcu", + "U2NlbmFyaW9SZXN1bHRTdW1tYXJ5EhYKDmNsaWVudF9zdWNjZXNzGAcgAygI", + "EhYKDnNlcnZlcl9zdWNjZXNzGAggAygIEjkKD3JlcXVlc3RfcmVzdWx0cxgJ", + "IAMoCzIgLmdycGMudGVzdGluZy5SZXF1ZXN0UmVzdWx0Q291bnQqQQoKQ2xp", + "ZW50VHlwZRIPCgtTWU5DX0NMSUVOVBAAEhAKDEFTWU5DX0NMSUVOVBABEhAK", + "DE9USEVSX0NMSUVOVBACKlsKClNlcnZlclR5cGUSDwoLU1lOQ19TRVJWRVIQ", + "ABIQCgxBU1lOQ19TRVJWRVIQARIYChRBU1lOQ19HRU5FUklDX1NFUlZFUhAC", + "EhAKDE9USEVSX1NFUlZFUhADKnIKB1JwY1R5cGUSCQoFVU5BUlkQABINCglT", + "VFJFQU1JTkcQARIZChVTVFJFQU1JTkdfRlJPTV9DTElFTlQQAhIZChVTVFJF", + "QU1JTkdfRlJPTV9TRVJWRVIQAxIXChNTVFJFQU1JTkdfQk9USF9XQVlTEARi", + "BnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Grpc.Testing.PayloadsReflection.Descriptor, global::Grpc.Testing.StatsReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Grpc.Testing.ClientType), typeof(global::Grpc.Testing.ServerType), typeof(global::Grpc.Testing.RpcType), }, new pbr::GeneratedClrTypeInfo[] { @@ -106,7 +107,7 @@ namespace Grpc.Testing { new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.LoadParams), global::Grpc.Testing.LoadParams.Parser, new[]{ "ClosedLoop", "Poisson" }, new[]{ "Load" }, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.SecurityParams), global::Grpc.Testing.SecurityParams.Parser, new[]{ "UseTestCa", "ServerHostOverride", "CredType" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ChannelArg), global::Grpc.Testing.ChannelArg.Parser, new[]{ "Name", "StrValue", "IntValue" }, new[]{ "Value" }, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream" }, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientConfig), global::Grpc.Testing.ClientConfig.Parser, new[]{ "ServerTargets", "ClientType", "SecurityParams", "OutstandingRpcsPerChannel", "ClientChannels", "AsyncClientThreads", "RpcType", "LoadParams", "PayloadConfig", "HistogramParams", "CoreList", "CoreLimit", "OtherClientApi", "ChannelArgs", "ThreadsPerCq", "MessagesPerStream", "UseCoalesceApi" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientStatus), global::Grpc.Testing.ClientStatus.Parser, new[]{ "Stats" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.Mark), global::Grpc.Testing.Mark.Parser, new[]{ "Reset" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::Grpc.Testing.ClientArgs), global::Grpc.Testing.ClientArgs.Parser, new[]{ "Setup", "Mark" }, new[]{ "Argtype" }, null, null), @@ -989,6 +990,7 @@ namespace Grpc.Testing { channelArgs_ = other.channelArgs_.Clone(); threadsPerCq_ = other.threadsPerCq_; messagesPerStream_ = other.messagesPerStream_; + useCoalesceApi_ = other.useCoalesceApi_; } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1198,6 +1200,20 @@ namespace Grpc.Testing { } } + /// <summary>Field number for the "use_coalesce_api" field.</summary> + public const int UseCoalesceApiFieldNumber = 19; + private bool useCoalesceApi_; + /// <summary> + /// Use coalescing API when possible. + /// </summary> + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool UseCoalesceApi { + get { return useCoalesceApi_; } + set { + useCoalesceApi_ = value; + } + } + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public override bool Equals(object other) { return Equals(other as ClientConfig); @@ -1227,6 +1243,7 @@ namespace Grpc.Testing { if(!channelArgs_.Equals(other.channelArgs_)) return false; if (ThreadsPerCq != other.ThreadsPerCq) return false; if (MessagesPerStream != other.MessagesPerStream) return false; + if (UseCoalesceApi != other.UseCoalesceApi) return false; return true; } @@ -1249,6 +1266,7 @@ namespace Grpc.Testing { hash ^= channelArgs_.GetHashCode(); if (ThreadsPerCq != 0) hash ^= ThreadsPerCq.GetHashCode(); if (MessagesPerStream != 0) hash ^= MessagesPerStream.GetHashCode(); + if (UseCoalesceApi != false) hash ^= UseCoalesceApi.GetHashCode(); return hash; } @@ -1314,6 +1332,10 @@ namespace Grpc.Testing { output.WriteRawTag(144, 1); output.WriteInt32(MessagesPerStream); } + if (UseCoalesceApi != false) { + output.WriteRawTag(152, 1); + output.WriteBool(UseCoalesceApi); + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1361,6 +1383,9 @@ namespace Grpc.Testing { if (MessagesPerStream != 0) { size += 2 + pb::CodedOutputStream.ComputeInt32Size(MessagesPerStream); } + if (UseCoalesceApi != false) { + size += 2 + 1; + } return size; } @@ -1423,6 +1448,9 @@ namespace Grpc.Testing { if (other.MessagesPerStream != 0) { MessagesPerStream = other.MessagesPerStream; } + if (other.UseCoalesceApi != false) { + UseCoalesceApi = other.UseCoalesceApi; + } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1510,6 +1538,10 @@ namespace Grpc.Testing { MessagesPerStream = input.ReadInt32(); break; } + case 152: { + UseCoalesceApi = input.ReadBool(); + break; + } } } } diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index c02c9844e3..ba2107a576 100755 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -19,7 +19,7 @@ <ItemGroup> <PackageReference Include="Google.Protobuf" Version="$(GoogleProtobufVersion)" /> <PackageReference Include="CommandLineParser" Version="2.1.1-beta" /> - <PackageReference Include="Moq" Version="4.7.0" /> + <PackageReference Include="Moq" Version="4.8.2" /> <PackageReference Include="NUnit" Version="3.6.0" /> <PackageReference Include="NUnitLite" Version="3.6.0" /> </ItemGroup> diff --git a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs index f71d6d197d..d18b9e7d5e 100644 --- a/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/MetricsGrpc.cs @@ -26,9 +26,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Grpc.Testing { @@ -121,7 +118,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.GaugeResponse> GetAllGauges(global::Grpc.Testing.EmptyMessage request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return GetAllGauges(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -144,7 +141,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.GaugeResponse GetGauge(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return GetGauge(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -166,7 +163,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.GaugeResponse> GetGaugeAsync(global::Grpc.Testing.GaugeRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return GetGaugeAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs index d2e4f2e4a5..46b328a773 100644 --- a/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/ServicesGrpc.cs @@ -22,9 +22,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Grpc.Testing { @@ -177,7 +174,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -201,7 +198,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -225,7 +222,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingCall(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -248,7 +245,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingFromClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingFromClient(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -271,7 +268,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.SimpleResponse> StreamingFromServer(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingFromServer(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -294,7 +291,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> StreamingBothWays(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingBothWays(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -470,7 +467,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ServerArgs, global::Grpc.Testing.ServerStatus> RunServer(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return RunServer(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -500,7 +497,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.ClientArgs, global::Grpc.Testing.ClientStatus> RunClient(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return RunClient(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -526,7 +523,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.CoreResponse CoreCount(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return CoreCount(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -548,7 +545,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.CoreResponse> CoreCountAsync(global::Grpc.Testing.CoreRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return CoreCountAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -570,7 +567,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Void QuitWorker(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return QuitWorker(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -592,7 +589,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> QuitWorkerAsync(global::Grpc.Testing.Void request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return QuitWorkerAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -692,7 +689,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Void ReportScenario(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return ReportScenario(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -714,7 +711,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Void> ReportScenarioAsync(global::Grpc.Testing.ScenarioResult request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return ReportScenarioAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index c0d147c150..6c4b77f7ac 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -23,9 +23,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Grpc.Testing { @@ -244,7 +241,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Empty EmptyCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return EmptyCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -266,7 +263,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> EmptyCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return EmptyCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -288,7 +285,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.SimpleResponse UnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -310,7 +307,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> UnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -334,7 +331,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return CacheableUnaryCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -360,7 +357,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return CacheableUnaryCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -385,7 +382,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncServerStreamingCall<global::Grpc.Testing.StreamingOutputCallResponse> StreamingOutputCall(global::Grpc.Testing.StreamingOutputCallRequest request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingOutputCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -408,7 +405,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncClientStreamingCall<global::Grpc.Testing.StreamingInputCallRequest, global::Grpc.Testing.StreamingInputCallResponse> StreamingInputCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StreamingInputCall(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -431,7 +428,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> FullDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return FullDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -456,7 +453,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> HalfDuplexCall(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return HalfDuplexCall(new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -481,7 +478,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -505,7 +502,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -613,7 +610,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The response received from the server.</returns> - public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnimplementedCall(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -635,7 +632,7 @@ namespace Grpc.Testing { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return UnimplementedCallAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -734,7 +731,7 @@ namespace Grpc.Testing { { } - public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.Empty Start(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Start(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -742,7 +739,7 @@ namespace Grpc.Testing { { return CallInvoker.BlockingUnaryCall(__Method_Start, null, options, request); } - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.Empty> StartAsync(global::Grpc.Testing.ReconnectParams request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StartAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -750,7 +747,7 @@ namespace Grpc.Testing { { return CallInvoker.AsyncUnaryCall(__Method_Start, null, options, request); } - public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual global::Grpc.Testing.ReconnectInfo Stop(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return Stop(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } @@ -758,7 +755,7 @@ namespace Grpc.Testing { { return CallInvoker.BlockingUnaryCall(__Method_Stop, null, options, request); } - public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncUnaryCall<global::Grpc.Testing.ReconnectInfo> StopAsync(global::Grpc.Testing.Empty request, grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return StopAsync(request, new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs index 0195186eba..e2263cfc90 100644 --- a/src/csharp/Grpc.Reflection/ReflectionGrpc.cs +++ b/src/csharp/Grpc.Reflection/ReflectionGrpc.cs @@ -22,9 +22,6 @@ #pragma warning disable 1591 #region Designer generated code -using System; -using System.Threading; -using System.Threading.Tasks; using grpc = global::Grpc.Core; namespace Grpc.Reflection.V1Alpha { @@ -97,7 +94,7 @@ namespace Grpc.Reflection.V1Alpha { /// <param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param> /// <param name="cancellationToken">An optional token for canceling the call.</param> /// <returns>The call object.</returns> - public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + public virtual grpc::AsyncDuplexStreamingCall<global::Grpc.Reflection.V1Alpha.ServerReflectionRequest, global::Grpc.Reflection.V1Alpha.ServerReflectionResponse> ServerReflectionInfo(grpc::Metadata headers = null, global::System.DateTime? deadline = null, global::System.Threading.CancellationToken cancellationToken = default(global::System.Threading.CancellationToken)) { return ServerReflectionInfo(new grpc::CallOptions(headers, deadline, cancellationToken)); } diff --git a/src/csharp/experimental/README.md b/src/csharp/experimental/README.md new file mode 100644 index 0000000000..f892a2ee95 --- /dev/null +++ b/src/csharp/experimental/README.md @@ -0,0 +1,16 @@ +This directory contains useful resources for getting gRPC C# to work on +not-yet-supported platforms. + +# Unity & Xamarin +gRPC C# currently doesn't support Unity or Xamarin, but some proof-of-concept +work has been done. Some of the resources are shared in this directory to +ease community work on Unity & Xamarin support. + +## Crosscompiling `grpc_csharp_ext` for Android + +* Install [Android NDK](https://developer.android.com/ndk/index.html) +* Run `./build_native_ext_for_android.sh` to crosscompile using cmake. + +## Crosscompiling `grpc_csharp_ext` for iOS + +TBD diff --git a/src/csharp/experimental/build_native_ext_for_android.sh b/src/csharp/experimental/build_native_ext_for_android.sh new file mode 100755 index 0000000000..958778e929 --- /dev/null +++ b/src/csharp/experimental/build_native_ext_for_android.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Copyright 2018 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Helper script to crosscompile grpc_csharp_ext native extension for Android. + +set -ex + +cd "$(dirname "$0")/../../../cmake" + +mkdir -p build +cd build + +# set to the location where Android SDK is installed +ANDROID_NDK_PATH="$HOME/android-ndk-r16b" + +cmake ../.. \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_SYSTEM_VERSION=15 \ + -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a \ + -DCMAKE_ANDROID_NDK="${ANDROID_NDK_PATH}" \ + -DCMAKE_ANDROID_STL_TYPE=c++_static \ + -DRUN_HAVE_POSIX_REGEX=0 \ + -DRUN_HAVE_STD_REGEX=0 \ + -DRUN_HAVE_STEADY_CLOCK=0 \ + -DCMAKE_BUILD_TYPE=Release + +make -j4 grpc_csharp_ext diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_3069_test_constant.py b/src/csharp/experimental/build_unitypackage.sh index 6eb7ba33f6..cca52658e8 100644..100755 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_3069_test_constant.py +++ b/src/csharp/experimental/build_unitypackage.sh @@ -1,4 +1,5 @@ -# Copyright 2015 gRPC authors. +#!/bin/sh +# Copyright 2018 gRPC authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,11 +12,21 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -"""A test constant working around issue 3069.""" -# test_constants is referenced from specification in this module. -from tests.unit.framework.common import test_constants # pylint: disable=unused-import +# Builds an experimental .unitypackage file to be imported into Unity projects. -# TODO(issue 3069): Replace uses of this constant with -# test_constants.SHORT_TIMEOUT. -REALLY_SHORT_TIMEOUT = 0.1 +set -ex + +cd "$(dirname "$0")/.." + +dotnet restore Grpc.sln + +mkdir -p GrpcUnity +dotnet build --configuration Release --framework net45 Grpc.Core --output ../GrpcUnity + +#TODO: add ThirdParty/Grpc.Core: +# - assembly +# - native libraries (mac dylib need to be renamed to grpc_csharp_ext.bundle) + +#TODO: add ThirdParty/Grpc.Tools: +# - protoc and grpc plugin diff --git a/src/csharp/global.json b/src/csharp/global.json index e4b797ee8e..815be4bfb9 100644 --- a/src/csharp/global.json +++ b/src/csharp/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "1.0.0" + "version": "2.1.4" } } diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index ac4596da25..ea658bca2b 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -108,6 +108,9 @@ static NSString * const kBearerPrefix = @"Bearer "; // The dispatch queue to be used for enqueuing responses to user. Defaulted to the main dispatch // queue dispatch_queue_t _responseQueue; + + // Whether the call is finished. If it is, should not call finishWithError again. + BOOL _finished; } @synthesize state = _state; @@ -206,6 +209,8 @@ static NSString * const kBearerPrefix = @"Bearer "; } else { [_responseWriteable enqueueSuccessfulCompletion]; } + + [GRPCConnectivityMonitor unregisterObserver:self]; } - (void)cancelCall { @@ -214,9 +219,10 @@ static NSString * const kBearerPrefix = @"Bearer "; } - (void)cancel { - [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeCancelled - userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]]; + [self maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeCancelled + userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]]; + if (!self.isWaitingForToken) { [self cancelCall]; } else { @@ -224,6 +230,19 @@ static NSString * const kBearerPrefix = @"Bearer "; } } +- (void)maybeFinishWithError:(NSError *)errorOrNil { + BOOL toFinish = NO; + @synchronized(self) { + if (_finished == NO) { + _finished = YES; + toFinish = YES; + } + } + if (toFinish == YES) { + [self finishWithError:errorOrNil]; + } +} + - (void)dealloc { __block GRPCWrappedCall *wrappedCall = _wrappedCall; dispatch_async(_callQueue, ^{ @@ -250,11 +269,13 @@ static NSString * const kBearerPrefix = @"Bearer "; if (self.state == GRXWriterStatePaused) { return; } - __weak GRPCCall *weakSelf = self; - __weak GRXConcurrentWriteable *weakWriteable = _responseWriteable; dispatch_async(_callQueue, ^{ - [weakSelf startReadWithHandler:^(grpc_byte_buffer *message) { + __weak GRPCCall *weakSelf = self; + __weak GRXConcurrentWriteable *weakWriteable = self->_responseWriteable; + [self startReadWithHandler:^(grpc_byte_buffer *message) { + __strong GRPCCall *strongSelf = weakSelf; + __strong GRXConcurrentWriteable *strongWriteable = weakWriteable; if (message == NULL) { // No more messages from the server return; @@ -266,14 +287,14 @@ static NSString * const kBearerPrefix = @"Bearer "; // don't want to throw, because the app shouldn't crash for a behavior // that's on the hands of any server to have. Instead we finish and ask // the server to cancel. - [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeResourceExhausted - userInfo:@{NSLocalizedDescriptionKey: @"Client does not have enough memory to hold the server response."}]]; - [weakSelf cancelCall]; + [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeResourceExhausted + userInfo:@{NSLocalizedDescriptionKey: @"Client does not have enough memory to hold the server response."}]]; + [strongSelf cancelCall]; return; } - [weakWriteable enqueueValue:data completionHandler:^{ - [weakSelf startNextRead]; + [strongWriteable enqueueValue:data completionHandler:^{ + [strongSelf startNextRead]; }]; }]; }); @@ -333,12 +354,17 @@ static NSString * const kBearerPrefix = @"Bearer "; _requestWriter.state = GRXWriterStatePaused; } - __weak GRPCCall *weakSelf = self; dispatch_async(_callQueue, ^{ - [weakSelf writeMessage:value withErrorHandler:^{ - [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeInternal - userInfo:nil]]; + __weak GRPCCall *weakSelf = self; + [self writeMessage:value withErrorHandler:^{ + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf != nil) { + [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeInternal + userInfo:nil]]; + // Wrapped call must be canceled when error is reported to upper layers + [strongSelf cancelCall]; + } }]; }); } @@ -360,12 +386,15 @@ static NSString * const kBearerPrefix = @"Bearer "; if (errorOrNil) { [self cancel]; } else { - __weak GRPCCall *weakSelf = self; dispatch_async(_callQueue, ^{ - [weakSelf finishRequestWithErrorHandler:^{ - [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeInternal - userInfo:nil]]; + __weak GRPCCall *weakSelf = self; + [self finishRequestWithErrorHandler:^{ + __strong GRPCCall *strongSelf = weakSelf; + [strongSelf maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeInternal + userInfo:nil]]; + // Wrapped call must be canceled when error is reported to upper layers + [strongSelf cancelCall]; }]; }); } @@ -387,30 +416,37 @@ static NSString * const kBearerPrefix = @"Bearer "; } - (void)invokeCall { + __weak GRPCCall *weakSelf = self; [self invokeCallWithHeadersHandler:^(NSDictionary *headers) { // Response headers received. - self.responseHeaders = headers; - [self startNextRead]; + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf.responseHeaders = headers; + [strongSelf startNextRead]; + } } completionHandler:^(NSError *error, NSDictionary *trailers) { - self.responseTrailers = trailers; + __strong GRPCCall *strongSelf = weakSelf; + if (strongSelf) { + strongSelf.responseTrailers = trailers; - if (error) { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - if (error.userInfo) { - [userInfo addEntriesFromDictionary:error.userInfo]; - } - userInfo[kGRPCTrailersKey] = self.responseTrailers; - // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be - // called before this one, so an error might end up with trailers but no headers. We - // shouldn't call finishWithError until ater both blocks are called. It is also when this is - // done that we can provide a merged view of response headers and trailers in a thread-safe - // way. - if (self.responseHeaders) { - userInfo[kGRPCHeadersKey] = self.responseHeaders; + if (error) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (error.userInfo) { + [userInfo addEntriesFromDictionary:error.userInfo]; + } + userInfo[kGRPCTrailersKey] = strongSelf.responseTrailers; + // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be + // called before this one, so an error might end up with trailers but no headers. We + // shouldn't call finishWithError until ater both blocks are called. It is also when this is + // done that we can provide a merged view of response headers and trailers in a thread-safe + // way. + if (strongSelf.responseHeaders) { + userInfo[kGRPCHeadersKey] = strongSelf.responseHeaders; + } + error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; } - error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; + [strongSelf maybeFinishWithError:error]; } - [self finishWithError:error]; }]; // Now that the RPC has been initiated, request writes can start. @synchronized(_requestWriter) { @@ -433,22 +469,8 @@ static NSString * const kBearerPrefix = @"Bearer "; [self sendHeaders:_requestHeaders]; [self invokeCall]; - // TODO(jcanizales): Extract this logic somewhere common. - NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host; - if (!host) { - // TODO(jcanizales): Check this on init. - [NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host]; - } - _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host]; - __weak typeof(self) weakSelf = self; - void (^handler)(void) = ^{ - typeof(self) strongSelf = weakSelf; - [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeUnavailable - userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]]; - }; - [_connectivityMonitor handleLossWithHandler:handler - wifiStatusChangeHandler:nil]; + [GRPCConnectivityMonitor registerObserver:self + selector:@selector(connectivityChanged:)]; } - (void)startWithWriteable:(id<GRXWriteable>)writeable { @@ -512,4 +534,12 @@ static NSString * const kBearerPrefix = @"Bearer "; } } +- (void)connectivityChanged:(NSNotification *)note { + [self maybeFinishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeUnavailable + userInfo:@{ NSLocalizedDescriptionKey : @"Connectivity lost." }]]; + // Cancel underlying call upon this notification + [self cancelCall]; +} + @end diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h index cb55e46d70..394d21792d 100644 --- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h +++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h @@ -19,44 +19,30 @@ #import <Foundation/Foundation.h> #import <SystemConfiguration/SystemConfiguration.h> -@interface GRPCReachabilityFlags : NSObject - -+ (nonnull instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags; - -/** - * One accessor method to query each of the different flags. Example: - -@property(nonatomic, readonly) BOOL isCell; - - */ -#define GRPC_XMACRO_ITEM(methodName, FlagName) \ -@property(nonatomic, readonly) BOOL methodName; - -#include "GRPCReachabilityFlagNames.xmacro.h" -#undef GRPC_XMACRO_ITEM - -@property(nonatomic, readonly) BOOL isHostReachable; -@end - +typedef NS_ENUM(NSInteger, GRPCConnectivityStatus) { + GRPCConnectivityUnknown = 0, + GRPCConnectivityNoNetwork = 1, + GRPCConnectivityCellular = 2, + GRPCConnectivityWiFi = 3, +}; + +extern NSString * _Nonnull kGRPCConnectivityNotification; + +// This interface monitors OS reachability interface for any network status +// change. Parties interested in these events should register themselves as +// observer. @interface GRPCConnectivityMonitor : NSObject -+ (nullable instancetype)monitorWithHost:(nonnull NSString *)hostName; - - (nonnull instancetype)init NS_UNAVAILABLE; -/** - * Queue on which callbacks will be dispatched. Default is the main queue. Set it before calling - * handleLossWithHandler:. - */ -// TODO(jcanizales): Default to a serial background queue instead. -@property(nonatomic, strong, null_resettable) dispatch_queue_t queue; - -/** - * Calls handler every time the connectivity to this instance's host is lost. If this instance is - * released before that happens, the handler won't be called. - * Only one handler is active at a time, so if this method is called again before the previous - * handler has been called, it might never be called at all (or yes, if it has already been queued). - */ -- (void)handleLossWithHandler:(nullable void (^)(void))lossHandler - wifiStatusChangeHandler:(nullable void (^)(void))wifiStatusChangeHandler; +// Register an object as observer of network status change. \a observer +// must have a notification method with one parameter of type +// (NSNotification *) and should pass it to parameter \a selector. The +// parameter of this notification method is not used for now. ++ (void)registerObserver:(_Nonnull id)observer + selector:(_Nonnull SEL)selector; + +// Ungegister an object from observers of network status change. ++ (void)unregisterObserver:(_Nonnull id)observer; + @end diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m index c8e10dd75f..7f31c7e23e 100644 --- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m +++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m @@ -18,175 +18,74 @@ #import "GRPCConnectivityMonitor.h" -#pragma mark Flags +#include <netinet/in.h> -@implementation GRPCReachabilityFlags { - SCNetworkReachabilityFlags _flags; -} +NSString *kGRPCConnectivityNotification = @"kGRPCConnectivityNotification"; -+ (instancetype)flagsWithFlags:(SCNetworkReachabilityFlags)flags { - return [[self alloc] initWithFlags:flags]; -} +static SCNetworkReachabilityRef reachability; +static GRPCConnectivityStatus currentStatus; -- (instancetype)initWithFlags:(SCNetworkReachabilityFlags)flags { - if ((self = [super init])) { - _flags = flags; +// Aggregate information in flags into network status. +GRPCConnectivityStatus CalculateConnectivityStatus(SCNetworkReachabilityFlags flags) { + GRPCConnectivityStatus result = GRPCConnectivityUnknown; + if (((flags & kSCNetworkReachabilityFlagsReachable) == 0) || + ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0)) { + return GRPCConnectivityNoNetwork; } - return self; -} - -/* - * One accessor method implementation per flag. Example: - -- (BOOL)isCell { \ - return !!(_flags & kSCNetworkReachabilityFlagsIsWWAN); \ -} - - */ -#define GRPC_XMACRO_ITEM(methodName, FlagName) \ -- (BOOL)methodName { \ - return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \ -} -#include "GRPCReachabilityFlagNames.xmacro.h" -#undef GRPC_XMACRO_ITEM - -- (BOOL)isHostReachable { - // Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs - // on top of it. - // connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on - // demand). - return self.reachable && !self.interventionRequired && !self.connectionOnDemand; -} - -- (NSString *)description { - NSMutableArray *activeOptions = [NSMutableArray arrayWithCapacity:9]; - - /* - * For each flag, add its name to the array if it's ON. Example: - - if (self.isCell) { - [activeOptions addObject:@"isCell"]; + result = GRPCConnectivityWiFi; +#if TARGET_OS_IPHONE + if (flags & kSCNetworkReachabilityFlagsIsWWAN) { + return result = GRPCConnectivityCellular; } - - */ - #define GRPC_XMACRO_ITEM(methodName, FlagName) \ - if (self.methodName) { \ - [activeOptions addObject:@ #methodName]; \ - } - #include "GRPCReachabilityFlagNames.xmacro.h" - #undef GRPC_XMACRO_ITEM - - return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "]; -} - -- (BOOL)isEqual:(id)object { - return [object isKindOfClass:[GRPCReachabilityFlags class]] && - _flags == ((GRPCReachabilityFlags *)object)->_flags; -} - -- (NSUInteger)hash { - return _flags; -} -@end - -#pragma mark Connectivity Monitor - -// Assumes the third argument is a block that accepts a GRPCReachabilityFlags object, and passes the -// received ones to it. -static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target, - SCNetworkReachabilityFlags flags, - void *info) { - #pragma unused (target) - // This can be called many times with the same info. The info is retained by SCNetworkReachability - // while this function is being executed. - void (^handler)(GRPCReachabilityFlags *) = (__bridge void (^)(GRPCReachabilityFlags *))info; - handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]); +#endif + return result; } -@implementation GRPCConnectivityMonitor { - SCNetworkReachabilityRef _reachabilityRef; - GRPCReachabilityFlags *_previousReachabilityFlags; -} +static void ReachabilityCallback( + SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { + GRPCConnectivityStatus newStatus = CalculateConnectivityStatus(flags); -- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability { - if (!reachability) { - return nil; + if (newStatus != currentStatus) { + [[NSNotificationCenter defaultCenter] postNotificationName:kGRPCConnectivityNotification + object:nil]; + currentStatus = newStatus; } - if ((self = [super init])) { - _reachabilityRef = CFRetain(reachability); - _queue = dispatch_get_main_queue(); - _previousReachabilityFlags = nil; - } - return self; } -+ (nullable instancetype)monitorWithHost:(nonnull NSString *)host { - const char *hostName = host.UTF8String; - if (!hostName) { - [NSException raise:NSInvalidArgumentException - format:@"host.UTF8String returns NULL for %@", host]; - } - SCNetworkReachabilityRef reachability = - SCNetworkReachabilityCreateWithName(NULL, hostName); +@implementation GRPCConnectivityMonitor - GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability]; - if (reachability) { - CFRelease(reachability); - } - return returnValue; -} ++ (void)initialize { + if (self == [GRPCConnectivityMonitor self]) { + struct sockaddr_in addr = {0}; + addr.sin_len = sizeof(addr); + addr.sin_family = AF_INET; + reachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&addr); + currentStatus = GRPCConnectivityUnknown; -- (void)handleLossWithHandler:(nullable void (^)(void))lossHandler - wifiStatusChangeHandler:(nullable void (^)(void))wifiStatusChangeHandler { - __weak typeof(self) weakSelf = self; - [self startListeningWithHandler:^(GRPCReachabilityFlags *flags) { - typeof(self) strongSelf = weakSelf; - if (strongSelf) { - if (lossHandler && !flags.reachable) { - lossHandler(); -#if TARGET_OS_IPHONE - } else if (wifiStatusChangeHandler && - strongSelf->_previousReachabilityFlags && - (flags.isWWAN ^ - strongSelf->_previousReachabilityFlags.isWWAN)) { - wifiStatusChangeHandler(); -#endif - } - strongSelf->_previousReachabilityFlags = flags; + SCNetworkConnectionFlags flags; + if (SCNetworkReachabilityGetFlags(reachability, &flags)) { + currentStatus = CalculateConnectivityStatus(flags); } - }]; -} -- (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler { - // Copy to ensure the handler block is in the heap (and so can't be deallocated when this method - // returns). - void (^copiedHandler)(GRPCReachabilityFlags *) = [handler copy]; - SCNetworkReachabilityContext context = { - .version = 0, - .info = (__bridge void *)copiedHandler, - .retain = CFRetain, - .release = CFRelease, - }; - // The following will retain context.info, and release it when the callback is set to NULL. - SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context); - SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue); -} - -- (void)stopListening { - // This releases the block on context.info. - SCNetworkReachabilitySetCallback(_reachabilityRef, NULL, NULL); - SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL); + SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; + if (!SCNetworkReachabilitySetCallback(reachability, ReachabilityCallback, &context) || + !SCNetworkReachabilityScheduleWithRunLoop( + reachability, CFRunLoopGetMain(), kCFRunLoopCommonModes)) { + NSLog(@"gRPC connectivity monitor fail to set"); + } + } } -- (void)setQueue:(dispatch_queue_t)queue { - _queue = queue ?: dispatch_get_main_queue(); ++ (void)registerObserver:(_Nonnull id)observer + selector:(SEL)selector { + [[NSNotificationCenter defaultCenter] addObserver:observer + selector:selector + name:kGRPCConnectivityNotification + object:nil]; } -- (void)dealloc { - if (_reachabilityRef) { - [self stopListening]; - CFRelease(_reachabilityRef); - } ++ (void)unregisterObserver:(_Nonnull id)observer { + [[NSNotificationCenter defaultCenter] removeObserver:observer]; } @end diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index 71b57cf1f6..8568e334dd 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -37,12 +37,6 @@ NS_ASSUME_NONNULL_BEGIN static NSMutableDictionary *kHostCache; -// This connectivity monitor flushes the host cache when connectivity status -// changes or when connection switch between Wifi and Cellular data, so that a -// new call will use a new channel. Otherwise, a new call will still use the -// cached channel which is no longer available and will cause gRPC to hang. -static GRPCConnectivityMonitor *connectivityMonitor = nil; - @implementation GRPCHost { // TODO(mlumish): Investigate whether caching channels with strong links is a good idea. GRPCChannel *_channel; @@ -90,17 +84,7 @@ static GRPCConnectivityMonitor *connectivityMonitor = nil; kHostCache[address] = self; _compressAlgorithm = GRPC_COMPRESS_NONE; } - // Keep a single monitor to flush the cache if the connectivity status changes - // Thread safety guarded by @synchronized(kHostCache) - if (!connectivityMonitor) { - connectivityMonitor = - [GRPCConnectivityMonitor monitorWithHost:hostURL.host]; - void (^handler)(void) = ^{ - [GRPCHost flushChannelCache]; - }; - [connectivityMonitor handleLossWithHandler:handler - wifiStatusChangeHandler:handler]; - } + [GRPCConnectivityMonitor registerObserver:self selector:@selector(connectivityChange:)]; } return self; } @@ -281,6 +265,13 @@ static GRPCConnectivityMonitor *connectivityMonitor = nil; } } +// Flushes the host cache when connectivity status changes or when connection switch between Wifi +// and Cellular data, so that a new call will use a new channel. Otherwise, a new call will still +// use the cached channel which is no longer available and will cause gRPC to hang. +- (void)connectivityChange:(NSNotification *)note { + [GRPCHost flushChannelCache]; +} + @end NS_ASSUME_NONNULL_END diff --git a/src/objective-c/tests/Connectivity/Base.lproj/Main.storyboard b/src/objective-c/tests/Connectivity/Base.lproj/Main.storyboard deleted file mode 100644 index 9a05b8635d..0000000000 --- a/src/objective-c/tests/Connectivity/Base.lproj/Main.storyboard +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11129.15" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11103.10"/> - </dependencies> - <scenes> - <!--View Controller--> - <scene sceneID="tne-QT-ifu"> - <objects> - <viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController"/> - <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> - </objects> - </scene> - </scenes> -</document> diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj b/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj index 3f26c98564..6a4c3519d6 100644 --- a/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj @@ -3,90 +3,104 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 48; objects = { /* Begin PBXBuildFile section */ - 500A4E0AC9D489EB214D1ED4 /* libPods-ConnectivityTestingApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C2AF815D8242A2172891621D /* libPods-ConnectivityTestingApp.a */; }; - 63BFB9CC1D2478DD00E17927 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 63BFB9CB1D2478DD00E17927 /* main.m */; }; - 63BFB9D21D2478DD00E17927 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 63BFB9D11D2478DD00E17927 /* ViewController.m */; }; - 63BFB9D51D2478DD00E17927 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63BFB9D31D2478DD00E17927 /* Main.storyboard */; }; + 5EC49F992043E46B00ED189A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC49F982043E46B00ED189A /* ViewController.m */; }; + 5EC49F9C2043E46B00ED189A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EC49F9A2043E46B00ED189A /* Main.storyboard */; }; + 5EC49F9E2043E46B00ED189A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5EC49F9D2043E46B00ED189A /* Assets.xcassets */; }; + 5EC49FA12043E46B00ED189A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EC49F9F2043E46B00ED189A /* LaunchScreen.storyboard */; }; + 5EC49FA42043E46B00ED189A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC49FA32043E46B00ED189A /* main.m */; }; + DDCBF00DB326F4FA4706953C /* libPods-ConnectivityTestingApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EF1DD1BF13CC3E1BDA1A1089 /* libPods-ConnectivityTestingApp.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 63BFB9C71D2478DD00E17927 /* ConnectivityTestingApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConnectivityTestingApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 63BFB9CB1D2478DD00E17927 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; }; - 63BFB9D11D2478DD00E17927 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = SOURCE_ROOT; }; - 63BFB9D41D2478DD00E17927 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; - 63BFB9DB1D2478DD00E17927 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; - BA96CBC1612BD2F70E66246C /* Pods-ConnectivityTestingApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConnectivityTestingApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp.release.xcconfig"; sourceTree = "<group>"; }; - C2AF815D8242A2172891621D /* libPods-ConnectivityTestingApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ConnectivityTestingApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - FC9BD3AE427396EDB4CD13E3 /* Pods-ConnectivityTestingApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConnectivityTestingApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp.debug.xcconfig"; sourceTree = "<group>"; }; + 5EC49F912043E46B00ED189A /* ConnectivityTestingApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ConnectivityTestingApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5EC49F982043E46B00ED189A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; }; + 5EC49F9B2043E46B00ED189A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + 5EC49F9D2043E46B00ED189A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + 5EC49FA02043E46B00ED189A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; + 5EC49FA22043E46B00ED189A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 5EC49FA32043E46B00ED189A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + 69B8F09A730364513700F23C /* Pods-ConnectivityTestingApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConnectivityTestingApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp.release.xcconfig"; sourceTree = "<group>"; }; + A16A6C6AAAC10A09DFF329F2 /* Pods-ConnectivityTestingApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ConnectivityTestingApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp.debug.xcconfig"; sourceTree = "<group>"; }; + EF1DD1BF13CC3E1BDA1A1089 /* libPods-ConnectivityTestingApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ConnectivityTestingApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 63BFB9C41D2478DD00E17927 /* Frameworks */ = { + 5EC49F8E2043E46B00ED189A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 500A4E0AC9D489EB214D1ED4 /* libPods-ConnectivityTestingApp.a in Frameworks */, + DDCBF00DB326F4FA4706953C /* libPods-ConnectivityTestingApp.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 16E6C67F2E48B42376DFFD2A /* Pods */ = { + 1AAFFD715A6EF1FEBF5AD796 /* Pods */ = { isa = PBXGroup; children = ( - FC9BD3AE427396EDB4CD13E3 /* Pods-ConnectivityTestingApp.debug.xcconfig */, - BA96CBC1612BD2F70E66246C /* Pods-ConnectivityTestingApp.release.xcconfig */, + A16A6C6AAAC10A09DFF329F2 /* Pods-ConnectivityTestingApp.debug.xcconfig */, + 69B8F09A730364513700F23C /* Pods-ConnectivityTestingApp.release.xcconfig */, ); name = Pods; sourceTree = "<group>"; }; - 48F8EC18C66D3416A41F76F5 /* Frameworks */ = { + 5EC49F882043E46B00ED189A = { isa = PBXGroup; children = ( - C2AF815D8242A2172891621D /* libPods-ConnectivityTestingApp.a */, + 5EC49F932043E46B00ED189A /* ConnectivityTestingApp */, + 5EC49F922043E46B00ED189A /* Products */, + 1AAFFD715A6EF1FEBF5AD796 /* Pods */, + 99E308FECF6991F75249AD00 /* Frameworks */, ); - name = Frameworks; sourceTree = "<group>"; }; - 63BFB9BE1D2478DD00E17927 = { + 5EC49F922043E46B00ED189A /* Products */ = { isa = PBXGroup; children = ( - 63BFB9D11D2478DD00E17927 /* ViewController.m */, - 63BFB9D31D2478DD00E17927 /* Main.storyboard */, - 63BFB9DB1D2478DD00E17927 /* Info.plist */, - 63BFB9CB1D2478DD00E17927 /* main.m */, - 63BFB9C81D2478DD00E17927 /* Products */, - 16E6C67F2E48B42376DFFD2A /* Pods */, - 48F8EC18C66D3416A41F76F5 /* Frameworks */, + 5EC49F912043E46B00ED189A /* ConnectivityTestingApp.app */, ); + name = Products; sourceTree = "<group>"; }; - 63BFB9C81D2478DD00E17927 /* Products */ = { + 5EC49F932043E46B00ED189A /* ConnectivityTestingApp */ = { isa = PBXGroup; children = ( - 63BFB9C71D2478DD00E17927 /* ConnectivityTestingApp.app */, + 5EC49F982043E46B00ED189A /* ViewController.m */, + 5EC49F9A2043E46B00ED189A /* Main.storyboard */, + 5EC49F9D2043E46B00ED189A /* Assets.xcassets */, + 5EC49F9F2043E46B00ED189A /* LaunchScreen.storyboard */, + 5EC49FA22043E46B00ED189A /* Info.plist */, + 5EC49FA32043E46B00ED189A /* main.m */, ); - name = Products; + path = ConnectivityTestingApp; + sourceTree = "<group>"; + }; + 99E308FECF6991F75249AD00 /* Frameworks */ = { + isa = PBXGroup; + children = ( + EF1DD1BF13CC3E1BDA1A1089 /* libPods-ConnectivityTestingApp.a */, + ); + name = Frameworks; sourceTree = "<group>"; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 63BFB9C61D2478DD00E17927 /* ConnectivityTestingApp */ = { + 5EC49F902043E46B00ED189A /* ConnectivityTestingApp */ = { isa = PBXNativeTarget; - buildConfigurationList = 63BFB9DE1D2478DD00E17927 /* Build configuration list for PBXNativeTarget "ConnectivityTestingApp" */; + buildConfigurationList = 5EC49FA72043E46B00ED189A /* Build configuration list for PBXNativeTarget "ConnectivityTestingApp" */; buildPhases = ( - 4DCA2703A0AA5DC1BD2751B8 /* [CP] Check Pods Manifest.lock */, - 63BFB9C31D2478DD00E17927 /* Sources */, - 63BFB9C41D2478DD00E17927 /* Frameworks */, - 63BFB9C51D2478DD00E17927 /* Resources */, - 8593A2388A8F7BF5A7E98D26 /* [CP] Embed Pods Frameworks */, - 5347BF6C41E7888C1C05CD88 /* [CP] Copy Pods Resources */, + 7CABF198C569A04592862390 /* [CP] Check Pods Manifest.lock */, + 5EC49F8D2043E46B00ED189A /* Sources */, + 5EC49F8E2043E46B00ED189A /* Frameworks */, + 5EC49F8F2043E46B00ED189A /* Resources */, + 9F67C72B6B6BAF2781078886 /* [CP] Embed Pods Frameworks */, + 735516C793AF7394FBB83B7F /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -94,87 +108,93 @@ ); name = ConnectivityTestingApp; productName = ConnectivityTestingApp; - productReference = 63BFB9C71D2478DD00E17927 /* ConnectivityTestingApp.app */; + productReference = 5EC49F912043E46B00ED189A /* ConnectivityTestingApp.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 63BFB9BF1D2478DD00E17927 /* Project object */ = { + 5EC49F892043E46B00ED189A /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0920; ORGANIZATIONNAME = gRPC; TargetAttributes = { - 63BFB9C61D2478DD00E17927 = { - CreatedOnToolsVersion = 8.0; - DevelopmentTeam = EQHXZ8M8AV; - DevelopmentTeamName = "Google, Inc."; - ProvisioningStyle = Automatic; + 5EC49F902043E46B00ED189A = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; }; }; }; - buildConfigurationList = 63BFB9C21D2478DD00E17927 /* Build configuration list for PBXProject "ConnectivityTestingApp" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + buildConfigurationList = 5EC49F8C2043E46B00ED189A /* Build configuration list for PBXProject "ConnectivityTestingApp" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); - mainGroup = 63BFB9BE1D2478DD00E17927; - productRefGroup = 63BFB9C81D2478DD00E17927 /* Products */; + mainGroup = 5EC49F882043E46B00ED189A; + productRefGroup = 5EC49F922043E46B00ED189A /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 63BFB9C61D2478DD00E17927 /* ConnectivityTestingApp */, + 5EC49F902043E46B00ED189A /* ConnectivityTestingApp */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 63BFB9C51D2478DD00E17927 /* Resources */ = { + 5EC49F8F2043E46B00ED189A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63BFB9D51D2478DD00E17927 /* Main.storyboard in Resources */, + 5EC49FA12043E46B00ED189A /* LaunchScreen.storyboard in Resources */, + 5EC49F9E2043E46B00ED189A /* Assets.xcassets in Resources */, + 5EC49F9C2043E46B00ED189A /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 4DCA2703A0AA5DC1BD2751B8 /* [CP] Check Pods Manifest.lock */ = { + 735516C793AF7394FBB83B7F /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 5347BF6C41E7888C1C05CD88 /* [CP] Copy Pods Resources */ = { + 7CABF198C569A04592862390 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ConnectivityTestingApp-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ConnectivityTestingApp/Pods-ConnectivityTestingApp-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8593A2388A8F7BF5A7E98D26 /* [CP] Embed Pods Frameworks */ = { + 9F67C72B6B6BAF2781078886 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -192,55 +212,72 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 63BFB9C31D2478DD00E17927 /* Sources */ = { + 5EC49F8D2043E46B00ED189A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 63BFB9D21D2478DD00E17927 /* ViewController.m in Sources */, - 63BFB9CC1D2478DD00E17927 /* main.m in Sources */, + 5EC49F992043E46B00ED189A /* ViewController.m in Sources */, + 5EC49FA42043E46B00ED189A /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ - 63BFB9D31D2478DD00E17927 /* Main.storyboard */ = { + 5EC49F9A2043E46B00ED189A /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( - 63BFB9D41D2478DD00E17927 /* Base */, + 5EC49F9B2043E46B00ED189A /* Base */, ); name = Main.storyboard; - path = .; - sourceTree = SOURCE_ROOT; + sourceTree = "<group>"; + }; + 5EC49F9F2043E46B00ED189A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5EC49FA02043E46B00ED189A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = "<group>"; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 63BFB9DC1D2478DD00E17927 /* Debug */ = { + 5EC49FA52043E46B00ED189A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -254,38 +291,48 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; - 63BFB9DD1D2478DD00E17927 /* Release */ = { + 5EC49FA62043E46B00ED189A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -293,61 +340,71 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; - 63BFB9DF1D2478DD00E17927 /* Debug */ = { + 5EC49FA82043E46B00ED189A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FC9BD3AE427396EDB4CD13E3 /* Pods-ConnectivityTestingApp.debug.xcconfig */; + baseConfigurationReference = A16A6C6AAAC10A09DFF329F2 /* Pods-ConnectivityTestingApp.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = EQHXZ8M8AV; + INFOPLIST_FILE = ConnectivityTestingApp/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.grpc.ConnectivityTestingApp; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "aa306b73-1a89-4299-8ead-c102fd059d96"; + PROVISIONING_PROFILE_SPECIFIER = "Google Development"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = ../../../..; }; name = Debug; }; - 63BFB9E01D2478DD00E17927 /* Release */ = { + 5EC49FA92043E46B00ED189A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BA96CBC1612BD2F70E66246C /* Pods-ConnectivityTestingApp.release.xcconfig */; + baseConfigurationReference = 69B8F09A730364513700F23C /* Pods-ConnectivityTestingApp.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - INFOPLIST_FILE = Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = EQHXZ8M8AV; + INFOPLIST_FILE = ConnectivityTestingApp/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.grpc.ConnectivityTestingApp; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = "aa306b73-1a89-4299-8ead-c102fd059d96"; + PROVISIONING_PROFILE_SPECIFIER = "Google Development"; + TARGETED_DEVICE_FAMILY = "1,2"; + USER_HEADER_SEARCH_PATHS = ../../../..; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 63BFB9C21D2478DD00E17927 /* Build configuration list for PBXProject "ConnectivityTestingApp" */ = { + 5EC49F8C2043E46B00ED189A /* Build configuration list for PBXProject "ConnectivityTestingApp" */ = { isa = XCConfigurationList; buildConfigurations = ( - 63BFB9DC1D2478DD00E17927 /* Debug */, - 63BFB9DD1D2478DD00E17927 /* Release */, + 5EC49FA52043E46B00ED189A /* Debug */, + 5EC49FA62043E46B00ED189A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 63BFB9DE1D2478DD00E17927 /* Build configuration list for PBXNativeTarget "ConnectivityTestingApp" */ = { + 5EC49FA72043E46B00ED189A /* Build configuration list for PBXNativeTarget "ConnectivityTestingApp" */ = { isa = XCConfigurationList; buildConfigurations = ( - 63BFB9DF1D2478DD00E17927 /* Debug */, - 63BFB9E01D2478DD00E17927 /* Release */, + 5EC49FA82043E46B00ED189A /* Debug */, + 5EC49FA92043E46B00ED189A /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 63BFB9BF1D2478DD00E17927 /* Project object */; + rootObject = 5EC49F892043E46B00ED189A /* Project object */; } diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..1d060ed288 --- /dev/null +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +}
\ No newline at end of file diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/LaunchScreen.storyboard b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..60a0921351 --- /dev/null +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="EHf-IW-A2E"> + <objects> + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q3l-Ud-sIn"> + <rect key="frame" x="167" y="131" width="41" height="30"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <state key="normal" title="Unary"/> + </button> + <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RfU-eq-cee"> + <rect key="frame" x="152" y="220" width="70" height="30"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <state key="normal" title="Streaming"/> + </button> + </subviews> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="53" y="375"/> + </scene> + </scenes> +</document> diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/Main.storyboard b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..1b4e0d050c --- /dev/null +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Base.lproj/Main.storyboard @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--View Controller--> + <scene sceneID="tne-QT-ifu"> + <objects> + <viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8QE-go-1ZJ"> + <rect key="frame" x="168" y="160" width="41" height="30"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <state key="normal" title="Unary"/> + <connections> + <action selector="tapUnary:" destination="BYZ-38-t0r" eventType="touchUpInside" id="OGf-25-OYx"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="p4l-br-UUo"> + <rect key="frame" x="153" y="229" width="70" height="30"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <state key="normal" title="Streaming"/> + <connections> + <action selector="tapStreaming:" destination="BYZ-38-t0r" eventType="touchUpInside" id="S3O-W8-Fed"/> + </connections> + </button> + </subviews> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> + </view> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> + </objects> + </scene> + </scenes> +</document> diff --git a/src/objective-c/tests/Connectivity/Info.plist b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Info.plist index 8a9fb88701..16be3b6811 100644 --- a/src/objective-c/tests/Connectivity/Info.plist +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/Info.plist @@ -3,7 +3,7 @@ <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> - <string>en</string> + <string>$(DEVELOPMENT_LANGUAGE)</string> <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> @@ -16,14 +16,12 @@ <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> - <key>CFBundleSignature</key> - <string>????</string> <key>CFBundleVersion</key> <string>1</string> <key>LSRequiresIPhoneOS</key> <true/> <key>UILaunchStoryboardName</key> - <string>Main</string> + <string>LaunchScreen</string> <key>UIMainStoryboardFile</key> <string>Main</string> <key>UIRequiredDeviceCapabilities</key> @@ -36,5 +34,12 @@ <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> + <key>UISupportedInterfaceOrientations~ipad</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> </dict> </plist> diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp/ViewController.m b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/ViewController.m new file mode 100644 index 0000000000..f91d2a6b62 --- /dev/null +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/ViewController.m @@ -0,0 +1,101 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#import <UIKit/UIKit.h> + +#import <GRPCClient/GRPCCall.h> +#import <ProtoRPC/ProtoMethod.h> +#import <RxLibrary/GRXWriter+Immediate.h> +#import <RxLibrary/GRXWriter+Transformations.h> +#import <RxLibrary/GRXBufferedPipe.h> + +#import "src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h" + +NSString *host = @"grpc-test.sandbox.googleapis.com"; + +@interface ViewController : UIViewController +@end + +@implementation ViewController +- (void)viewDidLoad { + [super viewDidLoad]; + + [GRPCConnectivityMonitor registerObserver:self + selector:@selector(reachabilityChanged:)]; +} + +- (void)reachabilityChanged:(NSNotification *)note { + NSLog(@"Reachability changed\n"); +} + +- (IBAction)tapUnary:(id)sender { + // Create a unary call + + // A trivial proto message to generate a response + char bytes[] = {0x10, 0x05, 0x1A, 0x07, 0x12, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; + + GRPCProtoMethod *method = [[GRPCProtoMethod alloc] initWithPackage:@"grpc.testing" + service:@"TestService" + method:@"UnaryCall"]; + GRXWriter *loggingRequestWriter = + [[GRXWriter writerWithValue:[NSData dataWithBytes:bytes length:sizeof(bytes)]] + map:^id(id value) { + NSLog(@"Sending request."); + return value; + }]; + GRPCCall *call = [[GRPCCall alloc] initWithHost:host + path:method.HTTPPath + requestsWriter:loggingRequestWriter]; + + [call startWithWriteable:[GRXWriteable writeableWithEventHandler:^(BOOL done, id value, + NSError *error) { + if (!done) { + return; + } + NSLog(@"Unary call finished with error: %@", error); + }]]; +} + +- (IBAction)tapStreaming:(id)sender { + // Create a streaming call + + // A trivial proto message to generate a response + char bytes[] = {0x12, 0x02, 0x08, 0x02, 0x1A, 0x04, 0x12, 0x02, 0x00, 0x00}; + + GRPCProtoMethod *method = [[GRPCProtoMethod alloc] initWithPackage:@"grpc.testing" + service:@"TestService" + method:@"FullDuplexCall"]; + + GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; + + [requestsBuffer writeValue:[NSData dataWithBytes:bytes length:sizeof(bytes)]]; + + GRPCCall *call = [[GRPCCall alloc] initWithHost:host + path:method.HTTPPath + requestsWriter:requestsBuffer]; + + [call startWithWriteable:[GRXWriteable writeableWithEventHandler:^(BOOL done, id value, + NSError *error) { + if (!done) { + return; + } + NSLog(@"Streaming call finished with error: %@", error); + }]]; +} + +@end diff --git a/src/objective-c/tests/Connectivity/main.m b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/main.m index 1642fdb2c6..1642fdb2c6 100644 --- a/src/objective-c/tests/Connectivity/main.m +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp/main.m diff --git a/src/objective-c/tests/Connectivity/README.md b/src/objective-c/tests/Connectivity/README.md index 851cb9d1da..907821e800 100644 --- a/src/objective-c/tests/Connectivity/README.md +++ b/src/objective-c/tests/Connectivity/README.md @@ -2,14 +2,12 @@ This app can be used to manually test gRPC under changing network conditions. It makes RPCs in a loop, logging when the request is sent and the response is received. -To test on the simulator, run `pod install`, open the workspace created by Cocoapods, and run the app. -Once running, disable WiFi (or ethernet) _in your computer_, then enable it again after a while. Don't -bother with the simulator's WiFi or cell settings, as they have no effect: Simulator apps are just Mac -apps running within the simulator UI. +To test on the simulator, run `pod install`, open the workspace created by Cocoapods, and run the +app on an iOS device. Once running, tap a few times of each of the two buttons to make a few unary and streaming +calls. Then disable/enable different network interfaces (WiFi, cellular) on your device. -The expected result is to never see a "hanged" RPC: success or failure should happen almost immediately -after sending the request. Symptom of a hanged RPC is a log like the following being the last in your -console: +The expected behavior is that the pending streaming calls fails immediately with error UNAVAILABLE. +Moreover, when network comes back, new calls have the same behavior. ``` 2016-06-29 16:51:29.443 ConnectivityTestingApp[73129:3567949] Sending request. diff --git a/src/objective-c/tests/Connectivity/ViewController.m b/src/objective-c/tests/Connectivity/ViewController.m deleted file mode 100644 index e39f3be594..0000000000 --- a/src/objective-c/tests/Connectivity/ViewController.m +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#import <UIKit/UIKit.h> - -#import <GRPCClient/GRPCCall.h> -#import <ProtoRPC/ProtoMethod.h> -#import <RxLibrary/GRXWriter+Immediate.h> -#import <RxLibrary/GRXWriter+Transformations.h> - -@interface ViewController : UIViewController -@end - -@implementation ViewController -- (void)viewDidLoad { - [super viewDidLoad]; - - NSString *host = @"grpc-test.sandbox.googleapis.com"; - - GRPCProtoMethod *method = [[GRPCProtoMethod alloc] initWithPackage:@"grpc.testing" - service:@"TestService" - method:@"StreamingOutputCall"]; - - __block void (^startCall)() = ^{ - GRXWriter *loggingRequestWriter = [[GRXWriter writerWithValue:[NSData data]] map:^id(id value) { - NSLog(@"Sending request."); - return value; - }]; - - GRPCCall *call = [[GRPCCall alloc] initWithHost:host - path:method.HTTPPath - requestsWriter:loggingRequestWriter]; - - [call startWithWriteable:[GRXWriteable writeableWithEventHandler:^(BOOL done, id value, - NSError *error) { - if (!done) { - return; - } - if (error) { - NSLog(@"Finished with error %@", error); - } else { - NSLog(@"Finished successfully."); - } - - dispatch_time_t oneSecond = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); - dispatch_after(oneSecond, dispatch_get_main_queue(), startCall); - }]]; - }; - - startCall(); -} -@end diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 9a6cb0e7d7..e0f220eaee 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -743,11 +743,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-CronetUnitTests/Pods-CronetUnitTests-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -845,11 +845,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -863,11 +863,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -935,11 +935,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -986,11 +986,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-AllTests/Pods-AllTests-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1037,11 +1037,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-Tests/Pods-Tests-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1091,11 +1091,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1124,11 +1124,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1142,11 +1142,11 @@ ); inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsRemoteWithCronet/Pods-InteropTestsRemoteWithCronet-resources.sh", - $PODS_CONFIGURATION_BUILD_DIR/gRPC/gRPCCertificates.bundle, + "${PODS_CONFIGURATION_BUILD_DIR}/gRPC/gRPCCertificates.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/gRPCCertificates.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1539,6 +1539,18 @@ INFOPLIST_FILE = CoreCronetEnd2EndTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_CONFIGURATION_BUILD_DIR}/BoringSSL\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/Protobuf\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/RemoteTest\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core-072e2d32\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-ProtoRPC\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-RxLibrary\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/nanopb\"", + "\"${PODS_CONFIGURATION_BUILD_DIR}/gRPC-Core\"", + ); PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests; PRODUCT_NAME = "$(TARGET_NAME)"; USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../..\""; diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme index a1da2e0c97..e62edd397a 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" shouldUseLaunchSchemeArgsEnv = "YES"> <Testables> <TestableReference @@ -55,6 +56,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Asan.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Asan.xcscheme new file mode 100644 index 0000000000..0a597e756e --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Asan.xcscheme @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableAddressSanitizer = "YES" + enableASanStackUseAfterReturn = "YES" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E8A5DA31D3840B4000F8BC4" + BuildableName = "CoreCronetEnd2EndTests.xctest" + BlueprintName = "CoreCronetEnd2EndTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Tsan.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Tsan.xcscheme new file mode 100644 index 0000000000..5fe60b9692 --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests_Tsan.xcscheme @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0920" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + enableThreadSanitizer = "YES" + language = "" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "5E8A5DA31D3840B4000F8BC4" + BuildableName = "CoreCronetEnd2EndTests.xctest" + BlueprintName = "CoreCronetEnd2EndTests" + ReferencedContainer = "container:Tests.xcodeproj"> + </BuildableReference> + </TestableReference> + </Testables> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/src/objective-c/tests/run_tests.sh b/src/objective-c/tests/run_tests.sh index cec34787cf..2fe2326207 100755 --- a/src/objective-c/tests/run_tests.sh +++ b/src/objective-c/tests/run_tests.sh @@ -92,6 +92,26 @@ xcodebuild \ echo "TIME: $(date)" xcodebuild \ -workspace Tests.xcworkspace \ + -scheme CoreCronetEnd2EndTests_Asan \ + -destination name="iPhone 6" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + +echo "TIME: $(date)" +xcodebuild \ + -workspace Tests.xcworkspace \ + -scheme CoreCronetEnd2EndTests_Tsan \ + -destination name="iPhone 6" \ + test \ + | egrep -v "$XCODEBUILD_FILTER" \ + | egrep -v '^$' \ + | egrep -v "(GPBDictionary|GPBArray)" - + +echo "TIME: $(date)" +xcodebuild \ + -workspace Tests.xcworkspace \ -scheme CronetUnitTests \ -destination name="iPhone 6" \ test \ diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h index 663cc6858d..663cc6858d 100755..100644 --- a/src/php/ext/grpc/call_credentials.h +++ b/src/php/ext/grpc/call_credentials.h diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h index 86bfdea51a..86bfdea51a 100755..100644 --- a/src/php/ext/grpc/channel.h +++ b/src/php/ext/grpc/channel.h diff --git a/src/php/ext/grpc/channel_credentials.h b/src/php/ext/grpc/channel_credentials.h index 357d732642..357d732642 100755..100644 --- a/src/php/ext/grpc/channel_credentials.h +++ b/src/php/ext/grpc/channel_credentials.h diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h index 366f5d05a4..366f5d05a4 100755..100644 --- a/src/php/ext/grpc/server.h +++ b/src/php/ext/grpc/server.h diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h index 310c28db76..310c28db76 100755..100644 --- a/src/php/ext/grpc/server_credentials.h +++ b/src/php/ext/grpc/server_credentials.h diff --git a/src/php/ext/grpc/timeval.h b/src/php/ext/grpc/timeval.h index f4d267f907..f4d267f907 100755..100644 --- a/src/php/ext/grpc/timeval.h +++ b/src/php/ext/grpc/timeval.h diff --git a/src/python/grpcio/grpc/_channel.py b/src/python/grpcio/grpc/_channel.py index 25a4210974..2eff08aa57 100644 --- a/src/python/grpcio/grpc/_channel.py +++ b/src/python/grpcio/grpc/_channel.py @@ -906,11 +906,6 @@ class Channel(grpc.Channel): self._call_state = _ChannelCallState(self._channel) self._connectivity_state = _ChannelConnectivityState(self._channel) - # TODO(https://github.com/grpc/grpc/issues/9884) - # Temporary work around UNAVAILABLE issues - # Remove this once c-core has retry support - _subscribe(self._connectivity_state, lambda *args: None, None) - def subscribe(self, callback, try_to_connect=None): _subscribe(self._connectivity_state, callback, try_to_connect) diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 479ed3aad8..d96cbec292 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -41,9 +41,6 @@ CORE_SOURCE_FILES = [ 'src/core/lib/gpr/sync.cc', 'src/core/lib/gpr/sync_posix.cc', 'src/core/lib/gpr/sync_windows.cc', - 'src/core/lib/gpr/thd.cc', - 'src/core/lib/gpr/thd_posix.cc', - 'src/core/lib/gpr/thd_windows.cc', 'src/core/lib/gpr/time.cc', 'src/core/lib/gpr/time_posix.cc', 'src/core/lib/gpr/time_precise.cc', @@ -53,6 +50,8 @@ CORE_SOURCE_FILES = [ 'src/core/lib/gpr/tmpfile_posix.cc', 'src/core/lib/gpr/tmpfile_windows.cc', 'src/core/lib/gpr/wrap_memcpy.cc', + 'src/core/lib/gprpp/thd_posix.cc', + 'src/core/lib/gprpp/thd_windows.cc', 'src/core/lib/profiling/basic_timers.cc', 'src/core/lib/profiling/stap_timers.cc', 'src/core/lib/surface/init.cc', @@ -98,6 +97,8 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/gethostname_sysconf.cc', 'src/core/lib/iomgr/iocp_windows.cc', 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_custom.cc', + 'src/core/lib/iomgr/iomgr_internal.cc', 'src/core/lib/iomgr/iomgr_posix.cc', 'src/core/lib/iomgr/iomgr_uv.cc', 'src/core/lib/iomgr/iomgr_windows.cc', @@ -106,12 +107,16 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/lockfree_event.cc', 'src/core/lib/iomgr/network_status_tracker.cc', 'src/core/lib/iomgr/polling_entity.cc', - 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset.cc', + 'src/core/lib/iomgr/pollset_custom.cc', + 'src/core/lib/iomgr/pollset_set.cc', + 'src/core/lib/iomgr/pollset_set_custom.cc', 'src/core/lib/iomgr/pollset_set_windows.cc', 'src/core/lib/iomgr/pollset_uv.cc', 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address.cc', + 'src/core/lib/iomgr/resolve_address_custom.cc', 'src/core/lib/iomgr/resolve_address_posix.cc', - 'src/core/lib/iomgr/resolve_address_uv.cc', 'src/core/lib/iomgr/resolve_address_windows.cc', 'src/core/lib/iomgr/resource_quota.cc', 'src/core/lib/iomgr/sockaddr_utils.cc', @@ -123,19 +128,24 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/socket_utils_uv.cc', 'src/core/lib/iomgr/socket_utils_windows.cc', 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client.cc', + 'src/core/lib/iomgr/tcp_client_custom.cc', 'src/core/lib/iomgr/tcp_client_posix.cc', - 'src/core/lib/iomgr/tcp_client_uv.cc', 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_custom.cc', 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server.cc', + 'src/core/lib/iomgr/tcp_server_custom.cc', 'src/core/lib/iomgr/tcp_server_posix.cc', 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', - 'src/core/lib/iomgr/tcp_server_uv.cc', 'src/core/lib/iomgr/tcp_server_windows.cc', 'src/core/lib/iomgr/tcp_uv.cc', 'src/core/lib/iomgr/tcp_windows.cc', 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer.cc', + 'src/core/lib/iomgr/timer_custom.cc', 'src/core/lib/iomgr/timer_generic.cc', 'src/core/lib/iomgr/timer_heap.cc', 'src/core/lib/iomgr/timer_manager.cc', @@ -156,7 +166,6 @@ CORE_SOURCE_FILES = [ 'src/core/lib/slice/percent_encoding.cc', 'src/core/lib/slice/slice.cc', 'src/core/lib/slice/slice_buffer.cc', - 'src/core/lib/slice/slice_hash_table.cc', 'src/core/lib/slice/slice_intern.cc', 'src/core/lib/slice/slice_string_helpers.cc', 'src/core/lib/surface/api_trace.cc', @@ -187,6 +196,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/transport/service_config.cc', 'src/core/lib/transport/static_metadata.cc', 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/status_metadata.cc', 'src/core/lib/transport/timeout_encoding.cc', 'src/core/lib/transport/transport.cc', 'src/core/lib/transport/transport_op_string.cc', @@ -221,6 +231,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/http/server/http_server_filter.cc', 'src/core/lib/http/httpcli_security_connector.cc', 'src/core/lib/security/context/security_context.cc', + 'src/core/lib/security/credentials/alts/alts_credentials.cc', 'src/core/lib/security/credentials/composite/composite_credentials.cc', 'src/core/lib/security/credentials/credentials.cc', 'src/core/lib/security/credentials/credentials_metadata.cc', @@ -234,23 +245,55 @@ CORE_SOURCE_FILES = [ 'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc', 'src/core/lib/security/credentials/plugin/plugin_credentials.cc', 'src/core/lib/security/credentials/ssl/ssl_credentials.cc', + 'src/core/lib/security/security_connector/alts_security_connector.cc', 'src/core/lib/security/security_connector/security_connector.cc', 'src/core/lib/security/transport/client_auth_filter.cc', - 'src/core/lib/security/transport/lb_targets_info.cc', 'src/core/lib/security/transport/secure_endpoint.cc', 'src/core/lib/security/transport/security_handshaker.cc', 'src/core/lib/security/transport/server_auth_filter.cc', + 'src/core/lib/security/transport/target_authority_table.cc', 'src/core/lib/security/transport/tsi_error.cc', 'src/core/lib/security/util/json_util.cc', 'src/core/lib/surface/init_secure.cc', - 'src/core/tsi/alts_transport_security.cc', - 'src/core/tsi/fake_transport_security.cc', - 'src/core/tsi/ssl_transport_security.cc', - 'src/core/tsi/transport_security_grpc.cc', + 'src/core/tsi/alts/crypt/aes_gcm.cc', + 'src/core/tsi/alts/crypt/gsec.cc', + 'src/core/tsi/alts/frame_protector/alts_counter.cc', + 'src/core/tsi/alts/frame_protector/alts_crypter.cc', + 'src/core/tsi/alts/frame_protector/alts_frame_protector.cc', + 'src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc', + 'src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc', + 'src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc', + 'src/core/tsi/alts/frame_protector/frame_handler.cc', + 'src/core/tsi/alts/handshaker/alts_handshaker_client.cc', + 'src/core/tsi/alts/handshaker/alts_tsi_event.cc', + 'src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc', + 'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc', + 'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc', + 'src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc', + 'src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc', + 'src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc', + 'src/core/lib/security/credentials/alts/check_gcp_environment.cc', + 'src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc', + 'src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc', + 'src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc', + 'src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc', + 'src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc', + 'src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc', + 'src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc', + 'src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc', + 'src/core/tsi/alts/handshaker/alts_tsi_utils.cc', + 'src/core/tsi/alts/handshaker/transport_security_common_api.cc', + 'src/core/tsi/alts/handshaker/altscontext.pb.c', + 'src/core/tsi/alts/handshaker/handshaker.pb.c', + 'src/core/tsi/alts/handshaker/transport_security_common.pb.c', + 'third_party/nanopb/pb_common.c', + 'third_party/nanopb/pb_decode.c', + 'third_party/nanopb/pb_encode.c', 'src/core/tsi/transport_security.cc', 'src/core/tsi/transport_security_adapter.cc', - 'src/core/ext/transport/chttp2/server/chttp2_server.cc', - 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', 'src/core/ext/filters/client_channel/backup_poller.cc', 'src/core/ext/filters/client_channel/channel_connectivity.cc', 'src/core/ext/filters/client_channel/client_channel.cc', @@ -262,21 +305,26 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/lb_policy.cc', 'src/core/ext/filters/client_channel/lb_policy_factory.cc', 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/method_params.cc', 'src/core/ext/filters/client_channel/parse_address.cc', 'src/core/ext/filters/client_channel/proxy_mapper.cc', 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', 'src/core/ext/filters/client_channel/resolver.cc', 'src/core/ext/filters/client_channel/resolver_registry.cc', 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/status_util.cc', 'src/core/ext/filters/client_channel/subchannel.cc', 'src/core/ext/filters/client_channel/subchannel_index.cc', 'src/core/ext/filters/client_channel/uri_parser.cc', 'src/core/ext/filters/deadline/deadline_filter.cc', - 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/tsi/alts_transport_security.cc', + 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/ssl_transport_security.cc', + 'src/core/tsi/transport_security_grpc.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', 'src/core/ext/transport/inproc/inproc_plugin.cc', 'src/core/ext/transport/inproc/inproc_transport.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', @@ -285,9 +333,6 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', - 'third_party/nanopb/pb_common.c', - 'third_party/nanopb/pb_decode.c', - 'third_party/nanopb/pb_encode.c', 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/subchannel_list.cc', @@ -375,7 +420,6 @@ CORE_SOURCE_FILES = [ 'third_party/boringssl/crypto/cpu-intel.c', 'third_party/boringssl/crypto/cpu-ppc64le.c', 'third_party/boringssl/crypto/crypto.c', - 'third_party/boringssl/crypto/curve25519/curve25519.c', 'third_party/boringssl/crypto/curve25519/spake25519.c', 'third_party/boringssl/crypto/curve25519/x25519-x86_64.c', 'third_party/boringssl/crypto/dh/check.c', @@ -561,6 +605,7 @@ CORE_SOURCE_FILES = [ 'third_party/boringssl/ssl/tls13_server.cc', 'third_party/boringssl/ssl/tls_method.cc', 'third_party/boringssl/ssl/tls_record.cc', + 'third_party/boringssl/third_party/fiat/curve25519.c', 'third_party/zlib/adler32.c', 'third_party/zlib/compress.c', 'third_party/zlib/crc32.c', diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json index e033c1063f..bbbc3ea360 100644 --- a/src/python/grpcio_tests/tests/tests.json +++ b/src/python/grpcio_tests/tests/tests.json @@ -38,6 +38,168 @@ "unit._cython.cygrpc_test.InsecureServerInsecureClient", "unit._cython.cygrpc_test.SecureServerSecureClient", "unit._cython.cygrpc_test.TypeSmokeTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyEmptyRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManyLargeRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ManySmallRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoEmptyRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoLargeRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.TwoSmallRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroEmptyRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroLargeRequestsZeroReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsTwoReadZeroSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadManyEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadManyLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadManySmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadTwoEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadTwoLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadTwoSmallResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadZeroEmptyResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadZeroLargeResponsesEarlyOKTest", + "unit._early_ok_test.ZeroSmallRequestsZeroReadZeroSmallResponsesEarlyOKTest", "unit._empty_message_test.EmptyMessageTest", "unit._exit_test.ExitTest", "unit._interceptor_test.InterceptorTest", @@ -56,12 +218,6 @@ "unit.beta._beta_features_test.BetaFeaturesTest", "unit.beta._beta_features_test.ContextManagementAndLifecycleTest", "unit.beta._connectivity_channel_test.ConnectivityStatesTest", - "unit.beta._face_interface_test.DynamicInvokerBlockingInvocationInlineServiceTest", - "unit.beta._face_interface_test.DynamicInvokerFutureInvocationAsynchronousEventServiceTest", - "unit.beta._face_interface_test.GenericInvokerBlockingInvocationInlineServiceTest", - "unit.beta._face_interface_test.GenericInvokerFutureInvocationAsynchronousEventServiceTest", - "unit.beta._face_interface_test.MultiCallableInvokerBlockingInvocationInlineServiceTest", - "unit.beta._face_interface_test.MultiCallableInvokerFutureInvocationAsynchronousEventServiceTest", "unit.beta._implementations_test.CallCredentialsTest", "unit.beta._implementations_test.ChannelCredentialsTest", "unit.beta._not_found_test.NotFoundTest", diff --git a/src/python/grpcio_tests/tests/unit/_compression_test.py b/src/python/grpcio_tests/tests/unit/_compression_test.py index 7550cd39ba..da1996b1d1 100644 --- a/src/python/grpcio_tests/tests/unit/_compression_test.py +++ b/src/python/grpcio_tests/tests/unit/_compression_test.py @@ -32,12 +32,10 @@ def handle_unary(request, servicer_context): def handle_stream(request_iterator, servicer_context): - # TODO(issue:#6891) We should be able to remove this loop, - # and replace with return; yield servicer_context.send_initial_metadata([('grpc-internal-encoding-request', 'gzip')]) - for request in request_iterator: - yield request + return + yield class _MethodHandler(grpc.RpcMethodHandler): diff --git a/src/python/grpcio_tests/tests/unit/_early_ok_test.py b/src/python/grpcio_tests/tests/unit/_early_ok_test.py new file mode 100644 index 0000000000..041532c661 --- /dev/null +++ b/src/python/grpcio_tests/tests/unit/_early_ok_test.py @@ -0,0 +1,206 @@ +# Copyright 2018 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests servicers sending OK status without having read all requests. + +This is a regression test of https://github.com/grpc/grpc/issues/6891. +""" + +import enum +import unittest + +import six + +import grpc + +from tests.unit import test_common +from tests.unit.framework.common import test_constants + +_RPC_METHOD = '/serffice/Meffod' + + +@enum.unique +class _MessageCount(enum.Enum): + + ZERO = ( + 0, + 'Zero', + ) + TWO = ( + 1, + 'Two', + ) + MANY = ( + test_constants.STREAM_LENGTH, + 'Many', + ) + + +@enum.unique +class _MessageSize(enum.Enum): + EMPTY = ( + 0, + 'Empty', + ) + SMALL = ( + 32, + 'Small', + ) # Smaller than any flow control window. + LARGE = ( + 3 * 1024 * 1024, + 'Large', + ) # Larger than any flow control window. + + +_ZERO_MESSAGE = b'' +_SMALL_MESSAGE = b'\x07' * _MessageSize.SMALL.value[0] +_LARGE_MESSAGE = b'abc' * (_MessageSize.LARGE.value[0] // 3) + + +@enum.unique +class _ReadRequests(enum.Enum): + + ZERO = ( + 0, + 'Zero', + ) + TWO = ( + 2, + 'Two', + ) + + +class _Case(object): + + def __init__(self, request_count, request_size, request_reading, + response_count, response_size): + self.request_count = request_count + self.request_size = request_size + self.request_reading = request_reading + self.response_count = response_count + self.response_size = response_size + + def create_test_case_name(self): + return '{}{}Requests{}Read{}{}ResponsesEarlyOKTest'.format( + self.request_count.value[1], self.request_size.value[1], + self.request_reading.value[1], self.response_count.value[1], + self.response_size.value[1]) + + +def _message(message_size): + if message_size is _MessageSize.EMPTY: + return _ZERO_MESSAGE + elif message_size is _MessageSize.SMALL: + return _SMALL_MESSAGE + elif message_size is _MessageSize.LARGE: + return _LARGE_MESSAGE + + +def _messages_to_send(count, size): + for _ in range(count.value[0]): + yield _message(size) + + +def _draw_requests(case, request_iterator): + for _ in range( + min(case.request_count.value[0], case.request_reading.value[0])): + next(request_iterator) + + +def _draw_responses(case, response_iterator): + for _ in range(case.response_count.value[0]): + next(response_iterator) + + +class _MethodHandler(grpc.RpcMethodHandler): + + def __init__(self, case): + self.request_streaming = True + self.response_streaming = True + self.request_deserializer = None + self.response_serializer = None + self.unary_unary = None + self.unary_stream = None + self.stream_unary = None + self._case = case + + def stream_stream(self, request_iterator, servicer_context): + _draw_requests(self._case, request_iterator) + + for response in _messages_to_send(self._case.response_count, + self._case.response_size): + yield response + + +class _GenericHandler(grpc.GenericRpcHandler): + + def __init__(self, case): + self._case = case + + def service(self, handler_call_details): + return _MethodHandler(self._case) + + +class _EarlyOkTest(unittest.TestCase): + + def setUp(self): + self._server = test_common.test_server() + port = self._server.add_insecure_port('[::]:0') + self._server.add_generic_rpc_handlers((_GenericHandler(self.case),)) + self._server.start() + + self._channel = grpc.insecure_channel('localhost:%d' % port) + self._multi_callable = self._channel.stream_stream(_RPC_METHOD) + + def tearDown(self): + self._server.stop(None) + + def test_early_ok(self): + requests = _messages_to_send(self.case.request_count, + self.case.request_size) + + response_iterator_call = self._multi_callable(requests) + + _draw_responses(self.case, response_iterator_call) + + self.assertIs(grpc.StatusCode.OK, response_iterator_call.code()) + + +def _cases(): + for request_count in _MessageCount: + for request_size in _MessageSize: + for request_reading in _ReadRequests: + for response_count in _MessageCount: + for response_size in _MessageSize: + yield _Case(request_count, request_size, + request_reading, response_count, + response_size) + + +def _test_case_classes(): + for case in _cases(): + yield type(case.create_test_case_name(), (_EarlyOkTest,), { + 'case': case, + '__module__': _EarlyOkTest.__module__, + }) + + +def load_tests(loader, tests, pattern): + return unittest.TestSuite( + tests=tuple( + loader.loadTestsFromTestCase(test_case_class) + for test_case_class in _test_case_classes())) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py index ca10bd4dab..a6cdf32e5b 100644 --- a/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py +++ b/src/python/grpcio_tests/tests/unit/_metadata_code_details_test.py @@ -107,9 +107,6 @@ class _Servicer(object): self._received_client_metadata = context.invocation_metadata() context.send_initial_metadata(_SERVER_INITIAL_METADATA) context.set_trailing_metadata(_SERVER_TRAILING_METADATA) - # TODO(https://github.com/grpc/grpc/issues/6891): just ignore the - # request iterator. - list(request_iterator) if self._abort_call: context.abort(self._code, self._details) else: @@ -127,9 +124,6 @@ class _Servicer(object): self._received_client_metadata = context.invocation_metadata() context.send_initial_metadata(_SERVER_INITIAL_METADATA) context.set_trailing_metadata(_SERVER_TRAILING_METADATA) - # TODO(https://github.com/grpc/grpc/issues/6891): just ignore the - # request iterator. - list(request_iterator) if self._abort_call: context.abort(self._code, self._details) else: diff --git a/src/python/grpcio_tests/tests/unit/_metadata_test.py b/src/python/grpcio_tests/tests/unit/_metadata_test.py index 5908421011..2309eeb733 100644 --- a/src/python/grpcio_tests/tests/unit/_metadata_test.py +++ b/src/python/grpcio_tests/tests/unit/_metadata_test.py @@ -117,9 +117,6 @@ def handle_stream_unary(test, request_iterator, servicer_context): validate_client_metadata(test, servicer_context) servicer_context.send_initial_metadata(_INITIAL_METADATA) servicer_context.set_trailing_metadata(_TRAILING_METADATA) - # TODO(issue:#6891) We should be able to remove this loop - for request in request_iterator: - pass return _RESPONSE @@ -127,10 +124,8 @@ def handle_stream_stream(test, request_iterator, servicer_context): validate_client_metadata(test, servicer_context) servicer_context.send_initial_metadata(_INITIAL_METADATA) servicer_context.set_trailing_metadata(_TRAILING_METADATA) - # TODO(issue:#6891) We should be able to remove this loop, - # and replace with return; yield - for request in request_iterator: - yield _RESPONSE + return + yield class _MethodHandler(grpc.RpcMethodHandler): diff --git a/src/python/grpcio_tests/tests/unit/_reconnect_test.py b/src/python/grpcio_tests/tests/unit/_reconnect_test.py index 8acba5a30b..a708d8d862 100644 --- a/src/python/grpcio_tests/tests/unit/_reconnect_test.py +++ b/src/python/grpcio_tests/tests/unit/_reconnect_test.py @@ -89,7 +89,10 @@ class ReconnectTest(unittest.TestCase): multi_callable = channel.unary_unary(_UNARY_UNARY) self.assertEqual(_RESPONSE, multi_callable(_REQUEST)) server.stop(None) - time.sleep(1) + # By default, the channel connectivity is checked every 5s + # GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS can be set to change + # this. + time.sleep(5.1) server = grpc.server(server_pool, (handler,)) server.add_insecure_port('[::]:{}'.format(port)) server.start() diff --git a/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py b/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py index df4b129018..e35f8f10d4 100644 --- a/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py +++ b/src/python/grpcio_tests/tests/unit/_resource_exhausted_test.py @@ -77,18 +77,13 @@ def handle_unary_stream(trigger, request, servicer_context): def handle_stream_unary(trigger, request_iterator, servicer_context): trigger.await_trigger() - # TODO(issue:#6891) We should be able to remove this loop - for request in request_iterator: - pass return _RESPONSE def handle_stream_stream(trigger, request_iterator, servicer_context): trigger.await_trigger() - # TODO(issue:#6891) We should be able to remove this loop, - # and replace with return; yield - for request in request_iterator: - yield _RESPONSE + return + yield class _MethodHandler(grpc.RpcMethodHandler): diff --git a/src/python/grpcio_tests/tests/unit/beta/_face_interface_test.py b/src/python/grpcio_tests/tests/unit/beta/_face_interface_test.py deleted file mode 100644 index c99738e085..0000000000 --- a/src/python/grpcio_tests/tests/unit/beta/_face_interface_test.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tests Face interface compliance of the gRPC Python Beta API.""" - -import collections -import unittest - -import six - -from grpc.beta import implementations -from grpc.beta import interfaces -from tests.unit import resources -from tests.unit import test_common as grpc_test_common -from tests.unit.beta import test_utilities -from tests.unit.framework.common import test_constants -from tests.unit.framework.interfaces.face import test_cases -from tests.unit.framework.interfaces.face import test_interfaces - -_SERVER_HOST_OVERRIDE = 'foo.test.google.fr' - - -class _SerializationBehaviors( - collections.namedtuple('_SerializationBehaviors', ( - 'request_serializers', - 'request_deserializers', - 'response_serializers', - 'response_deserializers', - ))): - pass - - -def _serialization_behaviors_from_test_methods(test_methods): - request_serializers = {} - request_deserializers = {} - response_serializers = {} - response_deserializers = {} - for (group, method), test_method in six.iteritems(test_methods): - request_serializers[group, method] = test_method.serialize_request - request_deserializers[group, method] = test_method.deserialize_request - response_serializers[group, method] = test_method.serialize_response - response_deserializers[group, method] = test_method.deserialize_response - return _SerializationBehaviors(request_serializers, request_deserializers, - response_serializers, response_deserializers) - - -class _Implementation(test_interfaces.Implementation): - - def instantiate(self, methods, method_implementations, - multi_method_implementation): - serialization_behaviors = _serialization_behaviors_from_test_methods( - methods) - # TODO(nathaniel): Add a "groups" attribute to _digest.TestServiceDigest. - service = next(iter(methods))[0] - # TODO(nathaniel): Add a "cardinalities_by_group" attribute to - # _digest.TestServiceDigest. - cardinalities = { - method: method_object.cardinality() - for (group, method), method_object in six.iteritems(methods) - } - - server_options = implementations.server_options( - request_deserializers=serialization_behaviors.request_deserializers, - response_serializers=serialization_behaviors.response_serializers, - thread_pool_size=test_constants.POOL_SIZE) - server = implementations.server( - method_implementations, options=server_options) - server_credentials = implementations.ssl_server_credentials([ - ( - resources.private_key(), - resources.certificate_chain(), - ), - ]) - port = server.add_secure_port('[::]:0', server_credentials) - server.start() - channel_credentials = implementations.ssl_channel_credentials( - resources.test_root_certificates()) - channel = test_utilities.not_really_secure_channel( - 'localhost', port, channel_credentials, _SERVER_HOST_OVERRIDE) - stub_options = implementations.stub_options( - request_serializers=serialization_behaviors.request_serializers, - response_deserializers=serialization_behaviors. - response_deserializers, - thread_pool_size=test_constants.POOL_SIZE) - generic_stub = implementations.generic_stub( - channel, options=stub_options) - dynamic_stub = implementations.dynamic_stub( - channel, service, cardinalities, options=stub_options) - return generic_stub, {service: dynamic_stub}, server - - def destantiate(self, memo): - memo.stop(test_constants.SHORT_TIMEOUT).wait() - - def invocation_metadata(self): - return grpc_test_common.INVOCATION_INITIAL_METADATA - - def initial_metadata(self): - return grpc_test_common.SERVICE_INITIAL_METADATA - - def terminal_metadata(self): - return grpc_test_common.SERVICE_TERMINAL_METADATA - - def code(self): - return interfaces.StatusCode.OK - - def details(self): - return grpc_test_common.DETAILS - - def metadata_transmitted(self, original_metadata, transmitted_metadata): - return original_metadata is None or grpc_test_common.metadata_transmitted( - original_metadata, transmitted_metadata) - - -def load_tests(loader, tests, pattern): - return unittest.TestSuite( - tests=tuple( - loader.loadTestsFromTestCase(test_case_class) - for test_case_class in test_cases.test_cases(_Implementation()))) - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/__init__.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/__init__.py deleted file mode 100644 index 5fb4f3c3cf..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/__init__.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/__init__.py deleted file mode 100644 index 5fb4f3c3cf..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_blocking_invocation_inline_service.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_blocking_invocation_inline_service.py deleted file mode 100644 index 5d8679aa62..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_blocking_invocation_inline_service.py +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Test code for the Face layer of RPC Framework.""" - -from __future__ import division - -import abc -import itertools -import unittest -from concurrent import futures - -import six - -# test_interfaces is referenced from specification in this module. -from grpc.framework.foundation import logging_pool -from grpc.framework.interfaces.face import face -from tests.unit.framework.common import test_constants -from tests.unit.framework.common import test_control -from tests.unit.framework.common import test_coverage -from tests.unit.framework.interfaces.face import _3069_test_constant -from tests.unit.framework.interfaces.face import _digest -from tests.unit.framework.interfaces.face import _stock_service -from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import - - -class TestCase( - six.with_metaclass(abc.ABCMeta, test_coverage.Coverage, - unittest.TestCase)): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must have an "implementation" attribute of type - test_interfaces.Implementation and an "invoker_constructor" attribute of type - _invocation.InvokerConstructor. - """ - - NAME = 'BlockingInvocationInlineServiceTest' - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self._control = test_control.PauseFailControl() - self._digest = _digest.digest(_stock_service.STOCK_TEST_SERVICE, - self._control, None) - - generic_stub, dynamic_stubs, self._memo = self.implementation.instantiate( - self._digest.methods, self._digest.inline_method_implementations, - None) - self._invoker = self.invoker_constructor.construct_invoker( - generic_stub, dynamic_stubs, self._digest.methods) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self._invoker = None - self.implementation.destantiate(self._memo) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response, call = self._invoker.blocking(group, method)( - request, test_constants.LONG_TIMEOUT, with_call=True) - - test_messages.verify(request, response, self) - - def testSuccessfulUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self._invoker.blocking(group, method)( - request, test_constants.LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response, call = self._invoker.blocking(group, method)( - iter(requests), test_constants.LONG_TIMEOUT, with_call=True) - - test_messages.verify(requests, response, self) - - def testSuccessfulStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - response_iterator = self._invoker.blocking(group, method)( - iter(requests), test_constants.LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response = self._invoker.blocking(group, method)( - first_request, test_constants.LONG_TIMEOUT) - - test_messages.verify(first_request, first_response, self) - - second_response = self._invoker.blocking(group, method)( - second_request, test_constants.LONG_TIMEOUT) - - test_messages.verify(second_request, second_response, self) - - def testParallelInvocations(self): - pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = [] - response_futures = [] - for _ in range(test_constants.THREAD_CONCURRENCY): - request = test_messages.request() - response_future = pool.submit( - self._invoker.blocking(group, method), request, - test_constants.LONG_TIMEOUT) - requests.append(request) - response_futures.append(response_future) - - responses = [ - response_future.result() - for response_future in response_futures - ] - - for request, response in zip(requests, responses): - test_messages.verify(request, response, self) - pool.shutdown(wait=True) - - def testWaitingForSomeButNotAllParallelInvocations(self): - pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = [] - response_futures_to_indices = {} - for index in range(test_constants.THREAD_CONCURRENCY): - request = test_messages.request() - response_future = pool.submit( - self._invoker.blocking(group, method), request, - test_constants.LONG_TIMEOUT) - requests.append(request) - response_futures_to_indices[response_future] = index - - some_completed_response_futures_iterator = itertools.islice( - futures.as_completed(response_futures_to_indices), - test_constants.THREAD_CONCURRENCY // 2) - for response_future in some_completed_response_futures_iterator: - index = response_futures_to_indices[response_future] - test_messages.verify(requests[index], - response_future.result(), self) - pool.shutdown(wait=True) - - @unittest.skip('Cancellation impossible with blocking control flow!') - def testCancelledUnaryRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Cancellation impossible with blocking control flow!') - def testCancelledUnaryRequestStreamResponse(self): - raise NotImplementedError() - - @unittest.skip('Cancellation impossible with blocking control flow!') - def testCancelledStreamRequestUnaryResponse(self): - raise NotImplementedError() - - @unittest.skip('Cancellation impossible with blocking control flow!') - def testCancelledStreamRequestStreamResponse(self): - raise NotImplementedError() - - def testExpiredUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.pause(), self.assertRaises( - face.ExpirationError): - self._invoker.blocking(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - - def testExpiredUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.pause(), self.assertRaises( - face.ExpirationError): - response_iterator = self._invoker.blocking(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.pause(), self.assertRaises( - face.ExpirationError): - self._invoker.blocking( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - - def testExpiredStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.pause(), self.assertRaises( - face.ExpirationError): - response_iterator = self._invoker.blocking( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.fail(), self.assertRaises(face.RemoteError): - self._invoker.blocking(group, method)( - request, test_constants.LONG_TIMEOUT) - - def testFailedUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.fail(), self.assertRaises(face.RemoteError): - response_iterator = self._invoker.blocking(group, method)( - request, test_constants.LONG_TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.fail(), self.assertRaises(face.RemoteError): - self._invoker.blocking(group, method)( - iter(requests), test_constants.LONG_TIMEOUT) - - def testFailedStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.fail(), self.assertRaises(face.RemoteError): - response_iterator = self._invoker.blocking(group, method)( - iter(requests), test_constants.LONG_TIMEOUT) - list(response_iterator) diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_digest.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_digest.py deleted file mode 100644 index b1c33da43a..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_digest.py +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Code for making a service.TestService more amenable to use in tests.""" - -import collections -import threading - -import six - -# test_control, _service, and test_interfaces are referenced from specification -# in this module. -from grpc.framework.common import cardinality -from grpc.framework.common import style -from grpc.framework.foundation import stream -from grpc.framework.foundation import stream_util -from grpc.framework.interfaces.face import face -from tests.unit.framework.common import test_control # pylint: disable=unused-import -from tests.unit.framework.interfaces.face import _service # pylint: disable=unused-import -from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import - -_IDENTITY = lambda x: x - - -class TestServiceDigest( - collections.namedtuple('TestServiceDigest', ( - 'methods', - 'inline_method_implementations', - 'event_method_implementations', - 'multi_method_implementation', - 'unary_unary_messages_sequences', - 'unary_stream_messages_sequences', - 'stream_unary_messages_sequences', - 'stream_stream_messages_sequences', - ))): - """A transformation of a service.TestService. - - Attributes: - methods: A dict from method group-name pair to test_interfaces.Method object - describing the RPC methods that may be called during the test. - inline_method_implementations: A dict from method group-name pair to - face.MethodImplementation object to be used in tests of in-line calls to - behaviors under test. - event_method_implementations: A dict from method group-name pair to - face.MethodImplementation object to be used in tests of event-driven calls - to behaviors under test. - multi_method_implementation: A face.MultiMethodImplementation to be used in - tests of generic calls to behaviors under test. - unary_unary_messages_sequences: A dict from method group-name pair to - sequence of service.UnaryUnaryTestMessages objects to be used to test the - identified method. - unary_stream_messages_sequences: A dict from method group-name pair to - sequence of service.UnaryStreamTestMessages objects to be used to test the - identified method. - stream_unary_messages_sequences: A dict from method group-name pair to - sequence of service.StreamUnaryTestMessages objects to be used to test the - identified method. - stream_stream_messages_sequences: A dict from method group-name pair to - sequence of service.StreamStreamTestMessages objects to be used to test - the identified method. - """ - - -class _BufferingConsumer(stream.Consumer): - """A trivial Consumer that dumps what it consumes in a user-mutable buffer.""" - - def __init__(self): - self.consumed = [] - self.terminated = False - - def consume(self, value): - self.consumed.append(value) - - def terminate(self): - self.terminated = True - - def consume_and_terminate(self, value): - self.consumed.append(value) - self.terminated = True - - -class _InlineUnaryUnaryMethod(face.MethodImplementation): - - def __init__(self, unary_unary_test_method, control): - self._test_method = unary_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.INLINE - - def unary_unary_inline(self, request, context): - response_list = [] - self._test_method.service(request, response_list.append, context, - self._control) - return response_list.pop(0) - - -class _EventUnaryUnaryMethod(face.MethodImplementation): - - def __init__(self, unary_unary_test_method, control, pool): - self._test_method = unary_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_UNARY - self.style = style.Service.EVENT - - def unary_unary_event(self, request, response_callback, context): - if self._pool is None: - self._test_method.service(request, response_callback, context, - self._control) - else: - self._pool.submit(self._test_method.service, request, - response_callback, context, self._control) - - -class _InlineUnaryStreamMethod(face.MethodImplementation): - - def __init__(self, unary_stream_test_method, control): - self._test_method = unary_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.INLINE - - def unary_stream_inline(self, request, context): - response_consumer = _BufferingConsumer() - self._test_method.service(request, response_consumer, context, - self._control) - for response in response_consumer.consumed: - yield response - - -class _EventUnaryStreamMethod(face.MethodImplementation): - - def __init__(self, unary_stream_test_method, control, pool): - self._test_method = unary_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.UNARY_STREAM - self.style = style.Service.EVENT - - def unary_stream_event(self, request, response_consumer, context): - if self._pool is None: - self._test_method.service(request, response_consumer, context, - self._control) - else: - self._pool.submit(self._test_method.service, request, - response_consumer, context, self._control) - - -class _InlineStreamUnaryMethod(face.MethodImplementation): - - def __init__(self, stream_unary_test_method, control): - self._test_method = stream_unary_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.INLINE - - def stream_unary_inline(self, request_iterator, context): - response_list = [] - request_consumer = self._test_method.service(response_list.append, - context, self._control) - for request in request_iterator: - request_consumer.consume(request) - request_consumer.terminate() - return response_list.pop(0) - - -class _EventStreamUnaryMethod(face.MethodImplementation): - - def __init__(self, stream_unary_test_method, control, pool): - self._test_method = stream_unary_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_UNARY - self.style = style.Service.EVENT - - def stream_unary_event(self, response_callback, context): - request_consumer = self._test_method.service(response_callback, context, - self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, - self._pool) - - -class _InlineStreamStreamMethod(face.MethodImplementation): - - def __init__(self, stream_stream_test_method, control): - self._test_method = stream_stream_test_method - self._control = control - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.INLINE - - def stream_stream_inline(self, request_iterator, context): - response_consumer = _BufferingConsumer() - request_consumer = self._test_method.service(response_consumer, context, - self._control) - - for request in request_iterator: - request_consumer.consume(request) - while response_consumer.consumed: - yield response_consumer.consumed.pop(0) - response_consumer.terminate() - - -class _EventStreamStreamMethod(face.MethodImplementation): - - def __init__(self, stream_stream_test_method, control, pool): - self._test_method = stream_stream_test_method - self._control = control - self._pool = pool - - self.cardinality = cardinality.Cardinality.STREAM_STREAM - self.style = style.Service.EVENT - - def stream_stream_event(self, response_consumer, context): - request_consumer = self._test_method.service(response_consumer, context, - self._control) - if self._pool is None: - return request_consumer - else: - return stream_util.ThreadSwitchingConsumer(request_consumer, - self._pool) - - -class _UnaryConsumer(stream.Consumer): - """A Consumer that only allows consumption of exactly one value.""" - - def __init__(self, action): - self._lock = threading.Lock() - self._action = action - self._consumed = False - self._terminated = False - - def consume(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - - self._action(value) - - def terminate(self): - with self._lock: - if not self._consumed: - raise ValueError('Unary consumer hasn\'t yet consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._terminated = True - - def consume_and_terminate(self, value): - with self._lock: - if self._consumed: - raise ValueError('Unary consumer already consumed!') - elif self._terminated: - raise ValueError('Unary consumer already terminated!') - else: - self._consumed = True - self._terminated = True - - self._action(value) - - -class _UnaryUnaryAdaptation(object): - - def __init__(self, unary_unary_test_method): - self._method = unary_unary_test_method - - def service(self, response_consumer, context, control): - - def action(request): - self._method.service(request, - response_consumer.consume_and_terminate, - context, control) - - return _UnaryConsumer(action) - - -class _UnaryStreamAdaptation(object): - - def __init__(self, unary_stream_test_method): - self._method = unary_stream_test_method - - def service(self, response_consumer, context, control): - - def action(request): - self._method.service(request, response_consumer, context, control) - - return _UnaryConsumer(action) - - -class _StreamUnaryAdaptation(object): - - def __init__(self, stream_unary_test_method): - self._method = stream_unary_test_method - - def service(self, response_consumer, context, control): - return self._method.service(response_consumer.consume_and_terminate, - context, control) - - -class _MultiMethodImplementation(face.MultiMethodImplementation): - - def __init__(self, methods, control, pool): - self._methods = methods - self._control = control - self._pool = pool - - def service(self, group, name, response_consumer, context): - method = self._methods.get(group, name, None) - if method is None: - raise face.NoSuchMethodError(group, name) - elif self._pool is None: - return method(response_consumer, context, self._control) - else: - request_consumer = method(response_consumer, context, self._control) - return stream_util.ThreadSwitchingConsumer(request_consumer, - self._pool) - - -class _Assembly( - collections.namedtuple( - '_Assembly', - ['methods', 'inlines', 'events', 'adaptations', 'messages'])): - """An intermediate structure created when creating a TestServiceDigest.""" - - -def _assemble(scenarios, identifiers, inline_method_constructor, - event_method_constructor, adapter, control, pool): - """Creates an _Assembly from the given scenarios.""" - methods = {} - inlines = {} - events = {} - adaptations = {} - messages = {} - for identifier, scenario in six.iteritems(scenarios): - if identifier in identifiers: - raise ValueError('Repeated identifier "(%s, %s)"!' % identifier) - - test_method = scenario[0] - inline_method = inline_method_constructor(test_method, control) - event_method = event_method_constructor(test_method, control, pool) - adaptation = adapter(test_method) - - methods[identifier] = test_method - inlines[identifier] = inline_method - events[identifier] = event_method - adaptations[identifier] = adaptation - messages[identifier] = scenario[1] - - return _Assembly(methods, inlines, events, adaptations, messages) - - -def digest(service, control, pool): - """Creates a TestServiceDigest from a TestService. - - Args: - service: A _service.TestService. - control: A test_control.Control. - pool: If RPC methods should be serviced in a separate thread, a thread pool. - None if RPC methods should be serviced in the thread belonging to the - run-time that calls for their service. - - Returns: - A TestServiceDigest synthesized from the given service.TestService. - """ - identifiers = set() - - unary_unary = _assemble(service.unary_unary_scenarios(), identifiers, - _InlineUnaryUnaryMethod, _EventUnaryUnaryMethod, - _UnaryUnaryAdaptation, control, pool) - identifiers.update(unary_unary.inlines) - - unary_stream = _assemble(service.unary_stream_scenarios(), identifiers, - _InlineUnaryStreamMethod, _EventUnaryStreamMethod, - _UnaryStreamAdaptation, control, pool) - identifiers.update(unary_stream.inlines) - - stream_unary = _assemble(service.stream_unary_scenarios(), identifiers, - _InlineStreamUnaryMethod, _EventStreamUnaryMethod, - _StreamUnaryAdaptation, control, pool) - identifiers.update(stream_unary.inlines) - - stream_stream = _assemble(service.stream_stream_scenarios(), identifiers, - _InlineStreamStreamMethod, - _EventStreamStreamMethod, _IDENTITY, control, - pool) - identifiers.update(stream_stream.inlines) - - methods = dict(unary_unary.methods) - methods.update(unary_stream.methods) - methods.update(stream_unary.methods) - methods.update(stream_stream.methods) - adaptations = dict(unary_unary.adaptations) - adaptations.update(unary_stream.adaptations) - adaptations.update(stream_unary.adaptations) - adaptations.update(stream_stream.adaptations) - inlines = dict(unary_unary.inlines) - inlines.update(unary_stream.inlines) - inlines.update(stream_unary.inlines) - inlines.update(stream_stream.inlines) - events = dict(unary_unary.events) - events.update(unary_stream.events) - events.update(stream_unary.events) - events.update(stream_stream.events) - - return TestServiceDigest(methods, inlines, events, - _MultiMethodImplementation(adaptations, control, - pool), - unary_unary.messages, unary_stream.messages, - stream_unary.messages, stream_stream.messages) diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_future_invocation_asynchronous_event_service.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_future_invocation_asynchronous_event_service.py deleted file mode 100644 index 3d9b2816aa..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_future_invocation_asynchronous_event_service.py +++ /dev/null @@ -1,508 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Test code for the Face layer of RPC Framework.""" - -from __future__ import division - -import abc -import contextlib -import itertools -import threading -import unittest -from concurrent import futures - -import six - -# test_interfaces is referenced from specification in this module. -from grpc.framework.foundation import future -from grpc.framework.foundation import logging_pool -from grpc.framework.interfaces.face import face -from tests.unit.framework.common import test_constants -from tests.unit.framework.common import test_control -from tests.unit.framework.common import test_coverage -from tests.unit.framework.interfaces.face import _3069_test_constant -from tests.unit.framework.interfaces.face import _digest -from tests.unit.framework.interfaces.face import _stock_service -from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import - - -class _PauseableIterator(object): - - def __init__(self, upstream): - self._upstream = upstream - self._condition = threading.Condition() - self._paused = False - - @contextlib.contextmanager - def pause(self): - with self._condition: - self._paused = True - yield - with self._condition: - self._paused = False - self._condition.notify_all() - - def __iter__(self): - return self - - def __next__(self): - return self.next() - - def next(self): - with self._condition: - while self._paused: - self._condition.wait() - return next(self._upstream) - - -class _Callback(object): - - def __init__(self): - self._condition = threading.Condition() - self._called = False - self._passed_future = None - self._passed_other_stuff = None - - def __call__(self, *args, **kwargs): - with self._condition: - self._called = True - if args: - self._passed_future = args[0] - if 1 < len(args) or kwargs: - self._passed_other_stuff = tuple(args[1:]), dict(kwargs) - self._condition.notify_all() - - def future(self): - with self._condition: - while True: - if self._passed_other_stuff is not None: - raise ValueError( - 'Test callback passed unexpected values: %s', - self._passed_other_stuff) - elif self._called: - return self._passed_future - else: - self._condition.wait() - - -class TestCase( - six.with_metaclass(abc.ABCMeta, test_coverage.Coverage, - unittest.TestCase)): - """A test of the Face layer of RPC Framework. - - Concrete subclasses must have an "implementation" attribute of type - test_interfaces.Implementation and an "invoker_constructor" attribute of type - _invocation.InvokerConstructor. - """ - - NAME = 'FutureInvocationAsynchronousEventServiceTest' - - def setUp(self): - """See unittest.TestCase.setUp for full specification. - - Overriding implementations must call this implementation. - """ - self._control = test_control.PauseFailControl() - self._digest_pool = logging_pool.pool(test_constants.POOL_SIZE) - self._digest = _digest.digest(_stock_service.STOCK_TEST_SERVICE, - self._control, self._digest_pool) - - generic_stub, dynamic_stubs, self._memo = self.implementation.instantiate( - self._digest.methods, self._digest.event_method_implementations, - None) - self._invoker = self.invoker_constructor.construct_invoker( - generic_stub, dynamic_stubs, self._digest.methods) - - def tearDown(self): - """See unittest.TestCase.tearDown for full specification. - - Overriding implementations must call this implementation. - """ - self._invoker = None - self.implementation.destantiate(self._memo) - self._digest_pool.shutdown(wait=True) - - def testSuccessfulUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = _Callback() - - response_future = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - response_future.add_done_callback(callback) - response = response_future.result() - - test_messages.verify(request, response, self) - self.assertIs(callback.future(), response_future) - self.assertIsNone(response_future.exception()) - self.assertIsNone(response_future.traceback()) - - def testSuccessfulUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - response_iterator = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(request, responses, self) - - def testSuccessfulStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - callback = _Callback() - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_future = self._invoker.future(group, method)( - request_iterator, test_constants.LONG_TIMEOUT) - response_future.add_done_callback(callback) - future_passed_to_callback = callback.future() - response = future_passed_to_callback.result() - - test_messages.verify(requests, response, self) - self.assertIs(future_passed_to_callback, response_future) - self.assertIsNone(response_future.exception()) - self.assertIsNone(response_future.traceback()) - - def testSuccessfulStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - request_iterator = _PauseableIterator(iter(requests)) - - # Use of a paused iterator of requests allows us to test that control is - # returned to calling code before the iterator yields any requests. - with request_iterator.pause(): - response_iterator = self._invoker.future(group, method)( - request_iterator, test_constants.LONG_TIMEOUT) - responses = list(response_iterator) - - test_messages.verify(requests, responses, self) - - def testSequentialInvocations(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self._invoker.future(group, method)( - first_request, test_constants.LONG_TIMEOUT) - first_response = first_response_future.result() - - test_messages.verify(first_request, first_response, self) - - second_response_future = self._invoker.future(group, method)( - second_request, test_constants.LONG_TIMEOUT) - second_response = second_response_future.result() - - test_messages.verify(second_request, second_response, self) - - def testParallelInvocations(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - first_request = test_messages.request() - second_request = test_messages.request() - - first_response_future = self._invoker.future(group, method)( - first_request, test_constants.LONG_TIMEOUT) - second_response_future = self._invoker.future(group, method)( - second_request, test_constants.LONG_TIMEOUT) - first_response = first_response_future.result() - second_response = second_response_future.result() - - test_messages.verify(first_request, first_response, self) - test_messages.verify(second_request, second_response, self) - - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = [] - response_futures = [] - for _ in range(test_constants.THREAD_CONCURRENCY): - request = test_messages.request() - response_future = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - requests.append(request) - response_futures.append(response_future) - - responses = [ - response_future.result() - for response_future in response_futures - ] - - for request, response in zip(requests, responses): - test_messages.verify(request, response, self) - - def testWaitingForSomeButNotAllParallelInvocations(self): - pool = logging_pool.pool(test_constants.THREAD_CONCURRENCY) - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = [] - response_futures_to_indices = {} - for index in range(test_constants.THREAD_CONCURRENCY): - request = test_messages.request() - inner_response_future = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - outer_response_future = pool.submit( - inner_response_future.result) - requests.append(request) - response_futures_to_indices[outer_response_future] = index - - some_completed_response_futures_iterator = itertools.islice( - futures.as_completed(response_futures_to_indices), - test_constants.THREAD_CONCURRENCY // 2) - for response_future in some_completed_response_futures_iterator: - index = response_futures_to_indices[response_future] - test_messages.verify(requests[index], - response_future.result(), self) - pool.shutdown(wait=True) - - def testCancelledUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = _Callback() - - with self._control.pause(): - response_future = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - response_future.add_done_callback(callback) - cancel_method_return_value = response_future.cancel() - - self.assertIs(callback.future(), response_future) - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - with self.assertRaises(future.CancelledError): - response_future.result() - with self.assertRaises(future.CancelledError): - response_future.exception() - with self.assertRaises(future.CancelledError): - response_future.traceback() - - def testCancelledUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.pause(): - response_iterator = self._invoker.future(group, method)( - request, test_constants.LONG_TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(face.CancellationError): - next(response_iterator) - - def testCancelledStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = _Callback() - - with self._control.pause(): - response_future = self._invoker.future(group, method)( - iter(requests), test_constants.LONG_TIMEOUT) - response_future.add_done_callback(callback) - cancel_method_return_value = response_future.cancel() - - self.assertIs(callback.future(), response_future) - self.assertFalse(cancel_method_return_value) - self.assertTrue(response_future.cancelled()) - with self.assertRaises(future.CancelledError): - response_future.result() - with self.assertRaises(future.CancelledError): - response_future.exception() - with self.assertRaises(future.CancelledError): - response_future.traceback() - - def testCancelledStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.pause(): - response_iterator = self._invoker.future(group, method)( - iter(requests), test_constants.LONG_TIMEOUT) - response_iterator.cancel() - - with self.assertRaises(face.CancellationError): - next(response_iterator) - - def testExpiredUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = _Callback() - - with self._control.pause(): - response_future = self._invoker.future(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - response_future.add_done_callback(callback) - self.assertIs(callback.future(), response_future) - self.assertIsInstance(response_future.exception(), - face.ExpirationError) - with self.assertRaises(face.ExpirationError): - response_future.result() - self.assertIsInstance(response_future.exception(), - face.AbortionError) - self.assertIsNotNone(response_future.traceback()) - - def testExpiredUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - with self._control.pause(): - response_iterator = self._invoker.future(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - with self.assertRaises(face.ExpirationError): - list(response_iterator) - - def testExpiredStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = _Callback() - - with self._control.pause(): - response_future = self._invoker.future( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - response_future.add_done_callback(callback) - self.assertIs(callback.future(), response_future) - self.assertIsInstance(response_future.exception(), - face.ExpirationError) - with self.assertRaises(face.ExpirationError): - response_future.result() - self.assertIsInstance(response_future.exception(), - face.AbortionError) - self.assertIsNotNone(response_future.traceback()) - - def testExpiredStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - with self._control.pause(): - response_iterator = self._invoker.future( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - with self.assertRaises(face.ExpirationError): - list(response_iterator) - - def testFailedUnaryRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_unary_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - callback = _Callback() - abortion_callback = _Callback() - - with self._control.fail(): - response_future = self._invoker.future(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - response_future.add_done_callback(callback) - response_future.add_abortion_callback(abortion_callback) - - self.assertIs(callback.future(), response_future) - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance(response_future.exception(), - face.ExpirationError) - with self.assertRaises(face.ExpirationError): - response_future.result() - self.assertIsNotNone(response_future.traceback()) - self.assertIsNotNone(abortion_callback.future()) - - def testFailedUnaryRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.unary_stream_messages_sequences)): - for test_messages in test_messages_sequence: - request = test_messages.request() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self._control.fail(), self.assertRaises( - face.ExpirationError): - response_iterator = self._invoker.future(group, method)( - request, _3069_test_constant.REALLY_SHORT_TIMEOUT) - list(response_iterator) - - def testFailedStreamRequestUnaryResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_unary_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - callback = _Callback() - abortion_callback = _Callback() - - with self._control.fail(): - response_future = self._invoker.future( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - response_future.add_done_callback(callback) - response_future.add_abortion_callback(abortion_callback) - - self.assertIs(callback.future(), response_future) - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is - # indistinguishable from simply not having called its - # response_callback before the expiration of the RPC. - self.assertIsInstance(response_future.exception(), - face.ExpirationError) - with self.assertRaises(face.ExpirationError): - response_future.result() - self.assertIsNotNone(response_future.traceback()) - self.assertIsNotNone(abortion_callback.future()) - - def testFailedStreamRequestStreamResponse(self): - for (group, method), test_messages_sequence in (six.iteritems( - self._digest.stream_stream_messages_sequences)): - for test_messages in test_messages_sequence: - requests = test_messages.requests() - - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_consumer before the - # expiration of the RPC. - with self._control.fail(), self.assertRaises( - face.ExpirationError): - response_iterator = self._invoker.future( - group, method)(iter(requests), - _3069_test_constant.REALLY_SHORT_TIMEOUT) - list(response_iterator) diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_invocation.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_invocation.py deleted file mode 100644 index efc93d56b0..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_invocation.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Coverage across the Face layer's generic-to-dynamic range for invocation.""" - -import abc - -import six - -from grpc.framework.common import cardinality - -_CARDINALITY_TO_GENERIC_BLOCKING_BEHAVIOR = { - cardinality.Cardinality.UNARY_UNARY: 'blocking_unary_unary', - cardinality.Cardinality.UNARY_STREAM: 'inline_unary_stream', - cardinality.Cardinality.STREAM_UNARY: 'blocking_stream_unary', - cardinality.Cardinality.STREAM_STREAM: 'inline_stream_stream', -} - -_CARDINALITY_TO_GENERIC_FUTURE_BEHAVIOR = { - cardinality.Cardinality.UNARY_UNARY: 'future_unary_unary', - cardinality.Cardinality.UNARY_STREAM: 'inline_unary_stream', - cardinality.Cardinality.STREAM_UNARY: 'future_stream_unary', - cardinality.Cardinality.STREAM_STREAM: 'inline_stream_stream', -} - -_CARDINALITY_TO_GENERIC_EVENT_BEHAVIOR = { - cardinality.Cardinality.UNARY_UNARY: 'event_unary_unary', - cardinality.Cardinality.UNARY_STREAM: 'event_unary_stream', - cardinality.Cardinality.STREAM_UNARY: 'event_stream_unary', - cardinality.Cardinality.STREAM_STREAM: 'event_stream_stream', -} - -_CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE = { - cardinality.Cardinality.UNARY_UNARY: 'unary_unary', - cardinality.Cardinality.UNARY_STREAM: 'unary_stream', - cardinality.Cardinality.STREAM_UNARY: 'stream_unary', - cardinality.Cardinality.STREAM_STREAM: 'stream_stream', -} - - -class Invoker(six.with_metaclass(abc.ABCMeta)): - """A type used to invoke test RPCs.""" - - @abc.abstractmethod - def blocking(self, group, name): - """Invokes an RPC with blocking control flow.""" - raise NotImplementedError() - - @abc.abstractmethod - def future(self, group, name): - """Invokes an RPC with future control flow.""" - raise NotImplementedError() - - @abc.abstractmethod - def event(self, group, name): - """Invokes an RPC with event control flow.""" - raise NotImplementedError() - - -class InvokerConstructor(six.with_metaclass(abc.ABCMeta)): - """A type used to create Invokers.""" - - @abc.abstractmethod - def name(self): - """Specifies the name of the Invoker constructed by this object.""" - raise NotImplementedError() - - @abc.abstractmethod - def construct_invoker(self, generic_stub, dynamic_stubs, methods): - """Constructs an Invoker for the given stubs and methods.""" - raise NotImplementedError() - - -class _GenericInvoker(Invoker): - - def __init__(self, generic_stub, methods): - self._stub = generic_stub - self._methods = methods - - def _behavior(self, group, name, cardinality_to_generic_method): - method_cardinality = self._methods[group, name].cardinality() - behavior = getattr(self._stub, - cardinality_to_generic_method[method_cardinality]) - return lambda *args, **kwargs: behavior(group, name, *args, **kwargs) - - def blocking(self, group, name): - return self._behavior(group, name, - _CARDINALITY_TO_GENERIC_BLOCKING_BEHAVIOR) - - def future(self, group, name): - return self._behavior(group, name, - _CARDINALITY_TO_GENERIC_FUTURE_BEHAVIOR) - - def event(self, group, name): - return self._behavior(group, name, - _CARDINALITY_TO_GENERIC_EVENT_BEHAVIOR) - - -class _GenericInvokerConstructor(InvokerConstructor): - - def name(self): - return 'GenericInvoker' - - def construct_invoker(self, generic_stub, dynamic_stub, methods): - return _GenericInvoker(generic_stub, methods) - - -class _MultiCallableInvoker(Invoker): - - def __init__(self, generic_stub, methods): - self._stub = generic_stub - self._methods = methods - - def _multi_callable(self, group, name): - method_cardinality = self._methods[group, name].cardinality() - behavior = getattr( - self._stub, - _CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE[method_cardinality]) - return behavior(group, name) - - def blocking(self, group, name): - return self._multi_callable(group, name) - - def future(self, group, name): - method_cardinality = self._methods[group, name].cardinality() - behavior = getattr( - self._stub, - _CARDINALITY_TO_MULTI_CALLABLE_ATTRIBUTE[method_cardinality]) - if method_cardinality in (cardinality.Cardinality.UNARY_UNARY, - cardinality.Cardinality.STREAM_UNARY): - return behavior(group, name).future - else: - return behavior(group, name) - - def event(self, group, name): - return self._multi_callable(group, name).event - - -class _MultiCallableInvokerConstructor(InvokerConstructor): - - def name(self): - return 'MultiCallableInvoker' - - def construct_invoker(self, generic_stub, dynamic_stub, methods): - return _MultiCallableInvoker(generic_stub, methods) - - -class _DynamicInvoker(Invoker): - - def __init__(self, dynamic_stubs, methods): - self._stubs = dynamic_stubs - self._methods = methods - - def blocking(self, group, name): - return getattr(self._stubs[group], name) - - def future(self, group, name): - if self._methods[group, name].cardinality() in ( - cardinality.Cardinality.UNARY_UNARY, - cardinality.Cardinality.STREAM_UNARY): - return getattr(self._stubs[group], name).future - else: - return getattr(self._stubs[group], name) - - def event(self, group, name): - return getattr(self._stubs[group], name).event - - -class _DynamicInvokerConstructor(InvokerConstructor): - - def name(self): - return 'DynamicInvoker' - - def construct_invoker(self, generic_stub, dynamic_stubs, methods): - return _DynamicInvoker(dynamic_stubs, methods) - - -def invoker_constructors(): - """Creates a sequence of InvokerConstructors to use in tests of RPCs. - - Returns: - A sequence of InvokerConstructors. - """ - return ( - _GenericInvokerConstructor(), - _MultiCallableInvokerConstructor(), - _DynamicInvokerConstructor(), - ) diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_service.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_service.py deleted file mode 100644 index f1c96b6dc5..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_service.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Private interfaces implemented by data sets used in Face-layer tests.""" - -import abc - -import six - -# face is referenced from specification in this module. -from grpc.framework.interfaces.face import face # pylint: disable=unused-import -from tests.unit.framework.interfaces.face import test_interfaces - - -class UnaryUnaryTestMethodImplementation( - six.with_metaclass(abc.ABCMeta, test_interfaces.Method)): - """A controllable implementation of a unary-unary method.""" - - @abc.abstractmethod - def service(self, request, response_callback, context, control): - """Services an RPC that accepts one message and produces one message. - - Args: - request: The single request message for the RPC. - response_callback: A callback to be called to accept the response message - of the RPC. - context: An face.ServicerContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryUnaryTestMessages(six.with_metaclass(abc.ABCMeta)): - """A type for unary-request-unary-response message pairings.""" - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, response, test_case): - """Verifies that the computed response matches the given request. - - Args: - request: A request message. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class UnaryStreamTestMethodImplementation( - six.with_metaclass(abc.ABCMeta, test_interfaces.Method)): - """A controllable implementation of a unary-stream method.""" - - @abc.abstractmethod - def service(self, request, response_consumer, context, control): - """Services an RPC that takes one message and produces a stream of messages. - - Args: - request: The single request message for the RPC. - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face.ServicerContext object. - control: A test_control.Control to control execution of this method. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class UnaryStreamTestMessages(six.with_metaclass(abc.ABCMeta)): - """A type for unary-request-stream-response message pairings.""" - - @abc.abstractmethod - def request(self): - """Affords a request message. - - Implementations of this method should return a different message with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A request message. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, request, responses, test_case): - """Verifies that the computed responses match the given request. - - Args: - request: A request message. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the request and responses do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamUnaryTestMethodImplementation( - six.with_metaclass(abc.ABCMeta, test_interfaces.Method)): - """A controllable implementation of a stream-unary method.""" - - @abc.abstractmethod - def service(self, response_callback, context, control): - """Services an RPC that takes a stream of messages and produces one message. - - Args: - response_callback: A callback to be called to accept the response message - of the RPC. - context: A face.ServicerContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamUnaryTestMessages(six.with_metaclass(abc.ABCMeta)): - """A type for stream-request-unary-response message pairings.""" - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, response, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - response: A response message. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and response do not match, indicating that - there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class StreamStreamTestMethodImplementation( - six.with_metaclass(abc.ABCMeta, test_interfaces.Method)): - """A controllable implementation of a stream-stream method.""" - - @abc.abstractmethod - def service(self, response_consumer, context, control): - """Services an RPC that accepts and produces streams of messages. - - Args: - response_consumer: A stream.Consumer to be called to accept the response - messages of the RPC. - context: A face.ServicerContext object. - control: A test_control.Control to control execution of this method. - - Returns: - A stream.Consumer with which to accept the request messages of the RPC. - The consumer returned from this method may or may not be invoked to - completion: in the case of RPC abortion, RPC Framework will simply stop - passing messages to this object. Implementations must not assume that - this object will be called to completion of the request stream or even - called at all. - - Raises: - abandonment.Abandoned: May or may not be raised when the RPC has been - aborted. - """ - raise NotImplementedError() - - -class StreamStreamTestMessages(six.with_metaclass(abc.ABCMeta)): - """A type for stream-request-stream-response message pairings.""" - - @abc.abstractmethod - def requests(self): - """Affords a sequence of request messages. - - Implementations of this method should return a different sequences with each - call so that multiple test executions of the test method may be made with - different inputs. - - Returns: - A sequence of request messages. - """ - raise NotImplementedError() - - @abc.abstractmethod - def verify(self, requests, responses, test_case): - """Verifies that the computed response matches the given requests. - - Args: - requests: A sequence of request messages. - responses: A sequence of response messages. - test_case: A unittest.TestCase object affording useful assertion methods. - - Raises: - AssertionError: If the requests and responses do not match, indicating - that there was some problem executing the RPC under test. - """ - raise NotImplementedError() - - -class TestService(six.with_metaclass(abc.ABCMeta)): - """A specification of implemented methods to use in tests.""" - - @abc.abstractmethod - def unary_unary_scenarios(self): - """Affords unary-request-unary-response test methods and their messages. - - Returns: - A dict from method group-name pair to implementation/messages pair. The - first element of the pair is a UnaryUnaryTestMethodImplementation object - and the second element is a sequence of UnaryUnaryTestMethodMessages - objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def unary_stream_scenarios(self): - """Affords unary-request-stream-response test methods and their messages. - - Returns: - A dict from method group-name pair to implementation/messages pair. The - first element of the pair is a UnaryStreamTestMethodImplementation - object and the second element is a sequence of - UnaryStreamTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_unary_scenarios(self): - """Affords stream-request-unary-response test methods and their messages. - - Returns: - A dict from method group-name pair to implementation/messages pair. The - first element of the pair is a StreamUnaryTestMethodImplementation - object and the second element is a sequence of - StreamUnaryTestMethodMessages objects. - """ - raise NotImplementedError() - - @abc.abstractmethod - def stream_stream_scenarios(self): - """Affords stream-request-stream-response test methods and their messages. - - Returns: - A dict from method group-name pair to implementation/messages pair. The - first element of the pair is a StreamStreamTestMethodImplementation - object and the second element is a sequence of - StreamStreamTestMethodMessages objects. - """ - raise NotImplementedError() diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_stock_service.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_stock_service.py deleted file mode 100644 index a84e02a79a..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/_stock_service.py +++ /dev/null @@ -1,390 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Examples of Python implementations of the stock.proto Stock service.""" - -from grpc.framework.common import cardinality -from grpc.framework.foundation import abandonment -from grpc.framework.foundation import stream -from tests.unit.framework.common import test_constants -from tests.unit.framework.interfaces.face import _service -from tests.unit._junkdrawer import stock_pb2 - -_STOCK_GROUP_NAME = 'Stock' -_SYMBOL_FORMAT = 'test symbol:%03d' - -# A test-appropriate security-pricing function. :-P -_price = lambda symbol_name: float(hash(symbol_name) % 4096) - - -def _get_last_trade_price(stock_request, stock_reply_callback, control, active): - """A unary-request, unary-response test method.""" - control.control() - if active(): - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, - price=_price(stock_request.symbol))) - else: - raise abandonment.Abandoned() - - -def _get_last_trade_price_multiple(stock_reply_consumer, control, active): - """A stream-request, stream-response test method.""" - - def stock_reply_for_stock_request(stock_request): - control.control() - if active(): - return stock_pb2.StockReply( - symbol=stock_request.symbol, price=_price(stock_request.symbol)) - else: - raise abandonment.Abandoned() - - class StockRequestConsumer(stream.Consumer): - - def consume(self, stock_request): - stock_reply_consumer.consume( - stock_reply_for_stock_request(stock_request)) - - def terminate(self): - control.control() - stock_reply_consumer.terminate() - - def consume_and_terminate(self, stock_request): - stock_reply_consumer.consume_and_terminate( - stock_reply_for_stock_request(stock_request)) - - return StockRequestConsumer() - - -def _watch_future_trades(stock_request, stock_reply_consumer, control, active): - """A unary-request, stream-response test method.""" - base_price = _price(stock_request.symbol) - for index in range(stock_request.num_trades_to_watch): - control.control() - if active(): - stock_reply_consumer.consume( - stock_pb2.StockReply( - symbol=stock_request.symbol, price=base_price + index)) - else: - raise abandonment.Abandoned() - stock_reply_consumer.terminate() - - -def _get_highest_trade_price(stock_reply_callback, control, active): - """A stream-request, unary-response test method.""" - - class StockRequestConsumer(stream.Consumer): - """Keeps an ongoing record of the most valuable symbol yet consumed.""" - - def __init__(self): - self._symbol = None - self._price = None - - def consume(self, stock_request): - control.control() - if active(): - if self._price is None: - self._symbol = stock_request.symbol - self._price = _price(stock_request.symbol) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - self._symbol = stock_request.symbol - self._price = candidate_price - - def terminate(self): - control.control() - if active(): - if self._symbol is None: - raise ValueError() - else: - stock_reply_callback( - stock_pb2.StockReply( - symbol=self._symbol, price=self._price)) - self._symbol = None - self._price = None - - def consume_and_terminate(self, stock_request): - control.control() - if active(): - if self._price is None: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, - price=_price(stock_request.symbol))) - else: - candidate_price = _price(stock_request.symbol) - if self._price < candidate_price: - stock_reply_callback( - stock_pb2.StockReply( - symbol=stock_request.symbol, - price=candidate_price)) - else: - stock_reply_callback( - stock_pb2.StockReply( - symbol=self._symbol, price=self._price)) - - self._symbol = None - self._price = None - - return StockRequestConsumer() - - -class GetLastTradePrice(_service.UnaryUnaryTestMethodImplementation): - """GetLastTradePrice for use in tests.""" - - def group(self): - return _STOCK_GROUP_NAME - - def name(self): - return 'GetLastTradePrice' - - def cardinality(self): - return cardinality.Cardinality.UNARY_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_callback, context, control): - _get_last_trade_price(request, response_callback, control, - context.is_active) - - -class GetLastTradePriceMessages(_service.UnaryUnaryTestMessages): - - def __init__(self): - self._index = 0 - - def request(self): - symbol = _SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest(symbol=symbol) - - def verify(self, request, response, test_case): - test_case.assertEqual(request.symbol, response.symbol) - test_case.assertEqual(_price(request.symbol), response.price) - - -class GetLastTradePriceMultiple(_service.StreamStreamTestMethodImplementation): - """GetLastTradePriceMultiple for use in tests.""" - - def group(self): - return _STOCK_GROUP_NAME - - def name(self): - return 'GetLastTradePriceMultiple' - - def cardinality(self): - return cardinality.Cardinality.STREAM_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_consumer, context, control): - return _get_last_trade_price_multiple(response_consumer, control, - context.is_active) - - -class GetLastTradePriceMultipleMessages(_service.StreamStreamTestMessages): - """Pairs of message streams for use with GetLastTradePriceMultiple.""" - - def __init__(self): - self._index = 0 - - def requests(self): - base_index = self._index - self._index += 1 - return [ - stock_pb2.StockRequest(symbol=_SYMBOL_FORMAT % (base_index + index)) - for index in range(test_constants.STREAM_LENGTH) - ] - - def verify(self, requests, responses, test_case): - test_case.assertEqual(len(requests), len(responses)) - for stock_request, stock_reply in zip(requests, responses): - test_case.assertEqual(stock_request.symbol, stock_reply.symbol) - test_case.assertEqual( - _price(stock_request.symbol), stock_reply.price) - - -class WatchFutureTrades(_service.UnaryStreamTestMethodImplementation): - """WatchFutureTrades for use in tests.""" - - def group(self): - return _STOCK_GROUP_NAME - - def name(self): - return 'WatchFutureTrades' - - def cardinality(self): - return cardinality.Cardinality.UNARY_STREAM - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, request, response_consumer, context, control): - _watch_future_trades(request, response_consumer, control, - context.is_active) - - -class WatchFutureTradesMessages(_service.UnaryStreamTestMessages): - """Pairs of a single request message and a sequence of response messages.""" - - def __init__(self): - self._index = 0 - - def request(self): - symbol = _SYMBOL_FORMAT % self._index - self._index += 1 - return stock_pb2.StockRequest( - symbol=symbol, num_trades_to_watch=test_constants.STREAM_LENGTH) - - def verify(self, request, responses, test_case): - test_case.assertEqual(test_constants.STREAM_LENGTH, len(responses)) - base_price = _price(request.symbol) - for index, response in enumerate(responses): - test_case.assertEqual(base_price + index, response.price) - - -class GetHighestTradePrice(_service.StreamUnaryTestMethodImplementation): - """GetHighestTradePrice for use in tests.""" - - def group(self): - return _STOCK_GROUP_NAME - - def name(self): - return 'GetHighestTradePrice' - - def cardinality(self): - return cardinality.Cardinality.STREAM_UNARY - - def request_class(self): - return stock_pb2.StockRequest - - def response_class(self): - return stock_pb2.StockReply - - def serialize_request(self, request): - return request.SerializeToString() - - def deserialize_request(self, serialized_request): - return stock_pb2.StockRequest.FromString(serialized_request) - - def serialize_response(self, response): - return response.SerializeToString() - - def deserialize_response(self, serialized_response): - return stock_pb2.StockReply.FromString(serialized_response) - - def service(self, response_callback, context, control): - return _get_highest_trade_price(response_callback, control, - context.is_active) - - -class GetHighestTradePriceMessages(_service.StreamUnaryTestMessages): - - def requests(self): - return [ - stock_pb2.StockRequest(symbol=_SYMBOL_FORMAT % index) - for index in range(test_constants.STREAM_LENGTH) - ] - - def verify(self, requests, response, test_case): - price = None - symbol = None - for stock_request in requests: - current_symbol = stock_request.symbol - current_price = _price(current_symbol) - if price is None or price < current_price: - price = current_price - symbol = current_symbol - test_case.assertEqual(price, response.price) - test_case.assertEqual(symbol, response.symbol) - - -class StockTestService(_service.TestService): - """A corpus of test data with one method of each RPC cardinality.""" - - def unary_unary_scenarios(self): - return { - (_STOCK_GROUP_NAME, 'GetLastTradePrice'): - (GetLastTradePrice(), [GetLastTradePriceMessages()]), - } - - def unary_stream_scenarios(self): - return { - (_STOCK_GROUP_NAME, 'WatchFutureTrades'): - (WatchFutureTrades(), [WatchFutureTradesMessages()]), - } - - def stream_unary_scenarios(self): - return { - (_STOCK_GROUP_NAME, 'GetHighestTradePrice'): - (GetHighestTradePrice(), [GetHighestTradePriceMessages()]) - } - - def stream_stream_scenarios(self): - return { - (_STOCK_GROUP_NAME, 'GetLastTradePriceMultiple'): - (GetLastTradePriceMultiple(), - [GetLastTradePriceMultipleMessages()]), - } - - -STOCK_TEST_SERVICE = StockTestService() diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_cases.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_cases.py deleted file mode 100644 index cff4b7cdea..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_cases.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Tools for creating tests of implementations of the Face layer.""" - -# unittest is referenced from specification in this module. -import unittest # pylint: disable=unused-import - -# test_interfaces is referenced from specification in this module. -from tests.unit.framework.interfaces.face import _blocking_invocation_inline_service -from tests.unit.framework.interfaces.face import _future_invocation_asynchronous_event_service -from tests.unit.framework.interfaces.face import _invocation -from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import - -_TEST_CASE_SUPERCLASSES = ( - _blocking_invocation_inline_service.TestCase, - _future_invocation_asynchronous_event_service.TestCase, -) - - -def test_cases(implementation): - """Creates unittest.TestCase classes for a given Face layer implementation. - - Args: - implementation: A test_interfaces.Implementation specifying creation and - destruction of a given Face layer implementation. - - Returns: - A sequence of subclasses of unittest.TestCase defining tests of the - specified Face layer implementation. - """ - test_case_classes = [] - for invoker_constructor in _invocation.invoker_constructors(): - for super_class in _TEST_CASE_SUPERCLASSES: - test_case_classes.append( - type( - invoker_constructor.name() + super_class.NAME, - (super_class,), { - 'implementation': implementation, - 'invoker_constructor': invoker_constructor, - '__module__': implementation.__module__, - })) - return test_case_classes diff --git a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_interfaces.py b/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_interfaces.py deleted file mode 100644 index d0de8e1c54..0000000000 --- a/src/python/grpcio_tests/tests/unit/framework/interfaces/face/test_interfaces.py +++ /dev/null @@ -1,212 +0,0 @@ -# Copyright 2015 gRPC authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Interfaces used in tests of implementations of the Face layer.""" - -import abc - -import six - -from grpc.framework.common import cardinality # pylint: disable=unused-import -from grpc.framework.interfaces.face import face # pylint: disable=unused-import - - -class Method(six.with_metaclass(abc.ABCMeta)): - """Specifies a method to be used in tests.""" - - @abc.abstractmethod - def group(self): - """Identify the group of the method. - - Returns: - The group of the method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def name(self): - """Identify the name of the method. - - Returns: - The name of the method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def cardinality(self): - """Identify the cardinality of the method. - - Returns: - A cardinality.Cardinality value describing the streaming semantics of the - method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def request_class(self): - """Identify the class used for the method's request objects. - - Returns: - The class object of the class to which the method's request objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def response_class(self): - """Identify the class used for the method's response objects. - - Returns: - The class object of the class to which the method's response objects - belong. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_request(self, request): - """Serialize the given request object. - - Args: - request: A request object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_request(self, serialized_request): - """Synthesize a request object from a given bytestring. - - Args: - serialized_request: A bytestring deserializable into a request object - appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def serialize_response(self, response): - """Serialize the given response object. - - Args: - response: A response object appropriate for this method. - """ - raise NotImplementedError() - - @abc.abstractmethod - def deserialize_response(self, serialized_response): - """Synthesize a response object from a given bytestring. - - Args: - serialized_response: A bytestring deserializable into a response object - appropriate for this method. - """ - raise NotImplementedError() - - -class Implementation(six.with_metaclass(abc.ABCMeta)): - """Specifies an implementation of the Face layer.""" - - @abc.abstractmethod - def instantiate(self, methods, method_implementations, - multi_method_implementation): - """Instantiates the Face layer implementation to be used in a test. - - Args: - methods: A sequence of Method objects describing the methods available to - be called during the test. - method_implementations: A dictionary from group-name pair to - face.MethodImplementation object specifying implementation of a method. - multi_method_implementation: A face.MultiMethodImplementation or None. - - Returns: - A sequence of length three the first element of which is a - face.GenericStub, the second element of which is dictionary from groups - to face.DynamicStubs affording invocation of the group's methods, and - the third element of which is an arbitrary memo object to be kept and - passed to destantiate at the conclusion of the test. The returned stubs - must be backed by the provided implementations. - """ - raise NotImplementedError() - - @abc.abstractmethod - def destantiate(self, memo): - """Destroys the Face layer implementation under test. - - Args: - memo: The object from the third position of the return value of a call to - instantiate. - """ - raise NotImplementedError() - - @abc.abstractmethod - def invocation_metadata(self): - """Provides the metadata to be used when invoking a test RPC. - - Returns: - An object to use as the supplied-at-invocation-time metadata in a test - RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def initial_metadata(self): - """Provides the metadata for use as a test RPC's first servicer metadata. - - Returns: - An object to use as the from-the-servicer-before-responses metadata in a - test RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def terminal_metadata(self): - """Provides the metadata for use as a test RPC's second servicer metadata. - - Returns: - An object to use as the from-the-servicer-after-all-responses metadata in - a test RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def code(self): - """Provides the value for use as a test RPC's code. - - Returns: - An object to use as the from-the-servicer code in a test RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def details(self): - """Provides the value for use as a test RPC's details. - - Returns: - An object to use as the from-the-servicer details in a test RPC. - """ - raise NotImplementedError() - - @abc.abstractmethod - def metadata_transmitted(self, original_metadata, transmitted_metadata): - """Identifies whether or not metadata was properly transmitted. - - Args: - original_metadata: A metadata value passed to the Face interface - implementation under test. - transmitted_metadata: The same metadata value after having been - transmitted via an RPC performed by the Face interface implementation - under test. - - Returns: - Whether or not the metadata was properly transmitted by the Face interface - implementation under test. - """ - raise NotImplementedError() |