text:
## This file auto-generated by Yggdrasil/build_hashes.jl
shard_hash_table = Dict{CompilerShard,String}(
    CompilerShard("Rootfs", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz)
      => "6b22211eac7a1487fb6d50fb9767ef1c2c4118197bf209b056166ff004c7cfa9",
    CompilerShard("Rootfs", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs)
      => "1f1d75015c5d7eb2f4a110f9f1075eba90af3e688c42df8016d8989bf4e65d99",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-gnu"))
      => "55b81de44c3c1288d978df3a313ec2dacdf962a7b3bd1241baf388f7f7aa9ebc",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-gnu"))
      => "a20a9901dd8591571371c605bd9eaf21a18b45be073d8532e372bef264ce0823",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-musl"))
      => "e6c0da34d84f5156cf8b891dc29f36aa7d6da870efed6d634b0cedda98496e70",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-musl"))
      => "82258dcf4cdea285b51ad0d71b17294397695cd235506d2609c1b62084c35524",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-gnueabihf"))
      => "1949af8aa6ce9e86a7a584361ff7c1189da4238d1ccae5363cdb9aec8c15e70b",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-gnueabihf"))
      => "a7a67c3c77145ad1c6e3885f2bc6de02a0c2071a242bc2613faa631fa720cd62",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-musleabihf"))
      => "b56ce7a1f033e0cadf69d3e1f38f43fc4062ba8d6d9c0b068529ec849030a801",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-musleabihf"))
      => "ee009119faa6421aaf786c73fd3fb252d801ba6f974b6f9717b57468e8c14548",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-gnu"))
      => "52d91b32ba0abc0fb32f8e74a69409e4a8d1df78679fc097065309857c38e2eb",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-gnu"))
      => "02f4c9e95e69ea218e0ddc5139e33d10971fdbcc84723d1a6855ce3f59c8b1c7",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-musl"))
      => "4bc43a39c248aaf16eddc88e521a9c2e8339c49537c5e5e83954c6bc916a4365",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-musl"))
      => "2d0c7266a9e167a01b494ed3eebf7b8164a0b0550721b12fa22971adff1fe7c5",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-w64-mingw32"))
      => "93dd9abb3f5330a36e13ea5464ae2dd92859b47eb5fabb943464c877158c57bf",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-w64-mingw32"))
      => "b21966a85df9b741c4c3d8955d98769e54407e323a445b75d2bc7eb14986e37c",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "6e0ed756a8b5e70be66aee6c2f1994ad25bef53366b473324bb04b479495138d",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "eb69b5dd82788d4118a604604831804970cd73c203689494bd128847d06a08ec",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-apple-darwin14"))
      => "1f8717d9c8a385454cb749d75bfe3160416115a9d630e1ea543ddecef24decf1",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-apple-darwin14"))
      => "22cce73c5b3c94011e7fc7fe4948768598d0021caab54c132328c52d66ad7864",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-gnu"))
      => "7e444fa3dd2c16cde37ca49b6df4c7ed230a30c4059a7ac347b0d5bb1c4a3c7b",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-gnu"))
      => "bcafae0da9a91ae68f3d74c9c1461fb16824406ed22ff89d98dc7e79545b5732",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-musl"))
      => "dbc616ad588219c31affd1889946ed0549a4d01a82ad35d68a4640ec60ad7642",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-musl"))
      => "9adea9fb5cad18d17e311cc81efdc43844e5c55ce40740af7bae7e5dba13fc99",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "c100d7d05811460810d498af764e342b76f2a7df4b54013936091a3609c38348",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "f0e0a974b2b41c9d2c831b0e2dd623a8b82ed32b60a7b54019d55348759c5a1e",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-w64-mingw32"))
      => "d70cdc527a8a503433f0f243fb309261fde062d269eeed3a020b9ead3576fcbc",
    CompilerShard("BaseCompilerShard", v"2018.11.11", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-w64-mingw32"))
      => "ac193e888b6ff4b32c71b9746634db444c25065ddb96240fe2818e6fc8057643",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-gnu"))
      => "7dc166b739b9aa587ce2d4cbf7fa020704489942ad4f2526e232ba30a6d3435d",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-gnu"))
      => "4ad459a0375b4c6c923e979bf0e0c6f65f93b7bd43f6f4916639bbf99b4e8d51",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-gnu"))
      => "fa9b07de69bac846718a7d4ec05687a7d626428db91019a9bdc8278caa880caa",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-gnu"))
      => "ee9273903f6da15d0bdb9d6730c8e77d11bc0e41bfe545fc3ecf2c4d262bef84",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-gnu"))
      => "5b55cc6ee306f93bfc4cb47f4a680e3ca84997e611b1d9d12c612f6d9eeaef70",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-gnu"))
      => "0fc66a43c744dc24b8e8a0dbcb5143af6eb2d6eaa3780a48dfc32254ccc0d38d",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-gnu"))
      => "c806a846d89dd7801ee561e14d1169e2e0f90c461b70de6beaa01347d7ef8c21",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-gnu"))
      => "139c48a5b6bc2f32ce4db7612b23d5ea44878a0ab436521a54c6820c863d77c7",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-musl"))
      => "3d3ce2af22169fc4626b30c2faa87fcbca2c6a178051c7acdf3d5874b927ea9a",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-musl"))
      => "d02784b6cb34ebe6ff2049294a4012e56977f6cdda717bc860eea1b9a84b66cd",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-musl"))
      => "6b007ad3f55665ebebaa99ffb29dff2c8c580e19407b729aefe60ae8f2724d0f",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-musl"))
      => "53282b8056a67c214d7eb8a107b7f5808b72c2a4dfc3e7b73214c58924bb36a5",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-musl"))
      => "0eef33759de7915b18c62880f29da77ece8ac296d2ba4e4f6acd1df081c1c2d8",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-musl"))
      => "199a52fed6b56c20f3615bbe3f884a532803862d602eb9ed797d93e273159980",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("aarch64-linux-musl"))
      => "7009e1e1e46074e8415ae303cb177783e376e277e959bef2de8dda75768d254b",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("aarch64-linux-musl"))
      => "551398cc28d32eea9651b77055baae05ce387f8396e09761c34052d7b19c6849",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-gnueabihf"))
      => "12c156f32eec4a295b7005263258faad0ee78131a6fe96a86e16e8a97993174e",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-gnueabihf"))
      => "886e765c8da85da3df6ae85cea7ba2d0636818c22cc7e9dd727dccbb0fe92bdf",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-gnueabihf"))
      => "0eccbb4767e97c514ed88d17e48a0f4cca5f52da5590e7576e122f00d093b131",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-gnueabihf"))
      => "162ed3c1786a0e5971ecbf45e4cf8551ab357d241b02d25d7760384f428d0e64",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-gnueabihf"))
      => "1a548f235068e991aae2dd6c48463d6ff4907939fe8805c32946e3407bd0324e",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-gnueabihf"))
      => "c3e7635405651f56151497415ee9d5dda93903e63d603bf2b67aae100691cb32",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-gnueabihf"))
      => "48de5284b5b49492bf4a93dd34875038b75cc6ed99a1967944af91d9b12b0d78",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-gnueabihf"))
      => "bb8f931df2253e617f71a612ff1f5c6be4b1f2fc6f2d546a2ea11b5f5198b571",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-musleabihf"))
      => "7850cb66369c8f76946dea0261cbd201140bfdcb2baae455a41f204a03075f92",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-musleabihf"))
      => "ae487494f2df74f534d442ffb8733ef45d8c2385f41150062fa4d18166449216",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-musleabihf"))
      => "cf02dc0fdf026bcb6a1592b31f16d56a612e82c0db92a6527f15b468f223fa11",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-musleabihf"))
      => "7232e5728f3bbac0884e6c040809bdac52d55ff5dd992df668953b375ee2ab6c",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-musleabihf"))
      => "6657446b0614928a5c45d704864c6d4ff6e1695f96d6ecdae97eadd2b72cf995",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-musleabihf"))
      => "7ab1c8af2e615e417f3cb6d2956a9190deb9305f0e29560ab58bae11b5765e01",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("arm-linux-musleabihf"))
      => "8780bad241eff84c17772277c083d26a47ab4590417467d978a2d289e7e3cd7f",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("arm-linux-musleabihf"))
      => "f92cdf344f688529c3d7b4979054a3a5a524c93dbc48246d59455628dcb20708",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-gnu"))
      => "748cf8fdf995b9b8455f911cdd724d3a5eaee2186aec8ef3d796360559fb3ff6",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-gnu"))
      => "196bbaea8ad69640ec84fc23c6a6dfc1a876ede81b0ada93ede6a81229fa8775",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-gnu"))
      => "8570a6ccd20ec4beedb8d086a3553de6125dcbbfc469be65178a37b89e18c6a1",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-gnu"))
      => "4d4f4dcb795371a749d263d99d7120a45e321d1e26724b99476ed6aef29cb135",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-gnu"))
      => "019612f298c42fc678dd8f2311ea4829e5a20f6bd064396de82729822e8e7ab4",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-gnu"))
      => "d01027dcd2449408dca68a60333302ff84517a05d48d1b0547dd97a20852a4ac",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-gnu"))
      => "28774a18cc00c353ac0fb2d0041337de46426cdc8ce88bbdb969c38eb666e0b8",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-gnu"))
      => "dce0beb546bd4060e6737b0a5d997049f0f386c173708934b4de73cecd691251",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-musl"))
      => "a61f94db0252c7a45fad4d296bc24ab662e19425aded183fcc9d0f0ace55383d",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-musl"))
      => "cd1543254059ca984d77d597e097375e65318c76b55680d7118bdbeee5667300",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-musl"))
      => "355ba0a7a99eefc1e9ee6712e936daee24fc593fd1706a5b5e6f7349af34b2b5",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-musl"))
      => "9381a8e1aa1061b9442233a67a487ec93713410392f237a2ba06e56070d187e3",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-musl"))
      => "10eeff7ae6adff390050a4cee9dd162ae574a93af3273207860fade15643d835",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-musl"))
      => "19f3255667baf9dc1ccbac7cf521af2a5f0d050e426fc1ad4fc640544f1b25f5",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-linux-musl"))
      => "c2d0fd3a594036ccdc5b67eaf9b9d150f7bcf7a76096208c38681a7cabe2960e",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-linux-musl"))
      => "d79987b21b85fddca127115df9ae7ff128b450e9408fa40b7f146a1b9e99916a",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-w64-mingw32"))
      => "a6df16225fe350b14445e11c4e9b87f9ff813e5286cece8c86f24fa14afc3e24",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-w64-mingw32"))
      => "5c2c0e51bac622b6c738ac6d9f6389ed67264e37e7e9b1bc5c9ef20fbbcf8841",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-w64-mingw32"))
      => "ec539c84938cf9f7116a1f117a02ca4c3359da35e11f142e47de18cd69670d66",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-w64-mingw32"))
      => "5d6db597ac4cd9739397acda4b3682960c7be078ea99f0208d8019defabc0a0c",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-w64-mingw32"))
      => "8c3232dd0c1cc579342b391e9756c6f33891eb0bc3dd82eb1eb26c658824bfe1",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-w64-mingw32"))
      => "711873d2999d3aa8fb9b61cd9ad0e5bee92de54e16928ecf0dde135e04d2f544",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("i686-w64-mingw32"))
      => "961f369d9d4d89ca328ed3ff072b744243fe2b7e63e0aed52a139ce1c64b9dab",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("i686-w64-mingw32"))
      => "9c696daf5a9411df1e9d3ed0b66a2d16f28de85a351a6eaf1127a7bff329efd5",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "dab6635da13f65823882b0eba7c07922993977d1e8860f7cb30e31028ff0a37f",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "f339fb925614b1525dc347127af57f933c83d9428b1211fe4a84a62a1752fb80",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "2dc53970f0827d12671c39e628048ee841b8b6ce7683998c0315c82ad229e386",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "df47cc1a103ff078a88239b5e18c4215bed8a48b2eb577fc046cb3acfe8143e6",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "985f3161eecdb4d9c9822292e126aaeab4ef4fd723f13b9774025cb33407fdd2",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "f5914829c645060358a914c410b4aa68c1cbf6110cd55d74f93c6378d3d9ec9b",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "e0b9d8d844385501583d660f8c63b34cd2ba1615696301b136a68c59330a80c7",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("powerpc64le-linux-gnu"))
      => "3ac831c81077e19ee62dbef57efd4eb9e1d36489e2f0b2a602578a4e1fd845ef",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-apple-darwin14"))
      => "988acdd754238b732c09ca6e401efa44f21ceacbdccfb8945df6f25d2be3e024",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-apple-darwin14"))
      => "7bfbf57cd1053fe502165602024ac7218d7112bcb416ce7113f5783e371416d2",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-apple-darwin14"))
      => "8f7cdbbd3a08126def1ec7fc4ca964159f82aaa1c702434e72a619003ddd6f76",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-apple-darwin14"))
      => "bbb7387c9f8d1540f55164bff1290e833cc6edb9c27ba0c45ed90e9cade6e33e",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-apple-darwin14"))
      => "fed1e6da9c2e1726bcd2943482343bdd3e7374f3cb3580d42a87e47061bef69a",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-apple-darwin14"))
      => "7fc25bdc08a94e2c05f065436fbe23612dff37b2d2aaa276348afe2b52bc6aca",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-apple-darwin14"))
      => "eca90a29631f07e64f8fcc8ebbf72f6f99518c95d449173ee3db43ff7f494fa2",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-apple-darwin14"))
      => "878deaa5632d4954f638c90c544855c64b7b1d409541568361e2da5b3f4a7a8c",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-gnu"))
      => "4566b42fc012f7c409c7b84c228448181554eb2a9c6ab4f6e11134d5020f2a1e",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-gnu"))
      => "a6227eac0630907fd2c5f809276eed9836a75e3d32fbd55b9475b266ab89cd49",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-gnu"))
      => "7464f662c7a7456494d1f21c42a4e3e27c4cd43608fcc62340afb8a950515813",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-gnu"))
      => "9102e0f47e191587b72094d95c35be82ada1606380c59d4bdeeb4e5b81ce2b42",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-gnu"))
      => "e7616725db345c8cb55273b8273a71106945b1fa5ded9b3111f8bdcc9096efb5",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-gnu"))
      => "173770508429468b54b7c10bbb3903ca244c7842956f5b1dbbe88a476451c2ea",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-gnu"))
      => "de416b08ee85a08c0c0de32dab9c77946d536b2b30a4bc9bfe96acfac0424f00",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-gnu"))
      => "e9584274e172c1dd96fbc20936bd303de12a7061b091315795465eac1709dffe",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-musl"))
      => "cfd8b9b5ece88bb4b31ccec23f842dcf1b42baa793bb5578aac7e3dee3dadd76",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-musl"))
      => "d8352a9097c7154caec3198d4d331381f2732d292a544d83d995d8c443ac34bf",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-musl"))
      => "8b1be200d93bd00ab1a765d90d89d31e8c123b9d2c70045f18879a2a5aae650c",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-musl"))
      => "a4534e525734b29839a301975b4aa1d0f2b3b8d637ae24c5892bf44f52df19f5",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-musl"))
      => "2c599807efb6a61d2fd03077fd51ebc439edac7dabc813a547cdef44423119cc",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-musl"))
      => "6db08582a959772ae6593305407bda521b527e8cd4c6ea4843bb61b17ca28a54",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-linux-musl"))
      => "199a430d2165a0c6cb6835399300b5ec4bba9e85a2e1d6ed4ed5c7167af6ecc7",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-linux-musl"))
      => "7fb291b9b5c9761a5f478f558a20d8e1af1734435d321e5e116d956a46094577",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "3cf20599c1ed19bd80148e36dfe241b808b35cdbe85db251de24211b5d5dc2ff",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "6755b76f31531352f29e0859aef6426ed0b529b2d466b629cdbf205abc100f87",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "d83f73ca0dd8931c8b68f8cdfa5427a06fcbddfc47740a77d45f2b27cf61a84a",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "6a24ad48e20220b3dd8d516b703600cfc1c932f59c72d2721defd5869e70b1eb",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "dc33e79115108bd8474aa84a6879e54f67d2ab3798a18e23c0c73afc236952b3",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "b12a4f982b80d619afcf7680e8f37d3d546f7b4d97e367abe3503f81f1ff5646",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "f85ce7085f811cc7e1fe64104e448e5c01f6f388bbf92af3d4af107c641abc8e",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-unknown-freebsd11.1"))
      => "cb7f44bd30968e86fde8db36ae0e70154180d24f43c187cc73b4d3b44e581bfb",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-w64-mingw32"))
      => "9c3f9e060d293746b5124197e91dbbd561b5b72f684a1c3fdf308c0e001166fb",
    CompilerShard("GCC", v"4.8.5", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-w64-mingw32"))
      => "4d3781a7769923f5d67c4ccdc99497d7baef5216bc370423395fb508cd6ed19c",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-w64-mingw32"))
      => "e67a19befac7cb9fb1e51c3a450aafcb94afadc7b3e9f0f319301daedff8edf2",
    CompilerShard("GCC", v"6.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-w64-mingw32"))
      => "e30c2b5159f6216c5b1db84cf34c8d1bc7722fcc19ac18b931bd4cff36363f4d",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-w64-mingw32"))
      => "779bfcf40c920e3cd744d20684af4cfbf9e553a0eb5522bc4451b1e3dd64571c",
    CompilerShard("GCC", v"7.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-w64-mingw32"))
      => "283dd88174df8be0c20b35aeb4ca7ee1fc143dbc239100b40cd0146075ada4e8",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :targz; target=platform_key_abi("x86_64-w64-mingw32"))
      => "5e9bc0b0970f3852f33e3360ed658a9b538b5b09e6b87d1f0dd0293402c17761",
    CompilerShard("GCC", v"8.1.0", Linux(:x86_64, libc=:glibc), :squashfs; target=platform_key_abi("x86_64-w64-mingw32"))
      => "b24ba4cb2a3749fe1f3d6503533fe9f504a24baf9f16ba37ecbb1fc7c44f05db",
    CompilerShard("LLVM", v"6.0.1-0", Linux(:x86_64, libc=:glibc), :targz)
      => "d53a63d79c29b7a3d0eb3e516e9b8b08cb6d2dfb692a67242323b3737d7e0e9a",
    CompilerShard("LLVM", v"6.0.1-0", Linux(:x86_64, libc=:glibc), :squashfs)
      => "80578c2f73b6d3cd4a5138bd732f4b2aff28968e0f2cf1b35e290f2a202de9f8",
)


