csv2svg-0.2.2/.cargo_vcs_info.json0000644000000001360000000000100124320ustar { "git": { "sha1": "a352933b117b68b1891c19e7e14c84514b35314f" }, "path_in_vcs": "" }csv2svg-0.2.2/.gitignore000064400000000000000000000000171046102023000132100ustar 00000000000000/target run.sh csv2svg-0.2.2/Cargo.lock0000644000000517550000000000100104220ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anyhow" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "argh" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ "argh_derive", "argh_shared", ] [[package]] name = "argh_derive" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" dependencies = [ "argh_shared", "proc-macro2", "quote", "syn", ] [[package]] name = "argh_shared" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" dependencies = [ "serde", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets 0.48.5", ] [[package]] name = "cli-log" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d2ab00dc4c82ec28af25ac085aecc11ffeabf353755715a3113a7aa044ca5cc" dependencies = [ "chrono", "file-size", "log", "proc-status", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossterm" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ "bitflags 2.4.1", "crossterm_winapi", "libc", "mio", "parking_lot", "signal-hook", "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] [[package]] name = "csv" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "csv2svg" version = "0.2.2" dependencies = [ "anyhow", "argh", "chrono", "cli-log", "crossterm", "csv", "directories", "libc", "log", "num-traits", "open", "svg", "tempfile", ] [[package]] name = "directories" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", "redox_users", "windows-sys 0.48.0", ] [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "file-size" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9544f10105d33957765016b8a9baea7e689bf1f0f2f32c2fa2f568770c38d2b3" [[package]] name = "getrandom" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "iana-time-zone" version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libredox" version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" dependencies = [ "bitflags 2.4.1", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mio" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", "wasi", "windows-sys 0.48.0", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcea7a30d6b81a2423cc59c43554880feff7b57d12916f231a79f8d6d9470201" dependencies = [ "pathdiff", "winapi", ] [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets 0.48.5", ] [[package]] name = "pathdiff" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "proc-macro2" version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "proc-status" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0e0c0ac915e7b76b47850ba4ffc377abde6c6ff9eeace61d0a89623db449712" dependencies = [ "thiserror", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", "libredox", "thiserror", ] [[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-mio" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" dependencies = [ "libc", "mio", "signal-hook", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "smallvec" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "svg" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583e1c5c326fd6fede8797006de3b95ad6bcd60a592952952c5ba7ddd7e84c83" [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", "windows-sys 0.52.0", ] [[package]] name = "thiserror" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets 0.52.0", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.0", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" csv2svg-0.2.2/Cargo.toml0000644000000024130000000000100104300ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "csv2svg" version = "0.2.2" authors = ["dystroy "] description = "take a csv as input and outputs svg" readme = "README.md" keywords = [ "csv", "svg", "graph", "cli", ] license = "MIT" repository = "https://github.com/Canop/csv2svg" [dependencies.anyhow] version = "1.0.49" [dependencies.argh] version = "0.1.12" [dependencies.chrono] version = "0.4" [dependencies.cli-log] version = "2.0" [dependencies.crossterm] version = "0.27" [dependencies.csv] version = "1.1" [dependencies.directories] version = "5" [dependencies.libc] version = "0.2" [dependencies.log] version = "0.4" [dependencies.num-traits] version = "0.2.14" [dependencies.open] version = "1.1" [dependencies.svg] version = "0.16" [dependencies.tempfile] version = "3.1" csv2svg-0.2.2/Cargo.toml.orig000064400000000000000000000010201046102023000141020ustar 00000000000000[package] name = "csv2svg" version = "0.2.2" authors = ["dystroy "] edition = "2018" repository = "https://github.com/Canop/csv2svg" description = "take a csv as input and outputs svg" keywords = ["csv", "svg", "graph", "cli"] license = "MIT" readme = "README.md" [dependencies] anyhow = "1.0.49" argh = "0.1.12" chrono = "0.4" cli-log = "2.0" csv = "1.1" directories = "5" libc = "0.2" log = "0.4" num-traits = "0.2.14" open = "1.1" svg = "0.16" tempfile = "3.1" crossterm = "0.27" [patch.crates-io] csv2svg-0.2.2/LICENSE000064400000000000000000000020461046102023000122310ustar 00000000000000MIT License Copyright (c) 2020 Canop Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. csv2svg-0.2.2/README.md000064400000000000000000000037701046102023000125100ustar 00000000000000[![MIT][s2]][l2] [![Latest Version][s1]][l1] [![docs][s3]][l3] [![Chat on Miaou][s4]][l4] [s1]: https://img.shields.io/crates/v/csv2svg.svg [l1]: https://crates.io/crates/csv2svg [s2]: https://img.shields.io/badge/license-MIT-blue.svg [l2]: LICENSE [s3]: https://docs.rs/csv2svg/badge.svg [l3]: https://docs.rs/csv2svg/ [s4]: https://miaou.dystroy.org/static/shields/room.svg [l4]: https://miaou.dystroy.org/3 # csv2svg Build a SVG graph from a csv document. ## Installation You must have [Rust installed](https://rustup.rs). Do ```cli cargo install csv2svg ``` ## Usage ### Viewing the SVG graph in a browser If you don't pipe the output of csv2svg, the graph is automatically displayed in your browser. For increasing readability the graph reacts to mouse hovering. ```cli app_whose_output_is_csv | csv2svg ``` ```cli csv2svg < some/file.csv ``` ### Making a file from the SVG graph ```cli app_whose_output_is_csv | csv2svg > some/file.svg ``` ```cli csv2svg < some/file.csv > some/file.svg ``` ## Example The `examples/irregular-times.csv` files contains this: ```csv time,something,another thing,a third one,whatever,bof 2020-12-25T11:24:49Z,580,,798,, 2020-12-26T12:00:20Z,200,2784,,, 2020-12-27T12:00:22Z,300,,7398,,826 2020-12-28T12:21:27Z,400,4282,,,1125 2020-12-29T16:13:49Z,220,4720,1409,6885,3162 2020-12-29T17:36:38Z,700,4920,1209,6730,4228 2020-12-29T21:43:01Z,800,4320,401,6737,2328 2020-12-30T06:42:39Z,920,4134,209,6856,2222 2020-12-30T07:41:22Z,1200,4136,410,6937,2481 2020-12-30T21:43:01Z,1300,2322,414,,4272 ``` It may be used with no configuration: ```cli csv2svg < examples/irregular-times.csv ``` which produces this: ![irregular-times](examples/irregular-times.png) ## Progress **Warning: still a work in project, expect disappointment** * [x] most basic features * [x] time abscissa * [x] integer abscissa * [ ] time stripes * [ ] bar graph on discrete non-time x * [ ] float abscissa * [ ] float y values * [ ] disjoint scales * [ ] Windows compatibility (if somebody really wants it) csv2svg-0.2.2/bacon.toml000064400000000000000000000011261046102023000132010ustar 00000000000000 # This is a configuration file for the bacon tool # More info at https://github.com/Canop/bacon default_job = "check" [jobs] [jobs.check] command = ["cargo", "check", "--tests", "--color", "always"] need_stdout = false [jobs.light] command = ["cargo", "check", "--color", "always"] need_stdout = false [jobs.clippy] command = [ "cargo", "clippy", "--color", "always", "--", "-A", "clippy::match_like_matches_macro", "-A", "clippy::collapsible_if", "-A", "clippy::module_inception", ] need_stdout = false [jobs.test] command = ["cargo", "test", "--color", "always"] need_stdout = true csv2svg-0.2.2/examples/irregular-times.csv000064400000000000000000000007611046102023000166740ustar 00000000000000time,something,another thing,a third one,whatever,bof 2020-12-25T11:24:49Z,580,,798,, 2020-12-26T12:00:20Z,200,2784,,, 2020-12-27T12:00:22Z,300,,7398,,826 2020-12-28T12:21:27Z,400,4282,,,1125 2020-12-29T15:10:49Z,250,4400,1401,,3140 2020-12-29T16:13:49Z,220,4720,1409,6885,3162 2020-12-29T17:36:38Z,700,4920,1209,6730,4228 2020-12-29T21:43:01Z,800,4320,401,6737,2328 2020-12-30T06:42:39Z,920,4134,209,6856,2222 2020-12-30T07:41:22Z,1200,4136,410,6937,2481 2020-12-30T21:43:01Z,1300,2322,414,,4272 csv2svg-0.2.2/examples/irregular-times.png000064400000000000000000004574321046102023000167000ustar 00000000000000PNG  IHDR[X=sBIT|dtEXtSoftwaregnome-screenshot>-tEXtCreation TimeMon 18 Jan 2021 06:59:19 PM CET[s IDATxy|T?Ͻd-$$$,VYTD|j V >-E>kV\ ťh[)X"aE@Y&:sg~L $s3$_/_2{99{\rADDDDDDDDDDa#Q_d+l%"""""""""RDDDDDDDDDD `HL)V""""""""""0JDDDDDDDDD&[d+l%"""""""""RDDDDDDDDDD `HL)V""""""""""0JDDDDDDDDD&[V Nխo>_š5kPSS7l裏3`͚5=Q3MPr䈢(axꩧp7c׮]"{_~9&O܃]z\;J?\0aB~m;]ǯk̛7&L$I oA+x,ADmFj[nD+ݷ/y睰lƍ9.QoHQC?(Q}Dt:|gQR o&#R4lnڤ|0?pCv,hll 74":FnHBUnYf᭷O??222d|شiy vUW]ضm^}U1O<^}0[Xp!֭[[bݺu;a0e-Z#99˗/ǖ-[wbbb?)^ul۶ 7oƊ+ L;v,J / lSTXh>#lڴ >l}xga֭xW1eʔ6ey>3|Apb{zo4ix㍠9Dze㣏>… |Dro9ӨQ}vhZ,]0ZNO=>~O?4n#??W^y%ylٲcǎe͚5ضm{=̙3'俫bA~~> ̙3n݊^{-PUD|ŋ^1zgj[l?~6mK/s9'}Ŋ?> @Ün .oߎ>K,j26??ӧOo7=Cؼy3Z-e9D$GZZVZ[⣏>]w\;cǎE~~>z=/ğC;}t{ضm-Zz^t:. /V+.]Eѣx衇pW%Irrrp;<Xj~~C=X-OLWX)S`ʕX`}r-[.^y p}u8Xx1}],^Ǐ=.a|zСCpw"''<@\ŋs=(<@a΢E\s @I`X~W]u/_kM+ҥKqQu]˃j?t 7܀|駘={6ϟ?j*;_|yyy;qQ< < ˖-pj8I$dggo#< RSSc:u gL1|X`V\)Sg(عs'*++1u6T*L<6mBss"#`˖-_2ξ}0{l455aƍ={66o(D2ǏGyy9}g_|̙N&^ۑyaݺuZ$%%oFUU`ƍx@_\Ç>_aÆ[n $$KF\wuxvQZZ 8tP5[ 뮻۷>Ǝtz/ۿ?|M@QQ>C̜9(a֬YpXpa ZYYe˖ozڷD=ARA`Ձ۷oGzz:MKh4⭷g}Xn̙c_ ?>vfW_}_3g~a|w|ƁS^^K.,pϝ;>zkc 47|s##IF3gÿ?X }+WW_egg#++ O>$z-Ν;P-gjhhѣGmK^ 6o?y5陔3fΙ3FǷ~ OOL(}ֿKG ^?aÆa׮]!,Q[;w_ ٌ$&&"11zdQЀL8@멩ؽ{7,Y -uaʔ)Xf ?x<))) fի /hSOsŖ-[駟/ns9~!(!"y ~@_O>Arr2*<x|h7 jfkII bcc1{F^:;n8ڵ]|8p 9r$t>`>3LZؽ{7Zm/9H@iii`\gveeeh4v_f͛}D$I/zyXZZ@Ïy_G>6>yd}„ oi4l߾YYYrpر ]̑#Gݎ(ɽ*uz_|f֜@,3okرMBatk;wB6?=vn݊\=z4vf|@D׋c7nAm\_}^o`>Fƍ {E _|Qpg߱cv;wQ5W^Azz:.\ 믿Ƨ~ކ۷۷s2hlyg`0`۶mAcl]Π#uYoW뒝Qֺf $[~8qQl̛79990!A r`|2{JKK1dȐ.c9Z>>CqqqǙǒX9~Q>v=豏;VD({K$IBeeeϺ:֙כnz맜)V]]kk'N#GĶmIՂ ddd~ȑ#={]VoӑFL||<UC|TVV /!"LO?40'9DԞnYf<wQlx}:V\իW 'NٳSO|u7صk qC?~W]uU)9Q_%:n~\r _+ۆ p7իpu]lKɾV*"j G̙3W_>߽{7b 1m4|(--ԩSÇcԨQ>|8ك2$&&b̙(,,D~~>ꫯFSSn ͆n F˖- !I6mڄ/C=&!544믇^Ǒ#Gpbرmf:!*ٺl2TUU.ܹs؈o޽{Gů~+,X> DQh"|gax&bAii)֮]eرfĉzCBB{سg>s==6[*(('̙3?<:g}?~<~_{^g6lIӟǃ {/^x,^{/}ِgb޼yojǎpbIpwâEPUUo0a„V/QO ݾx0k,nS4SNa…Xp!/^rɁVECCC}kگ"v؁k&`PZZ ){VŋDcc#>/.R,\?:;4iNF~~>?ԩS~عs'?M=K :u wnls(4qQ__W_}J\" Np\ankNZ|\\9,q뭷F:"ad,$OG}7x#py(fH.x*ቨoǘkver.j?fqŽCdwJuE+6aSNW\uuuX knތ<{F%K`s^/>舀:,F: "c~?ދt FvZ-:1c+ĺu"mH@D}]""ľW0HE1qD̟? سg/_HFDԆ^ǂ 0uT8cÆ xx"Yd+{""""""""""0JDDDDDDDDD&[d+l%""""""""`#`ƍشiVZvӉ'|[l **}ȑxװm6[3fhs3gbݺuؾ};^{51Bl%""""""""pCꫯӱk.<쳁c=JnwqLsl6yl۶ fs=cǎL0-qWc֭xa6V""""""""" C'|jx<lذHOOGnn.paUW]6mN:+V7n1sL3vZ|(**ʕ+v1m4gK/v111~(**BEEE={t:Lݻكl@ff&v%0JDDDDDDDDDQ駟ZƍuV\r%Xd ~?l6ڔвkUUUlv%; Ng2꘰XS!WԈVPQQƺ˛Lv͎Y8)Pa+j ԰䷱l^~>*&Z嶱FE\\lc߇òQQUUry&qqa Ehnn 1Mڸ UUe!WO1J'ԩ#*f]]CocV؁a nwz<8uꨬ}AmJTW\^6hUDvAU]{EjfKObb!6VPVvOSV' KXuv!͵11z8Jq!<[mNhœV[[wcb p8U' mxoٝ60@Z7jj*.#]6>ry%mFii} Nq66DSS.axp5-/-p{GB.0p ~}"ىaݮUccD@rrx~քAawNlc"mX+3~|>ى4b*-;.;6錰Q]]]>X8phm%In^g 2my\__%q48:Gv"011:1zKK e&[WUL!))if堬JKZ:.t-)9*/٪U6@$9n;[M~W+ơ?rR3~dWmdd`2ٚ'ѽ!qVZ|ݗA~r(/o (4J&J~<3!hzO}؉ЯY.I:[T*~۷p:(-m=pػw/n6=zt`׽{"77ׯ{ԨQx#\.RVkPi"JFL͍vQDQNgE" VZFCC]pD=Wz~Ih fx^I|j`0CR?Uw :^MBzը?:p ={6jTVVbŊ$ ˖-nǣ>_k֬d?O]1f,YկPPPѣG3~cK.3f@}d|Gl%""""""""t:pB7Z=}Y,Y'k׮Ŋ+Kbw SNǿ/r򗿄ā裏d+[|0JDDDDDDDDDnϋtb(K z k|>73Qh 3 QQWWpHNR|ߞNDj5^os/2d,Đ.âj*"23'`0,Z "+v4 O8 2dT*5*#b8D$%e@RG:~E3"))5ҡYM 2`6;" QaطTE:" ^ojlB 8SC>Ñ b+2p t HJ@llRebbZAF#hVѭָH( ZT*5 )BLM$O!Z3^os!ط$;T*5Z!INMM!;z OBsSCCz|> MgMz9DQDL^OSQ DDDDDDDDDD 2DDDDDDDDDD `HL)@e"EaHI$~_!7,X <^O2,:t,A@]]U!LTV"jubQzXi١CdHNIDJШIͨ*t8 əv42d4|> 5=!EΈa΁JFmmeQ gR͖DT*uC!Wt:#]0hHLtlvD:~juطTE:" ^oBb z˲s&|l=.b+]p8#F&8211$&`8R[KlCQrFQT~?O "*h41z!IHC'^gt8DF(ٷ$WZ=?C:v4Ccb %477E:^} (ht$/H&[ed+l%"""""""""RnE:HIDyIHCoX,2d,^j#Qd0X0l9uuU_HN,TT'E:" Đc4a 66 E!;55qhQTCEQDNyͨ*t8 >< fTVoGŁCBIh)tFdfNJFmmEQ gR4THBԯtF$$`D:>K"!! &=ҡKD}^gBBBzSe 6 iW$$nOt ?cbeb9n6;"tj-DQQOEj^3ΒL$o!j DQž%Q *՚Zұ[ǡM^4G:^v AY9 I$pd+l%"""""""""RDDDDDDDDDD PHA!uPRSQ^^p %C$yPp$̉uu_HNArPQQr6[<   ̜# E!;5uz"*ơ(b0̨,t8 1b2FKhbsyQ_qP5 j55G1JVPT_ O^ot(D}FTLHBoX̱[:]ظmCE1z#F&B[AKFFDGG:E . (:TO "*TjH>_E B@aGCT*5DQ~?DYkt9TʮE8Tt(ZW("T*3AVkI$oQ DDDDDDDDDD 2DDDDDDDDDD `HL)@e"El .='"XN v^j"Qd0X5 2 ))HKAEE1$/>!l :͞&46vZ6+k"PVv"c:=PQ1b0,,t8 ͽXb1lx|{8Bz ٓ jVD:pf+Xqp:SR" QәP,F 3F5ҡfNg D}KNchL]M1 cC^tjuF:^M8)uҎZm8HqPRitlG:E . (: PQ!j||>!:AJY3DAkR<9 Wj ~@3qq޻!iږqd,}*qq]>mtA(QG_^{~ʉA *~d ط$;ZgZ߷(B^R7W@K;vOm_>:DDDDDDDDDD x""""""""""0JDDDDDDDDD&[ϋ`DPSHA!-m8҇8&A6[<2&:937cb`[UHE祦f!ݕ dT5Oj:-s.d#>]|'`TAĨSa0v܎K'PWWR4M>|Tj9gR@#)H"Z$tqG>;fJIj("g=3?-9 bbu$!&eY=V[|6Q7u$bt u$jL8(~K ֑P%\. "~DDD?~ޒ2ЧaCt.ᨈAhIoI鯶&M}>)cG8TD0uՎ xD= \G/(2Z:\)I0OJ0 K=Q?җMDjI{s9hozϙ _?I% ?$"""""e0JDDD}ʥR.u>ؙz0g;Qd+> &uAi&,QgTv=/APtHO+QnQ_e#;{fWG:^+;CbR]&$@׮_Q/[k4(#Q^^IF:" ÑhnnDCCMe?qq(- %\G0*zEG  **{w 1c h!'\H> uuU=!Eތ RQ]|RDoę`2a'ID=CnODL gc76?uv{"za.1hva4~["ZUCemxXmΐm6E߽nOt a5]M*v{" KCQrk[A>&8h'oGs).ŷ^< hʁ3 W/^DD}KU~4+1(v8=(~:zkQwL&Z-q|'h=`̷h@6?ה|D D}KU~4+1(v8d+ZC}&t{blXP kf4e)#LQd1DkXw'lw;F"""""_Tv=/APtprB|HCo >^ՑWM…o:‡hXsъ#. qۯDhBЈs|vےzn'ii9pF|}d"DdgOӄNˎ1Ng JK C:v+\8TU=zF+Hk 1ch>DNy$/z8B#FLJAuu6JFv{DQOhb`' &FPzIKn{S}{$Ej kTǂn.r){Sv{-z=hQr&5Kl6lQ6nO,Nv4:,:=EQTnOAot(\.WzY<< ϋ7J:Y4RRm/3UyNDDDDDʊGGDDDDRMxS~֪ObXt\o:ADDDDDDDD+̓Ґo34MU!$A^b| ds7"""""jV"""z?&\tۛ8!6*^#(AO&~uQd+E1>Ju^  u5@ RDDDDDtnϋt\\2F|>_!7 ?׃4"1ÓmL,ۚc4ڐ;6p #]. ZIY#/G c($y#sinB}}uessLEIɱr+c$JJPQTa0mp] bjubᓼS#/ZAuuYQ cP`AiAԓ4X-qhu( vjDQ԰Z⠋ZhE5vD}^obߒW51]50!hlQsj`t h4ZX-q1`dMD>oAp\HADDDt"*4)8,h3nRn[*v5WtXnYkOzGDDDDDDфV"""VdȑB( !Lg,<8eEzz. $o!080|xQ__i SGC:ơG7,¸q?dCyyQA9tގ6[9hzT7Eսd!ҡjvlaJ(ɎʒgZ۶m Z~…t:q}ahjj† OCZ&9w}7\.K/aڵ̜97tN'~<(((P\._ɔ3bg(TAV7z_U'"KW^y%f͚3fFf+E*M~-VD+<9!2 z2s:Q(JJJ7w\Z nE^^>EEEXr%n7MXLQ"vXxB{3Xh=T=QvE!66(**BEEE̞={t:Lݻ1كpl%"")%adoO1+O-J.Yd"&,.9jj8uH5 JU@]]p$H qjjV/.GIIhoPV ^|F>ʴqJJ\^6>q{441ǀ$/-k_ HM N(*x3-VkRi65Ζ28tH^ &6.EiIaSáZ866 v{J<8|XދrVw2TU4Y:əa 'N|_76[BXuzf9G>iiá%(+F$' N8~C_8Ι zq䈼6NOPV(z  N(,MM!w:S`:êiѣ{.x\T M+*e9^o0c`Ґ^ %? IDAT.:=F=낧qr!ᵱ}nɐ $%6رhn6nnnce66#IX~4yܽ6 Q B;)!aUw8#c!y"T:F'l6O>\ ௥mlV p7"x^o ^/;h`U'*ՊqY @FU3]'|59z%(\+ml4Zt.BYY!C`<$׻^g ^++Vk9KKJR J>,o ._U^V^Ճ>n-*-]NM1#|x͝n7l>EEqw5:]x&Y ~<<]y 4XTlK%yKJAK K籷YvjGLLxf<&Ri9<ic G /hŇ`FN8..& Ǐ~T\|HV}c+ơzCO]9y4qSS=`LJР^VUPyDyHf M&\\|(--QSpȞ V\;]QAY׮h ADR`tF|]~a?4GFy|ʛL.堡6wjCC-lr :ǎGqqםe#1d-@˄%]qW@Kl3AK7cĈ(.>41|%?lsopݰmʵvvXvJ\.r[ղEbbo_?$Y@Až-RWVbNİ gRa߼zM"Ta>ƚ6KFv$< z`!bOApgz[NlD y[W}?=^;h6-؞ݎ= `HB6B %@B.$,+BK!ߖe %,eM͌F]=Όd^IsGz ]ѹԛquq^$3А,qee-6oދg_;[|}*~=y$I8, M/ɥ/w;\>C۰dx:ή4M=Y$}l%Y74Bs6\kΛaY&y)q[Nխq},IW_}pwy{sM{9X]nC(ε@.+2tPغ:^&I~bxꩧ_r%on촇ox;p7[oŝwމnmo>A}CG?x<}cssA|+_Lb+j춒o;4<Y)Yg~iU=}!|QMs3QXd o<0M> ~áCpwíފtn3]DDD%#ccԓxFd?iǶ'|MYvL"""|S]Zf aD|_Ů]_TMM رX ?<7k{n: K_~M7݄mǑ#GpȑzXDDDtm*3qߖ_5Fcc[![ZY%AL'tqNADDD்(FS?.ꤘN޼ͪDXqFd%""""jB(B4@6$v!"""ZjTɛq }Kďňcjl]NDDDDE/;WVbuMD3[iuvf Ik'O"L06ahF#^!ʪ7+êIz|L r!099k:9$Ob]vIRϋIv%򫦦X~+FG{aq(ѹa7t=xuӦcɒ>s[uv\Wc||89KqТjE=/Ȳ Ԗc2G41P)-{!+*"Qd gU)BIq$I: QAQ%eDZ{ǶJiײYFIq%k) H>@\kweA@ie=JfNNg$ (UU+VPPP*(ɛScp!-Xl%""+ [`R 1&iR[gN d :.,$l\^?XS*(Ve J*sK_̴l4B{IV"""ʚ\a[lz;Okm}=E]NDDDti6.ǒR6\HSչj2PD֟OXl%""iǶG?;fVǶb G]NDDD>IWP:7CTUP*3UUC(^ ,W_ r0uu`-: DnYkFnv)Jo< zꂊ 6o~$I`rr,N&$'DVmR[]ev1~%@+BsVi^! -dž {iqD KWۉREXR[Mz 3ׯߊu6ch,ℐ}Q^ؘ7"ԕ`ieWcMmWcmC+XZYTHE"CZuRVVSp|h/ a ફi[,DgbuEV\h~BJ bK[_"uG(}$ %e}rqvS)$#4±%QBIy*Ř`L% WW ٭#g6(ʥK-%w[RK_cLcY6bn nbiaÉL[}$͎ѕzp"""ȟWL9"%zR^o4V9=ˉҧ 2{{EEUUFuT}c ČjL7?WTcQbU:&"""Wh,u,cJ Iy嶂[Ƥی[ CcjEg#Eji@nοnbFL=ĹiJ[lqܱ܇1z  ^a6$mƔd kqE=EI^p%ivVzVSz |4D01fLUT2DDD%v>5TԳ?ƱK8..'"""J_@jE=Kg]R@.RP)s0-u3犧1ݼf@36Xl%""_[")<GL~7|^m-:*Ƕè(}EVԣ$y+g.'ZX*f*۶1sScﭛzv"[i[Q[ۈ_u^fԩ04t8Dz /3Ϫ'pZxja5؅c8{qaWem QY?knB}J?ŽCD[u6ĉbddW6o ۶?Ij+QlX^wQbffDYYL@<$ov񺩹Fo1Rt̙00pظ {'k3 e(ٛ%-B( !B^a;Z XhI(*}8pJɳ֮]EŽg[b7m"H:UEQaY?tcnӬh'fd7ff 4st#/E,_vI9>̝۵0gQJڬ O_-yA2[gN#y})C"""J_i@U+P^{MkuS5}vP [hQUj-wY?b_RO|Ztl )ʽ%0XV;~cZ\uj`FMtDtDDDwkJޤgH VǶb G]NDDDt邥Ũ)_ղ틊7"JDDD X̥Ia3q*lV%ޣsl{Z_+QV]m:ZJ yNLKsAuU7+]NDDDDDD ^ X~+7oЩyw$쫭mU^C0==u9MV)Rߓ]e؃T[BDƼ}"d`We$pԺu҅0M8DxյqDݲXt N۰yq*;oFeE FGرfT`l̹kjM/i[]U `bb8YÙ4GB/r ɡEbxqT)yU=Hu$ y9{RAy6c91\NDt$[QΓR !CK ~#<1}l~Qz |A.J D²ٱ%D9ϲGMHhj/µ1e"( Uѥ& _Mj?+!%̓TxRDDDDDD3[ mFcՄJ S1ցJ.'"""""},fpUr9G# ֿMlb'/9ADDDDDc(Kqڱ0~"fQE/!u:]NDDDDDXl%""S-n}!'NDp@  |0pر*>(w`p!Z[wmNpE"-ǖ'05:呷W`UǗ3 Ry]_ ! LLxwC"kJh+3y܃d֯ߊn i(54-{%bXخx%ܭm;ں}q*IW 59u%ǚeغ: ʪcMPdq3[iil: QA, %WKc:P1 ]N-۞"ίN<slk*N}ˉ(qlI?,l%""# vW:Tc£Jc^k n6(wJDDGX*IL#xBplh5fˉrDDDy*4[IG҇^) ~";o&v׈ DDDy`ـ;~f\ND(xZ8.c-Ze.'"""""79 :C[N LغU^ǡjUFc۷Xu9TVb! ٚhXuxZcZʯd);ލS FMDа [D  }|:K[[wm':Tv~-*+k12r89K~-*+02r񘚚eضzqUaΛ(C^l9!m4H`B.AjǶC8(ȟ,B"isv)>)֎*[q9HplI',˜P մ8Z,6P IHĠi D"]ng*Xy%&ш(mbv)gp!e L3\mEBr*"""""V""uܱЪ>Z) _ty6ZLDDDDDDb+Qڬ RǶG^.'B0  vlkm.'"""""[rLU7۾+dn0@ϐ՗ۖ%xDDDDDDb+Q)W kƿ(BsRDtUwk\NDDDDDr0u򇶶hkv$쨯_mۮ'05:+fW&=/iH<;Ŏ7Ca))V0m]r[!y҃d--޾ CC`q( uuд cMhl\~P[[7ں10pס$gPUU3^YB:TUۏi`rwfjy T5Ph8YÙ4GŦ`۶Q iŦ`j.vƱ1raYb):(CxTulk--2hZ: ehn'xx4u]JXl D(9/B"13o iŦiqd3kY0tlrOʣ.'"5F㼅RˉDDD>.[Վm?GXh%}SOd2VcYr""""""oJDDswP%Sv kCǔ~j@p^` IDATvˉb+O]g6`U܋>>y3vT۪ˉ#^ hoF{GL~t=)[ptZSccۿ QYYݷ@px8ygr=f J\+09+;ﵶ@G. D_Kƶb /x[q=C:TTVbxqr{Ykk}ǫa:&']NH~P^^[(B^l9 l?Dn2Mh: D]*Ƕc7eDϲLD$Q  /LtH̎-:Q3k; Jϟ+r*IbQr^4A|~4MAβ,DhZ~ݱ'555FDDDjka'~LgJ J2hmR r"""""ˋ3[|䏍cJ S8>ض..'"""""Xl%""k:lpl|'ogsRDẕŪ;u絉rDDD>.ǝJǶo˃(w;cvoODDDDkXl%""X-.})kʀ)9G^ǶVn1(`0ރc, H䖆Ug/wklW$=~F3q֌UV֡!{`QHhqxZt3$ˡm'::Iu"%WzhZ SSWG_єc{סB\sçRǺؾFȘ˫ӳZPh8Yx#63l: QAu SS!z(یFlI[q@CH}]]]H$z9;;~ƍx{ߋ&q޷o.ĉ'>{.kٹf+V%xʱ0 VO1vތVˉ:;;q_"nvg?ý Y<dYƝwމƞ={pw}O=^oo[ll߾y{/|v~㡇BEE2W3[\&>7c][Cq˙ GVKs\J|ӧ/~1m͚5D8<޽{w_=^ow177r{ク O}j__ַ3[\VscuDJB+`\r^Z'J i& 3 x ^$j%y,+L?4-U$e^a,a'ת_?k9s؃>4-_}<Ǜ*\~@ó—>8} e˫p8yw@ 5AMӄiz, љuIm嶊km@?t ӑ8>niݎ% WX[-4R>޻18]i_(*di4;i/Y]xŧ08pbc{z-<>w{{V੧Bד]W_};FFwΛR/r~ p&۶:lp  Ξ=v'`۩߿cB n@_=+7gyp}'? *94_K`/֞.[iN4AII6n3ۥM@7^{ɯwޱcA8/JK+P]]11HK^_g~7-QR\뎍d}\RR3zM8rD"c)tY75 SG߯ ogCG}Vk'/5^:^z)lٕhl24 ͏h"=Ҳkjd N~a))-GG^Hbٲ^s>vںEE%)ͨ7_]O`fzܯ]9JJ*ޝVN'SS_|.]kz>;ϡS)]WZZW[ۈjttrzŧ0==._6>ִ~F-eu}\VV&F?+cɒf"1iMGǮ 'RǗҲ=ff7srE3ǣūdeeU$ z ɩqؖ~JK+ٹڴxe W|$J̜N3ѭ||EE esXl*WjE] $TWףıhMg?9ٰaOȾ8񕕵X~kF EŬZՆ$giƍ{ I{ (seDF/zix<>7[fff011:B!BIs,֞ ,ҜSAbi]9Ig μ5=qff*+HZ'i bZV8^b4L}i3`Dz3AffwDxQ5>CSS}Fvu-[ը/r)P39=nYF#Y$F#I?*LL gLL"\"+t40p<=0;(o)x{swL8띷MDFӚ1>>ŲVzfZ{0ǙJgf/"OJJJ 1<<1,]i& ! ^wymڴin^x6lwZ*|+_p]Fo2VfC`8NKOT(zp/ވ(ݻo{8|0w`ƍx^44M<裏GAyy9x 姰cc[9ܯ[i$Io "IuD˰êql{Tב~ǀ\__r"r±%QHg:{\}I;Wo g#(?r0u؅0M8Daoq(M[jX|PTUաg$I :eAHqJ`uv,_}GRz F{.e9/&! ),!d\{PUU7o?խ7A7tLFA!*/b׮۠(ϲkLO hZ ≨Q(Mv1lrl{Rʨˉh>#D,6uʢ߉^qjFy:rtt: eHbH,zl8<{j*sE(4hQf6B'JyTL@(4h4ulC{f˓N b*o0 TZ\Cyߞךu.'""""Bb+Q^e6ޱ"r""z\KwXfˉJDDrەEï!gY9g{2֢JNDDDDM,!d!{H!dHO~Wcy %]eD3!9w_POE1.-G:toxc\X?:u.^ ix`447B Is} yfQi uZ}== I!es9-V5&̬f$Ӄdu':;wcp8 C:e`5xSSwӳסHJy5w,s^TVbhqr2V`ǎ&#c.'$?(/b׮@QTx'ktLx/ 0>ޏx|(7+i'ǔ~n|N5cfy#埏"$im:Pfs6=>7BDJ$f΍W a">!]crſLض~D(J:`~Ddi8DDD cncc) "FZ\X,:  Qpf+suZXh%򱐤KmP1DDDDDXl%""G--J_O z//n`]]NDDDDDV""yX(MzWcJRc[U?׸4Gk/]^$ *)O^c4bth҇ĝs$I>gPNDn]V ԝ Kwal)y2$DUYV JPc~pľ.T蒔_r0u򇎎ރ0M8Da%o54-qV 8:_Uˉ(SUUu$!:yWLtZUImMvl 8"#rDg<TDlɒ+u=(^V4._^%\Pguh_XBk_Z 9HGBƵǺرF膆Ș ˃صkEq&(MNA/ I"hb1^Jogá`.u }F#^G!PAfsYRkFLB3:JZclo[ȝ4-3gV΢VeʸE1A2,[x<3ǧ=vtmQضBy##g051C&l^5-3:`7:Rhqì_0V=A*"Eסͪtlx ˉof+嵻ՎA)J(W}R=Ҍc~K($"""b+[ea8}MŨp92Q$ͱZ*|V""K[jhtl{DkCQf$SBz'ڠQb+QE%$^ I2,̖Fw+۞GcyD5!ĹgRƤ>xɱ >ȟԀO IDATcK|q~*EE%S~n?^J\&S ]Aރ֝; 4CT0Wb۶H099uzV4CR_VN Oee-vydE1V]R[TZ lG{{0tQ,[ xSS vK5HJу֝=27#Bs˃:u%={^yv9n'0s9!AYY5o,+:NְJsJ ¶o#e D,6uw[`/)0)cR!e@8,[vQr28}U9SbDWAm^G$^mK_&DXn zz( SCJÒ%W,zmcW--]Dهn4!dC{{ϼ,CO>4._b2*Ú5Ur0u@h/l:QeBM{'gg|+/&Y(LL #fitf UHsU0%ÃdQ"~X2 e@!-..C4A(伎 i :T$!E$2ux?#r92T#:NHMMM!Qj+a!|Q=A*"2Pc;+Q2DDsjv,I1^-..CqqrO)*uhIIbcQA{ Мm%D~W[ۈ[ti]hg oWaWg(\RYY[jUQFLӎm۬ 4V2[v3oAQ D~%o%=vWa)?PEqLmBtw߂n4!dtw߂yY[qOJK+} ֬:JV`p!T5]O`te,+B B,~CFT2=HED2Eшq1%VUR[]Hț%Q5066y( @ y|[՝?ݴY-YnDZ !2my:{)migtЧЕNZ(e+59}ENmɖu|ޯ_ Asϱ1:B0ؓUۊbE2 ́ ɩ;׏$IE A P >!A7;""\k"gD4E ֠|Kќ!UĠaI6`m]hl%"9Vu |ܰ8&DDD_UNM1,prSEfVJXp8\PIpsj-V COv$` 4Œ'2h"elFn+pDa^[rA4IrzU̺8u8\Zf1MՏAqbub:X۶퀢p JTHUضm**ʜpZa38#D -.۶@]JC!ʠA]QaQOk6b۶Zh1km-MoƆ˲nyl:W&mv`ʭf2mv`ժmx*AÍmv~١x0;dYA"@'tƈ E%@0؃X,lr4jN9ɥ f6ZҀ QB DQB(ԇHdp2$v!\!!3"A7!Q JifCD30zx&9 #AY-Q ΍q Xl0=gbch~E A,6Zh.$#C8p6?-Xn(Xbu{F3 #*|PDDV; n!Y8""""" "9da/!-RUvܞX^(LުVac$㸦#PMm,3,k]@Ql%""S]:Ұ~->HC.9jma""""p,JZPUphvȲ@_t,ՋOj={I= DSE  @8S3b1!)wFY^9|Cjf+%&h>;wS_.X,V$ٍ+8T@QQ1$I&f^%IE71(.ktl[uv(P[nGyRC)( D[C3^x@4!*Z\.nݎfC!^<$uݠVR_؂[b ͐W[nʺ6].ͺ+ơlIy#nݎe^E [nʕ['T`8Q[~١gR$ˈ#삮hE B0؃X,lv8Mgbr w)DQ  QN#(zQFنT18: MNݜD4ω ] "LZW- f$)#(CC4;yMQ&GQz'5hQG8lǰKQg'-ԧp׎;с;w~?nvlٲx?0^hnCCCvJu 7[oqwc߾}y?M""YI+0c DDrK.ʔ+ߒY07$""ytsT뮻0::oNwy'|%%%򗿌Gzjqhooݻqy0>Oc߾}رc^ܹ###ykQT6ܬ E(Gx=)JDDDBOO?cQ,]֭w܁'Ob߾}Wop嗣_Յ{ =nΝ;k.<7 \~yV""*u虛 ÄMq(ݟKshp\ذa~w/jftuu! ߿~>8pߏUV_ VJيtz ١-*@Qf2nU \Bf(Jp:=rIohJ!8L6N(D4ɲ%բ"7wmϽqiv Txn5jwpVJ[ljkWE2v;D,]DQDq;'B"˺V4/P] 4_x_5V؂_ñc̓>A_uQ/8u)Y-I2Ҝ42:D2i<#͖̈x.t=P(%> ~ݡ|HZ>NR].Z[©Sq|V.ls>6g n ##j2v 6LX@Vu8g5enȸOgVmDz嚺-ۄ:g~ iή).Ag>"FS@rcwƀ7W漃{qIX,nle\.ijylFhhX{FWױI붴\-9u77oAii5^xwmL?+DQ֭y {V+Ι'JiSW<+ Aq; g35Mbh?6yQb>t>+ w-ȑoq>Z[[ 9Oa²lsd+ v JϔȮ@6bŲe}s}DO Ԭ9u]K/=kVt@'Nx--/}|nYGR)szMݒUp3gP'Oͺjvl:tw))WZZ:9b?%`ec _ڪԣxE eL~1dxKP]4sZ~,ׯq'OffСr+-FUUΩ ',O$bho?4.YX_v޾8uϲWʆs>6tz{OSWuH2%}/}HDg9a>ɵvW^<$Kftx<^{246,lho?u}#?}39t+/_3 cާrzLf+pԿYN/ADCzz84>oG@v^ظa7q;;hw^9Bj'1F8q5h߷MM-3qdoTTT>޻KU蜑0&,[0_}<5T񌿑s`FyIIIzفVJ:X,efh I9 j"|Y22'(3̵D^8ì _}{'wwC綛s2Oxޡ͕eezŚ K_7VBx'|qn?C~h9 42>:UO&j@8 I AUD0uG@W13ɼI Lf龏g:5q4/}`/gOf:ߙC3gK GǹW(7>~N<:Ypn}Gǹ=s kӹ.8>> 0v}cx`+d%$ᡌ;Q8>S6D$L gδbd$e<x8|w]zى>TTTl"EKK zzzp|liiIz[=Y6liEhV|(لDŽQ|r ,ODDc.Jjaٟ>XmY~chhst]?ATTTছni׿M/} ӟwN'|Aڵ qF|>۷---W~ػw/㪫Ν;v}"LѬxZqhr|јrC5~.쒺 Q4 w=ܓzO|[nE4Ů]=$իя~˖-Coo/o}y۷}/~?>8|pg"3!R!"׽EvҰG<.Fxu PCF{ϋaBTDc7_T*`pfq5պ=lsʃb6Xb BUh1*+Æ !;or ʗ}-[7xܞR+PRU[Q:zDQÍ2 .EE[@<@#[\ A08ev8yd+Iht@7t=hzAD*x~c~u)Vi~9 xbE)fyk[ʋٝ%b0)g&"4M={$D(4LN?@$T*UMWWp0@gg 9 I2B)49IR N؏߇B C$2lv0y5[(/nT+C2,rGDDpGb%j f''O͆+>T77FF7`77#)sA=""mMy'L~_>D+ T(`3 QbK'LBu0""bfVjò<%q%"" ##Hx$V®KzlvDjʕhDDDLѴIpKȜG/e3"""S1ި2>\Qh:oFݚ5 `RD(VݥXԛnMa8>(S݄I VPLqR QòJݎ'vص(r,|mʺ.N'y;w6xb4'6lkӵ|u6 GaϻMՏl}E (JpKaJ^1Fi-ؼj( "*$7_:C5j9.L ~(A/pDDs:KyըBh8 +qò H6|KWb]"**l|5|)_1֮hܱk wrΈ]2gu=M `櫱|ykAϻЈ͛Ɗ'SRR͛FyFFsWn١x0;DQD4:^D @Ӓ{G'+Rްr;" A "mxu1$6J2ʪu;ܐ8s Xl@/R)-/m9A& My*F14:=őtIO/!=/qFH2 hO4)("igB% W"E,.lp4Gt}Ds1 A-hr9zL?I}1 "hä IDATx.#u8""Z.z׻аiS`Wq6nE7ݔQ7Lo~'OZDDe('N&Z #97""~/!ǰlZ5#" q+g2M^ Ad >~RQnNb^q<"hҎ$ |܉'~ò:y -$%hòݿ݄T_o0؞TYC\}^c$" LQV.d,sK֭%yac6:ʹM"e0 ߓO3JDu 1|N9lXVd3DjW6Li%Z޽e۷gz%"2DDa^weIxF,pDDDT('5,mXkQ z9p`FmC;y2㸧[vQDDd+cZ8!g,eBTDDTHa|U9aXBwɆGDDsEڵGo~sWn-[ r"|`ҊC *$Vk;=φ[%hLeaBDD3' 7ǡkakEiM͔ʆ xt䥽k~Tqq\"I24?BXJNNz1:a[d#Le~ikCY5"ERDJ9QnUp7,| rDEicg>Y9geɰ^{mVKD .#@DDE5L ӄh|/O9O7$ZlDI’u2:;g- G3,kݾ{MD4&[uXʼ F?ۑ "1_QNhM69M%\_}e ^/λY??Dl%"Zެ  r]B\_1tkߞ\%:w&Z,޸1yj"s%a$"d+9%z+ |[b[5)7.YmX YVVnw ѼG _T"$d&8Ӊ(ӭ(**[A%|=}~=Gʜ:s|V$qW2,tu([46A[˓k鍦s+q\"IF-]--C-fBr;GW.Eb?z  Q!rTU5 т[9$RJ6cej\V(߼J\o/YD1a n8TVl'vpقn02xh(hiMM'vAeu݅Q[PJxk9S/t({k4nSF9K6D474| ‹>hBDÍa3?9R#"Ŋ-"iVrV""~i_SN]dC#"ٶdZ=_xװlˎp7%"7&[Tީ ?r ~ 6,kMypZuɉhvd1Vɓ'D-x bd+"`Eܤ®Ke/쒺M'R0,L+NlXnV+ܳDŽh2C<8^bZʄha\.J9HD3cVf+sG]zf]B?gDs,[Vp тԋJ=Q I.~V7kefFD3`VjujMjz %%87͓VxpK4u?bDs$) Y5J_ C!ZTJJr|Yi[S^òg0,r^ȍPYɵ#fۯN<) bpA^rKŅD[P۸K֭(?w/dzXj[m/]:6}͙=qzʰl7B&| hi Ieʞ(hi &vreu݉PSc|%y<;]Oad4PѢ:B^$Ѽ}^ʃU㋗ɧ4]:8B>bQ~ ßiomL][JuxkPޘyGP_ 0HvǡC;Fʕp;nu8,)}&x-"E8kRIQG<E(؋x< ?j`QCY5"&em2虳~'~ӄh1=)aG (#."(˸ _0,{7A38""Z $Bjau8D++!VhrzD47-]h0 QTOwaٖ;PZ3;xd+d̙D!gL4ܣEHHߑhFn-pTD4K7yW InN*NjqAoքhchF+ǛR>ò˧/$ -fA!#P2,# 71(I_aTסCvwQn>DB%Ke""VJs!|[bW mROjaO3'D,[Up Ѣry^[JcrİCrȺP([Z- +-!V)Ϻm0GUqdAU.xlb竆,`d4HEESWGU5XȲP2_ |RkdV$1FgEEn_ *+3wP&Q[_elcb(G V6|$Qo Y~}z<6|<ժѼrkmJp ]e[o YA~%hhȜK_F'.k %v_u y<;TJ B~nv8D !0D$\F=aaTNM&CG"E(ԇX,lv8Dصet}[O[csʓQ+ju;^&*-;)h+xz"Hv˩A 8iT46;.[p|䱪& "x:UM"u]G,PA4%Q}F%ofՈ湛5"V*TC h>G]bX'9̀(ٟr2a7\9o/w$)܄h!fRohɧh%"9 iv]۵%t.F乿Q6DI )CHfw@Z!2$y6 |k_5Cɓ;y҄h`h[r&WR'^2w]%"" {Qie7hU L:Af( "K9csłrD"ChQpيdZ +q>asK%GۧLzN0 T4X?A!I24Ms܏Ceº^o%t]G0ݍՎN g@C$,;**144~!ZJ8%< MSoO@n7,҆IB,:VbI9Pi'>'Jj$Db²I_H߹nÓ>jtuϪm >.4lڔoņT*UF{zBt31'#jkݰ_`##,F K12@0kv8yd+%nC4E)G80m7~-4= SQOU]{~ ǕU6S1|ݣR/DYr8\X":Ǐ1;yGZQq6ZowӸ(V(!(gVAkhQr岬$Rד%3PUI@ZJJʰzhk{I\ZJsy0WO T<Έ X" vaϞ1p̸rOeeOǓskoiKK ==uw#ՅN 1 X"x?qKvE8rt7"gl]Lj8a9a gRZii:qf+Q9.xK bt44d֤3^sP8Qv,ʗ`xhfC4/[ZT%ilêd"LҴ606 Bu@סE;MϹ`g'D*)t̪sΎӾ1O f9b0:zKVǗlbd$`v8׏Xh;QP## gV#MwTj گurl&d+uZ4$q QB%J*-K/[dmijmֆ޼VשB#R%"s7li56=5aC UU(9OϮۍ`G9g5h:~6x"{|a|p"9 0 SN3JDDy*-EES&YřOSdkmֆ#ԯN8ukSkf( hY {,-__~ցC ;_D9l¿d nu ݍٯFBf$ZR$QV"9fuʍ%!#3wh*ҳWgVg[:ֆ$:%ـ֔_SNE.aեo 6)"soUVV%`++s^%#KUƍo2,;~=pnrf+R+xg08D+嬨dVi5:8 ֎|>ԭY>Fu`z9رƉ/TVzb?l6;AQY12? ` aw[!nMXV ؈h!*OonU|9z4\ 0vKa;gҮd3jtaC8.1Y|يP\pȤu}:&߰MI!"T5xv5W!![FכEV IDAT.cfvt7 tvmP8sHV;JJ02D$›d %^9߳-cOG020!VWeeu@"QfͅXlJehUv&Zfpa͚ q~1;\.ؑ!t0Y b͚ tOluJfͅ8rt7"gl]0ha_}5-d+gR[݅HRfCh.\eǍvÙ?_WNq|jH 1%Z|/(In@ lyp]9 MGkRvܑh"dެnW $p'nr8x N9b)t]Go (V;>IR "ɱd]Q<ӎ|APUՄX,|(Ԡު*x੬Dc س`CG&LlE(-FGfBt[PΝq<L;1ڧ! `dM"|(ل" ~#wAۄhc3W-CeSJ*+V"I'WD26g.םDbaYZ'%%V\K b#*~w1d:[UҚח!ᘵE0200eӧ8kcŅħ4 ]&%?q""9 ]ZaE1D+"穬L/Pbdum nGQr L6eU6[ 1!2"$ssl.j&\Bk1ϗ{nC.;s&t8(.^yc#Jo}+/%51L{t`T7'kѐ*8-DDDɊ^ i%h?x0jtdlC|6d]d0!2"2(<᭶3Z =iowǒggVWYj8˗+к:gwU+pma'p⥅Y-DD&A)㋖+g01GD1(&\ͭzcssR-j]FYkʃwu|ƄȈM2AV"}d (/G<E0cZsM2Cn䉒oUsrUWC*M3D8HKO\tMtu!þo,v [V I2ʺG2˲>_5"-V[ی:H$l%-5)NI$ɰٜ"xH8qv'VNgL*-}}egDF^=t1^B";o!:-sZ%"[xb?VmmOl]|36iDIbA-P b˭pz X v1:a?+.//Μi3|}uS3Z6"AG}hVc*8}փ]1yZ2sJW_CLyPԕhZDIWK(RWr-\(ڞ_#T.[NDCWy.>PwlBIeV i~#\0<< OYWI7zYDL'ZVMI꺎 zhp_"]AQ ?QpW*Q({'??ug%3Ax>ƿx5lXPBCC>u5""ʇ[%`rovW;<@eS\lګmmH&O\M6bSİIvL W?bgu|DŽ(.aե<V"F+7LwSLC%K3WgVϱccqb8x0`Uo:/+'rQoerGEnLIT(E5SQ&"xyǎ]@gNcBd4"lYaXvA{>?$\s5?ؽ{7oǖ-[{~zvmhhhc׮]6oz8~8n۷//L:n;j3Gy굯op0 理V{<\Dz N+/0M܂@W\Yp[ N?gr|x| t:qwbxxwPRR/˸|Ww܁v޽w>ӟ4ۇ;v{Ν;12#/-Ѽ@8{ğ28|Y0~k "))SB==$db %:w܁^EfY}177/7gÇX32Rϸd6QBNDԩA&iҤhg.:+@#$")! &vW1y[ԩ!7w갟{\f3RNJ'@PqQdxPkP@]XԜ⃁ПοV,m޲)<ղTD<)s!1C}Fqd=N̒)2Z0Dn.J_`0J>t{v}YmN'v?vBw܁ YIf_d k@anfww*u rcap5X-`Ϝy\.L&TMV2[Vdb7l%!KdJ͋8V--@Y1aJJF3 Nk#1Qy x@1MUQ*RS3Tft@ QFvhD?ǹN77c/UR3S.KVB?Y?4}IM͂R5#:kq;;;`0 '$9nl-2~<ΦDBBl0jB͌ p:lJKAJJr5x07]P0 Al3f,6 xD1tNhh9R>'Gv6|̇hͭhkByyDnKN,KY)y xz_Rnmk 2<ySA~e|,IA)Vz5xwgaa1x?+a2qDIqUW__HUyP#;(Ǎ LT.7Gӑuu#t6*r ƅ96~A,NFNN/"I<2 ް퉉@z m?V5ۗn뾡?Mhb 99E1jjJJQ^YHOcmv+xMMMسy3~rmaET2}wt=/Ȑk| sx{DmmYT\4&"xt?2K"Zm=EJ)Sv=L]c3p7h4b׮]:s:EP09}0 =Zl%aD$H$%ш PK <^taL1( bqrL獔ˤ/'>84>?1( "QRLu[BU &DLgFTpQ';Dwد5b B!{D3<8(q}>o;E"<06p88}G{c>Lc89kM57/!A4b8ց(YטaJTOS_*+{[8֖(G{I2fQ @cntK9>s- Xc/$Ỹ]]pƉqCX"?ﵳ9ѭ\#scƮAH1_h&?lj2?x?(C|pŸ>q6~\~pOP󟇶q8<0 sKwxaih̢X?#]!&/#5)ǃvDF^*]\5X|9nfr=sLz+:y[VH dE(^GE0S20d>ߐ1Ъ?n{oJi&ؿ?v;n' sKSSnxv'Ъf2{Qv'$0eyO\r%ao6#EEn^w؁;vD ɘ`f3T  # "0G9xwe\݌6x&!Z0aAgg (YEE(:|d Y'#I}e^ۍcp|.|e muup٣+pFaf3ǵP9DQ@Ta QqP!f%bPvҏRX˭- @U(0)<(e6~ta2( ta6Z5difyfdhaW'34䗰W[`0 }0:aE]|4Y;0p݁n<'QxBP.n3}zL266r" qz&E@Uf(B`]JDIxu?Z?}`vth e^7r󓟠+#XQ1=1kqzĶC ?J1Ci!$WT6@\jYT]h~?X u1~lHݓ g2W3~f2\IhD͡8ϱQw(LMMh'iC0Dxq܀&bFg ?( ii9}ޯTe ("MnFB}}s"+*~0,@ E+uLH|HOC0IGAq18\nq%$lhܒ yF"kYE>?jg0fIHKAG*5҇l&;;'*PT4UUE9p bG}}9l6  YTe^^Lj>wID^cZZ 7#NW}IA)Vz _/))EE}Zl~Շm4a +JeC\S "0" IDAT(*&Ss*CHo~W@ZĶ7hejQ^v{7q:(/Mq$JNʛ9D᠏Yx<`6CSSLfx`I VPd rb0 Ip`4Ҫ ].;"_GPLXq+^_7[naianiAsE.D*֍މD|~pX9~l%!Z堽[ )q7m@"c乨:HVBP(sQ__>!\>O^t3W*+᲏$~j j F Z<)Dij_*czGH%%b7Z lF ZzRdfjT栭^owލ<3qȃ'υB0 'υl5*0y\T<`+9B9JfTW|cŜ+UPY}-42D Fь;Bݾ?8ϑ"+ n{6o -\e ;oPBN $b73bgV昆GRZd tM:,!owXmRif;FٳZ Iz$ly\ K[]lތ -ci̓|3=: 3 Lf7:qAV 8 MMhXHE8c^[$JBjj&V8 Rj5j99Piș6 i1ǏI`S BD"CsI# Bpp/Vv^؜_5 sߌ@KK(B OgLDIhjB lUd]adgr#~Eq'ut;: (ٰX`m~Ç!U(0Xmf֌soȄ"w0{p6xݣc] HJ m7l%!HKAkk< V_X;C{%PXXl%db|T[DPq+Mq1Ba/EcVn*CFJZ lõ؟ '|C:KPHJJAaa1| j43CH3[B #Je Z߬H$H8̞hJAaa1L ƀa8(,,l5*<U< ctRnbi [IZAZZZ[PO4CnpT"Gaa1R!"QXXA[2TZ-LA1 uǎtq+Bƫ<7L`+3 ݓ"_)ӂhg[^MpJ(,,N]hiX\8{VZR@Slg$1 bL-lpPXX kU,NFaa1O`+ټ*GO=wMXIn30C zqle4Mp;AFT!fC#e x JXm8&l3n7-"d(xdJ8v8&-/*ٳ!Sc:{]GN6ʛE&TVZ5SRNƸ_;3g"'!AT `K_ S[=}R $Z0_ٗNs}.f))x#r!r sÉe2\b$ ws}e՚Pdlr956<l%Lzp^ NTЃ !P*홽ZT|fǃcBŭ> BWA_AĶjƎՔ? `ͼ΂E?(HOcmߵ?~,zm'w<=L=IoɊVף7DAi!µ>u@+o@+!Tjv6TZ-fDjNNL275 M:q!!d<:[cM JqO1BL9͞AF>-`+֚ټ nVt0z߾0+jX(:JP2W*FĶ:q!@(YS\>&D٫JxG\b/VoO"&JfΞ5Fb@CL\D.ǬK/ea7a8yrz8$1fr߱%AVBȸai/vp ǥTwr*f!-??cYP_Z& =B@}m<\cR(!KpY؂F=[;*(;v}a1C&U ֶn ?e -(JBlI7"d\;,ȃ*82bLˆdN_BbjYTmI B!>_?ЗIn^2fB,I_ Uq-q[ rp]d?.Ə\HUfdlas߬Ip1M@SxlE{55:KJO0->)p9hob9S"ʴtZ`?bv~+z[~;VsTbv'4iA"0o[IJZjP}˽( HYM;ѳ,O"A[0U>[ 4 7kԅv<W.uǎICkMPuq/--jmul&\\He]ˀ~|dH h f7ؚw?"Xn5NʇRS[--抠?n] ڂ0Z(@[0f`k2Q} e+V@,m 7~Lq*KdsMe%- ĸ 2T*6!1ܗ+4>ϯ c{; N nwHw1@"c$@ À! D|kBhe!20 -W6 8TZ-6ؿmې?ov2Jz)u6\.ɩptgd4$n1' i}0(!CE3{uLdNk`:ÁGѤӡĉxw9SaA)L7~ip\dy<.ͮkhKfpDs ☷5 ҽy\sPSvhRV۔ .'sP|U@+`+!d\I&`/LQ;9m;BzфfJV[ŭlF. >> bTr9s"d3 z2D-uY$|"Si=C,c%_'zNfXQ_(JW GO06ǧjv$ZΘ3b:GB_Q&1B/,ߖyp0)DPֳc ƀg+BG]))).>l{Xʂe0oI&]G>cC& [ !-l $;?z bB)ss{f#)-ׇ[uƩ28$ щe (V[7l{JdH}ir95V+k>0L(,^>M (JBr '*6?rf#mLʥH\L-Lb1TZ-rOfΜ Pw(Ѥz BFF:;;`PTfA"C?/ GV2$lͰ}] "vZ. w:-=bQRTϡFEm8me鴣>.ǜA^9}^ Ʋ=ᅬVH& ۞ w܁7n#s$b-p@ DfVq\nLTyhfc/ d4Z^J9aQt$4HJJ2dR23qO~~kܸf ~_ :ڊcO}lڄ>g$&̄B[ DIK.۱w^lٲ . x<<صk݋W^y)))L+ꫯsN<3HN~̙3{o'|L>?oΝw}{~ UV?7|70Ԯho믿_|{'W_СCꫯB)J̧~zO>k|>~a{>}z'| vE޽{uְGy>o޼K/Ŗ-[w^|駸{{g,^B=+J+سgvڅGy\.c!ȡ̄Tܩ-dbSUhf\0c#;o"S8af&j{ 0h43Q>"Q4H)gԓ&a>cO^sc-$Bcl%!uuehn> /i Y,$}c?Mgg;݅&$Ğ٫眃0wx8t:t;_"eѣr9b!d*ZnHweH,Z<|ITUUK/źupC\,ý IDAT~կ~{{/8^{5B' /t. u岏{t @?&| X.mixjdP991a21++^Ws|>/2̺z 199_aٲe8sqW^@ii)gln矇s>C,[ p-4CŸkz ~,_K.ev;׿ڊ={`֬Y[ॗ^ ;?J%rssP(K/`0ƒ%KG9].x<ZZZŽ-JCA*. kp8DhllD{/gߏ'|_|D"yܹ^{-k߫۷oӟ /ƶmpwbXdI5\.o׿+W m袋PZZ[aꪫ~zXп)`ѢE;r?+>+ގB@+W/ĢEB1c \r%X,_?phWg[{:PKK^j(.VgX~޿̸bV7ވNTWX)B_yUnN~gP2f]ļ{o8>={U[RY3|B24tp=!dزeKD"bBTBP2n6a00uT|XjUJe(HYTT6qwڟO>׶f*J{fC 6mw}aKϐH$`7pΝ ag n}뭷Bomm 𞭨oVضrL:۶mѣGpϪ[܌:, [[[1uTyr9֮]W_} TTT\.fΜYfR(^t ,u]l-**BKKK(z:(Jh_LQ2;},Mnȳ+o+!q ˡ=vɽb /ˀ$^O^Tnq/>l%IRq?F~=φ )5*%%+Fe0<BcO<;=z&Mg_.gᦛn½ d*2X]wj5k{oR)V^?ONíފw}r˖-˅D"ah Bf\.裨?,YVK/ov@rxǰ~z8J:tv(ClT+FlUd'o+!ͦM(j[t} v^yޒ HR,nAE]* BƜy۶q[p3~Y2LbDh9ch:qv)N$#fs_a ö͛7֭ڵkf;v@z?i]|9nf<a_Sxװo߾Ppj'O<V[lܹsܗ޴K. ۶~z_֭c!HgZ a0v[x555YdggRCD/@yy99<#Ɨ_~nfFڵk@9k1X"Y`k|.>xJ@ng oۄ|¤¤dK[e>޺ˏ[[ux`+ IKˁX,CCC&\D2أ9{^ddjF'2$rybO,˅Rԗĉ80z "ddhalY lr ߌ%R: >x<=3f3ق?)Cmp㡇o~󛰊5V&?a0Xz5f͚n mmx&Mk{^vލK/ɰ t:Y3<㥮 55QC .Zƣ>z-;YկrΚ5 wq.VzBUU n~X|9nfu#D2Fc-<2^x^U)ִ\Q?Z"!ZN pw\.;Z[c?Q1 }^GH|-Z7z9f~}\rzueq_ >gJLb>j=c@Iٌ0ZAV47W`+y0)(emocaz;8 xl%#&=?YӦA[\ arrL2;z~T D4/`+!$55jéqlxذa***>lfhDGGO* Vzbʕ{P[[v슊 ,_g*CJJ ֮]B3PpB$%%w mKJJ+m۶?JRDGGOٳguV94vl͛ oWx*U.45UElxx!ϢMɎ5 h43`2P5 ÁF3f`ksVhƾ]=mO [GNΝ/ R" ͠h$̀^d|-ESS|1BƞU M^=}HlǑ#;tRA 2|Rdh#+BQh|>ԗM:(2ܺ8rd'sJH/P'%ߣM7݄dR mw:p8裏rJ v^{Ess3D"~7߄@ZZZ/rJ\1̙E?J @,C i41e,]sb1*^7Ng)PZZֿNx^s=>}:|M̞=/C=4뗜3Z0+ݍ͛7CRᗿ%nJSp]w/jjj  !~?/ˑݎ[92,]B04StΜ9(,,Ė-[f͚+ ػw/֭[Պ `Æ {raڴiXz5 ֆ7?Oꫯ HX/b`͔NqP(ݻ#Wvލ"EAA:::}k5kր ۾~z\tEW^]vaxQXXł;wbƍT ǓO>/7|3~aֱ>CK+wuJ%ϣ pwbXd yyy裏PRRve{RDuu5^|Et:HRx~؟ 6bgW\[oX,ٻ3_s%K$[%/q,v-$tBN{)3{3sB{!SJhX K$cgO,~sd[VK翜ͱ,syy?F4^>,رcɄKwAX߉Ԅ.96vWXkWDH( !"Ѐk{o[?ZKsv4TV7UhpU/Ssv k|6(1 3x@F4W3z@+!SbrF[[QdMM }]]\C~@B!H]y8rH6bJi1=*-uj)JG]wAc/v̀ z9jgx7l%۹Jlz|YT+֢ۓio2!Bwg i1$ǖ+,Q19ե]r!]AH&NMՇrmXŻEcj5F#f|xrI[ɢ*J $!ފ+CыI&?tE(+ffwNEeell%+ zwW ;:NI6FBPSk*|CzC V'&'13|%P(HݔC+[ʪy:#j_{aXԸ6#HҍaԸ ŘhFj059 o"=Lg?QwAҦ1q;RةJ5**Mbjr(I EVkV'"`+ɮ-pm8dGz3"[IL ^Wm F&]1 W:כ^6JCVB26 C)ؚaE#m#yY5HzuŨތPпbUWX Q|_ճR ʼn3k zt&t%5ez3)ؚaQSȊ͈֚F#yln)ʇ,1, rJ Й3Ya)c3ylYdVkBD1u( ITė5eeP(Tjg JIaӛLWKΤ ގ'0NB#)# g;쬇vMe w?<'9c!S*h / ZŇ@އ<IvqWk%} _a_O0(*Z>*JhE~֣2^ lR3?CuBu>o76nf+à/A$D0?)he+YJA}h-#%^ފ8N>( ŵ(@[sK4XEeKNJ͚(Ҙ!%VP}@+0õj ג@+wE$Tx o[ !96΁VDr<?-'C[Tdž mmE۝ԵCO{;z)n%B!|Qzގ #iYa"-RHy_g3[JZ v`Hnv% R\O_*3$iGVBH_1W`ɯ%0YP܌V(_ի#B!+(1f@<^Qx4İRT QZXC y$svdRK3ⵇQ\0%U[`Wg3Mfl%zAlAʆp-">$z8nm!vt ORB!E/gDQZ\ 7,X+[R*Qd٩N2c(# #";#>KnI~~MwWIQ,*/^_N I('Jc|%a=8,z}1k011VsEnEI]kvb=`zXxj**dZфޓo#& C}|5ZG=B@~],:{0YMbNhP*5swzb|caaɹv^oU*5p:7du(JY,Z?H$s[I1AJO5jB[)Z={uvTlڔOBO{NB$H3RAҠjDQ`+!RZY /H?0gpUd^usvdG}5|}1Bxat3VmJ `j+[JX,NZsoX đxV6ajjI`UU,lσ‘кVn6bbI}0 &n]з h~/:RM`3yle\.U \i"!SF/p2OPA# zQ\^[nm,-<;z d Lpp 4 %IPHj - BRބ/p51w㈌r!S(sn tFΟGO{;mB27+\}̶1&o*P^BUňkWm(( JIʙ0aOե) Z^xG;`EC[^ }tYC^j *tgo{;^|d dE-\OF)JrbA-p,khK!GL7{%i+QMyanBc-~ԊLZ0=LJ [IZgh K[bwJڪmö1?L}%EVBHE0C%i`}/O Ia`Gp$}^ sO zH!ƛ4>UID=+bY$REYtJJr \X.$hHg0S ?<9OE!O*f36}ݷ Kud^_<e2ߩt6"`kh TmقVVIK& ?y@JS 1\8M|QUҶW"xTNߝ Z[31D*},^<k- X,Nv%l ZZ`L\`l0[0,Fx<#Gf~ EިǶkbq88ĵ_L?S?"̮O'R  Uq\b;ArFB4D$DGݰIexP?_˕j D(yXnmEmk+*i~Ď/=-2:]j=J!ЃuNWllOs?B֮Vוj[Q¯}g0E~/׾;@O|nJK]8220M٤SȌF38.@ vR.W3 "YC?SN^=EeeHǎS^Ʋ2NV(C0X+ I}h=t8.lwcSipnn{{+kk@Wz1p$IȺaB!['?B#"Kn+dx BHmP)6^Y `~uB:@+yzTC`ɑHV )}(ffrJ'cZSh^>p{%m1;5?!-[ !ii1p b3;WU*羟zz~j{;& !^qA4HBpO],({w57͡Gs+ńg(()^TbWem%q 6Nى6=ӧqwîomj^;r$-MGVBHZ][>v^=gh BBeSj[[aohHzΡ ]]W/@!xܯWupؕh  'sr,AU(4\dRx:[[cbd N &zg2ɌP^/]!p" " PZW\!i}|<}:mO֎rBf`]Q1F>elq(bCo{;z1ݝB!.zAgq@w"X$?߯zT6I>= +cpL*?N]bKc]b+͛c=o5%JJJp=`P(8}4^={`X} ;wD8SO=֭[|. /~C-^nwł/[:;;S Evz} .^lsYD9E(} l ?fS9`(x?lw'X Φ&[[QiSAO[:;WF xF099Rk`4}vw :h=d`%mApGY Ts=l2* tAt`>ã@+qhlS?5?-DX^AH*+ [9vpp2 > OI 0۾}pabbӹbz27v~풶a<!cQ4w><D"|(9|߇^??Ob:t<:M6n;v v½ދ@gg'z|طofgSEfV]l%I4W3K9i}/ƀ f 6j0޾VJc?=^Й3)!YJ5*+79 !& 6 (غN]`"WNҦ_Wyx:Ŧ=]Zyg^Bן& Bų(1~<1鿗Q ^:`8ә`g0( +0@eLM S5 .>^>.̃!N[kuh.v  qx|,MRbc׾}x3ڧTQ( W_?c<>h4d2a˖-1== ~{?Og cp1۷3<qWbϞ=xR`+Yt1t"$97qv\"$EQЂxFp +TMpz֤55868)Ffgq蓈DSB֣vF$B+׳Nv)zߢ52QwDX]:4]er`űߏb;ɡǫmm(??w;SP3${6a03|WD=$x>"AW^X΍ό%Z+#]Џza2+⪿+I{yh{,,9 }cQC-z+:;;{n /Z ذaN<}~ag?oܸ$B!?*Cs9ou|지?Rtcɝ?㢘^uNW\ WK ܭ(./OZ ̯^moGWWzH {FH.  Ab:zKJV'h54~ZyP^b |"/'/XE\^5 4fHc̓*٠-٘ gW8u o>vpm޽x8Yڭ6FaXobbޗZ\\%>˾p$Y@VFNx>p8˲PI'>**(7HǁSLnbfj'X9,ٸM +ʠVK&$j{~JrbW=6aU+uUۤWEQ( q8N 3nojLe=^ EZQ0,4+B!XEc 8Uǡ?=NqX|J'w8>HBcJ,{qJ/]qpc*ikrB;7/uxx&Sr5aȲ,}GxzC'Ш kX&CҮ~*ƌJ R;V[3]/TlE`bE}ܺs}GDDxb&O߼c0}> >swpvLy(++g>?>- YnU=0+_=Ql%K\['ua\ MM'pqLO}RSEVbKmIMN#JMJ]&29P,A.WBT|1W) kܵjV0dbr='>_J=>މO|R>yǎ=kj뚡0RhT00+-]9'EV}hىގ8RYw7ſbua]ǞK5uu-IO'&Η.%3gbf&tVkvwRq?DŽ^S[jur>\w5z4m=.+J=rZRҒ^|ƀMv'pz>m։FhkSBoR_匎tkFl(%ԩ17q$D{ aG:]/I=ZehQV&]`@PIEENjGV2YO qy&&P$78&?ŽIOE]IzZxҟDR9N'^%p|?EH$U8U/3K'H=NteԪcgca`lƏK:H5;=)lj~ff&.{jr4]eAUs-,~Ltq띘-ιsji/󸵬M:"[cwbMk-xtUk_fB%_9sxozYVڀy=Nv'e2_GO{a0 N:rX,L㱹x<8y$n%lnn^zIlٲ^m<~rR n &\ж )\rK?*KWb0`16wOcll jmmmW|_bٳgq]w, V*rLJeϹQ&JoPDB)K$,BԶ̝\.Mxqqjp6bʠNs@YJ\`pvM)kH@{"nԋs/Yn+3KMWY`v.Q^Hnx5|YyV9)K?^@3繄rl)h(ɡV2ҕ(p0X >tjofe7^qvg_==˲h y7FdQ8@87%pyBM5hcMmώmoZ?SBssYfd9> =MҢ,+ϫܭ,2ZӶ")NQۦW,(?h4z8u=(}ʍnmEmIgvrrqgTp! cbɄ2l.v"Ռ \rN \ 1t +S|.;˴Z#:L]k <;⺶lŋC=AKhrJOnr80553)O` ȲQᨃ?0ѴPmoyqZ>Յ;^Ҷe^y,LJ Bi"R\`pH ԭp/QfI|d29T* "08.ɨhDmk+ܭ1s$j\&$ jA$DŽ%J5rA?*@pe;+z3ۡuJ=VI9,x8~x3H&S@R# + [yѢwHaE5y+%+[>.̃pʩr\~{%mݙx}ɍ7bl=H$b l% ތq1۾66?\:{}=X}~xoo#By[8c?~A6_3ܣpqǦ IDAT ^9S_Bh*wCgR,9o5% Y%i8&#{Ea;ch(?GiAi!+sl{T>D4RiUsE!x=ikC8@O !^%,pz{%6hX>"1O^L Jq%gu1'Ճyhk\?ʖSy xWBh.r [ !҉2uMփIpڊ;ʓmoGO{{N%A'BHx@>(n,i!=n)Vte;DZãxWObb\r\[!RԴl{MFh8||K)\.:|,.QO#LPжT)Įn:kM/^ h!B?E*In2<(K\дǂB N<3sWL c/`Bfޒ̩n)+Kc;4K;ؓOfgKVRRlw xF b}GyLa< 9("={8?^>pDo/a*D{imðA!`wg;$Y/=̐*a cG zю:†^;8SYlFc)F#VNյ,O<4P YrbwieY46^o2-) bƕPf<wAՔf;~q^/qp0 2 9LdXJECC+XV?R& 62-x.t&cXރ^ZCd]~{yGF7UC8u*ͽ$d}S*T0L !읱e|H~ 2~0;y1VeдǂݟLx^p5^LFun ָeeհU}m6;ZQS9Li66&-m) fsl=J ~**Sr02ic'/y /kl䦛ljp;c++rQ CP*5 (TNQ#5@-i{R6BE @&cåaM@O{;z;:2;BðPuQZMH&(jd B~5EUM.fM,βYJMWR;AG1r.L&RA$ϯ7V/H *,yV4|`ChH<'9W1磈Dr#ؽ^vYVJh4K.BT> ş%u|;ذAr<Sg l%, j$HX`}BzUXş;Ͻކ,B!$E= 8|Syˬ].3cG,+1G Bz686p'hy ե9dpxG=)Zkj]~krzC9[ !wb b;D]((Dsɗ^9B!)&>6Kc;`$`phQ:j/-Y>"N>?P"DE]*͌E0=D+X׳m+CuK4$_rf qAΝ`إE&F#:;Ի@VB|c%Wq, @ҭqLC(M!6ʄ1Ƅ*H:8zeo𱺹;o!Mϵ`<8Ppѳ!P^.9 E_fBb| k|ggCͶm2#)6H|(JAP;8g̭\mYT$B!a a5Fa,pZMc[U:} *\|M&p(!zО)v5JKr% G#Fl{6f[nM\ ќr]$MJ5XVp8QE!$x鶋 8iL0, Jhb50*v~=h*1vcMY ;>=*`k&vS (ĵ4`xy?ctJ#@ O< 8ů:DQs43B2Ɩ$UF0ƙ0Zp:yA.5[Л9c t@ݿxKw? ½Sy 9JǕg-וù(} /"O&ffG֭ rέ Aȯ1:[ ssN10@*. @B! [cz5J8 ,7)|m9/3Z8mO8KGa?V##g{zC,Ww$ wt<^_/i cq[ cp-;Gw0SxB!Y?xFFA 5o^`ŹՈ]8`,{DO؅DWh]W`qhY[c:oЩ؅Hz@[dRV0|YYn`+!yj;_;x'HLދvU&B!9vJy&Qǹ 5b^+kHz~oyBWD[R+7m۠,hPTVӧE"q_O֡d ϪۑSTdZLF֗[9;ltivYYZZV%J VkzikBHz4$i+y?W}/>^Zi6~^rղ\<:~ԃj*X,0:vq(JjxG'3&I}衸m MMz qdYA0 ^onхP($(AUcv+M/,ր8'L/$\*DQ$)4ʉ e圧Q-5-̉R*A7z3ey<&@}r|=hY0Q I~ʲ MS_S!P•Rwc ͟c/BQ0 nKo"4wA`&<݇/|骫t5qxQzᅳMehi9eS§:mi{SCr Q2Mg~39c‡(s8LjVW:` 3[]gU*@ejoHL-|u!G Ca,|:B[go=2aSTa=s{a6?BW9 ;YmmTW[ڪWPOF9:-Ai5XeZ S.""""<)V*l$6"T>ȧsg[ ?Ho Ì[J֚m.<ֵ[ɶ|'z۶^&[ \jįtҎ)p e qסvc⓮t`!bm(ǵESQcEN7u>xK^*\ۇPZߏ7w5Kl%ZB~ܢ[r;:'"""&;DlwWPNrܬ?ppW[#i tYݴEn)ekҜ[SIء̨5]CX'zkφ(Xj4ME4:fw8D9.Ě5; ǭwى(jkנn=z:(=ؾUk OcoK#nCTq\d}Dr\:ht|޾k@II5O-֡nOvX:m`E\>HID:|/S0ʝ|mY}$I2GLLN)-aF"۸V\QP)]{!$IXpN|Naa)`a'GRfHj5L06t `pPr8 `bVLzkp7Qz>֬ߍ{Vtx@]Z#}\[(t 3t ֠9Г8𪨨d:Ԯ3SOFqdY%\ē A@0XI9nVC ֣]j{mb]u_cZC7q! 1DQ u&Z24,#|#L/i B%fr' Qm?(?1|V| IDAT_qe6 |5I:kjLйqAӁ/4'M7>HE櫒4klmmŧ&0&~֒&Ije_ZiQ}2Y[m{GоV/9iԛ;W\f*nW-į/DKg "J+~Έ2sKJ[qk5|AG>8sϋ' pBo38&NON)\3y^љ~ gw#jDI@qeWWꀫP!qgL"ԬYY7cVThjюɑ8?4qe+Qdjq&?;Gr)X3M֭dz āp1|uDXOAZ‰!Q Li0=aj\GtTԸ1 *Nŕg;>~eSDdχ "K[΍Le2Ӊ?WCXfZO|NN"""""Z\5|tM9\tLb/=zBk'Lk1pY^+}Rg$`cљxnnT[CMYCuۇ G8x@QUh=&3JDDDD9GRP+.n3={/ wx-;-mewr #=6Q慏OaK;&C | |݊gҦ옊1 ӧtLCtd&!`Kk\ĠDkkoNj?x?)cׯCqqZ[_%;V-t<|@_'$+W >6! kTСWqu ͯ.CqUA>iu'/IpAhzӃ;C /G]Ztvy]{!Lᄊ箯_ xqj-D*Tаaz:ז"NޒWάgVǞ.W03 hr)Vlc灙Dkaa B>>z l޵ (`;:_gwXid+JPRR Qno\WmWiC,֟kJJmw(D9KQ()XuxsKP(˭w0y"("pCG5롴k P-%o9nT/(!T@OduXT+q E6L"莜PAO''+CR~3t3a=fՑIʾ9]`dqA@m#L]렁EާcxҼfJsf/FJՆUCz7󿥓xR-6A b֜Jf?g (8s)*wbAT&35aS}ܿ[_.-Ipd(d+dc3عg$dϛnWB)8[QE&\se+ɦ_nД[h}U`5L.#ZLe4ЖblxCcdѱ̬v|.SeuF d?]1_LL e#UxƊXtFBJpz%U$^%khL >L3[?UD+np~F`(KܦנѴ$#!"""""rd u 1tDp F6ߔNU[㜟p^IOtn'4 SVv3IӉY6OAr;RQiZcV<[w100u.axЫ z_*"6m8~|,:e@a/*ٙýs+e]&bgJW]0=QĢg,,,pQu5iiq|Xz:qQI&[iG PyE:'D֫Q k1waQ@&Z,+*1::`w(Dy->=wk+g%%p35wгlV0|!Y~9$szT4=ýbl^g%eP:w$|n:^#OKd?2! 7A!av> :4]L Y2ӠE'A8}#(e7-Oa>\-5:{{ߨ+ o :q1{V.d櫳7XЙ9`SSBe8o=%@yrqd+ qVw&Nf8"""""5麊y!w; crdkыm}m;|lSj-D:7{% '"""+ ?j&Z_N/=bK2V#`JXZ.""J?l%ʠ*Ӆ[*Ȃ3bXK^lė'> Z?_Oh,muwj r S`•(1JA*T.cxXHDDDDt"~gV]ߑ EJKjÇϨ r܆Ȉ([imDCG8y}'+Qn9ceXvtM$H-7\LJ(/խCh& tC5á4+uaǭ5YKg,Û?7r{=q nHh4Jep-q؆O Pk.F4:6o/A0XHkA mD}D@*QCz08kw8K hjJv݅0t-Nq|ذRȒ˕4)F_Q,bKzm*m8(@_H&D[i]{^9elDZ%v%u_i!"""""Zk.V8ž_1xj*eQxPiFTOiu ^m2Ez7Uq+Lg8""""" Wϛh= މN܎ ߩ6`cRDDdUPPSO=^x<VX1 } /ك/| $i}ӦMx+7|#c=W_}?06lؐl9 r5d9έ`^r7ޒZ8--Wlݡ,--WPF}z\ úҏWiWZĺCkX$W9O3_這!0%Ɩ@--W4uΙ6l_6jHRv\bEݡ,i s2eۇl٪u]?a˿\eHo. 7W^yr oؾ}; {/>_7 |l9O-Ηǔ[tX~'Qdž()EEA8.C!Y('hQz>-p"PxN<؁l8(-n[$ܡ֣Xz7 ,I4{*]a('qb:7Aq,NGQg FFDerY_WpA  Ѐ7F{{;8oG? عs'0>twwg?|#o>(z)tww vܙ r/"Zˍ0x0l(1_vbeR:<ӇG2uQ?R&58X""Zmۆ~عs'Jq馛+++c׮]; "aǧ?i|s|œO>9ww4 OxђwV7 *LQ֩o*;M7kIxA]qۖnܡ)n;_"~LOO##g>)㶏xfAN3QN!-ɘI%cdXt].T?L&"آ'rNs+RK@2bv6&LGN&3@E5'0:c”8dǸگ -|eY_kvSIL:+{b ?l1=cc`;IwE4:񅏱8P\AU>^oq-p81.$:c_xMyEq,c]08\Y*bjI1LLd~д'n .C}DQ:j_ n bbbbmo6%|c%w uILiiu%=^hZ) :q:xbb pPTTk`/t=c|>&?y@׵/,,әZyMW15RaLN*++o} *~%70AHضd1JgZrrl`;D8АoGT/CMͪswi(nB/_Ia,=Ս)_Jri#GJ*R\tz)^EO&lu8 3GLj2TWL5u]K:Z]x?T"0]c|ɍq)jeVgc<1vUA$E0t6,Г)VKvB}2E\bBb0.{1ط֔+kG"]I-L'جF2>>S__'FFSz㶶iH}<991N"0;Ƒ^3ʳߗ#ZUUo~Wgy欶C`5558x n~ 躎~g<#x衇z_>(~ӟ_җ''8pp=~zصknfLNiW/]|S M=J;`Hy"%&2|@[\^- ظk'; C桮w֒b' 8p{Hb~}WID 9|Җhl0 Y58'>$wYφ-q`Ӯ2TJ\g=6~tLW`ƯR:LDDV$N֪&Zh%""""m*B kHǾ_a|%2eDҁFUj1 I;2+DyhV:zzqqߗO %aMĉVh}69D z)/a ީ(pTDD.L%aVmqǠ>݆fT`gQR@W['{!)5m$P:`"~U?UL\;Ri5q۾]pDDDDDD36\U*pYK]: ^nL;}4NSqs0Q-%LҜ7qE0 pJËOit /J\@/ @Sc;c+ "FM F4hF):E%P]_e| c8*fna B ͆u52V^G0){}VRR.}7muVb >0nJDQ–app,Qe.x=ۧ 6^ C0>C]MWBN,SPSA%An EpX^kCTK$ISE@D!"8vB7sKpnXV`5e9w~ՇX) <1]Z+mM|UVDwE\ Svk%I>}oq IDATYE^O.CI+! /CD?VpQjy*`dǜǯ`5\glRG~լKGJܤWm'ʱ^(pe+E3grܐqn](j=W:2%V~|LvҎnq*Q"6*CCǻ͝wa .SFQ˝6DFDD d+Q47_ Y12;V(8.`w(.`:[{~%qe$p:]%~%-Qt e";fnPyt055%S.SUFmQ;DE|U:g_T,\]a&Nݡ,yN R]Nxf;BP:ox- p<~BrkCDDDDDE 6_[5֕Ԩ=hDEzrK|"oP޹Yh5`^͆(Wn(D P f0F;2%4ŖRLB ODIJyezmܶ{6'2"I|}_$'}7x^LO 8IXizPjZ008nCdDD+[)o vkUq;i8Q.* y|C%J>C'к'qLAr;RQk-Ӫ0ijxVΝgܦzJ`;q?{lrͺ54g{m~Cz:AX$]ET04`CtDDehʕ[be z{;`ejX (tpK *iP&ƹšh1|~4\Q12ow8DyaXz+:, 'vVM L x0<N?) m$pZ#(FE0sVcS1112oߦ杨Goo{vtl&_p=^?Y|\/qt%t]8Gnw!Z\ YV0<;Wi#I2Rt^`ܶ{6`/to(¡8!ݡ,A"7eJ-i?m@I+a}x{'u`4Q9&N~Q%(ΝBPddٱ$ ;+LӄCqBq(NHR3 99GBRj֋Y5)Τr^6"""":OB-G+u={(sm1|qd"":&:CQzuDSRV"""":o6r(;SG'p{߶MAe,m~8_WO!:"d+$ nѫQeZrGr1DDDDDeMזaK=׏CϱF49.S­zt_Wa _S(ltV&:zQrhޒ(OFq7`dDR 7蕖w58t҅V9҃^^wn(i,ŚKs!{* SPPD .HةY֛ESk=J &mwV܂U4gB|JkևN! JJt%T5qG-WCDp[/Q&466cm;Kfwa;nA梄}&cd_Lt<(G)Vm0#$` 6oQLL$--W2%pW܊U';CEQŽ7Η(رF|D"1@SӇx`>r u.H>Il9(A%ݡ-Zqϋ<#GlA(JDM"JYChqxsgw$ov?cjVx&M(S~xPn["Q-DA\QIaƂ㒤1b!QQPJru.B!{pڀ 6qgCDDDDDxe4]_굾})(5.H|l9V?ԓᨈr WRNV7Ѫ2,[_*=:{"`dDBJRc鶴D$t<-v^"LctM`@~!ለhD ZXoqy~kP~w( m4`CtDDK0حWm| o'"""" 6rC}NM~`dD8Km۴&c&"JZnURU(6K zmuW?7z!<LRN9*~ @c\H\~vAaժXjz{`'eb-w Q_DZSSRRAb;eˇ!FF(Vhkvc :pmը\Ġw˃0~:ђŰ^WI=iJY6oXܲèh@OXjVڊE ;v׏vd;nG$ 45턦uP>r u5eùS+e A;^eFiܶ{6LT>{DK猟5Lָݏוۧs(>İ8,Zܡ?3~6~W?8k&!p~f>kK'PB(bVUK~ I#FDDDDV^MWz7au={sJ/i=&n[0;0 | ":l%tcVx'&Z(uh+hPT##Oa!z2Cהcиh^LҒဈZK~q?m hm;\?=ǚΔ~, ">YSm@= *(څ(1JKUhX0bOi!"""""fz7o/HgZD>ƤP˔pQbibô,Nsu=&\(0JKUzW1GnCZIDDDDcX{EUg{mFŌ EE4qkEKNwf[z;VކE ^t3|".YDQą݌B_q+в*蚆 GH)¶B Npe+1 aYTtbV '$KIEa0?gDfsY͏յ{"D P:pڈz(HW/31brv|WK]/U3(;ka! ֿrg֚@G1 Qqz$4__u}iFpu"!JF!dl,F! Q`ոN</:aDj"""|W·*p[wB @NMe02やPcMKtd>(" cJ%qpD+Qʱ|=׏CϱQ&D1 (wQWJc6EGDlbVhF3e:>м֡)PDvHROܶV<"\•UZ5 X^K)wN#"""䭽k,ׇl?iO6'GOa4lSQA yNYשtPN^? :G7l9WoG0X^{:mK Xaz-w xP|@DPZZ5k.{}prRaa 6m]DA! +WnAyyx㗘|A'/G䰊g#ϝPDTVVUѷ1omۮaxR=/4JOkauk\jдbLD\|G08؋_;%KE\tG04FkKqXBǩSl($EJ:N'JL*8u:qP`E Fum"k2Vc:4M`ϙi7镸X/b=ea4a Q2M(cf疦r6?/OׁQ`b- aKVksqLC5[C?HVOu-al1I~UWzh8dx \ F] P(;(]`p oCpIh5'Ҧ k/e?Rpa-8{hNߚ?90u;`w]g;vV\J7ܸE V""">F0xj*Q2~. sgfpXdzrĦȈ2ϥ8NoşMnwϭ.}"1Jrvii{[n""""";5]Wۭq7נ/^ݮ8q҆ȈO܃Vp<8KܔaR09bRU1;KM,U_nBL>n)!"""+u.4P2YShAщ FFDxCr)]-Zq̆(}'ϙKg'ONfBo_,ibt*X܄dLD,1 1UFT1elK/Ǖz0n۽YR KJk]3cFNAD') 7Vg``f=6EFKՙ?g&EO<=s;ۡ2U%jr]TU16bt*VfWN/k5حů=TQZZkwѷsprRaa 6opªU[Q^^_ф|4P`;aa{"79b8rw4 z\X#DWAСMc9('CD\r08؃֗gE_1 qXbtbcf} R\aZuØYe:htω%0xr5N:'mI"4WmpZ "OK}xAd-/#0tC!Yi Bs&QҴs-C[|CżsZD0>KwD@W5u{8{[{4M8, {Ă BT[X'#$"ָZdlf16l_B T93Grk.B/DK6Űx8/+Gl2I)rc%jcZt# u9bϋ|_!"ʴtR ل锊m0%+lڭUMA=r Q&U 8=DO&.?@DKOSXnxP/7xaqܦ(6U2:14:ttjznT 1 SZ&[)cKqؽJ;ƅZ6NDDDDg|m9/x?ӻo"pJOS#v^dkXxV00>?#ʄ)-&&[)#>֪="w]q,Qֹ|}9 ˝ LuOGd!/Hdav>n5h28lSdDA9 9:rLt2u*i S:LҢ+5حW؏>""""LX{y)^9*7Ѻ'XE=RĒlz A8!"~vAa͚XBtwKk˰(<&Li{*! D/(bx72aӶm+P8h0{aUID٪-[>X,b\p=*Աs>oDaMr >g:wq]w} ; H"I#y"=Ǫ$ɛd2.<WŎTHuK{XW2hBDYy'x`)Z~ZnjZdOml*Ytj>6g.ߍF7)Ӹ{*nݺ&"[F]Aj&G?fgg778sL~O|_ux<ܼy'˗/yC51Co+Wo)YwI+f#""%D-juI:w^=SѕDDK>t/pMd~>]г4"Z/~~$l7h4~p:x7ڊ3gq _"~w.;??×%\|=^x@ ׾~7ӧOcjj ȑ#'>w}?<Μ9_N:ӧOn]ڿ~"iC8yV d&JDDDvvhh|}l핺^)@PDXA, Ѡ$y}EDb' [pފ`ZF:(=2ѠFo9Rn|\ǃN߿|A>W\oo [xARvbX7DDDT?T4 (AdBӅLų᪦T^琈fJV aqtb cP4~ʴ" - 3 mmmhmmhP(EBغ+~F!v6{p ޴GZtc6@U,*CwΚi\y}VV:^6tu阆ʕUϞ=G`9j:n 0*fĴXػhMWT>\=αi7gޣXoccoo:go7}H` ۵cj?8 _x{͉={p%DuwǻcWߪj}bs>|&\ؽ羚 n^D,Z~ìxj:*ax0m+[]nd0Qb 6KA(JLآ T3 %(qJ\0L..@qUsc0-U8|ook5nܸDh[BGgm7BJ:k~^><d鸳3c^hA5n޸28TCCjdSMǝd%V:T1HTd(aRdTO=]LMTنk:1{&0`nPwa?ߗlݺ=57cx|U $LMԝwڱc=5#W}o^w/,ΒAS7T}OBJ'ڪ+h5A*x}hm+ LpUs=CŚ[9!{z AXr2Wa+ q\-=aq [ k ^G S\,DAzlH\q>X( ח"cIKڝc[||҄'Hx qT^! ,+ IڟRusAZsvwȑ'12n߾AaJ&X Ul5 26&k&\ET&VydDEl\Cҁ;. *[{{j;s.x f 5I~e>j=ǂ `CZt'˦1  G:=2S:n H8tQܸqDlګ\۫S(R}d\B\:x166+0hYwߏ@ P5_v` / m׮{ލ ~T8u >~SBDY{{7vw^l"يw޾|;3<̨g}[g}?<~~ + _DQ1$^~e/Μ9o~] [*hꎲbKbxmDDDD,`ʝjsJƃ bA/p*DSDDÊ/Êϛ)!/](-Z} ,gͩ/2US^yh|+hkk×e=oNp?~|ǧ?i\|w/?\t 'SOG<^y90l5N+?Hc<ՀVJX]A+7ٔTCb4LxP)d*V$9QxQ݊gߕ}8#qV詁ԑEnڜJ}sxގx衇H$p+R>={`jj _08x<—e U>grREZ cQ:y[_+/FODDD+ʰe]^AKű>T]5=RcA|}xg5 h3i5LmFqǟfrYp}m/Zw |RmT *򂺵l:&$!uQk)b4:ި ]e?""**NhE]0ᣚE^,Տ$aCÊ +Vt؄[Ϭqka+-j7;ˮKӭ5n QsE. 6RդxHE,. R A*Yy'l5lEOSkqRIڞaC i_*haJy065%G>;#֭k~|Ag'E-޿^T%gkI J$Tj`8wܛgf&kO՟cb۞8< C[oU6=Ӈm(RqVn+Z. ">zMӝQ /.HS<.;\PUDƦKwvnC egt Vʋ#ga:h ;^T,Mb`-GԴ%pxJ_^MS"lj],Hu˰9eiig'M6/2HI$EזDqWrT" #ln,?G Njm8?wU1Rq' E<^jr;eymI4Mk5ďGK+J,t={5z#q,* Sz'-%.A|մDDD@plj_Aj2T*HC:&U! N/Y~^ k]R41v:L k[BQ1M`.\,`ܙD8Ih`V*eK٠u)i%" J|x꒳5Q=T&*`z>D gT% RaEˏm8])ؠՏjF`xˎlq`77.LU4 s$f 2jd&`&*LR'5/N޲t IDD==5S3z R3!j'jғ%|5 o=Cن_%Uq(~"qhÁӺ*MWXDQO-:2A`a[cKJՊhxDq߇/68|q\.޽-lñc5 otsa+^P{/C ĠrB,UN'L("ʨtnt3=RuRD<F 0뎈6T*@` D¡YU L}r^qRk bӚoI~gxՌnlkwc{]ihVݦztdqP|uGԃak*J ]WL!4)u%s@nHvۊi'"d㾖t=R6O[]g䉢e].{A¶6wÆzF1Kbn0!ڨR_QGW<􂈈hufazZ`.ZcŶ6pMk֮P"5??F!^D͊e?>;u;V""""""Zעfqn»$t\\ЛTܷTY|@$@#sa Dp7d8U\}5VʻGno|Y&>m8xQ F7)tѧ02r7om GOO?|;H&nՠ{'OշXrۓ'?MֹU>} 5U. λ,yGG@› tb 0f('^Bל@DQO| ~.\x68|q _wq i=p:p0:z ݜaJyh&ΙR:D 0T*5-UUL"6)DF,D 0 ]$0D]:^.I08]CĠ [i;Qk.MGZ00y#sae'D$⯩-a&^<*J5'MSL"7)u%7O?]"""""""Zvvc)_(_(MdT z7q^ xDDKaV"""""""ZS+/V E1a<h00J^Z uw~u/V"Z [hM l ēb F 0#.^=)zG.U1Y:n5:5)DƁԩ`"QW:zzv-C.@|V x]֜h( oNp;ENC[[6#QpK8|E؊S^Bo5l'Ng+Nz {VʋD/NbC2ktS1!4)DF4^[6OO*J`C*_v@`iTMٚYhD5idVTKmmm_lt#h}8xۡ* wMv<mwȶ.fh: / y}8vcHhtĉ!F7hS8 Q wA2C$_rۓ'?-[`ltd/؎esZ [KY!R (xOUif xShi\Mֹ--!p7СGl 6enqqZ/nW','07hMӌDQcpᵲttlÏc{u[Hъc>k> V f!4mMNt%"ڰlm$ZV(Iqt[Wv2%BYšWgp"X",'U0 ̌"]<ә$6-MS033H^oB?І0Մ8 y#lC1LE ǐP86,g^T7ku >,?+Mo{҆K+5­^Jmv+\]Uk7{# t6l$5=%Ʋ7ګ4Nh7EOk%qC,h9 [)II\rrLɖ't0V=(b{ްh7l$Yև%"""" %r%-$xU'k|Dx!"*GDAHBvH B[__bcB$d$A,.VPXO> Ei'Ҧ3*],Ivѕ ^3veK8%iU09M`,k"+KG86㉈V@Vo) V 2zo1Kb.ćsEA@ˎn#[/KuaW,$c {o.iiCͲjmiqʼn,W4 c(FLcAl$o 2==Ţ |Oڠ LXZxȒ$YK6E[$Bqp} oi-=d ZJcp2v==G {4'r5`}b Il'5lx KD@7E- CEβ.v.V/6bV iCˁnlOXOgNN0ӑqym %92CN$%`M1CPui5)t6-Kkz!ˬϬ!=6 ,j?L2&e"diuJ.mf#NJC WgxKKy[\ՠU~}OװXI#P2kl.y& qÄ/H"oF2AA̿ icV d6l4+ zK.W,Aˆ.mz X0PfH<6@a*T A ϲB/%*7R>,YՐҖ KsaA['jʢ _ll 0- l ȒdN*^$b%VQT6-Β.ϔzyO =fkgkЛ a% uÕ_VTz`LH'$1'^xR=E%*։!Hz@sTЂӪ%ZXFeDQZ񵥪눤url {Ֆ[I(iD$3\im>-fֶ-(}/l oE%ȲUي{K(6wNP ja6[a:%1_aHUvV6XQ{|+$ÑK ^ avf)<Ϩ]%騔LI 04 Eo & +y-ꅛyM/[wOշXr|Ox~jx6(yDQ#9Gn4\=2Ȇ}= j!dϰSBa d\H@\Gy~.\x uAZ%,0yh;< w6HG FGax|S7 [)/a4,lddӁu-[nw&MֵZ31;&Ҳ|ڰXA߼*ajjxiD"&Q,;9Z}PySa(bIB섺jeAD*/~lF g!b^[zR`(]m%p|-_ڶ%%4Iʊ{nd2$# "mEj@`p^SS#DJ n 1D \~y+A0veXeXq8ZI$;dža`jjh %\b0MLUכM+NEDUD!?IWہ\밢YZ6`-#v24fh))K0KBҐ@6j.*_NTh.,t^zՊTGg5ߋs痫d|䡒wJ6av^T X2o1AlHX~/_o\5~jQU4x(evvprz[]mu,O( ƃ1Ll<|{R+-4l[noa,ڛ3-*_6,UuR9V"_> @j,,"WZЫV+jQ n)Yvq|A3B3R16m ԁS i& PԀD\ǵAy1V"ö,I܎|d][f2a=%$&iH/rf'@*YR3T,X1r3/19YiaXZ@""Vފh@WBY7zB.s eߝ&±ESlT,EaVHئ۰=;׶lkD\&i8Ssdd ׀- Jw8+9A͋a+`"`"kS.;]ްnvxɺU V٭.;a Չ&V- *D- ipj=Wʇ/)ƞ;DDK lW8ޖ f)Xo՚`)j|01+s0:]}c V* bÈ" l/X; bux6l5l4o l L_?uϰ,8b.m%ZXL& DQB:EZdQ/I+OriC}-&i(;a: bCɕE &mRGha&H*) BE*i^ ,{/]d| EMCUFDd(ʼ GŃY\󶒒 &n܈A̾_CS~?6 cȾmxNFu?s]!Xs@ְװNqDzA LT *%5>Qa2Yi*T~+a+<(mxo#^0&8mMkޞp2 `06pl^^6<(2baP2h^&Q,3TЙZ&,8GKK'} ##Wpv"70p==x 79D #KԸ] yk2a:LƠ(d [:`@,/?7>= g^[K>ZEO<|x595k7n!laX4PSx־ i :;{q.F^]6tرat79u2 NA5~DB,l7gKö٭^@֕)KZ= V V xJNFPv2nH&cXoY\ج M g7IbhvjAY3tAg.<-mcj19yM!4BMRuєh>#+Lh0[KT\ ]ס(iүgE)2%ihc5Ci/ҸP Ðݰլ能.dzCnÉݚ3HjM☜ 9j=ktS=[hSʇAljt7땇ٺi^ԃ3jZP TK/F(y0Ն{z:zxZVTO!t+ ;L=Ƿq|_W [%>l.rᲬ^#j,M/HьPsPtX}i(x"""d%}^׵o?FЫevl3lcX?=V V"e8-Ix]vXd_-a,sd\mЂ >4đmVZ?nO4e<u{QOVcy3/.~$-]RY0l%"AÚQ3A0 :iAY81RUm;;ܸ {9 .nphm 0a\Hui?fD5ðf+DIF*D5.{qmX.;EngX4,̬ ]8F"T% UM/ՌזDCdV(*[,RxE+6'tMC:htS64 CאJ?g)j}&EE-vhEI59uS} jZ=wxHnцf&1Lc%,TT[a64 phrhf^\m8z)\͛79Do$.D>y<}o߃8~9ވ-8~9tw7)DMja۶n Ѧ{}8~9X,uEy<}8~9xۗ'q+~3%pEǏ?nʆ&'ݦǏ?-ְen?v츧M+ ijB$1|7N@82m4 C%Qn / C&(iVK绁h4flh/{ְe(J=itSJ7""""""""""Y|O?4V+~_827802DkljltSdM!4r9Q", վ~:-hi鬥yu%ꤥGˢy^Qsr:YTOxG~/DQ/ܠVVa+uw@wF7hS,Q8bq83Xl|#j dTI}Y6yCU&eIժy^QEI-{x"ܹ/}K8r0l;n5T*&X:\~#";ǃv|e~h`* (O0,M!T$Չd25-Q`:JE{QL&+V'A~&!ɲ YQi:zl A`cJ{k5P$eWYѺ,6@(Twq9ZcpuQo-qX,T9ZXj#ըs\(pjłPU;Sui: $ъ$Ngsr%o*Ѩsi՜cgCN796l]vڎ;e? TUfslޘ8#U,p,ӈs"aUݝ5[`6[k:kD=ǵ=q1\e_CkY^g=DGG/~5Z$⇮Wɴs,I{>M̌U_}+bUuesEx9qAC=@`%u]ц;kC辺d Td2no9v\^CUD#Ysl׷T~xnm2aJyHPUBss>ܼ~ۛs{&+޾j:ax}v鸳ubֺۛ"xzc]qg{`j:nߺTmno(چ ToglݺwE4[z; fgpsl<b.s+n]1UM{Z>;w9Hۗ+j]еk?*Ŗ-j:{g]()LM oΝjazFF>x{K՞c==5ss\Νj Fp·oe:r^}0CwΚ()\ZU9wVV9v; "v:\! V|\sN'?j]sD,Z~,w;tT*R>w[ԫi%&&natZ+W~VU'ztd2K~Z>wW9vۑH+z܅+{;E{՟xV^QDgϑ;Ռ_w8Z<|މe mL$$*쀱w(wV{cK? 9Z10\YUw/ FG01qm%px\!/I֖_1l@`2s2v RyOUUU0:6T\]*#0k`(GUu9ɪϱ^>S!xST9VԊVɂQ$n9 TMQljDn]*M9NjOPXsܠq(4^+\lbV7*8 kz4*z͗՟cFj+DEx? ْ*>Xm=_7 k</8Ju8zVvg΃#9ishձҬwd)!@J+c;w]!bc ؔ Q` !9bblllY׮VJI3>~Ө5լkgڣ{~aXgd:`P\=YƂT G6D(4d|vM8FcD]2 ~qD3ȯ#sq1a`fzt]0Mכϧk;D[e.PJGQ(d)H$02ra0ͼf:F˯/R7l]ܰb1)$O8C8G0444pH^Ϸ3rpEv^y(׿SB6Fh"N *!di & ljM +T* љMߴ"ԎNgFG&XS |htv́a5X̯=JxOpkGB!B²2h4z<˥R)K(jj\. FJK=}߄hO? J_xk=5`+!B!BTg!zYhFhF r,ˮIs(P*ՐjJv/O駟P6ds.!B!(*(*Ju\/P(dM7#WdMk4kKȥB!B0:6mT%Un#R kknV*ѣ`kHD>,0oQ~*۶N5>fPTU݅3B˲AXT~W.i#ҹT*T*,@ ,hm6oa(JtZj}a`˥!5IE:;v ԧ0 l6T*Q]ZlF{sss8ve|5!bA ˲P` ܚB~V*x,X,bppvqRv܊JurrSSStPkWK:PصH$P(VQ(Ar/ FWնT@$hA{^;v wN>Y477cvv~Kc2E{L&ó>+Z9C0l%lq"Y,IRYw0488A.KzΕ9j$ `CZrVU6{Vi-|;qq1 `Ģ(u8/^{5pnL&_R BP,@T*GB~FbbZe[gB y\Y(pYDa6]|[v:F__Rd ќOoދ@ 655dU"n.*l#^yɁ*C8N5R~^!+R)q"YQyTf!CP FhTr!],!dcYKv<'l8W .jW\qŒj,U,%jW\'Vi-HDh`;?F{kRf.ep]w/^q?i6.dXo||?Op|ǕW^)Y. I <x@r%OWWj5Ɛf~aqɅJ*BԄcrr>&I glJ|>T. `0  A:t*psdJF؈JjVU*j5~?fffpqN,@ d2C&k,q嗯aŌ:łN ood B08|pZlw|7߼2SSSx#7خan bZl;X ?8'3?x t:n&￟ldDoo/r88^/z="jv@@Q%S`KRP(t,r|(z L&d2"ԀjfÙ3gNDP. bVjʮ.LLL  "J! tsssb:ݎb}avvVhZX,@TB,@4 Xmep8|#|A&3*zX,ܹsHBtzHRbmݕk=AVC,y~;Ξ={ <~`eBnPH7qLOO_x  y }q(`c>Css3RrdNja@d*Bss3l6xE&o f% f3, J<$L&Z-X 8C60X,ْFrlV\.Wp5z8{잾9+P__|>/n eaZp8022t: χ)Eq#s6dzNxpaER)ǂUW]O|x099Y_\lCgg'N8R(˸+Q.C&w܁^x.@ eN˅X,H$^/{j'"Bڸ\.۷* . RjqH$0M ـJ'q^:8N `YMMM*!5V~XVD"fa6T*1::*Al6l].KL(˘ZdB6Źs r$̏Nd>J077\eF`0eYfnC@V#KQ * g*}zvΊJR8x<LLLH-oǏG{{;n477㭷ޒ\. l6q׾&(غ 0 /zG'P.կ.w}7,&dGx:ZJN%ABƕe466"NY V2WVvϟQR0 l6A2B`,ˢhTrlzP(j01B&E,o`4҂gn:brԩSB$ASS=*rAcc#cvvV<;Npt:-6FF8ΊAsȑ#˿K|wWM7݄~?dYqqd2p x衇opۇdϟ=܃Ud2 n !d2tF{{;~?R044ah4de{vit:R)rVd28y$AXo0p:P0 1== \.6 bEqz122`>(L]<LJBR `& "N> B!6^l{9"y-N{7 {x'_=}=hӃb@ _~aZc!lNeLU*V FBVC$dH$On`P|P׋5 Y"_ǂM&d28dY: P(`0DFp8`: ] hhh@$A6E.Ckk+R8-> a2055t:6ar-?yx<NڊiB[<'fh4hjjD Њ  <(d`X133ϣoŌ l%o9C8:;; .V!˷6nĄܲ?S} IDATߏ^xV?Os _>򑏈TgNLLpG5[wV6̙3B28p^~e;v !ᩧհ d2A'L*jQS0J%YJ͉Bl6[uvR/BP*ZqjxA\cBŵڜTd21Z(?ah4 prt:-y:+JϟwR$Hj!\n~ aooxP,o>8i˖?+j5Y{dhkkC:dZK9ja111 122"o#G?A ѣxg$%"QזKܱc|w|eGtlLV8TLkFLuYfnd2$I89ˠ/v@%˲<6łb1 R뙓j4pd=TɄN(JE/eh2H$^gY0L'O.;7fMMMX,󘘘x= a٠P(F%u\eo>vM|VU(pjH& Vtzviζ}'> (JSԲߣdbW^qݫAՊn}}}˖lv,^D" L.9dQ5r8hllD $j5P*(* I;!zqܚ4@+!59i\^2Fӡ8wj51==\.z!qLNN)L& F7Uu4K${߾} piT*!T-?0 (J:466bll \B###Fkk˲hmmRD<I;!qx%kVX,466y\pF礋555azzX a||\lʲҐp]jbjjJҧ赌eֺ d>S8t`fjٕ7^/~ۗlíފ'|w N\. z衪t; :::_ ^{g> xOd0LE-j199ZVuK%٪9bq8r9`^/ X\.N DQz:tH|b̏׺4gd`ryɿ+V޴Z-4W fwpUWW_K/w]]vZZZj4K,nVN:L&H$w>g?B t4x< af"333T+ `Y. W&ZJ<'d d2r9r9dYy@a]2D8UVIA@TB6RDOO Nڊt:\.H$dV`܌d2:HRBX,0Le~ud24gX\.C.#s-BH$".'ɐrJ<"V+%%d22m]@.hG-܂gyjqG?b~-[r/`%d2ᮻG>p'yWcbb>BNMaB6a8N8(Jr9$Ix^( ILA0 ,˚BU&Z|dl <&&&j8bBv6\׋l6+^pd2Z^6BgRuIF6E6pH$H$ALօ=0hllDSST*44v;l6bdrFLNNnvh4F<qZz^l8$7- w\p\f b[A %@}{q=%xɓ'F`? vx AC-9D_fC$???BWWq?q|_şٟjȄ àv СCݼJ&r|'dn7|>xfÇQ__~( twwKWQ*8t ":;;a٪D"bICK!z!wVϬuNRp/[w++Aww7l6 x`YVLRh4K6\(^Juf3<\.גrad*)UqbI CCCuW|'zꩪ2RIqBG}@O<^}Ud2\Ox.\căۉ'j㒋)Bڵ!JܹsF8N333U X,J% PV!5J yĻP( iNj`Xt:H$P(NdB@{{;&KSSZ8?NRx<qjB3P*d[T1NrIW_:eFxbYf3X`DoooDR8hZX,1u!AP.t:/ɼ&;no9r'N=:tvvⳟ,կ#QxŒtz+oOy_җn![t"ϋTQThiiA0$r9,Ξ=\.WsX,h4AT*\.6iT*:.\\hB֧ bـr U`Y~ R#rN}}}pUbUCںdtU|04J,t,d2A.`0b^qܹ-AZ.zŀkn_q2Z L1WNRB#ԗ 󿥾>455WF#M&000@a'?I|b"N{__ _%q|>^.COO!V:Fss3%###PThkkC  p…+NCCH. 0J%ۇX,Ypl:N^Z%#˲H$rАr꜔8d2|Ӄ>IvĊ5d2ARԩSbٳgmdfjQ.ۋr`Yb ٹΝ;׋D" ?:PKoo⿍F䠥P(pmU lNeZȈ81!l\.C8FgggUݯJfL]]]FG444$,$YKw\pXXk VFQRõjzkbzz===b WJ?… 8ZFT8L&٬ (кKJ% ǡC0>>.>F`kd2fI^7KP੧j1DBv-R 188'yP(P(MB6<ܹs`Yݒ HREb-P(Z,ˢk:S9W. f,\.455-P(bPv2 6 ~r*JJ%t]Xxy5\J~bde ;fer ?|fk45O~?|ɂeY<B/~`Y<'dZ+hNopS XV0 C z R+6 !@TtY,EzJajEH,[8']pZ62^I-`>:;;NB!IŲXOj/JNa4133) cll,N Ӊ@ H$>DQqV+>pζ3N':;;1889Jj𓝩?qu׉ C|I׋ͯIs:U4 jgYqIߏ Jǃ`0( X bqٱv8(bC_0Eˉ fb\./y]g [V1X,V0 BUs6`>ra``DB|vl6IoX,8|A>}izzCݱGF>}kk,p |ӟ@+!T鲬fKEXX_jxR Ν[s~?$ZK:F&3STlypxmF&ZR&|c===(JmhpLNNv7Ͳm~7﮺^_7E֎2[ɻnq?CXN`~;|;ߡPB6@T̙3Pը_ѧ#RR BFn'OD0D.[­q|>OB6pØ˲K6R VΕB 08p099)>lωD"Džcpx(H&HR[rzp8jkJD"jۋP(bwwU d2d>,_җpu!ɠoX-D5[I{{/uxaX(J&l6LMM`0(v\HT/f!lfdT.ؖݽjdBj5FX7yDb& Bز@ !d}Ѐ>e0 f>|Fss4[<']UP( GM& \{P. `FD&IҼb͏u: ׁgb1v[[9MMMr}o 333D"\hζ;jz'Y{8XPA>z$;W*[oL&q;wǏG6K tww#H̙3w ^ pX/B$+೟,~?^zZOH&kjZFVrLb( |>ǃY5LhooGss3T*\"r9x}Dv믿_q- a``LF|qU_2z%{`Erp-%w Zp{1==z2Z ^Ce'gZP. NT5Z~Ʋ,\.a6ՅN!d:;;܌H$l9J5J 088H7 ΕBDPHoCbbbB|a\q&R fx\,z%Ftuu!H,y0 ǃP(eפݘD$ASSXkV, 677czzzg߿2 SSSdbV$A ܪz^h H$`\+Jva`Gq7g>OԩSU ~#ԩS5E֋`\08z(fggPMl`Yc@Jf\.u[~̗hoo 2!d}ڐظlh4BVneY M|Bjdse+ш:աNjFI0oe6!BU6JrRn~Wjڸ;{<~`0, waNnU*<e@kU3 :@kTZ‫b0h%bNZ MNNbjjjXp5LhnnF4W2WZzmIFj Ra/Z{KRp\b؁jEXȗ<#LA.K3˲hhh`U w}8}4T=L&'O⪫O<~l-ٺA@k>Zc=L kQVB6󡩩 px]"&&&Ģ{}2GZףtzδy@dZ5pqR0 5"FbNZ @8^3rPWWWUt=c3lYzցeY1J5ZA_vehllBfl^2+T o6N'~?z=j5ږlTF.}y{c``@`ABW^'xǏfXۈ2[7`a|o&N㗿咓[oMMMh%d*w~zq܊=L&@Q%ull v:FB9 3Z4<ϊ]YEgg'"5"FbNZ MOOCpTc^HR `*к477[h]:0 ۍz bm[$AXDSSZZZh000 ]X/ |hhh@,v.g}VA577ǟ kQu /} ! ᷿׷~?(J&n!Ӵ8z!kd6с~!HpA\ "Z-^W^DVIJ%׿u8N;_(//(ooP,hRٌh4z@ i!Ib+2 |~`@$R sbTJ!^,lmNZYY B!l f PWWɴ`u{>[򱱱CCCVh* +!KUbT__ӧO#L v;jYg7Z-\@8vAAs=Ç@7ވn E8|>~ ;b ].ϣh>,lǃO~w}0͸; ]TBVd2 NaPQQٌnx<z,zEd@P(3B._>˲0VJh4L&_y$R@\:Nt,`rrce.V\.ͽ128KJJell v梮s9C1^K$pݨ@EEł9fffhDhjjZ 555Jt,\.c=w£gQZZ۷;D.C?>u]˿KڵKx:H$`XPUU>'dAYYt:t:ǑJ `hP!2+X,N'&''iN0 娭E?N:Bzw'\t:-D ˱fx^b1T UUU0 ByHR8u\.E

.P Y *lFQ__0::*f0LHg4rvggd2AR! Jp\0 4zhwt:Ԅ3`0h4 ՊZ~LLL L hnnqB0p8E.0R)Qav477l6#H`jj |. h||\vh566xp* 6 `Pw&fӧEtB,p8p8 R)<V 6`7{{E4Egg'с  |>~OϺ-)< apػw/>Cq z-DQ8qj> KOYł!$ 8D"!ZYY)$yjuuuMV!v~vd2 EH W_}50`4\rVU @,6 05Vax^B! }TѠB(thmm(jjj0 &&&099)hA]]^hcss3X l6&&&LOOcxxXxpZVV\.Tu(Fsmdd,nc||@:6MhJlhmmV+ Jim6|>m޼wuyx^ѣ㏑Jk.atwwjjjpw?9՗)E \{سgO>ETWW?)Z-?`۶mE{_ O9Cyy90ՅF|+_A,NP?9'b]D066F|+dXD(X edL&Fn:B@Rȅ[,d2X,هwى&lذ`zXJZt:oߎۿV777n:~?<nP* ʬV<:;;%KQbhJL&C>GMM 4 044 AxKՊ%˲dhkkC.;rh4,JV͆l6+`0`͚5xWӃ^xGM7ш\Ǖ,?l=CGG ^/dB)_pg\.'J)+<yP(`=D;fxpf $IbA:H$"QQXt:1>>~`YZLLL  k׮_|A:W嘜ķm#>c? 㷿mK.[pf~@! Q4BV'_R(UfL& )YzgYz~c8}4= dj?58Ú5k}·d]]],+jE."+JC1D"T`}}}BT*_pn3 XE$M"+O"w-9y$|A}g\.0.r7$eY<3`͟7ϥ0 hnnd!FȪAbFca477a$BI1Vy B!q/89:, eZbKUb4&''Em0E2TJ(af8o1ѣG@+0&Y(z7nLOOp`߾}عs'Z Y" àx<.tYur9 x(G+!˜c%0B M] !Ʉ@kWWגܤC1R TVVӗ| Ja,0>>T*%uQ2a̦hoo?_KVt֊ '?G Q__R˲h%dr9ZYSqT* ?KR(++CKK g àAFims6& 555f`ݺu`h%2 aPZZ*\C՞5HR`Ŕp n{сVP.UTT`Æ av|2 ---شi6n܈X,FW N5k,ȅ ̮dBuu5^u>|/2^x(1Y٪+w~;|;O?m۶ZݻwѣG.~GCVBv ĉB'J6~ᕙP(A B.0hmm*Q?#dqT*QRRqIQDшFQn`0(JHH1'5 hjj˲d2x<ܙ6 fYؼ(L"N Aʹs|>le p݋^(Eѥ7ߏX,eF.r Lr璕#<B^,+zZk0pמugFBQ?#di,DKR)0 QSS$)3E@P8ׯ\;uN<) ұ,+ ZV\T=/D:#)q9soGl4155%|޸q#ZvHUl׿[n?D&j\.G{{'Sg4~z(sP('룧\$g, NV"Zٕt'Xqs^c\.'YnqA̲V+C:#)qo3%%% 7b޽(ZuOVUl]f _@L&`0@P`Νh%d*++P(vm>B@ii)x ~FRh4 <eLC)))J\kמ3X!ɠh ٩SEן7po j``9r%%Wh4Ѕ ~$I{o} >$XEkk+?Z HYY^/L&, |>/L<(Dȥ~F8/xBP\Tu(FR7jyfz*8}41==P(xEu:hKN aE[g,jx!UB$0 :|deYB. *pI Y.t:LOOSAVW8VUU˅'O󡤤VLH5'mkk].***vvFш+A:#h4PT`۷oG<Ǿ}+$gL&C}}=d2$ގq iLMMa˖-o~SyL&T*B!8HRH$,%!+3BL&nD"aPSS2TcZFEE&''ECL&4(ud2l6qQP- r]WƥC1Qt:}kmrf? ۶m8 XJ,`bK/۷{E4Ň~`0 R)lٲj M*QRRrd2i$I\.$I6ˁaJ#@e~FbtH$H$0 0 V:l6JQEHI1Vr H$FaZq(h Mi0::,& & L333Ӊ@ ZJ`0 a׮]ɓ'$8õ^{P%̲rƎ;piTWWd2!WaX)JHH1VF]Jh4D"p:P(t! àZ(c0jZB())>0Lds)Poe֭xꩧPQQ|P>(vڅQpjw-`r7pjٳ|ذa8z{{vx`֭;a0сT*Ub*:v^/$N'Fr,$j5N'N:E}D ߏ>l6aLOO# T l6 r\ +YE[[<FFF055ӉiDQD"8F!}HCC(xf٠T* Jr၍V@>G*BYY, F@:#)qٴiz!s=8x FGGގ^{ nX (--sN8pD(tGBB. _t9}nv?.zr***Dx~?\.j5^/(v;6l؀L&B΍!KKRAv333jŚ5kP( ]bRԤ+M&$0W~QRRh4p8cǎf᪫󘘘Ȃ kz^]ׇJXV#+!UڿtM8t萐oСC: 7Dww7y[x'd9YÇpq'Zϲ,gBr9h4ѱL&L&|Fpݐd􀃐@<4/ɀa|>P#dbrP`|^Nׇs^'C +٬l^G'Tu(FR7jH$rD"Jvc۶m̷wq >#9r?ш@ `6к~n+(2!Mb@PǗ HBȥ~Fd2F<) Q>a`,LHI1Vr9  6L VzD"ɿ?C1QD{hnns'l/NR)߿_~%QXx,gL&kVfTWWy|>LLL& 2L40 hnn%IOH1~F8v%%%b8uJe>7J>)D )zR˲]eY!qzz~l6Fix޳^c~j ǎ[J7)Ponw܁L&C??D}X.C s7b޽xG)9'§,{ с~wu/d2 e2L&۷r4EAmm-FGGaٰn:!EaEA>&@\$g,-Bu!Ncxx kl7J\z{{ QdBc^Gss3~?FGGa4q50  ɚR0< & r9t:!Hv)gh4A0P^^FQWTvtzm!ETڿx,m۶>_w}7 8Bڒ[n{EGG=ZȢ`[{zzp7qرF7o>s<h%d\.d2ݹ?>yG4ECCzzz(D%~FcYV`bbp:Phf àTcR]y󡵵Up8$cY|^  Fuuu5T*v% JUb$}/*JY0DZo>0 \.nttt`8rHKMV ha!aLzd2$I KӘ–-[>?CaB$a2RDyxG0nG*B"!G'`199)z7Ha`||333y4$+j5***099): `2h L&N1::L6 xdRt<r! cxxTJHF_<4RR5Omۆq brr.>$ Z,Kؾ};^DQ| SlقW_}uJȪSRRrd2i$I\.$I$ \.aPZZ)R)I'fYz& & Lhf~J'͆@ a9bdtH$D"Z8nA06Jfatt\NmܦGN@@1J`0yb1D/q08DhPoť 7tahhv0N<)L&qZ{:$Kl8o;vx';O?E"ݻZ-GQnaWBȥ~FjJl6 aD?;B)\.hńkt}}};u.u|r9j0 #lw9u(FR7jH$rD"Jvc۶m\%afx<IvV%̢~FқJjEUU@B)ʹ?h˺Pa|zKodR7jP(<2 6n܈{b׮]ӝHfY[g,;wR%0RΚH!K/CTNӅ.!+sT* x.9eO?rt:;uԒ+Tڿx̥-[w^tttѣ.YMF.D8Fee%R%d4Q]]M B g,l6 Z MVBV)Jat:N}Y jaz)w oŃy|_ߎz G)t*#s:限"e0 +"H!K@TE!\)JaP[[EmBYP^^%W:)q '(d`+!B!B!H`Yl%B!B!B!B!" B!B!B(J!B!B!`+!B!B!HB!B!" B!B!B(J!B!B!`+!B!B!HB!B!" B!B!B(J!B!B!`+!B!B!H8cVfIENDB`csv2svg-0.2.2/examples/irregular-times.svg000064400000000000000000000154201046102023000166760ustar 00000000000000 0 1000 2000 3000 4000 5000 6000 7000 8000 2020-12-25T11:24:49Z 2020-12-26T12:00:20Z 2020-12-27T12:00:22Z 2020-12-28T12:21:27Z 2020-12-29T15:10:49Z 2020-12-29T16:13:49Z 2020-12-29T17:36:38Z 2020-12-29T21:43:01Z 2020-12-30T06:42:39Z 2020-12-30T07:41:22Z 2020-12-30T21:43:01Z something another thing a third one whatever bof csv2svg-0.2.2/fmt.sh000075500000000000000000000000231046102023000123420ustar 00000000000000cargo +nightly fmt csv2svg-0.2.2/rustfmt.toml000064400000000000000000000000531046102023000136210ustar 00000000000000ignore = ["/"] imports_layout = "Vertical" csv2svg-0.2.2/src/app/mod.rs000064400000000000000000000003741046102023000137220ustar 00000000000000use {anyhow::*, directories::ProjectDirs}; /// return the instance of ProjectDirs holding the app specific paths pub fn app_dirs() -> Result { ProjectDirs::from("org", "dystroy", "csv2svg").context("Unable to find app directories") } csv2svg-0.2.2/src/cli/args.rs000064400000000000000000000013461046102023000140660ustar 00000000000000use argh::FromArgs; #[derive(Debug, FromArgs)] /// I need to explain this, I guess /// /// Source at https://github.com/Canop/csv2svg pub struct Args { #[argh(switch, short = 'v')] /// print the version pub version: bool, #[argh(option, short = 'f')] /// output format: "svg" or "html" pub format: Option, } #[derive(Debug, Clone, Copy, PartialEq)] pub enum Format { Svg, Html, } impl std::str::FromStr for Format { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_ref() { "s" | "svg" => Ok(Self::Svg), "h" | "html" => Ok(Self::Html), _ => Err(format!("unrecognized format {:?}", s)), } } } csv2svg-0.2.2/src/cli/mod.rs000064400000000000000000000035551046102023000137150ustar 00000000000000mod args; pub use args::*; use { crate::*, anyhow::*, crossterm::tty::IsTty, std::{ fs::File, io::{self, stdout, Write}, path::PathBuf, }, }; pub fn run() -> Result<()> { let args: Args = argh::from_env(); debug!("args: {:#?}", &args); if args.version { println!("csv2svg {}", env!("CARGO_PKG_VERSION")); return Ok(()); } let raw_tbl = RawTbl::read(io::stdin())?; //debug!("raw tbl: {:#?}", &raw_tbl); let tbl = Tbl::from_raw(raw_tbl)?; //debug!("tbl: {:#?}", &tbl); debug!("tbl dim: {:?}", tbl.dim()); let graph = Graph::new(tbl); let svg = graph.build_svg(); if is_output_piped() { // when the output is piped, the default format is svg let mut w = io::stdout(); match args.format { Some(Format::Html) => { html::write_embedded(&mut w, &svg)?; } _ => { svg::write(&mut w, &svg)?; } } w.write_all(b"\n")?; } else { // when the output isn't piped, we'll write a temp file // and ask the system to open it; // As it's the most expressive format, we prefer to // open some HTML in a browser let (mut w, path) = temp_file()?; match args.format { Some(Format::Svg) => { svg::write(&mut w, &svg)?; } _ => { html::write_embedded(&mut w, &svg)?; } } open::that(path)?; } Ok(()) } fn is_output_piped() -> bool { !stdout().is_tty() } pub fn temp_file() -> io::Result<(File, PathBuf)> { tempfile::Builder::new() .prefix("csv2svg-") .suffix(".html") .rand_bytes(12) .tempfile()? .keep() .map_err(|_| io::Error::new(io::ErrorKind::Other, "temp file can't be kept")) } csv2svg-0.2.2/src/graph.rs000064400000000000000000000250021046102023000134570ustar 00000000000000use { crate::*, anyhow::*, std::io, svg::{ node::{self, *}, Document, }, }; pub struct Graph { tbl: Tbl, width: usize, height: usize, gr: IntRect, projector: Projector, scale: Scale, hover: bool, // whether to build elements only visible on hover } impl Graph { pub fn new(tbl: Tbl) -> Self { let width = 800; let height = 500; let x_seq = &tbl.x_seq(); let (y_min, y_max) = tbl.y_min_max(); let scale = Scale::new(y_min, y_max); let sr = IntRect::new(x_seq.min, scale.max, x_seq.max - x_seq.min, -scale.range()); let (top, right, bottom, left) = (26, 50, 85, 70); let gr = IntRect::new( left, top, width as i64 - (left + right), height as i64 - (top + bottom), ); let projector = Projector::new(&sr, &gr); Self { tbl, width, height, gr, projector, scale, hover: true, } } fn legend_group(&self) -> node::element::Group { let mut group = node::element::Group::new(); let mut x = 0; let y = 10; let w = self.width / self.tbl.y_seqs_count(); for (seq_idx, y_seq) in self.tbl.y_seqs().enumerate() { let square = node::element::Rectangle::new() .set("x", x + 4) .set("y", 10) .set("width", 8) .set("height", 8) .set("fill", COLORS[seq_idx]); group.append(square); let label = element::Text::new(&y_seq.header) .set("x", x + 14) .set("y", y + 7) .set("fill", LEGEND_COLOR) .set("font-size", 10); group.append(label); x += w; } group } fn y_scale_group(&self) -> node::element::Group { let mut group = node::element::Group::new(); let x_seq = &self.tbl.x_seq(); for tick in &self.scale.ticks { let data = element::path::Data::new() .move_to(self.projector.project_point((x_seq.min, *tick))) .horizontal_line_to(self.projector.project_x(x_seq.max)); let path = element::Path::new() .set("fill", "none") .set("stroke", TICK_LINE_COLOR) .set("stroke-width", 1) .set("opacity", 0.4) .set("stroke-dasharray", "10 7") .set("d", data); group.append(path); let tick_label = element::Text::new(tick.to_string()) .set("x", self.gr.left + self.gr.width + 2) .set("y", self.projector.project_y(*tick) + 2) .set("fill", TICK_LABEL_COLOR) .set("text-anchor", "left") .set("font-size", 8); group.append(tick_label); } group } fn x_ticks_group(&self) -> node::element::Group { let mut group = node::element::Group::new(); let x_seq = &self.tbl.x_seq(); let y = self.gr.bottom(); struct Tick { idx: usize, x: i64, tx: i64, vis: Visibility, // visibility when non hovered } let mut ticks = Vec::new(); for idx in 0..x_seq.len() { let x = self.projector.project_x(x_seq.ival[idx].unwrap()); ticks.push(Tick { idx, x, tx: x, vis: Visibility::Visible, }); } // we improve the ticks position to avoid overlap let dots = ticks.iter().map(|t| t.x).collect(); if let Some(dots) = unoverlap(dots, 10) { // we can show all ticks for (idx, dot) in dots.iter().enumerate() { if idx != 0 && idx != ticks.len() - 1 { ticks[idx].tx = *dot; ticks[idx].vis = Visibility::Faded; } } } else { // ticks will be shown only on hover for idx in 1..ticks.len() - 1 { ticks[idx].vis = Visibility::Invisible; } } // and we draw them for tick in ticks { let mut tick_group = node::element::Group::new().set("class", tick.vis.css_class()); let data = element::path::Data::new() .move_to((tick.x, self.gr.top)) .vertical_line_to(y); let hoverable_path = element::Path::new() .set("fill", "none") .set("stroke", TICK_LINE_COLOR) .set("stroke-width", 4) .set("opacity", 0) .set("d", data.clone()); tick_group.append(hoverable_path); let path = element::Path::new() .set("fill", "none") .set("stroke", TICK_LINE_COLOR) .set("stroke-width", 1) .set("stroke-dasharray", "1 3") .set("opacity", 0.5) .set("d", data); tick_group.append(path); // the opt_group may be hidden or faded when not hovered, depending // on tick.vis let mut tick_opt_group = node::element::Group::new().set("class", "opt"); let data = element::path::Data::new() .move_to((tick.x, y - 3)) .vertical_line_to(y) .line_to((tick.tx, y + 7)); let path = element::Path::new() .set("fill", "none") .set("stroke", TICK_LINE_COLOR) .set("stroke-width", 1) .set("opacity", 0.5) .set("d", data); tick_opt_group.append(path); let tick_label = element::Text::new( x_seq.raw[tick.idx].as_ref().unwrap() ) .set("x", tick.tx + 1) .set("y", y + 9) .set("fill", TICK_LABEL_COLOR) .set("text-anchor", "end") .set("font-size", 8) .set( "transform", format!("rotate(-45 {} {})", tick.tx + 1, y + 9), ); tick_opt_group.append(tick_label); tick_group.append(tick_opt_group); group.append(tick_group); } group } fn curbs_group(&self) -> node::element::Group { let mut group = node::element::Group::new(); let x_seq = &self.tbl.x_seq(); for (seq_idx, y_seq) in self.tbl.y_seqs().enumerate() { let mut points_group = node::element::Group::new(); let mut curve_data = element::path::Data::new(); let mut started = false; for idx in 0..y_seq.len() { let p = ( x_seq.raw.get(idx), x_seq.ival.get(idx), y_seq.raw.get(idx), y_seq.ival.get(idx), ); if let (Some(Some(raw_x)), Some(Some(x)), Some(Some(raw_y)), Some(Some(y))) = p { let (x, y) = self.projector.project_point((*x, *y)); let label = format!("{}, {}", raw_x, raw_y); if started { curve_data = curve_data.line_to((x, y)); } else { curve_data = curve_data.move_to((x, y)); started = true; } if self.hover { let mut point_group = node::element::Group::new().set("class", "inv"); let circle = node::element::Circle::new() .set("fill", COLORS[seq_idx]) .set("cx", x) .set("cy", y) .set("opacity", 0) .set("r", 8); point_group.append(circle); let mut point_opt_group = node::element::Group::new().set("class", "opt"); let point_label_shadow = element::Text::new(&label) .set("x", x - 5) .set("y", y - 10) .set("stroke", "#222") .set("stroke-width", 5) .set("text-anchor", "end") .set("font-size", 8); point_opt_group.append(point_label_shadow); let circle = node::element::Circle::new() .set("fill", COLORS[seq_idx]) .set("cx", x) .set("cy", y) .set("r", 4); point_opt_group.append(circle); let point_label = element::Text::new(label) .set("x", x - 5) .set("y", y - 10) .set("fill", TICK_LABEL_COLOR) .set("text-anchor", "end") .set("font-size", 8); point_opt_group.append(point_label); point_group.append(point_opt_group); points_group.append(point_group); } } } let curve = element::Path::new() .set("fill", "none") .set("stroke", COLORS[seq_idx]) .set("stroke-width", 3) .set("opacity", 0.8) .set("stroke-linejoin", "round") .set("d", curve_data); group.append(curve); group.append(points_group); } group } fn graph_group(&self) -> node::element::Group { let mut graph = node::element::Group::new().set("font-family", "Arial, Helvetica, sans-serif"); graph.append(self.y_scale_group()); graph.append(self.x_ticks_group()); graph.append(self.curbs_group()); graph.append(self.legend_group()); graph } pub fn build_svg(&self) -> Document { let (width, height) = (self.width as i64, self.height as i64); Document::new() .set("viewBox", (0, 0, width, height)) .set("style", DOCUMENT_STYLE) //.add(node::element::Style::new(SVG_STYLE)) .add(self.graph_group()) } pub fn write_svg(&self, mut writer: W) -> Result<()> { let document = self.build_svg(); svg::write(&mut writer, &document)?; writer.write_all(b"\n")?; Ok(()) } } csv2svg-0.2.2/src/html.rs000064400000000000000000000014151046102023000133240ustar 00000000000000use {anyhow::*, std::io, svg::Document}; static CSS: &str = r#" html, body { margin:0; padding:0; overflow:hidden; } body { background:#222; } svg { position:absolute; top:5%; left:5%; width:90%; height:90%; } svg g.fad { pointer-events:all; } svg g.fad g.opt { opacity:.3; } svg g.fad:hover g.opt { opacity:1; } svg g.inv g.opt { display:none; } svg g.inv:hover g.opt { display:block; } "#; pub fn write_embedded(mut w: W, svg: &Document) -> Result<()> { writeln!(w, "")?; writeln!(w, "")?; writeln!(w, "")?; writeln!(w, "", CSS)?; writeln!(w, "")?; writeln!(w, "")?; svg::write(&mut w, svg)?; writeln!(w, "")?; writeln!(w, "")?; Ok(()) } csv2svg-0.2.2/src/lib.rs000064400000000000000000000006021046102023000131230ustar 00000000000000#[macro_use] extern crate log; mod app; mod cli; mod graph; mod html; mod nature; mod projector; mod raw; mod rect; mod scale; mod seq; mod skin; mod tbl; mod unoverlap; mod visibility; pub use { app::*, cli::*, graph::*, html::*, nature::*, projector::*, raw::*, rect::*, scale::*, seq::*, skin::*, tbl::*, unoverlap::*, visibility::*, }; // Reexport svg crate pub use svg; csv2svg-0.2.2/src/main.rs000064400000000000000000000002171046102023000133030ustar 00000000000000#[macro_use] extern crate cli_log; fn main() -> anyhow::Result<()> { init_cli_log!(); csv2svg::run()?; info!("bye"); Ok(()) } csv2svg-0.2.2/src/nature.rs000064400000000000000000000003221046102023000136520ustar 00000000000000use chrono::FixedOffset; #[derive(Debug)] pub enum Nature { //NaiveDate, // not yet supported /// we'll take the first offset for the whole column Date(FixedOffset), Integer, } impl Nature {} csv2svg-0.2.2/src/projector.rs000064400000000000000000000015661046102023000143760ustar 00000000000000use crate::*; pub struct Projector { rx: f64, ry: f64, sx: i64, sy: i64, dx: i64, dy: i64, } impl Projector { pub fn new(src: &IntRect, dst: &IntRect) -> Self { let rx = (dst.width as f64) / (src.width as f64); let ry = (dst.height as f64) / (src.height as f64); let sx = src.left; let sy = src.top; let dx = dst.left; let dy = dst.top; Self { rx, ry, sx, dx, sy, dy, } } pub fn project_x(&self, x: i64) -> i64 { self.dx + (((x - self.sx) as f64) * self.rx) as i64 } pub fn project_y(&self, y: i64) -> i64 { self.dy + (((y - self.sy) as f64) * self.ry) as i64 } pub fn project_point(&self, p: (i64, i64)) -> (i64, i64) { (self.project_x(p.0), self.project_y(p.1)) } } csv2svg-0.2.2/src/raw.rs000064400000000000000000000025131046102023000131510ustar 00000000000000use {anyhow::*, std::io::Read}; #[derive(Debug)] pub struct RawCol { pub header: String, pub cells: Vec>, } impl RawCol { fn new(header: String) -> Self { Self { header, cells: Vec::new(), } } } #[derive(Debug)] pub struct RawTbl { /// by construction, all cols are guaranteed to have the same number /// of items (the "row count") pub cols: Vec, } impl RawTbl { /// read from csv pub fn read(r: R) -> Result { let mut csv_reader = csv::Reader::from_reader(r); let mut cols = Vec::new(); for header in csv_reader.headers()? { cols.push(RawCol::new(header.to_string())); } if cols.is_empty() { bail!("empty table"); } for record in csv_reader.records() { let record = record?; let mut cells = record.iter(); for col in cols.iter_mut() { let cell = cells.next(); col.cells.push( cell.filter(|&s| s.chars().any(|c| !c.is_whitespace())) .map(|s| s.to_string()), ); } } Ok(Self { cols }) } /// return the number of rows pub fn row_count(&self) -> usize { self.cols[0].cells.len() } } csv2svg-0.2.2/src/rect.rs000064400000000000000000000010611046102023000133120ustar 00000000000000use num_traits::Num; pub struct Rect { pub left: N, pub top: N, pub width: N, pub height: N, } impl Rect { pub fn new>(left: I, top: I, width: I, height: I) -> Self { Self { left: left.into(), top: top.into(), width: width.into(), height: height.into(), } } pub fn bottom(&self) -> N { self.top + self.height } pub fn right(&self) -> N { self.left + self.width } } pub type IntRect = Rect; csv2svg-0.2.2/src/scale.rs000064400000000000000000000014771046102023000134570ustar 00000000000000#[derive(Debug)] pub struct Scale { pub min: i64, pub max: i64, pub ticks: Vec, } impl Scale { pub fn new(mut min: i64, mut max: i64) -> Self { debug_assert!(min <= max); if max < min + 3 { max += 2; min -= 2; } if min > 0 && (max - min) * 4 > max { min = 0; } let l = ((max - min) as f64).log10().floor() as u32; let d = 10i64.pow(l); min = (min / d) * d; let mut tick = min; let mut ticks = vec![tick]; loop { tick += d; ticks.push(tick); if tick > max { break; } } max = ticks[ticks.len() - 1]; Self { min, max, ticks } } pub fn range(&self) -> i64 { self.max - self.min } } csv2svg-0.2.2/src/seq.rs000064400000000000000000000106711046102023000131540ustar 00000000000000use { crate::*, anyhow::{anyhow, bail, Result}, chrono::{DateTime, FixedOffset, SecondsFormat, Utc}, }; #[derive(Debug)] pub struct Seq { pub header: String, pub nature: Nature, pub raw: Vec>, pub ival: Vec>, pub min: i64, pub max: i64, } impl Seq { pub fn from_increasing_times(header: String, times: Vec>) -> Result { if times.is_empty() { bail!("empty column"); } let mut raw = Vec::new(); let mut ival = Vec::new(); for time in times { raw.push(Some(time.to_rfc3339_opts(SecondsFormat::Secs, true))); ival.push(Some(time.timestamp_millis())); } Ok(Self { header, nature: Nature::Date(FixedOffset::east_opt(0).unwrap()), // should be Utc if I understand chrono raw, min: ival[0].unwrap(), max: ival[ival.len() - 1].unwrap(), ival, }) } pub fn from_integers(header: String, ival: Vec>) -> Result { let mut min_max: Option<(i64, i64)> = None; let mut raw = vec![None; ival.len()]; for (idx, val) in ival.iter().enumerate() { if let Some(val) = val { raw[idx] = Some(val.to_string()); min_max = Some(match min_max { Some((min, max)) => (min.min(*val), max.max(*val)), None => (*val, *val), }); } } if let Some((min, max)) = min_max { Ok(Self { header, nature: Nature::Integer, raw, ival, min, max, }) } else { Err(anyhow!("Empty column")) } } pub fn new(raw_col: RawCol) -> Result { let RawCol { header, cells: raw } = raw_col; let mut ival = vec![None; raw.len()]; let mut nature = None; let mut min_max: Option<(i64, i64)> = None; for (x, cell) in raw.iter().enumerate() { if let Some(s) = cell { let v = match nature { Some(Nature::Date(_)) => { if let Ok(dt) = DateTime::parse_from_rfc3339(s) { dt.timestamp_millis() } else if let Ok(int) = s.parse::() { // we change the seq nature nature = Some(Nature::Integer); int } else { bail!("cell can't be used: {:?}", s); } } Some(Nature::Integer) => { if let Ok(int) = s.parse::() { int } else { bail!("cell can't be used: {:?}", s); } } None => { if let Ok(dt) = DateTime::parse_from_rfc3339(s) { nature = Some(Nature::Date(*dt.offset())); dt.timestamp_millis() } else if let Ok(int) = s.parse::() { nature = Some(Nature::Integer); int } else { bail!("cell can't be used: {:?}", s); } } }; ival[x] = Some(v); min_max = Some(min_max.map_or((v, v), |mm| (mm.0.min(v), mm.1.max(v)))); } } nature .map(|nature| { let (min, max) = min_max.unwrap(); Self { header, nature, raw, ival, min, max, } }) .ok_or_else(|| anyhow!("empty column")) } pub fn is_full_and_increasing(&self) -> bool { for idx in 1..self.ival.len() { match (self.ival.get(idx - 1), self.ival.get(idx)) { (Some(a), Some(b)) if a < b => {} // ok _ => { return false; } } } true } pub fn len(&self) -> usize { self.raw.len() } pub fn is_empty(&self) -> bool { self.raw.is_empty() } } csv2svg-0.2.2/src/skin.rs000064400000000000000000000005531046102023000133260ustar 00000000000000pub static COLORS: &[&str] = &[ "#e9967a", "#9cd3d3", "#e91e63", "#795548", "#8bc34a", "#00bcd4", "#ffc107", "#4e5050", "#317334", "#7126b1", "#ce0d08", "#959e75", ]; pub static LEGEND_COLOR: &str = "#eee"; pub static TICK_LINE_COLOR: &str = "#aad"; pub static TICK_LABEL_COLOR: &str = "#eee"; pub static DOCUMENT_STYLE: &str = "background-color: #222"; csv2svg-0.2.2/src/tbl.rs000064400000000000000000000034041046102023000131410ustar 00000000000000use { crate::*, anyhow::{bail, Result}, }; /// this table is garanteed to contain at least 2 sequences. #[derive(Debug)] pub struct Tbl { seqs: Vec, } impl Tbl { pub fn from_raw(mut raw_tbl: RawTbl) -> Result { if raw_tbl.row_count() < 2 { bail!("two rows needed for a graph"); } let mut seqs = Vec::new(); for (col_idx, raw_col) in raw_tbl.cols.drain(..).enumerate() { match Seq::new(raw_col) { Ok(seq) => { seqs.push(seq); } Err(e) => { info!("column {} can't be used: {}", col_idx, e); } } } if seqs.len() < 2 { bail!("not enough usable columns") } Ok(Self { seqs }) } pub fn from_seqs(seqs: Vec) -> Result { if seqs.len() < 2 { bail!("not enough columns"); } if !seqs[0].is_full_and_increasing() { bail!("first sequence must be full and increasing"); } Ok(Self { seqs }) } pub fn seqs_count(&self) -> usize { self.seqs.len() } pub fn y_seqs_count(&self) -> usize { self.seqs.len() - 1 } pub fn dim(&self) -> (usize, usize) { (self.seqs_count(), self.seqs[0].len()) } pub fn x_seq(&self) -> &Seq { &self.seqs[0] } pub fn y_seqs(&self) -> std::iter::Skip> { self.seqs.iter().skip(1) } pub fn y_min_max(&self) -> (i64, i64) { let mut y_seqs = self.y_seqs(); let first_y = y_seqs.next().unwrap(); y_seqs.fold((first_y.min, first_y.max), |(min, max), seq| { (min.min(seq.min), max.max(seq.max)) }) } } csv2svg-0.2.2/src/unoverlap.rs000064400000000000000000000147751046102023000144100ustar 00000000000000/// build a new vec of dots with no dot less than /// `margin` from another one. /// When this can't be done, return `None`. /// The `dots` vec is assumed /// - to contain 2 or more elements /// - to be strictly increasing /// Extremities (first and last point) are guaranteed to be /// returned unchanged. pub fn unoverlap(mut dots: Vec, margin: i64) -> Option> { let l = dots.len(); let w = dots[l - 1] - dots[0]; assert!(w > 0); if margin * (l - 1) as i64 > w { return None; } #[derive(Debug)] struct Subset { first_idx: usize, len: usize, // number of dots width: i64, } #[allow(unused_variables)] #[allow(clippy::collapsible_else_if)] for i in 0..2 * l { // 2l is probably overkill but I can't prove it // candidate subsets are subsets with more than margin before // and after and with more dots than allowed. // The best one is the one with smallest width/dots_counts ratio. let mut best_subset: Option = None; let mut cur: Option = None; // index of the subset start for idx in 0..l { if let Some(mut first_idx) = cur { if idx == l - 1 || dots[idx] + margin < dots[idx + 1] { // we build the subset, starting by looking back // if we must take back some points while first_idx > 0 { if dots[first_idx] - dots[first_idx - 1] > margin { break; } first_idx -= 1; } let width = dots[idx] - dots[first_idx]; let subset = Subset { first_idx, len: idx - first_idx + 1, width, }; // we close this subset and compare it wit the previous one if let Some(best) = &best_subset { if (best.width * subset.len as i64) < (subset.width * best.len as i64) { best_subset = Some(subset); } } else { best_subset = Some(subset); } cur = None; } // the dot is implictely added to the current subset } else { if idx < l - 1 && dots[idx] + margin > dots[idx + 1] { // we start a new subset cur = Some(idx); } } } let Subset { first_idx, width, len, } = match best_subset { Some(s) => s, None => { // we should have finished break; } }; // by construction, the subset contains 2 points or more // and is more dense than allowed. let last_idx = first_idx + len - 1; //println!( // " subset before enlargment: {}", // dots[first_idx..last_idx+1].iter() // .map(i64::to_string) // .collect::>() // .join(",") //); // The optimal new position of the subset is symetric around // the mean of its dots but we must account for the available // space let mean = dots[first_idx..last_idx + 1] .iter() .map(|&d| d as f64) .sum::() / (len as f64); let optimal_width = (len - 1) as i64 * margin; let optimal_start = (mean - optimal_width as f64 / 2.0).round() as i64; let optimal_range = (optimal_start, optimal_start + optimal_width); let left_limit = if first_idx > 0 { dots[first_idx - 1] + margin } else { dots[0] }; let right_limit = if first_idx + len < l { dots[first_idx + len] - margin } else { dots[l - 1] }; let (o_left, o_right) = optimal_range; let (new_start, new_end) = if left_limit <= o_left && right_limit >= o_right { // we can enlarge the subset as optimal optimal_range } else if left_limit > o_left { // we're limited at left (left_limit, (left_limit + optimal_width).min(right_limit)) } else { // we're right limited ((right_limit - optimal_width).max(left_limit), right_limit) }; // now we move the dots to be equidistant in (new_start, new_end) let w = (new_end - new_start) as f64 / (len - 1) as f64; for i in 0..len { dots[first_idx + i] = (new_start as f64 + w * i as f64).round() as i64; } //println!( // " subset after enlargment: {}", // dots[first_idx..last_idx+1].iter() // .map(i64::to_string) // .collect::>() // .join(",") //); //println!( // " all dots after enlargment: {}", // dots.iter() // .map(i64::to_string) // .collect::>() // .join(",") //); } Some(dots) } #[cfg(test)] mod unoverlap_tests { use super::*; #[test] fn test_unoverlap_1() { assert_eq!( unoverlap(vec![0, 49, 51, 100], 10), Some(vec![0, 45, 55, 100]), ); } #[test] fn test_unoverlap_2() { assert_eq!( unoverlap(vec![0, 51, 52, 53, 100], 10), Some(vec![0, 42, 52, 62, 100]), ); } #[test] fn test_unoverlap_3() { assert_eq!(unoverlap(vec![0, 51, 52, 53, 100], 60), None,); } #[test] fn test_unoverlap_4() { assert_eq!( unoverlap(vec![0, 1, 2, 20, 51, 52, 53, 100], 10), Some(vec![0, 10, 20, 30, 42, 52, 62, 100]), ); } #[test] fn test_unoverlap_5() { assert_eq!( unoverlap(vec![0, 1, 2, 20, 26, 28, 51, 52, 53, 100], 10), Some(vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 100]), ); } #[test] fn test_unoverlap_6() { assert_eq!( unoverlap(vec![0, 1, 2, 20, 26, 28, 51, 52, 53, 99, 100], 10), Some(vec![0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]), ); } #[test] fn test_unoverlap_7() { assert_eq!( unoverlap(vec![0, 16, 17, 20, 86, 87, 99, 100], 10), Some(vec![0, 10, 20, 30, 70, 80, 90, 100]), ); } } csv2svg-0.2.2/src/visibility.rs000064400000000000000000000004601046102023000145460ustar 00000000000000#[derive(Debug, Clone, Copy)] pub enum Visibility { Visible, Faded, Invisible, } impl Visibility { pub fn css_class(self) -> &'static str { match self { Self::Visible => "std", Self::Faded => "fad", Self::Invisible => "inv", } } }