text:
module ApproxTools

using LinearAlgebra
using SparseArrays
using FFTW
using IterTools
using Statistics

include("tucker.jl")
export tucker

include("grideval.jl")
export grideval, @gridfun

include("LogNumbers.jl")

include("ArgumentWrappers.jl")

include("LinearCombinations.jl")
export LinearCombination, coeffs, basis, @evaluate

include("approximate.jl")
export approximate

include("inner_products.jl")
export WeightedL2, LegendreInnerProduct, JacobiInnerProduct, evaluate, evalpoints

include("points/ChebyshevPoints.jl")
export ChebyshevPoints

include("points/TrapezoidalPoints.jl")
export TrapezoidalPoints

include("points/Midpoints.jl")
export Midpoints

include("bases/Basis.jl")
export Basis

include("bases/Monomials.jl")
export Monomials

include("bases/NegativeMonomials.jl")
export NegativeMonomials

include("bases/Chebyshev.jl")
export Chebyshev

include("bases/Combined.jl")
export Combined

include("bases/Poles.jl")
export Poles

include("bases/Weighted.jl")
export Weighted

include("bases/Mapped.jl")
export Mapped

include("bases/Barycentric.jl")
export Barycentric

include("bases/LanczosBasis.jl")
export LanczosBasis

end # module

ext: jl


text: include("deprecated/datapre.jl")


text:
using jInv.Mesh
using Multigrid
using SparseArrays
using LinearAlgebra
using Test
println("************************************************* GMG RAP for Elasticity 2D using Vanka ******************************************************");

domain = [0.0, 1.0, 0.0, 1.0];
n = [128,128];
Mr = getRegularMesh(domain,n)
mu = 1.0*ones(prod(Mr.n));
lambda = 10.0.*mu;
#Ar = GetLinearElasticityOperator(Mr,mu,lambda);
Ar = GetLinearElasticityOperatorMixedFormulation(Mr, mu,lambda);
println(norm(Ar - Ar'))
Ar = Ar + 1e-3*opnorm(Ar,1)*sparse(1.0I,size(Ar,2),size(Ar,2));

levels      = 4;
numCores 	= 2;
maxIter     = 5;
relativeTol = 1e-10;
relaxType   = "EconVankaFaces";
relaxParam  = 0.75;
relaxPre 	= 1;
relaxPost   = 1;
cycleType   ='V';
coarseSolveType = "Julia";
transferOperatorType = "SystemsFacesMixedLinear";

MG = getMGparam(Float64,Int64,levels,numCores,maxIter,relativeTol,relaxType,relaxParam,relaxPre,relaxPost,cycleType,coarseSolveType,0.5,0.0,transferOperatorType);

N = size(Ar,2);
b = Ar*rand(N); b = b/norm(b);
x = zeros(N);
MGsetup(Ar,Mr,MG,size(b,2),true);
println("****************************** Stand-alone GMG RAP: ******************************")
solveMG(MG,b,x,true);
@test norm(Ar*x - b) < 0.05;
println("****************************** GMG + CG: ******************************")
x[:].=0.0;
solveCG_MG(Ar,MG,b,x,true)
@test norm(Ar*x - b) < 0.01;

println("************************************************* GMG RAP for Elasticity 2D using HybridVanka(Kaczmarz) ******************************************************");

domain = [0.0, 1.0, 0.0, 1.0];
n = [128,128];
Mr = getRegularMesh(domain,n)
mu = 1.0*ones(prod(Mr.n));
lambda = 10.0.*mu;
#Ar = GetLinearElasticityOperator(Mr,mu,lambda);
Ar = GetLinearElasticityOperatorMixedFormulation(Mr, mu,lambda);
println(norm(Ar - Ar'))
Ar = Ar + 1e-3*opnorm(Ar,1)*sparse(1.0I,size(Ar,2),size(Ar,2));

levels      = 4;
numCores 	= 2;
maxIter     = 5;
relativeTol = 1e-10;
relaxType   = "hybridVankaFacesKaczmarz";
relaxParam  = getHybridKaczmarzParam([4,4],0.9,numCores,2);
relaxPre 	= 1;
relaxPost   = 1;
cycleType   ='V';
coarseSolveType = "Julia";
transferOperatorType = "SystemsFacesMixedLinear";

MG = getMGparam(Float64,Int64,levels,numCores,maxIter,relativeTol,relaxType,relaxParam,relaxPre,relaxPost,cycleType,coarseSolveType,0.5,0.0,transferOperatorType);

N = size(Ar,2);
b = Ar*rand(N); b = b/norm(b);
x = zeros(N);
MGsetup(Ar,Mr,MG,size(b,2),true);
println("****************************** Stand-alone GMG RAP: ******************************")
solveMG(MG,b,x,true);
@test norm(Ar*x - b) < 0.05;
println("****************************** GMG + CG: ******************************")
x[:].=0.0;
solveCG_MG(Ar,MG,b,x,true)
@test norm(Ar*x - b) < 0.01;

println("************************************************* GMG RAP for Elasticity 3D  using Vanka ******************************************************");

domain = [0.0, 1.0, 0.0, 1.0,0.0,1.0];
# for some reason this test fails to converge at [8,8,8] and succeeds at larger sizes. TODO: check this.
n = [8,8,8];
# n = [128,128,128];

Mr = getRegularMesh(domain,n)
mu = 1.0*ones(prod(Mr.n));
# lambda = 10.0.*mu;
lambda = 1.0.*mu;
Ar = GetLinearElasticityOperatorMixedFormulation(Mr, mu,lambda);
Ar = Ar + 1e-2*opnorm(Ar,1)*sparse(1.0I,size(Ar,2),size(Ar,2));
MG.relaxType = "VankaFaces";
MG.relaxParam = 0.5;

N = size(Ar,2);
b = Ar*rand(N); b = b/norm(b);
x = zeros(N);
clear!(MG)
MGsetup(Ar,Mr,MG,size(b,1),true);
println("****************************** Stand-alone GMG RAP: ******************************")
solveMG(MG,b,x,true);
@test norm(Ar*x - b) < 0.05;


text:
using LinearSolve, LinearAlgebra, SparseArrays
using Test
import Random

n = 8
A = Matrix(I,n,n)
b = ones(n)
A1 = A/1; b1 = rand(n); x1 = zero(b)
A2 = A/2; b2 = rand(n); x2 = zero(b)

prob1 = LinearProblem(A1, b1; u0=x1)
prob2 = LinearProblem(A2, b2; u0=x2)

cache_kwargs = (;verbose=true, abstol=1e-8, reltol=1e-8, maxiter=30,)

function test_interface(alg, prob1, prob2)
    A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
    A2 = prob2.A; b2 = prob2.b; x2 = prob2.u0

    y = solve(prob1, alg; cache_kwargs...)
    @test A1 *  y  ≈ b1

    cache = SciMLBase.init(prob1,alg; cache_kwargs...) # initialize cache
    y = solve(cache)
    @test A1 *  y  ≈ b1

    cache = LinearSolve.set_A(cache,copy(A2))
    y = solve(cache)
    @test A2 *  y  ≈ b1

    cache = LinearSolve.set_b(cache,b2)
    y = solve(cache)
    @test A2 *  y  ≈ b2

    return
end

@testset "LinearSolve" begin

@testset "Default Linear Solver" begin
    test_interface(nothing, prob1, prob2)

    A1 = prob1.A; b1 = prob1.b; x1 = prob1.u0
    y = solve(prob1)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(Diagonal(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(SymTridiagonal(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(Tridiagonal(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(Symmetric(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(Hermitian(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1

    _prob = LinearProblem(sparse(A1), b1; u0=x1)
    y = solve(_prob)
    @test A1 *  y  ≈ b1
end

@testset "UMFPACK Factorization" begin
    A1 = A/1; b1 = rand(n); x1 = zero(b)
    A2 = A/2; b2 = rand(n); x2 = zero(b)

    prob1 = LinearProblem(sparse(A1), b1; u0=x1)
    prob2 = LinearProblem(sparse(A2), b2; u0=x2)
    test_interface(UMFPACKFactorization(), prob1, prob2)

    # Test that refactoring wrong throws.
    cache = SciMLBase.init(prob1,UMFPACKFactorization(); cache_kwargs...) # initialize cache
    y = solve(cache)
    cache = LinearSolve.set_A(cache,sprand(n, n, 0.8))
    @test_throws ArgumentError solve(cache)
end

@testset "KLU Factorization" begin
    A1 = A/1; b1 = rand(n); x1 = zero(b)
    A2 = A/2; b2 = rand(n); x2 = zero(b)

    prob1 = LinearProblem(sparse(A1), b1; u0=x1)
    prob2 = LinearProblem(sparse(A2), b2; u0=x2)
    test_interface(KLUFactorization(), prob1, prob2)

    # Test that refactoring wrong throws.
    cache = SciMLBase.init(prob1,KLUFactorization(); cache_kwargs...) # initialize cache
    y = solve(cache)
    X = copy(A1)
    X[8,8] = 0.0
    X[7,8] = 1.0
    cache = LinearSolve.set_A(cache,sparse(X))
    @test_throws ArgumentError solve(cache)
end

@testset "Concrete Factorizations" begin
    for alg in (
                LUFactorization(),
                QRFactorization(),
                SVDFactorization(),
                RFLUFactorization()
               )
        @testset "$alg" begin
            test_interface(alg, prob1, prob2)
        end
    end
end

@testset "Generic Factorizations" begin
    for fact_alg in (
                     lu, lu!,
                     qr, qr!,
                     cholesky,
                     #cholesky!,
    #                ldlt, ldlt!,
                     bunchkaufman, bunchkaufman!,
                     lq, lq!,
                     svd, svd!,
                     LinearAlgebra.factorize,
                    )
        @testset "fact_alg = $fact_alg" begin
            alg = GenericFactorization(fact_alg=fact_alg)
            test_interface(alg, prob1, prob2)
        end
    end
end

@testset "KrylovJL" begin
    kwargs = (;gmres_restart=5,)
    for alg in (
                ("Default",KrylovJL(kwargs...)),
                ("CG",KrylovJL_CG(kwargs...)),
                ("GMRES",KrylovJL_GMRES(kwargs...)),
    #           ("BICGSTAB",KrylovJL_BICGSTAB(kwargs...)),
                ("MINRES",KrylovJL_MINRES(kwargs...)),
               )
        @testset "$(alg[1])" begin
            test_interface(alg[2], prob1, prob2)
        end
    end
end

@testset "IterativeSolversJL" begin
    kwargs = (;gmres_restart=5,)
    for alg in (
                ("Default", IterativeSolversJL(kwargs...)),
                ("CG", IterativeSolversJL_CG(kwargs...)),
                ("GMRES",IterativeSolversJL_GMRES(kwargs...)),
    #           ("BICGSTAB",IterativeSolversJL_BICGSTAB(kwargs...)),
    #            ("MINRES",IterativeSolversJL_MINRES(kwargs...)),
               )
        @testset "$(alg[1])" begin
            test_interface(alg[2], prob1, prob2)
        end
    end
end

@testset "KrylovKit" begin
    kwargs = (;gmres_restart=5,)
    for alg in (
                ("Default", KrylovKitJL(kwargs...)),
                ("CG", KrylovKitJL_CG(kwargs...)),
                ("GMRES",KrylovKitJL_GMRES(kwargs...)),
               )
        @testset "$(alg[1])" begin
            test_interface(alg[2], prob1, prob2)
        end
    end
end

@testset "PardisoJL" begin
    @test_throws UndefVarError alg = PardisoJL()

    using Pardiso, SparseArrays

    A1 = sparse([ 1. 0 -2  3
                 0  5  1  2
                -2  1  4 -7
                 3  2 -7  5 ])
    b1 = rand(4)
    prob1 = LinearProblem(A1, b1)

    lambda = 3
    e = ones(n)
    e2 = ones(n-1)
    A2 = spdiagm(-1 => im*e2, 0 => lambda*e, 1 => -im*e2)
    b2 = rand(n) + im * zeros(n)

    prob2 = LinearProblem(A2, b2)

    for alg in (
                PardisoJL(),
                MKLPardisoFactorize(),
                MKLPardisoIterate(),
               )

        u = solve(prob1, alg; cache_kwargs...).u
        @test A1 * u ≈ b1

        u = solve(prob2, alg; cache_kwargs...).u
        @test eltype(u) <: Complex
        @test_broken A2 * u ≈ b2
    end

end

@testset "Preconditioners" begin
    @testset "Vector Diagonal Preconditioner" begin
        s = rand(n)
        Pl, Pr = Diagonal(s),LinearSolve.InvPreconditioner(Diagonal(s))

        x = rand(n,n)
        y = rand(n,n)

        mul!(y, Pl, x); @test y ≈ s .* x
        mul!(y, Pr, x); @test y ≈ s .\ x

        y .= x; ldiv!(Pl, x); @test x ≈ s .\ y
        y .= x; ldiv!(Pr, x); @test x ≈ s .* y

        ldiv!(y, Pl, x); @test y ≈ s .\ x
        ldiv!(y, Pr, x); @test y ≈ s .* x
    end

    @testset "ComposePreconditioenr" begin
        s1 = rand(n)
        s2 = rand(n)

        x = rand(n,n)
        y = rand(n,n)

        P1 = Diagonal(s1)
        P2 = Diagonal(s2)

        P  = LinearSolve.ComposePreconditioner(P1,P2)

        # ComposePreconditioner
        ldiv!(y, P, x);      @test y ≈ ldiv!(P2, ldiv!(P1, x))
        y .= x; ldiv!(P, x); @test x ≈ ldiv!(P2, ldiv!(P1, y))
    end
end

@testset "Sparse Precaching" begin
    n = 4
    Random.seed!(10)
    A = sprand(n,n,0.8); A2 = 2.0 .* A
    b1 = rand(n); b2 = rand(n)

    prob = LinearProblem(copy(A), copy(b1))
    linsolve = init(prob,UMFPACKFactorization())
    sol11 = solve(linsolve)
    linsolve = LinearSolve.set_b(sol11.cache,copy(b2))
    sol12 = solve(linsolve)
    linsolve = LinearSolve.set_A(sol12.cache,copy(A2))
    sol13 = solve(linsolve)

    prob = LinearProblem(copy(A), copy(b1))
    linsolve = init(prob,KLUFactorization())
    sol21 = solve(linsolve)
    linsolve = LinearSolve.set_b(sol21.cache,copy(b2))
    sol22 = solve(linsolve)
    linsolve = LinearSolve.set_A(sol22.cache,copy(A2))
    sol23 = solve(linsolve)

    linsolve = init(prob,MKLPardisoFactorize())
    sol31 = solve(linsolve)
    linsolve = LinearSolve.set_b(sol31.cache,copy(b2))
    sol32 = solve(linsolve)
    linsolve = LinearSolve.set_A(sol32.cache,copy(A2))
    sol33 = solve(linsolve)

    @test sol11.u ≈ sol21.u
    @test sol11.u ≈ sol31.u
    @test sol12.u ≈ sol22.u
    @test sol12.u ≈ sol32.u
    @test sol13.u ≈ sol23.u
    @test sol13.u ≈ sol33.u
end

@testset "Solve Function" begin

    A1 = rand(n) |> Diagonal; b1 = rand(n); x1 = zero(b1)
    A2 = rand(n) |> Diagonal; b2 = rand(n); x2 = zero(b1)

    function sol_func(A,b,u,p,newA,Pl,Pr,solverdata;verbose=true, kwargs...)
        if verbose == true
            println("out-of-place solve")
        end
        u = A \ b
    end

    function sol_func!(A,b,u,p,newA,Pl,Pr,solverdata;verbose=true, kwargs...)
        if verbose == true
            println("in-place solve")
        end
        ldiv!(u,A,b)
    end

    prob1 = LinearProblem(A1, b1; u0=x1)
    prob2 = LinearProblem(A1, b1; u0=x1)

    for alg in (
                LinearSolveFunction(sol_func),
                LinearSolveFunction(sol_func!),
               )

        test_interface(alg, prob1, prob2)
    end
end

end # testset


text:
module TestCore

import AtomixCUDA

using Atomix: asref
using CUDA
using CUDA: @allowscalar
using Test
using UnsafeAtomics

using ..Utils: cuda

# Not implemented:
#=
function test_get_set()
    A = CUDA.ones(Int, 3)
    cuda() do
        GC.@preserve A begin
            ref = asref(A)[1]
            x = UnsafeAtomics.load(ref)
            UnsafeAtomics.store!(ref, -x)
        end
    end
    @test collect(A) == [-1, 1, 1]
end
=#

function test_cas()
    idx = (
        data = 1,
        cas1_ok = 2,
        cas2_ok = 3,
        # ...
    )
    @assert minimum(idx) >= 1
    @assert maximum(idx) == length(idx)

    A = CUDA.zeros(Int, length(idx))
    cuda() do
        GC.@preserve A begin
            ref = asref(A)[idx.data]
            (old, success) = UnsafeAtomics.cas!(ref, 0, 42)
            A[idx.cas1_ok] = old == 0 && success
            (old, success) = UnsafeAtomics.cas!(ref, 0, 43)
            A[idx.cas2_ok] = old == 42 && !success
        end
    end
    @test collect(A) == [42, 1, 1]
end

function test_inc()
    A = CUDA.CuVector(1:3)
    cuda() do
        GC.@preserve A begin
            ref = asref(A)[1]
            pre, post = UnsafeAtomics.modify!(ref, +, 1)
            A[2] = pre
            A[3] = post
        end
    end
    @test collect(A) == [2, 1, 2]
end

end  # module


text:
# Modified from:
# <https://github.com/JuliaDocs/IOCapture.jl/blob/d8b27045cec8953e0e5a8d719ca1692b3a6d0d02/src/IOCapture.jl>
using Logging
import Random

# pass a `cast` to write events to it "as they happen".
# pass a `process` function to modify the event data before writing it.
function capture(f, cast::Cast; rethrow::Type=Any, color::Bool=false, process=identity)
    # Original implementation from Documenter.jl (MIT license)
    # Save the default output streams.
    default_stdout = stdout
    default_stderr = stderr

    # Redirect both the `stdout` and `stderr` streams to a single `Pipe` object.
    pipe = Pipe()
    Base.link_pipe!(pipe; reader_supports_async = true, writer_supports_async = true)
    @static if VERSION >= v"1.6.0-DEV.481" # https://github.com/JuliaLang/julia/pull/36688
        pe_stdout = IOContext(pipe.in, :color => get(stdout, :color, false) & color)
        pe_stderr = IOContext(pipe.in, :color => get(stderr, :color, false) & color)
    else
        pe_stdout = pipe.in
        pe_stderr = pipe.in
    end
    redirect_stdout(pe_stdout)
    redirect_stderr(pe_stderr)
    # Also redirect logging stream to the same pipe
    logger = ConsoleLogger(pe_stderr)

    old_rng = nothing
    if VERSION >= v"1.7.0-DEV.1226" # JuliaLang/julia#40546
        # In Julia >= 1.7 each task has its own rng seed. This seed
        # is obtained by calling rand(...) in the current task which
        # modifies the random stream. We therefore copy the current seed
        # and reset it after creating the read/write task below.
        # See https://github.com/JuliaLang/julia/pull/41184 for more details.
        old_rng = copy(Random.default_rng())
    end

    # Bytes written to the `pipe` are captured in `output` and eventually converted to a
    # `String`. We need to use an asynchronous task to continously tranfer bytes from the
    # pipe to `output` in order to avoid the buffer filling up and stalling write() calls in
    # user code.
    buffer_redirect_task = @async begin
        while isopen(pipe)
            write_event!(cast, OutputEvent, process(String(readavailable(pipe))))
        end
    end

    if old_rng !== nothing
        copy!(Random.default_rng(), old_rng)
    end

    # Run the function `f`, capturing all output that it might have generated.
    # Success signals whether the function `f` did or did not throw an exception.
    result, success, backtrace = with_logger(logger) do
        try
            f(), true, Vector{Ptr{Cvoid}}()
        catch err
            err isa rethrow && Base.rethrow(err)
            # If we're capturing the error, we return the error object as the value.
            err, false, catch_backtrace()
        finally
            # Restore the original output streams.
            redirect_stdout(default_stdout)
            redirect_stderr(default_stderr)
            close(pe_stdout)
            close(pe_stderr)
            wait(buffer_redirect_task)
        end
    end
    (
        value = result,
        error = !success,
        backtrace = backtrace,
    )
end


text:
using BenchmarkTools, Random
using LinearAlgebra, RecursiveFactorization, VectorizationBase
nc = min(Int(VectorizationBase.num_cores()), Threads.nthreads())
BLAS.set_num_threads(nc)
BenchmarkTools.DEFAULT_PARAMETERS.seconds = 0.5

function luflop(m, n=m; innerflop=2)
    sum(1:min(m, n)) do k
        invflop = 1
        scaleflop = isempty(k+1:m) ? 0 : sum(k+1:m)
        updateflop = isempty(k+1:n) ? 0 : sum(k+1:n) do j
            isempty(k+1:m) ? 0 : sum(k+1:m) do i
                innerflop
            end
        end
        invflop + scaleflop + updateflop
    end
end

bas_mflops = Float64[]
rec_mflops = Float64[]
rec4_mflops = Float64[]
rec800_mflops = Float64[]
ref_mflops = Float64[]
ns = 4:8:500
for n in ns
    @info "$n × $n"
    rng = MersenneTwister(123)
    global A = rand(rng, n, n)
    bt = @belapsed LinearAlgebra.lu!(B) setup=(B = copy(A))
    push!(bas_mflops, luflop(n)/bt/1e9)

    rt = @belapsed RecursiveFactorization.lu!(B) setup=(B = copy(A))
    push!(rec_mflops, luflop(n)/rt/1e9)

    rt4 = @belapsed RecursiveFactorization.lu!(B; threshold=4) setup=(B = copy(A))
    push!(rec4_mflops, luflop(n)/rt4/1e9)

    rt800 = @belapsed RecursiveFactorization.lu!(B; threshold=800) setup=(B = copy(A))
    push!(rec800_mflops, luflop(n)/rt800/1e9)

    ref = @belapsed LinearAlgebra.generic_lufact!(B) setup=(B = copy(A))
    push!(ref_mflops, luflop(n)/ref/1e9)
end

using DataFrames, VegaLite
blaslib = if VERSION ≥ v"1.7.0-beta2"
  config = BLAS.get_config().loaded_libs
  occursin("libmkl_rt", config[1].libname) ? :MKL : :OpenBLAS
else
  BLAS.vendor() === :mkl ? :MKL : :OpenBLAS
end
df = DataFrame(Size = ns,
               Reference = ref_mflops)
setproperty!(df, blaslib, bas_mflops)
setproperty!(df, Symbol("RF with default threshold"), rec_mflops)
setproperty!(df, Symbol("RF fully recursive"), rec4_mflops)
setproperty!(df, Symbol("RF fully iterative"), rec800_mflops)
df = stack(df, [Symbol("RF with default threshold"),
                Symbol("RF fully recursive"),
                Symbol("RF fully iterative"),
                blaslib,
                :Reference], variable_name = :Library, value_name = :GFLOPS)
plt = df |> @vlplot(
                    :line, color = {:Library, scale={scheme="category10"}},
                    x = {:Size}, y = {:GFLOPS},
                    width = 1000, height = 600
                   )
save(joinpath(homedir(), "Pictures", "lu_float64_$(VERSION)_$(Sys.CPU_NAME)_$(nc)cores_$blaslib.png"), plt)

#=
using Plot
plt = plot(ns, bas_mflops, legend=:bottomright, lab="OpenBLAS", title="LU Factorization Benchmark", marker=:auto, dpi=300)
plot!(plt, ns, rec_mflops, lab="RecursiveFactorization", marker=:auto)
plot!(plt, ns, ref_mflops, lab="Reference", marker=:auto)
xaxis!(plt, "size (N x N)")
yaxis!(plt, "GFLOPS")
savefig("lubench.png")
savefig("lubench.pdf")
=#


text:
export GradientBanditLearner

using Flux: softmax, onehot

Base.@kwdef struct GradientBanditLearner{A,B} <: Any
    approximator::A
    baseline::B
end

(learner::GradientBanditLearner)(s::Int) = s |> learner.approximator |> softmax
(learner::GradientBanditLearner)(env::AbstractEnv) = learner(state(env))

function RLBase.update!(L::GradientBanditLearner, t::Any, ::AbstractEnv, ::PreActStage) end

function RLBase.update!(L::GradientBanditLearner, t::Any, ::AbstractEnv, ::PostActStage)
    A = L.approximator
    s, a, r = t[:state][end], t[:action][end], t[:reward][end]
    probs = s |> A |> softmax
    r̄ = L.baseline isa Number ? L.baseline : L.baseline(r)
    errors = (r - r̄) .* (onehot(a, 1:length(probs)) .- probs)
    update!(A, s => -errors)
end

function RLBase.update!(
    t::Any,
    ::QBasedPolicy{<:GradientBanditLearner},
    ::AbstractEnv,
    ::PreEpisodeStage,
)
    empty!(t)
end


text:
module SQLStrings

export @sql_cmd

"""
    Literal(str)

Literal string argument to `@sql_cmd`. These are literal query fragments of SQL
source text.
"""
struct Literal
    fragment::String

    Literal(val::AbstractString) = new(convert(String, val))
end

Literal(val) = Literal(string(val))

"""
    Sql

An SQL query or query-fragment which keeps track of interpolations and will
pass them as SQL query parameters. Construct this type with the `@sql_cmd`
macro.
"""
struct Sql
    args::Vector
end

struct SplatArgs
    args
end

function process_args!(processed)
    return processed
end

function process_args!(processed, a, args...)
    push!(processed, a)
    return process_args!(processed, args...)
end

# Query fragments can be interpolated into other queries
function process_args!(processed, a::Sql, args...)
    return process_args!(processed, a.args..., args...)
end

function process_args!(processed, splat::SplatArgs, args...)
    for (i,a) in enumerate(splat.args)
        process_args!(processed, a)
        if i < length(splat.args)
            push!(processed, Literal(","))
        end
    end
    return process_args!(processed, args...)
end

function parse_interpolations(str, allow_dollars_in_strings)
    args = []
    i = 1
    literal_start = i
    literal_end = 0
    in_singlequote = false
    prev_was_backslash = false
    while i <= lastindex(str)
        c = str[i]
        if !allow_dollars_in_strings && in_singlequote && c == '$'
            error("""Interpolated arguments should not be quoted, but found quoting in sql`$str`
                     subexpression starting at `$(str[i:end])`""")
        end
        if c == '$' && !in_singlequote
            if prev_was_backslash
                literal_end = prevind(str, literal_end, 1)
                if literal_start <= literal_end
                    push!(args, Literal(str[literal_start:literal_end]))
                end
                literal_start = i
                i = nextind(str, i)
            else
                if literal_start <= literal_end
                    push!(args, Literal(str[literal_start:literal_end]))
                end
                (interpolated_arg, i) = Meta.parse(str, i+1; greedy=false)
                if Meta.isexpr(interpolated_arg, :...)
                    push!(args, :(SplatArgs($(esc(interpolated_arg.args[1])))))
                else
                    push!(args, esc(interpolated_arg))
                end
                literal_start = i
            end
        else
            if c == '\''
                # We assume standard SQL which uses '' rather than \' for
                # escaping quotes.
                in_singlequote = !in_singlequote
            end
            literal_end = i
            i = nextind(str, i)
        end
        prev_was_backslash = c == '\\'
    end
    if literal_start <= literal_end
        push!(args, Literal(str[literal_start:literal_end]))
    end
    args
end

"""
    allow_dollars_in_strings[] = true

Set this parsing option to `false` to disallow dollars inside SQL strings, for
example disallowing the `'\$s'` in

    sql`select * from foo where s = '\$s'`

When converting code from plain string-based interpolation, it's helpful to
have a macro-expansion-time sanity check that no manual quoting of interpolated
arguments remains.
"""
const allow_dollars_in_strings = Ref(true)

"""
    sql`SOME SQL ... \$var`
    sql`SOME SQL ... \$(var...)`
    sql`SOME SQL ... 'A \$literal string'`
    sql``

The `@sql_cmd` macro is a tool for tracking SQL query strings together with
their parameters, but without interpolating the parameters into the query
string directly. Instead, interpolations like `\$x` will result in the value of
`x` being passed as a query parameter. If you've got a collection of values to
interpolate into a comma separated context you can also use splatting syntax
within the interpolation, for example `insert into foo values(\$(x...))`.

Use this rather than direct string interpolation to prevent SQL injection
attacks and allow systematic conversion of Julia types into their SQL
equivalents via the database layer, rather than via `string()`.

Empty query fragments can be generated with ```sql`` ``` which is useful if you
must dynamically generate SQL code based on conditionals. However you should
also consider embedding any conditionals on the SQL side rather than in the
Julia code.

*Interpolations are ignored* inside standard SQL Strings with a single quote,
so using `'A \$literal string'` will include `\$literal` rather than the value
of the variable `literal`. If converting code from using raw strings, you may
have needed to quote interpolations. In that case you can check your conversion
by setting `SQLStrings.allow_dollars_in_strings[] = false`.

If you need to include a literal `\$` in the SQL code outside a string, you can
escape it with `\\\$`.
"""
macro sql_cmd(str)
    args = parse_interpolations(str, allow_dollars_in_strings[])
    quote
        Sql(process_args!([], $(args...)))
    end
end

function Base.:*(x::Sql, y::Sql)
    Sql(vcat(x.args, [Literal(" ")], y.args))
end

default_placeholder_string(i) = "\$$i"

function prepare(sql::Sql, to_placeholder = default_placeholder_string)
    querystr = ""
    arg_values = []
    i = 1
    for arg in sql.args
        if arg isa Literal
            querystr *= arg.fragment
        else
            querystr *= to_placeholder(i)
            push!(arg_values, arg)
            i += 1
        end
    end
    querystr, arg_values
end

function Base.show(io::IO, sql::Sql)
    query, arg_values = prepare(sql)
    print(io, query)
    if !isempty(arg_values)
        args_str = join(["\$$i = $(repr(val))" for (i,val) in enumerate(arg_values)], "\n  ")
        print(io, "\n  ", args_str)
    end
end

end


text:
#=

  Euler #32 in Julia.

  Problem 32
  """
  We shall say that an n-digit number is pandigital if it makes use of
  all the digits 1 to n exactly once; for example, the 5-digit number,
  15234, is 1 through 5 pandigital.

  The product 7254 is unusual, as the identity, 39 × 186 = 7254,
  containing multiplicand, multiplier, and product is 1 through 9
  pandigital.

  Find the sum of all products whose multiplicand/multiplier/product
  identity can be written as a 1 through 9 pandigital.
  HINT: Some products can be obtained in more than one way so be sure
  to only include it once in your sum.
  """

  This Julia program was created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#

include("Euler.jl")

# 0.17556319s
function euler32a()
    prod_hash = Dict{Int,Int}()
    for a in 2:98
        as = string(a)
        if occursin("0",as) continue end
        for b in a+1:9876
            p = a*b
            if occursin("0",string(p)) continue end
            l = "$as$b$p"
            if length(l) == 9 && length(Set(l)) == 9 && !occursin("0",l)
                prod_hash[p] = 1
            end
        end
    end
    return keys(prod_hash)|>sum
end

#
function euler32b()
    prod_hash = Set()
    for a in 2:98
        # as = string(a)
        if occursin("0",string(a)) continue end
        for b in a+1:9876
            p = a*b
            if occursin("0",string(p)) continue end
            l = "$a$b$p"
            if length(l) == 9 && length(Set(l)) == 9 && !occursin("0",l)
            # if length(l) == 9 && !occursin("0",l) && length(unique(l)) == 9
                push!(prod_hash,p)
            end
        end
    end

    return prod_hash|>sum
end

function euler32c()
    prod_hash = Dict{Int,Int}()
    for a in 98:-1:2
        if a % 10 == 0 continue end
        as = string(a)
        for b in a+1:9876
            p = a*b
            if p % 10 == 0 continue end
            l = "$as$b$p"
            # l = string(a,b,p)
            if length(l) != 9 continue end
            if length(Set(l)) != 9 continue end
            if occursin("0",l) continue end
            prod_hash[p] = 1
        end
    end
    return keys(prod_hash)|>sum
end

GC.gc(true)
GC.gc(false)
run_euler(euler32a)
# run_euler(euler32b)
# GC.gc(true)
# GC.gc(false)
# run_euler(euler32c)

max_stars_repo_name: Wikunia/hakank

max_issues_repo_name: Wikunia/hakank

max_forks_repo_name: Wikunia/hakank


text:
function compress(X, i_c, j_c, ij_d)
# Compresses a sparse, square matrix X into a rectangular matrix where each column is a full layer
    X_comp = sparse(i_c, j_c, X[ij_d])
    return full(X_comp)
end

function decompress(X_comp, i_d, j_d, ij_c)
# Decompresses a rectangular compressed matrix X into
# a square sparse matrix.
    X = sparse(i_d, j_d, X_comp[ij_c])
    return X
end

# TODO Make sure I can use these tricks to compute a quick analytical Jacobian
# for use in ∂xregen!
function waterColumnStructure(wet3d)
    Iabove = buildIabove(wet3d)
  # loop to make all powers of A
    B = spones(Iabove + I)
    foo = true
    while foo
        last_B = copy(B)
        B = spones(B^2)
        foo = B != last_B
    end
    return B
end

function buildCompressionIndices(wet3d)
  # TODO describe what this does
    layer_idx_3d = zeros(wet3d, Int64)
    ndepth = size(wet3d, 3)
    iwet = find(wet3d)
    for idepth = 1:ndepth
        layer_idx_3d[:,:,idepth] .= idepth
    end
    layer_idx = layer_idx_3d[iwet]

    B = waterColumnStructure(wet3d)
    ij_d = find(B)
    i_d, j_d = ind2sub(size(B), ij_d)

    i_c = copy(i_d)
    j_c = layer_idx[j_d] # This is because I know that this is the right scheme
    B_c = compress(B, i_c, j_c, ij_d)
    ij_c = sub2ind(size(B_c), i_c, j_c)
    return i_c, j_c, ij_c, i_d, j_d, ij_d
end

export buildCompressionIndices, compress, decompress


text:
module TestBaseAPI

  using Test, Dates, NumerAPI

  using NumerAPI.Tournament

  public_id = ENV["NUMERAI_PUBLIC_ID"]
  secret_key = ENV["NUMERAI_SECRET_KEY"]
  username = ENV["TOURNAMENT_USERNAME"]
  model_id = ENV["TOURNAMENT_MODEL_ID"]

  tournament_api = TournamentAPI(public_id, secret_key)

  # Test Base API Functions
  @testset "test get_account" begin
    data = get_account(tournament_api)

    @test haskey(data, "apiTokens")
    @test haskey(data, "models")
    @test haskey(data, "username")
    @test haskey(data, "walletAddress")
    @test haskey(data, "availableNmr")
    @test haskey(data, "email")
    @test haskey(data, "id")
    @test haskey(data, "mfaEnabled")
    @test haskey(data, "status")
    @test haskey(data, "insertedAt")
  end

  @testset "test get_models" begin
    models = get_models(tournament_api)

    @test typeof(models) == Dict{String,String}
  end

  @testset "test get_account_transactions" begin
    account = get_account_transactions(tournament_api)

    @test haskey(account, "nmrDeposits")
    @test haskey(account, "nmrWithdrawals")
    @test typeof(account["nmrDeposits"]) <: Vector
    @test typeof(account["nmrWithdrawals"]) <: Vector
  end

  @testset "test get_current_round" begin
    round_number = get_current_round(tournament_api)

    @test ((typeof(round_number) == Int) || (isnothing(round_number)))
  end

end # module


text:
using OrdinaryDiffEq, DiffEqFlux, Plots

# model + problem parameters
(aₐ, aₙ, bₙ, cₙ, dₙ, aₘ, bₘ, cₘ, dₘ) = Float32.([-0.3, 19.373, -31.023, -9.717, -1.948, 40.44, -64.015, 2.922, -11.803])
(m, I_yy, S, d, ωₐ, ζₐ) = Float32.([204.02, 247.439, 0.0409, 0.2286, 150.0, 0.7])
(g, ρ₀, H, γₐ, Rₐ, T₀, λ) = Float32.([9.8, 1.225, 8435.0, 1.4, 286.0, 288.15, 0.0065])
(a_max, α_max, δ_max, q_max, M_max, h_max) = Float32.([100.0, deg2rad(30), deg2rad(25), deg2rad(5), 4, 11E3])

(k_a, k_δ, k_R) = Float32.([1.0, 0.1, 0.00001])
t_span = (0.0f0, 3.0f0)
x₀ = Float32.([10E3; 900; zeros(7)])
a_z_cmd = 100.0f0

# cost function
L(a_z, a_z_cmd, δ_c) = k_a * ((a_z - a_z_cmd) / a_max)^2 + k_δ * (δ_c / δ_max)^2   # + k_q * (q / q_max)^2
ϕ(a_z, a_z_cmd) = ((a_z - a_z_cmd) / a_max)^2
R(p_NN) = k_R * p_NN' * p_NN

# NN construction
dim_hidden = 64
(K_lb, K_ub) = Float32.([-0.3, 0])
Π = FastChain(
    FastDense(3, dim_hidden, tanh),
    FastDense(dim_hidden, dim_hidden, tanh),
    FastDense(dim_hidden, 3),
    (x, p) -> (K_ub - K_lb) * σ.(x) .+ K_lb
)
p_NN = initial_params(Π)

# plant + controller dynamics + running cost integrator
function fwd_dynamics_model(x, p_NN, t)
    (h, V, α, q, θ, δ, δ̇, x_c, _) = x

    ρ = ρ₀ * exp(-h / H)
    Vₛ = sqrt(γₐ * Rₐ * (T₀ - λ * h))
    M = V / Vₛ
    γ = θ - α
    Q = 0.5f0 * ρ * V^2

    C_A = aₐ
    C_N = aₙ * α^3 + bₙ * α * abs(α) + cₙ * (2.0f0 - M / 3.0f0) * α + dₙ * δ
    C_M = aₘ * α^3 + bₘ * α * abs(α) + cₘ * (-7.0f0 + 8.0f0 * M / 3.0f0) * α + dₘ * δ

    # K_A, K_I, K_R =  min.( Π([α / α_max; M / M_max; h / h_max], p_NN) , K_ub)
    K_A, K_I, K_R =  Π([α / α_max; M / M_max; h / h_max], p_NN)
    δ_c = -K_I * x_c - K_R * q

    α̇ = Q * S / m / V * (C_N * cos(α) - C_A * sin(α)) + g / V * cos(γ) + q
    a_z = V * (q - α̇)

    return dx = [V * sin(γ);
                Q * S / m * (C_N * sin(α) + C_A * cos(α)) - g * sin(γ);
                α̇;
                Q * S * d / I_yy * C_M;
                q;
                δ̇;
                -ωₐ^2 * (δ - δ_c) - 2.0f0 * ζₐ * ωₐ * δ̇;
                -K_A * (a_z - a_z_cmd) + q - a_z_cmd / V;
                L(a_z, a_z_cmd, δ_c)]
end

function eval_output(x)
    (h, V, α, q, θ, δ, _, _, _) = x

    ρ = ρ₀ * exp(-h / H)
    Vₛ = sqrt(γₐ * Rₐ * (T₀ - λ * h))
    M = V / Vₛ
    γ = θ - α
    Q = 0.5f0 * ρ * V^2

    C_A = aₐ
    C_N = aₙ * α^3 + bₙ * α * abs(α) + cₙ * (2.0f0 - M / 3.0f0) * α + dₙ * δ

    α̇ = Q * S / m / V * (C_N * cos(α) - C_A * sin(α)) + g / V * cos(γ) + q
    return V * (q - α̇)
end

function eval_tune_params(p_NN)
    return function (x)
        (h, V, α) = x[1:3]

        Vₛ = sqrt(γₐ * Rₐ * (T₀ - λ * h))
        M = V / Vₛ

        # return min.( Π([α / α_max; M / M_max; h / h_max], p_NN) , K_ub)
        return Π([α / α_max; M / M_max; h / h_max], p_NN)
    end
end

# ODE problem
prob = ODEProblem(fwd_dynamics_model, x₀, t_span)

# loss construction
function loss(p_NN)
    # InterpolatingAdjoint(autojacvec = ReverseDiffVJP(true)) also works well for ODE function defined in IIP form
    # InterpolatingAdjoint(autojacvec = ZygoteVJP()) requires ODE function defined in OOP form
    fwd_sol = Array(solve(prob, Tsit5(), p = p_NN, saveat = 0.01f0; sensealg = InterpolatingAdjoint(autojacvec = ZygoteVJP() )))

    return ϕ(eval_output(fwd_sol[:, end]), a_z_cmd) + R(p_NN) + fwd_sol[end, end], fwd_sol
end

# learning progress callback setup
# cb_progress = function (p, loss_val)
cb_progress = function (p, loss_val, pred)
    println("Loss = $loss_val\n")
    display(scatter([vec(mapslices(eval_output, pred, dims = 1)) rad2deg.(pred[3:4, :]')], ylabel = ["\$a_{z} [m/s^{2}]\$" "\$\\alpha [deg]\$" "\$q [deg/s]\$"], label = :false, layout = (3, 1)))
    return false
end

function view_result(p_NN)
    fwd_sol = solve(prob, Tsit5(), p = p_NN, reltol = 1e-8, abstol = 1e-8, saveat = 0.01)
    a_z = eval_output.(fwd_sol.u)
    K = eval_tune_params(p_NN).(fwd_sol.u)

    f_aq = plot(fwd_sol.t, [a_z rad2deg.(fwd_sol[4,:])], label = :false, xlabel = "\$t [s]\$", ylabel = ["\$a_{z} [m/s^{2}]\$" "\$q [deg/s]\$"], layout = (2, 1))
    f_K = plot(fwd_sol.t, hcat(K...)', xlabel = "\$t [s]\$", ylabel = ["\$K_{A}\$" "\$K_{I}\$" "\$K_{R}\$"], label = :false, layout = (3, 1))
    display(plot(f_aq, f_K, layout = (1, 2)))

    f_hVα = plot(fwd_sol.t, [fwd_sol[1:2,:]' rad2deg.(fwd_sol[3,:])], label = :false, xlabel = "\$t [s]\$", ylabel = ["\$h [m]\$" "\$V [m/s]\$" "\$\\alpha [deg]\$"], layout = (3, 1))
    display(f_hVα)
    return fwd_sol
end

# view pre-training result
view_result(p_NN)

## NN training
println("1st Phase\n")
# res1 = DiffEqFlux.sciml_train(loss, p_NN; cb = cb_progress, maxiters = 100)
res1 = DiffEqFlux.sciml_train(loss, p_NN, ADAM(0.01); cb = cb_progress, maxiters = 50)
fwd_sol = view_result(res1.u)

println("2nd Phase\n")
res2 = DiffEqFlux.sciml_train(loss, res1.u, LBFGS(); cb = cb_progress, maxiters = 30)
fwd_sol = view_result(res2.u)

## Validation
p_NN_final = res2.u
t_span = (0.0f0, 10.0f0)

prob = ODEProblem(fwd_dynamics_model, x₀, t_span)

function view_result(p_NN)
    fwd_sol = solve(prob, Tsit5(), p = p_NN, reltol = 1e-8, abstol = 1e-8, saveat = 0.01)
    a_z = eval_output.(fwd_sol.u)
    K = eval_tune_params(p_NN).(fwd_sol.u)

    f_aq = plot(fwd_sol.t, [a_z rad2deg.(fwd_sol[4,:])], label = :false, xlabel = "\$t [s]\$", ylabel = ["\$a_{z} [m/s^{2}]\$" "\$q [deg/s]\$"], layout = (2, 1))
    f_K = plot(fwd_sol.t, hcat(K...)', xlabel = "\$t [s]\$", ylabel = ["\$K_{A}\$" "\$K_{I}\$" "\$K_{R}\$"], label = :false, layout = (3, 1))
    display(plot(f_aq, f_K, layout = (1, 2)))

    f_hVα = plot(fwd_sol.t, [fwd_sol[1:2,:]' rad2deg.(fwd_sol[3,:])], label = :false, xlabel = "\$t [s]\$", ylabel = ["\$h [m]\$" "\$V [m/s]\$" "\$\\alpha [deg]\$"], layout = (3, 1))
    display(f_hVα)
    return fwd_sol
end

view_result(p_NN_final)


text:
# Basic FizzBuzz with Julia
# Author: satrio-pamungkas

for num in 1:100
    if num % 3 == 0 && num % 5 == 0
        println("FizzBuzz")
    elseif num % 3 == 0
        println("Fizz")
    elseif num % 5 == 0
        println("Buzz")
    else
        println(num)
    end
end


text:
abstract type RangeConstraintsData end

""" Data Container to construct range constraints"""
struct DeviceRange <: RangeConstraintsData
    name::String
    limits::MinMax
    additional_terms_ub::Vector{Symbol}
    additional_terms_lb::Vector{Symbol}
end

function DeviceRange(name::String, limits::MinMax)
    return DeviceRange(name, limits, Vector{Symbol}(), Vector{Symbol}())
end

function DeviceRange(name::String)
    return DeviceRange(name, (min = -Inf, max = Inf), Vector{Symbol}(), Vector{Symbol}())
end

struct DeviceTimeSeries <: RangeConstraintsData
    name::String
    bus_number::Int
    multiplier::Float64
    timeseries::Vector{Float64}
    range::DeviceRange
    function DeviceTimeSeries(name, bus_number, multiplier, timeseries, range)
        @assert isnothing(range) || name == range.name
        return new(name, bus_number, multiplier, timeseries, range)
    end
end

function DeviceTimeSeries(
    device::PSY.Device,
    multiplier_function::Function,
    ts_vector::Vector{Float64},
    get_constraint_values::Union{Function, Nothing} = nothing,
)
    name = PSY.get_name(device)
    bus_number = PSY.get_number(PSY.get_bus(device))
    multiplier = multiplier_function(device)
    if isnothing(get_constraint_values)
        range_data = DeviceRange(name)
    else
        range_data = DeviceRange(name, get_constraint_values(device))
    end
    return DeviceTimeSeries(name, bus_number, multiplier, ts_vector, range_data)
end


text:
#= DOES NOT WORK PROPERLY
for (TT,dot_f) in [(:ArbReal, @libarb(arb_dot)),(:ArbComplex, @libarb(acb_dot))]
    @eval begin
        function LinearAlgebra.dot(x::ArblibVector{T}, y::ArblibVector{T}) where {T<:$TT}
            @assert x.len == y.len
            res = T(0) # Aliasing is allowed between res and s

            P = precision(T)

            ccall($dot_f, Cvoid, (Ref{T}, Ref{T}, Clong,
                                  Ref{T}, Clong,
                                  Ref{T}, Clong,
                                  Clong, Clong),
                  res, res, 0, x.ptr, 0, y.ptr, 0, x.len, P)

            res
        end
    end
end
=#

function LinearAlgebra.dot(x::AbstractVector{T}, y::AbstractVector{T}) where {T<:ArbNumber}
    length(x) == length(y) || throw(DimensionMismatch("x and y must have the same lengths"))
    xy = x .* y
    return sum(xy)

    #= does not work properly
    xv = ArblibVector(x)
    yv = ArblibVector(y)

    d = dot(xv, yv)

    free!(xv)
    free!(yv)

    T(d)
    =#
end


text:
# # [Parallel processing tutorial](@id tutorial-parallel)
#
# See also: [Overview of parallel processing in Transducers.jl](@ref
# overview-parallel)

using Test                                                             #src

# ## Quick examples
# ### Sequential processing
using Transducers

xs = randn(10_000_000)
foldl(+, Map(sin), xs)

# ### Thread-based parallelism

# Just replace `foldl` with `reduce`, to make use of multiple cores:

reduce(+, Map(sin), xs)

# (In my laptop (4 core machine) I start seeing some speedup around
# `length(xs) ≥ 100_000` for this transducer and reducing function.)

# ### Process-based parallelism

using Distributed
#md addprocs(4)

dreduce(+, Map(sin), xs)

# (Note: there is likely no speedup for light-weight computation and
# large input data like this, when using `dreduce`.)

# ### Parallel processing with iterator comprehensions

# You can also use [`eduction`](@ref) to use iterator comprehension
# with multi-thread `reduce`:

reduce(+, eduction(sin(x) for x in xs if abs(x) < 1); basesize = 500_000)

#-

if VERSION >= v"1.3"                                                   #src
@test 36 ==                                                            #src
reduce(+, eduction(x * y for x in 1:3, y in 1:3))
end                                                                    #src

# You can omit `eduction` when using Transducers.jl-specific functions
# like [`tcollect`](@ref)/[`dcollect`](@ref):

tcollect(sin(x) for x in xs if abs(x) < 1)

# and [`tcopy`](@ref)/[`dcopy`](@ref):

using StructArrays: StructVector
table = StructVector(a = [1, 2, 3], b = [5, 6, 7])

@test StructVector(A = [2, 4], B = [4, 6]) ==                          #src
tcopy((A = row.a + 1, B = row.b - 1) for row in table if isodd(row.a))

# ## When can I use `reduce` and `dreduce`?

# ### Requirement 1: Associative reducing step function
#
# Parallel reductions such as [`reduce`](@ref) and [`dreduce`](@ref)
# requires _associative_ [reducing step function](@ref Glossary).
# Recall that
# [associativity](https://en.wikipedia.org/wiki/Associative_property)
# means that the _grouping_ of evaluations of binary operator does not
# matter:

op = +  # for example
a, b, c = 1, 2, 3  # for example

@assert op(op(a, b), c) == op(a, op(b, c))

# Given this property, computations like `a + b + c + d` can be done
# with different "groupings":
#
# ```
#   a + b + c + d
#
# = ((a + b) + c) + d                +
#                                   / \
#                                  +   d
#                                 / \
#                                +   c           foldl-like grouping
#                               / \
#                              a   b
#
# = (a + b) + (c + d)                +
#                                   / \
#                                  /   \
#                                 /     \        reduce-like grouping
#                                +       +
#                               / \     / \
#                              a   b   c   d
# ```
#
# Notice that, in the last grouping, computation of `c + d` does not
# have to wait for the result of `a + b`.  This is why we need
# associativity for parallel execution.
#
# !!! note
#     Do not confuse associativity with _commutativity_ `op(a, b) =
#     op(b, a)`.  For example, matrix multiplication `*(::Matrix,
#     ::Matrix)` is associative but _not_ commutative in general.
#     However, since `reduce` only requires associativity, it is valid
#     to use `reduce(*, xf, matrices)`.

# As reducing function `+` is associative, it can be used with
# `reduce` (and `dreduce`):

y1 =                                                                   #src
reduce(+, Map(identity), 1:10; init = 0, basesize = 1)

# and the result is the same as the sequential version:

y2 =                                                                   #src
foldl(+, Map(identity), 1:10; init = 0)
@test y1 == y2                                                         #src

# Note: `basesize` is for forcing `reduce` to avoid falling back to
# `foldl` for small length container such as `1:10`.

# On the other hand, binary function `-` is not associative.  Thus,
# `reduce` cannot be used instead of `foldl` (they produce different
# result):

y3 =                                                                   #src
reduce(-, Map(identity), 1:10; init = 0, basesize = 1)
#-

y4 =                                                                   #src
foldl(+, Map(identity), 1:10; init = 0)
@test y3 != y4                                                         #src

# ### Requirement 2: stateless transducers

# Parallel reduction only work with stateless transducers
# [`Map`](@ref), [`Filter`](@ref), [`Cat`](@ref), etc. and you will
# get an error when using stateful transducers such as `Scan` with
# `reduce` or `dreduce`:

err = try                                                            # hide
reduce(+, Scan(+), 1:10; basesize = 1)
catch err; err; end                                                  # hide
@test occursin(                                                        #src
    "Stateful transducer Scan(+) does not support `combine`",          #src
    sprint(showerror, err),                                            #src
)                                                                      #src
#md print(stdout, "ERRPR: ")                                         # hide
#md showerror(stdout, err)                                           # hide

# Stateful transducers cannot be used with `reduce` because it is
# impossible to start processing input collection from the middle when
# the transducers need to know all previous elements (= stateful).
#
# [`ScanEmit`](@ref) is a stateful transducer but it is assumed that
# it is used in a context that outputs can be treated as stateless
# (see: [Splitting a string into words and counting them in
# parallel](@ref parallel-word-count)).

# ## [Example: parallel `collect`](@id tutorial-parallel-collect)
#
# !!! note
#     This section explains the _implementation ideas_ of parallel
#     `collect`.  Pre-defined functions such as [`tcopy`](@ref) and
#     [`dcopy`](@ref) should cover many use-cases.
#
# Suppose (pretend) there is a compute-heavy transducer:

xf_compute = Filter(!ismissing) |> Map(x -> x^2)
nothing                                                              # hide

# Transducers.jl supports applying this to an input container and then
# collecting the results into another container.  It can be done
# sequentially ([`collect`](@ref), [`copy`](@ref), etc.) and in
# parallel using threads ([`tcollect`](@ref), [`tcopy`](@ref)) or
# using multiple processes ([`dcollect`](@ref), [`dcopy`](@ref)).  For
# example:

xs = [abs(x) > 1 ? missing : x for x in randn(10_000)]
y1 = collect(xf_compute, xs)
nothing                                                              # hide

# Doing this in parallel is as easy as using `tcollect` or `dcollect`.
# However, it is easy to do this manually, too:

using BangBang: append!!

singleton_vector(x) = [x]
y2 = reduce(append!!, xf_compute |> Map(singleton_vector), xs)
@assert y1 == y2

# This code illustrates the common pattern in parallel processing:
#
# 1. Put a result from the transducer in a "singleton solution".
#    Here, it is `[x]`.
#
# 2. Then "merge" the (singleton) solution into the exsiting one.
#    This is done by `append!!` in the above example.

# To illustrate how `reduce(append!!, ... |> Map(singleton_vector),
# xs)` works, let's create a reducing function that records the
# arguments and returned values of `append!!`:

chan = Channel(Inf)

function append_and_log!!(a, b)
    # As arguments and output may be mutated later, we use `copy` to
    # record the snapshots of their values at this moment:
    #+
    a0 = copy(a)
    b0 = copy(b)
    c = append!!(a, b)
    put!(chan, (a0, b0) => copy(c))
    return c
end
nothing                                                              # hide

# This function can be used instead of `append!!`.  Let's try simpler
# and shorter example.  This is equivalent to `collect(1:4)`:

reduce(append_and_log!!, Map(singleton_vector), 1:4; basesize = 1, init = Union{}[])

# (See below for why we are using `init = Union{}[]` here.)
#
# Here is the list of arguments and returned value of `append!!` in
# this reduction:

records = Pair[]
while isready(chan)
    push!(records, take!(chan))
end
if VERSION >= v"1.3"                                                   #src
    @test Set(records) == Set([                                        #src
        (Union{}[], [1]) => [1],                                       #src
        (Union{}[], [2]) => [2],                                       #src
        (Union{}[], [3]) => [3],                                       #src
        (Union{}[], [4]) => [4],                                       #src
        ([1], [2]) => [1, 2],                                          #src
        ([3], [4]) => [3, 4],                                          #src
        ([1, 2], [3, 4]) => [1, 2, 3, 4],                              #src
    ])                                                                 #src
end                                                                    #src
records

# This recorded inputs and outputs of `append!!` show that its "call
# tree" is:
#
# ```
#           [1,2,3,4] <------------- append!!([1,2], [3,4]) == [1,2,3,4]
#          /         \
#     [1,2]           [3,4] <------- append!!([3], [4]) == [3, 4]
#    /     \         /     \
#  [1]     [2]     [3]     [4] <---- append!!([], [4]) == [4]
#  / \     / \     / \     / \
# [] [1]  [] [2]  [] [3]  [] [4]
# ```
#
# Compare this to the example `a + b + c + d` above.

# ### Optimization and generic container handling
#
# Above usage of `reduce` is not quite efficient as `singleton_vector`
# allocates small objects in the heap.  Thus, it makes sense to use
# immutable objects for the singleton solutions so that Julia compiler
# can eliminate allocation of the intermediate singleton solutions.
# Here, this can be done by simply using `SVector`instead of
# `singleton_vector`:

using StaticArrays: SVector

y3 =                                                                   #src
reduce(append!!, Map(SVector), 1:4)
@test y3 isa SVector                                                    #src
@test y3 == 1:4                                                        #src

# However, notice that the return value is a static vector.  This is
# not ideal when the input collection is large.  The output collection
# type can be specified by `init`.  We can simply use `init =
# Union{}[]` in this case:

y4 =                                                                   #src
reduce(append!!, Map(SVector), 1:4; init = Union{}[])
@test y4 isa Vector                                                     #src
@test y4 == 1:4                                                        #src

# Note that passing `Vector` to `init` of `reduce` is usually a wrong
# choice as it would mean that the same object is simultaneously
# mutated by different threads.  However, since `Vector{Union{}}`
# cannot have any element (as there is no object of type `Union{}`),
# using `Union{}[]` for `init` is an exception and it is a good way to
# indicate that output vector should use the "smallest" `eltype`
# required.  That is to say, `append!!` widens the vector "just
# enough" to fit the resulting elements.
#
# For generic containers (e.g., various table types), use
# [`BangBang.Empty`](https://juliafolds.github.io/BangBang.jl/dev/#BangBang.NoBang.singletonof-Union{Tuple{T},%20Tuple{Type{T},Any}}%20where%20T)
# as the empty initial value.  This is useful for creating a table
# object such as
# [`DataFrame`](https://github.com/JuliaData/DataFrames.jl) as the
# result of parallel processing:

using BangBang: Empty
using DataFrames: DataFrame

y5 =                                                                 # hide
reduce(append!!, Map(x -> SVector((a = x,))), 1:4; init = Empty(DataFrame))
Text(y5)                                                             # hide
@test y5 isa DataFrame                                                  #src
@test y5.a == 1:4                                                      #src

# It is slightly more tricky to make this approach work with other
# table types such as
# [`StructArrays`](https://github.com/JuliaArrays/StructArrays.jl) and
# [`TypedTables`](https://github.com/JuliaData/TypedTables.jl).  Use
# [`tcopy`](@ref) or [`dcopy`](@ref) to work with generic containers.

#src It doesn't work:
#src
#src # Similar approach works with
#src # [`StructArrays.StructVector`](https://github.com/JuliaArrays/StructArrays.jl):
#src
#src using StructArrays: StructVector
#src
#src y6 =                                                                   #src
#src reduce(append!!, Map(x -> SVector((a = x,))), 1:4; init = Empty(StructVector))
#src @test y6 is StructVector                                               #src
#src @test y6.a == 1:4                                                      #src
#src
#src # or [`TypedTables.Table`](https://github.com/JuliaData/TypedTables.jl):
#src
#src using TypedTables: Table
#src
#src y7 =                                                                   #src
#src reduce(append!!, Map(x -> SVector((a = x,))), 1:4; init = Empty(Table))
#src @test y7 is Table                                                      #src
#src @test y7.a == 1:4                                                      #src
#src
#src # or any type that can be constructed from a
#src # [table](https://github.com/JuliaData/Tables.jl) and behaves like a
#src # vector (e.g., support `push` and `vcat`).

# ## Example: ad-hoc histogram

# Following example counts number of occurrence of each leading digit
# in a distribution of random numbers.  First, let's create "singleton
# solutions" using transducers:

xf = Map(abs) |>
    Filter(x -> x > 1) |>
    Map() do x
        y = digits(floor(Int, x))[end]
        Dict(y => 1)
    end
nothing                                                              # hide

# The singleton solutions can be merged using `merge!(+, a, b)`:

merge₊!(a, b) = merge!(+, a, b)
## merge₊! = mergewith!(+)  # in Julia >= 1.5
nothing                                                              # hide

# Note that it is OK to use in-place function `merge!` here because
# the dictionary passed as `a` is created by `xf` and not shared by
# anyone.  When there is no such guarantee, passing [`init =
# OnInit(Dict{Int,Int})`](@ref OnInit) is a good option.  Note that
# passing `init = Dict{Int,Int}()` to `reduce` is not correct as
# multiple tasks would share and try to mutate the same dictionary
# this way.

# Let's try this with some random data:

xs = 1_000_000 * randn(10_000_000)
counts1 = reduce(merge₊!, xf, xs)
nothing                                                              # hide

# Compare the result with `foldl`:
counts2 = foldl(merge₊!, xf, xs)
@assert counts1 == counts2

# Hopefully the result is close to the [Benford's law -
# Wikipedia](https://en.wikipedia.org/wiki/Benford%27s_law):
let n = sum(values(counts1))
    sort!(keys(counts1) .=> values(counts1) ./ n)
end

# Since we are counting only nine elements, it is actually better to
# use fixed-size container such as a tuple in this case:

xf2 = Map(abs) |>
    Filter(x -> x > 1) |>
    Map() do x
        y = digits(floor(Int, x))[end]
        ntuple(i -> i == y, 9)
    end

counts3 = reduce(xf2, xs; init=ntuple(_ -> 0, 9)) do a, b
    map(+, a, b)
end
@assert Dict(zip(1:9, counts3)) == counts1

# Note that, as tuples are immutable, it is valid to pass it as `init`
# of `reduce`.

# ## Example: early termination

# Find the _first_ element that is multiple of three:

ans =                                                                  #src
reduce(ReduceIf(x -> x % 3 == 0), 1:10; init = nothing, basesize = 1) do _, x
    ## # Uncomment for demo:
    ## x == 3 ? sleep(0.1) : @show x  # give other tasks a chance to finish first
    return x
end
@test ans == 3                                                         #src

# This snippet always returns `3`, even though the reductions for `c =
# 6` and `c = 9` may finish first.


text:
module CMDC

import HTTP, JSON, CSV, DataFrames
using Parameters: @with_kw

# other functions/types are exported from __init__()
export Client, request!, reset!, register

const BASE_URL = "https://api.covid.valorum.ai"
_url(x::Union{Symbol,String}) = string(BASE_URL, "/", x)
const _counties = Ref(DataFrames.DataFrame())

include("endpoints.jl")
include("client.jl")

# On import, dynamically create types for each API endpoint
# Also extract a list of counties to use when filtering on state
function __init__()
    res = HTTP.request("GET", _url("swagger.json"))
    sw = JSON.parse(String(res.body))
    datasets = Symbol[]

    # define Endpoint subtypes
    for path in keys(sw["paths"])
        occursin("swagger", path) && continue
        tn = Symbol(strip(path, '/'))
        push!(datasets, tn)
        code = make_endpoint(tn, sw)
        eval(code)
        @eval export $tn
        docs = make_endpoint_docs(tn, sw)
        eval(:(@doc $docs $tn))
    end

    @eval datasets() = $datasets
    @eval export datasets

    # fetch list of counties for later
    _counties[] = fetch(Client(), "us_counties")
    _counties[][!, :state] = map(
        x -> parse(Int, lpad(x, 5, '0')[1:2]),
        CMDC._counties[][!, :location]
    )
    nothing

end

end # module


text:
function load( filename )

    trans = Dict( "."=>0, "L"=>1,"#"=>2)

    open(filename) do f

        data = []
        for line in readlines(f)
           line = replace(line, "."=> "0" )
           line = replace(line, "L"=> "1" )
           line = replace(line, "#"=> "2" )
           push!(data, parse.(Int, collect(line)))
        end
        data = vcat(data'...)
        m, n = size(data)
        seats = zeros(Int, m+2, n+2)
        seats[2:m+1,2:n+1] .= data
        return seats

    end

end

function adjacents( i, j, seats )

    adjacents = [seats[i+1,j-1],
                 seats[i+1,j  ],
                 seats[i+1,j+1],
                 seats[i  ,j-1],
                 seats[i  ,j+1],
                 seats[i-1,j-1],
                 seats[i-1,j  ],
                 seats[i-1,j+1]]

    adjacents
end

state = Dict( 0=>'.', 1=>'L', 2=>'#')

function plot( seats )

    m, n = size(seats)
    for i in 2:size(seats)[1]-1
        for j in 2:size(seats)[2]-1
            print(state[seats[i,j]])
        end
        println()
     end
     println()
end

function round1!( seats )

    m, n = size(seats)
    old = copy(seats)
    state = true
    for i in 2:m-1, j in 2:n-1
        if old[i,j] == 1 && all(adjacents(i,j, old) .< 2)
            seats[i,j] = 2
            state = false
        end
        if old[i,j] == 2 && count(adjacents(i,j, old) .== 2) >= 4
            seats[i,j] = 1
            state = false
        end
    end
    state
end

function get_seat( seats, row, col)

    for (i,j) in zip(row, col)
        seats[i,j] == 1 && return false
        seats[i,j] == 2 && return true
    end
    return false
end

function get_seat(seats)
    for s in seats
        s == 1 && return false
        s == 2 && return true
    end
    false
end

function visible_seats( k, l, seats)

   m,n = size(seats)

   res = [
   get_seat(seats[k-1:-1:1,l]),
   get_seat(seats[k+1:1:m,l]),
   get_seat(seats[k,l-1:-1:1]),
   get_seat(seats[k,l+1:n]),
   get_seat(seats, k-1:-1:1+k-min(k,l), l-1:-1:1+l-min(k,l)),
   get_seat(seats, k-1:-1:k-min(k-1,n-l), l+1:1:l+min(k-1,n-l)),
   get_seat(seats, k+1:1:min(m-k,n-l)+k, l+1:1:min(m-k,n-l)+l),
   get_seat(seats, k+1:1:k+min(m-k,l-1), l-1:-1:l-min(m-k,l-1))]

   res
end

function round2!( seats )

    m, n = size(seats)
    old = copy(seats)
    state = true
    for i in 2:m-1, j in 2:n-1
        if old[i,j] == 1 && all(.!(visible_seats(i,j, old)))
            seats[i,j] = 2
            state = false
        end
        if old[i,j] == 2 && count(visible_seats(i,j, old)) >= 5
            seats[i,j] = 1
            state = false
        end
    end
    state
end

seats = load("input11.txt")
while true
   if round1!( seats)
       println(count(seats .== 2))
       break
  end
end

seats = load("input11.txt")
while true
   if round2!( seats)
       println(count(seats .== 2))
       break
  end
end

max_stars_repo_name: pnavaro/AdventOfCode

max_issues_repo_name: pnavaro/AdventOfCode

max_forks_repo_name: pnavaro/AdventOfCode