ufmt-0.2.0/.cargo_vcs_info.json0000644000000001360000000000100120060ustar { "git": { "sha1": "a2648999fa91fd88d36864e11180676e6ab35b36" }, "path_in_vcs": "" }ufmt-0.2.0/.github/bors.toml000064400000000000000000000000550072674642500140300ustar 00000000000000delete_merged_branches = true status = ["ci"]ufmt-0.2.0/.github/workflows/ci.yml000064400000000000000000000044330072674642500153450ustar 00000000000000on: pull_request: branches: [main] push: branches: [main, staging, trying] name: CI jobs: test: name: test runs-on: ubuntu-latest strategy: matrix: include: - target: x86_64-unknown-linux-gnu toolchain: stable test: true macros_test: true - target: i686-unknown-linux-musl toolchain: stable test: true # no panics - target: thumbv7m-none-eabi toolchain: stable no_panics: true # miri - target: x86_64-unknown-linux-gnu toolchain: nightly miri: true steps: - uses: actions/checkout@v3 - name: Set up toolchain run: | rustup default ${{ matrix.toolchain }} rustup target add ${{ matrix.target }} - name: Build ufmt run: | cargo check -p ufmt --target ${{ matrix.target }} - name: Build ufmt-utils if: ${{ matrix.only_build_ufmt }} run: | cargo check -p ufmt-utils --target ${{ matrix.target }} - name: Run tests if: ${{ matrix.test }} run: | cargo test --target ${{ matrix.target }} --features std - name: Run tests in macros crate if: ${{ matrix.macros_test }} run: | cd macros cargo test - name: Check absence of panicking branches if: ${{ matrix.no_panics }} run: | cd nopanic cargo build --target ${{ matrix.target }} --examples --release size $(find target/${{ matrix.target }}/release/examples -executable -type f ! -name '*-*' | sort) - name: Run tests within miri if: ${{ matrix.miri }} run: | rustup component add miri cargo miri test --features std clippy: name: clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run clippy (workspace) run: | cargo clippy - name: Run clippy (nopanic) run: | cd nopanic cargo clippy ci-success: name: ci if: github.event_name == 'push' && success() needs: - test - clippy runs-on: ubuntu-latest steps: - name: Mark the job as a success run: exit 0 ufmt-0.2.0/.gitignore000064400000000000000000000000410072674642500126110ustar 00000000000000**/*.rs.bk .#* Cargo.lock target ufmt-0.2.0/CHANGELOG.md000064400000000000000000000021210072674642500124330ustar 00000000000000# Change Log All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ## [v0.2.0] - 2022-08-10 ## Changed - [breaking-change] Minimum Supported Rust Version (MSRV) guarantee has been removed ## Fixed - fixed miri errors around use of `mem::uninitialized`; updated code to use `MaybeUninit` ## [v0.1.2] - 2022-08-10 ## Added - added support for hexadecimal formatting of integers using the `{:x}` format string ## [v0.1.1] - 2022-08-09 ### Fixed - fixed link in crate level documentation - fixed `uDebug` implementation for `i32` / `u32` on 16-bit platforms (like AVR and MSP430) ## v0.1.0 - 2019-11-17 Initial release [Unreleased]: https://github.com/japaric/ufmt/compare/ufmt-v0.2.0...HEAD [v0.2.0]: https://github.com/japaric/ufmt/compare/ufmt-v0.1.2...ufmt-v0.2.0 [v0.1.2]: https://github.com/japaric/ufmt/compare/ufmt-v0.1.1...ufmt-v0.1.2 [v0.1.1]: https://github.com/japaric/ufmt/compare/ufmt-v0.1.0...ufmt-v0.1.1 ufmt-0.2.0/Cargo.toml0000644000000021420000000000100100030ustar # 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 = "2021" name = "ufmt" version = "0.2.0" authors = ["Jorge Aparicio "] description = "A (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt`" documentation = "https://docs.rs/ufmt" readme = "README.md" keywords = [ "Debug", "Display", "Write", "format", ] categories = [ "embedded", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/japaric/ufmt" resolver = "2" [[test]] name = "vs-std-write" required-features = ["std"] [dependencies.ufmt-macros] version = "0.3.0" [dependencies.ufmt-write] version = "0.1.0" [features] std = ["ufmt-write/std"] ufmt-0.2.0/Cargo.toml.orig000064400000000000000000000015470072674642500135240ustar 00000000000000[package] authors = ["Jorge Aparicio "] categories = ["embedded", "no-std"] description = "A (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt`" documentation = "https://docs.rs/ufmt" edition = "2021" keywords = ["Debug", "Display", "Write", "format"] license = "MIT OR Apache-2.0" name = "ufmt" readme = "README.md" repository = "https://github.com/japaric/ufmt" version = "0.2.0" [dependencies] ufmt-macros = { path = "macros", version = "0.3.0" } ufmt-write = { path = "write", version = "0.1.0" } # NOTE do NOT add an `alloc` feature before the alloc crate can be used in # no-std BINARIES [features] # NOTE do NOT turn `std` into a default feature; this is a no-std first crate std = ["ufmt-write/std"] [[test]] name = "vs-std-write" required-features = ["std"] [workspace] members = [ "macros", "utils", "write", ] ufmt-0.2.0/LICENSE-APACHE000064400000000000000000000251370072674642500125620ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ufmt-0.2.0/LICENSE-MIT000064400000000000000000000020420072674642500122600ustar 00000000000000Copyright (c) 2019 Jorge Aparicio 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. ufmt-0.2.0/README.md000064400000000000000000000064300072674642500121100ustar 00000000000000# `μfmt` > A (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt` ![Call graph of formatting structs](cg.png) Call graph of a program that formats some structs (generated using [`cargo-call-stack`]). Source code can be found at the bottom of this file. The program was compiled with `-C opt-level=z`. [`cargo-call-stack`]: https://crates.io/crates/cargo-call-stack ## [API docs](https://docs.rs/ufmt) ## Design goals From highest priority to lowest priority - Optimized for binary size and speed (rather than for compilation time) - No dynamic dispatch in generated code - No panicking branches in generated code, when optimized - No recursion where possible ## Features - `Debug` and `Display`-like traits - `core::write!`-like macro - A generic `Formatter<'_, impl uWrite>` instead of a single `core::Formatter`; the `uWrite` trait has an associated error type so each writer can choose its error type. For example, the implementation for `std::String` uses `Infallible` as its error type. - `core::fmt::Formatter::debug_struct`-like API - `#[derive(uDebug)]` - Pretty formatting (`{:#?}`) for `uDebug` - Hexadecimal formatting (`{:x}`) of integer primitives (e.g. `i32`) # Minimum Supported Rust Version (MSRV) This crate does *not* have a Minimum Supported Rust Version (MSRV) and may make use of language features and API in the standard library available in the latest stable Rust version. In other words, changes in the Rust version requirement of this crate are not considered semver breaking change and may occur in patch version release. ## License All source code (including code snippets) is licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [https://www.apache.org/licenses/LICENSE-2.0][L1]) - MIT license ([LICENSE-MIT](LICENSE-MIT) or [https://opensource.org/licenses/MIT][L2]) [L1]: https://www.apache.org/licenses/LICENSE-2.0 [L2]: https://opensource.org/licenses/MIT at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions. ## Appendix ### Formatting structs (snippet) Full source code in [nopanic/examples/struct.rs](nopanic/examples/struct.rs). ``` rust // .. #[derive(Clone, Copy, uDebug)] struct Pair { x: i32, y: i32, } static X: AtomicI32 = AtomicI32::new(0); static Y: AtomicI32 = AtomicI32::new(0); #[exception] fn PendSV() { let x = X.load(Ordering::Relaxed); let y = Y.load(Ordering::Relaxed); uwrite!(&mut W, "{:?}", Braces {}).unwrap(); uwrite!(&mut W, "{:#?}", Braces {}).unwrap(); uwrite!(&mut W, "{:?}", Parens()).unwrap(); uwrite!(&mut W, "{:#?}", Parens()).unwrap(); uwrite!(&mut W, "{:?}", I32(x)).unwrap(); uwrite!(&mut W, "{:#?}", I32(x)).unwrap(); uwrite!(&mut W, "{:?}", Tuple(x, y)).unwrap(); uwrite!(&mut W, "{:#?}", Tuple(x, y)).unwrap(); let pair = Pair { x, y }; uwrite!(&mut W, "{:?}", pair).unwrap(); uwrite!(&mut W, "{:#?}", pair).unwrap(); let first = pair; let second = pair; uwrite!(&mut W, "{:?}", Nested { first, second }).unwrap(); uwrite!(&mut W, "{:#?}", Nested { first, second }).unwrap(); } // .. ``` ufmt-0.2.0/cg.png000064400000000000000000004331360072674642500117370ustar 00000000000000PNG  IHDRk ObKGD IDATxyXSw>;,aEPTl]Ff[۪]8vj۷t:m.SU*U"AMAv?DZEsNN`T;tvvv4:bW@DDDDDDDDDDDD0D#"""""""""""" C4""""""""""""]]v]3SNe (N """""#H.;wbٲebADDDD4(ىFDDDDD414! DDDDDwk݄!M݄!M݄! 9H$駟.)hDDDDDDtxxx@"@"2 Ç.GXv-<<<`bbOOO,_III1_}$ ~xl"""""}%ݻw^Cuu5̙-[]jL:| ͛? ,@NNEAOO~8v,]/'""""^'vDDDDDD45 `lذO<D|7tbcc1}ta;#cnϬYp!@~"##!J1u>z;шOBPiiiZ8pf͂9\]]zjks%H$:u {Y0bxxx`Ϟ= \]]aeeKqEEEnJGG-^t)ulss3:zHx"""""x=9###@KKo툈oߎ^xF{{{xױqF̛7۶mzn7c U}/^ ===߿_kcpu(;)QqDDDDDD _?<>3Ḱ0믿GPՈ)=RSS1w\@bb"݋up#Tv ,glڴ @pp0VZ%K`ĈZZXX`ƌؿ?>Sadd$lll?&""""";шOj`ӦM|r888N8z\R6^^^;vu#}?ZydoO j*bӦM:vҥ(**FRj߿K,(G""""A}D]]]wիK8… H$-//8T*H$3uss||(Jxyy᭷￯uŋ}PYYQDDDDD9}%COOpvv QF~G;}H$n4kMg..] _}֭['\oiiӧcHXYY!$$w?? ш5jϟc4D"_=:;;QRRѺׇ]P(ϣXd tuul"""""8ΑD7c 3}:::9s&`׮]Z:T*$%%aҤIݮӬŋHDDDD4Dg``??0OUVǏGTTaggw…?~<|McرطokjjT,X~~~C^^Kj| ӦM?=ӧO/D#"""""~ᡇBll,_GEEUX -66˖-g}W^y#F#G366Fbb"mۆ6|Wx 1 :;;9ʑhtvvv]D;wbٲebBDDDDDwEN4""""""""""""0D#"""""""""""" C4""""""""""""0D#"""""""""""" C4""""""""""""0D#"""""""""""" C4"""""";~888@__ttt`۶m?~< ggg]W\(ؾ}*~hDDDDDDD  .Dmmm/lذ?0"##n:ڵ > 駟db9s&^z%lܸ~IgggEQH$عs'-[&v)µk 0bl۶ oZXXX`ӦMذaYf ǍKKK:u hii \R5󁈈(ىFDDDDDD\v G 0zh_/^Dhh([oifLMM1al߾7v399zzzx'D";#F; .@VEk=Hw)\xO>$tuuXYYzL""""""꿞|IkƍsN꫘4i6oތ5kPSS')ك_|x嗅Ǻuyf>~!T*mٲE붏=jho#** /? /ֶ6X'&&uvpA<ðʕ+؈3g 99666±ѣGoߎeeeV"""""=hDDDDDDtKD{SSS@ss-o+H憓'Ov;v쀿?/_{⧟~]jhhmt퍋/"** 4i0vXj7~ p1^*JDDDDDtD#"""""^UV &&&H$سg-K/{bΜ9XdIV{&&&HKKŋ DP!$QFm6B.{C4""""""g Dnn.n݊ v-f055ɓ'q{[RR=xwPQQ066ϟǏcȑذapfcvv6>#k@*z8Α?O444`Ϟ=66l@II 'xN}S__}-wqq=`aasssAJJCXX<<<gΜAGG{DDDDDtk шC"k@kk+ 'N`֭ '@P?īzW5Gs ވEnn.܄HKK/$$...Į];v쀡!y>q#ݳŋ>,k.̞=Allpl]]|I,]x뭷%Yگcǎرc())Z۰a1{l|Ljׯc˖-±7oD"G}={`ڵϱn:FDDDD$2vsMMM(++Cii)jjj}eee(..Ƈz۷o֭[www~?eXtΰqg!Jannsssrakk T KKK߶~2ѐF'jbqq1Z[[nodd-d0666rtmٲ dpvvL&L&إ[ шhjnnFuuo#Z ź_2[fnn{{{1B%5CCC$$$~@ee_* &\|ZhaFDDDDDFss3PQQJTUU 娪XCC #ၐ=lzϚ5k5ZZZPRRJ8p~:_텐ₑ#GyJDDDDDC4"""""Zaحz666B1M@5 HgJ aXOjjjl* 111(((55]7o6cc<-""""^zUSSoM\B{{͵$zyy իxױ~C.cXdI_#v]r* B_EEEBhnff& \\\''n?"""""10D#""""ZZZp[vu\QQZ-Ht7[-a`` ҝ.6LJ@W>##nQuu5QWWիWhnnrf|PTT-d;y$\z GGG!Ty355""""!! ؈r+Ls eeeDee%PWWu[SSSHRa-1GGGL4 ƤR)&K#Gă>mٲHHH7`ҥN__]l͸tҥK8z1DDDDc=/v]i:4a9sf1;;;9R3ޒ'OԩS}x_~%kddd ƍ{>1118~8^ SSSCpp0BBB`hhx_Ggwa#~iݻW+o\zpqq;3۲e=Iֆ+V{ rrr`kk+&M)**p#ٳgfffOf󃟟_4[O]lkdsqqa.6zxJa(E\Yfi1ꦽ鈋*0eʔ>۷mmmXrGy< x'4H$ɓwahh(*Gyy9&L#33>(,Y ў~iXln:<쳐}m8.`kiiAIIi:ٔJ>-"""" шhinnFuumñ^B&J!ɴB1:c" Dmmm8wbbb'O֘11SL\pAgcc:ͽZz5>c˪յu(..>|}_Ɵ&T7N&"""!{@qq1***Dk+5R)lll`ee;;;XZZPijIIIBٙ3g L lݺgφإ&MZ]6ͨ¼y}Nhhh={`mm-]wGRޫj 5Q.]bwX=駟/ r9-ZEaҤI.$ nw]M3"ȑ#OgCCC ku?"""~!źT,((@CCpٳTJ|DSYY$a=4jd2gAhh(.abO͛^{ ?VWj,^Xgcc`|x뭷`oo/\ب.Z}}=$ LMM}(,,Zo{G/ӧk@aaoo;v`ܹӧOӦMǟ4i-[$$$ ::w͛aii9s@Pݝ$ æ˩ 4JBLL rss6###dnڭFOzoceee(**B[[p1OOOPg8u0155:::pssCpp0֭[3f f̘O?or9o {۶m ''7oٳu۶mChh(zj"33;ve˖/^>,V^B|gDMM bcc{zǏٳpBСCoꫯ]8VWWׯի1l0̟?ظq#&O?T.{FTT.\#G",, XhQ9]*`>P2rHt 4oQt@{""""7±}%(JKK.8deeAOO>>> Bpp0`nn.J};wXv-"## 777|]| ;dggC&!""o6gaa!^u$'' Xl֬Y5N?֭[Q\\ www 22_-<<˖-c@#Jx IDATpMegg ݘ=vyyyq 6"""OЀBB ZQQ5Rmᰳ5}]KߨT*ȑ#|!((ӦMÈ#.h4ի8pqA466xv+߫[R&=lr."""~!`VQQQ Xqquuum akk ;;; ɺeD4hF!66EEE011 .`]jQW|hjjBLL JrDDD <<AAAO[<rxzzbܸqIDDDCC4"""Rzhkk LvˑNNN̈VGGrrrǏիWajjal`Ms zzz"}?i1J_ `aac( ir"HOO3=zĔ)S L2e Ѩ|̄RDtt4RRR0j(̚5 Xx1I_kk+T*򴶜TTTرcWWWnnn zC4""""RibQQ˵akk {{{HR8::B*Ngcc#YoMMMHII:f'N4#QW}|X!,, X`$]/jueee!77ׯ_=kA@7o*Avejx͛LLj#D<# BY\\!Ĵ Mhv3hԕχ+WRѣGގDDD`ɒ%7n\B}Fպl]JZc!59N!hDDDDw(,,Dqq1.\X?DWWWspp={{{ښoz$%%!&&qqq8s Z[[!Ʉ.ٳgYR C4JCcc#;R(B.#"" 6ڊbMR!##Cihh!Tӄl>>> JDDDbaFDDDhASYAA͵ƺvd28::BOOO3"*++$gLKKZL&BCC(v;4?=:::R~ %%%prr‚ P9ho233 ]]4;tuuE>"""tp .\@]]p-1T '''xFDCKYY񌩩с0qƌ=zإ)4!Cff&J%J%`aay!""s΅%HQXXcNhHOOOHRO>hDDD4577Zc[,**B}}po;bq̘1066񌈨T2CVV#g إ[7Fq}voCT*T*CCC̚5 Xh.Z\tIk-++ 999ͺ7l6lg@DDDC4"""ߚQZZ*Irs7Yׅ agg׭sDԿT*ȑ#|21~xahDDDtkmmmA ._,ѣGw ӄe\ĝhjllDjji& $$D48q"Lb>s^1111cz |0~xrT*T*qic̙P(Xp!;ک״P+XST@EE$""$ uUUUtR1(--EGGXX'''HRv  HHH@\\q)@* ]fAAA ͆,L< ׿]NiooǴiPWW3gX쒆" &'֭[.pctLL zr( ,[ r\ihiiŋo;R*v Éh0y][aa!t_===τVYY$ahݻyyyĜ9sP(@2i+--ֹv9TVVF kMٙa0Q`FDD_477n,455`kkcP&˹ Viie,GsssKJx)J''':tohFTTN>aÆaƌP(Xh.iB5W033øqBj:tuuEh@aFDDԗˑ'Ium*D"d2dBDJ>cxӧa'XhҐ_1uT{xW.~>|QQQ8|0tuup,[݅/i&W磳;vVךL& .?bFDDnyz `ooc7;LLLD> "T*ev BYpp0.//bcc,v9;#/]Ftt4qA466Xb.p…n[NNj5\???vhDDDwVL?e]?ς={V4;~8^Çcʔ)B??~<^x{bj̞=HMM.ASSbbb}2 P( N0#jP022<<<\///8;;   шnyyyE^^.\ |Ռ^ׇ3ƍWWW7ƍرcDԫۑ.tšVVV3?':;;1g!55PRR,Y_|P/@BBO?ŋpttĜ9s9s@___2~V\pAk$dεBrxxx""l#''.:::prr>"x >>MMMAHH0qĉ| Ql۶ N>ɓ']Nw^,Y?#/_.v9tdffBT"::)))077GXXñxbv!Ҁp hDD44Hxx9TVV "\ԼeDW_DaC_'OD``H:::R={cƌ… ӧC___2&\ӼKIIAzz:_JFBr\9 b шvΟ? ;w8<\9r$<<>>򂷷7r9 D:::pYرc1e!((+Wĉdwe/Zf kbܸqbC\cc#;RڵkP(%KwZw"WNDD"cFDDOCCΝ;'fiii8>>?~<õmhDDtk׮]CFFRRR-77033֧&O CCC&"#HMM3ǣ 666 3N8DrDڊ & {A)++ 'OƟ'lܸQrhRHKKCTTvڅl=s΅B<ӉP[[FB9sntuX􄗗 N шj9sHIIAZZ ń  ___.LDב(g}Ƙ9s& ,X#G]"рu[ss31n8pmҤIJbMDD!PԄ4!4;s .\prrɓ1qD!4b"߯IIIxdA& ]f<.*(('z-[NrE!%%gϞ QYYCATȑ#@@@ .] ;;;K$4]k]ZʂJkui.\9!hDD]GGrrrF2hiiF2#(( ̈h@rr01-- j2L21c.hJٳgVW&O~IrhALL o>A.CP`ٲebH4(TVV k;wDKK &Yo^첉;hDD͕+Wpi#)) )))~:1qDL<ݵ2 SSS777a<̙3AtكKĉ>}  f͚x.f c BSBGGG2vjueff";;1b8Qht7srrrSL3ooo\-+--␕===f!!!\煨455ӦMÿor֭[> +.Z4DEEDnn.,--1g( <000LAvYdff„ #l#Fl"!@ͨ N<Md?3fѣ.螨T*!0{7l0 fAAA\S>_! !!!hkkCBB sFTTN>aÆaƌP(Xh.hPkooGnnZkɨHnrH$+'"tgmmmHLLD\~ BI&HrZ 66UUU011A``]HHGg%%%psso 6]ΐu%gŖ-[.B:tQQQ8|0ttt"}dNwJKK@V83hDDIgg'222p1ɓ~:1m4Md\ h@ٳg̎;j >SLفAq#++o쫯O?h̛7OrP]]hDGGhll/ñb %A}}=򴂵4466BOOZZ@@,--.h`FD$2!&&?3D eݓv O:k׮ xF___~HHDIII ݻd!<;sZ_]]7x۷o.ESSbbb}2LP sH$==u߹>.\PܼyBԮ]۷`kk kkkhjjJHJD#"*wU|Cȑ#Ɂ{!JٳgVL,ŠY[:*b֭FLL ڷo/uz͉'н{w|HIIŋ ̝;_}",, A:u777xzzf͚RG$S\x.\q9\|^ڵk4֦MhkkKpH]bcc={ ** իWG=}ARG$"* 9sFqٙ3g+XXX(2ٳ'̤JD+j ;wƆ C>}:\Xncc .Hh7vڅ=zZZZҥ 0x`~."̻ZLL .\ϟ+]g-zzzR&"RJNN9]vaϞ=HLLDFf=zfDTi=xgΜQLxyrXXX(2ѣ7n,uT"*???|gz*,,,Co èQd2޽ J=z~ ػw/^| '''͛7:""77oV\c-&&HMM&{^zR&"*+Ull,~W!99Z;x1m"pIΝڷo8ӬG`LT<VVV:t(VX!uzMFF&M7B& ĨQ$HHTz/_={ --M%޽,XZZ!5)))I錵g"99`bbtƚ#%NLDT"D#"*wbFll,5kÇc6UJ "!!ZZZQiֵkWԮ][Db .IJeKߓ'OСCܼy455ѷo_S2"ũSpܽ{M6G}777ի`Z|9k[ Rsr"*Hڕ+Wg]zB3xd"8FDT\/_DDDo>| yU* ;v QlmmѥKYեJDj KKK̚5 ͓:!22Æ ÃիB׫^:ҠS'66aaa@LL ֭ WWWw/NNN8}4?~1ȃDV/_חi;v,#G 55zzzprrRLصkW^3sLlڴ E&ddd`̙Xv-444 \?K9#R[na éS gggCij(^'ZZZ@xx8t!qyk׮A.A5{{{^A4">>qrss!ɰm6߿௿RLtԯ_mmmYˈHa BBBjԨ!u*`Ĉ8p@3LMM(Q2򗑑{bΝSԩSJhF@@9-ӧO_) t5{{{tرLxɓѾ}{Y*|$DT q(ɓ'1sL9s}_~;J/__e˖ACC9999O?ʕ+xΝSLx dff]tQLhgg5Q=zsss|嗘5kqXf M\мys I#++  TfffG۶m1!ZFF.^X5'''k#Fƍ!ɐqaѢE[UPD#"JLL_|P8;;cҥ:C"##1b$&&*^ײeK,[ ǏqY ݺuᅬnݺ̬Q4o<ϸuj֬)u*WbȐ!Enn.d|gRG#Dhh( ҂;L6QyHKK˗֮\:w\%kkkzX`L™==D#w/3f˗/G߾}DDt̛7~~~(2 BXXX(2裏дirNLDUAzz:0k,?:Avv6+|w֭;&u,"I 6 ۶mwd2 6 ?3՜r}={V1vY$''w`-o6f͚ĤxLѱIhDnJKKȑ#{n̘1| UV}XN鈨 ޽{1zh۷3fԑ԰aCwÇ$%%!99Y鿷oFjj*bbbKŋ : Q$''+~ЀrbX  [9,,,퍾}{l"\8FDUZll,뇗/_ptt:Q`dd6mH+WGh߾Q*?PV-9Ѵiڵkwd"*'D#*0`gUqqq8pv%u""" K.HOOdDFDURXX\]]p:y$d2o.uJ͛d:Jfozzz066FϞ=q-cIfŊD>}R!B&)~ݥboS06D kU*s]6HeX# VYkшYj q!44իW:[}d022BVVm͚5\nYӱ{nܹs.d2TV VVVpwwÇVZ ƍC׮]~z̚5 hܸqUfشiL СCJW:n[lqA4lP*۔6ղeKſeXXX:kUaRG#Y=>}aÆ022 9RCCCѹsgԪU G޽S`(?Fp:G"2rss1uT`ժU%K_~Xh:uӧaggZF_ekDDsxzz_~͛+ZPP} (={b2e 9YfaΝV\ 66[l!CgM޿ꫯ)qR޽{•5֭ÀPN5j+~ Aח7iDT6F5 uօ+ &ĉԩ|}}"AZR5*c*ꔎ<<<`ccXV~}ź  СC033… uaǎ>ݻwGZ_*/낈"|2Μ9ѣGKm}Pq,oHuG}2=d_mەhjjbˋ}yuvvUVFf8Zޙhٲ%mSu$σ2VVU5ꓴѹsgeժUm1j(ehٲe$ֈFO{h׮hڴ~Z@~ϟ?B[[[XZZ}[gz"==]>Lq>|~~~B&?Xl޼YQF]vիWJWٳg[~Ix{{&M z8qtRq q \ B&iӦ[ӧ5jb۶m%~N|bر_~ ~ȷnqի8q"c[| ._o;qbYq߼ VVVQP۷o*ovvpppbӦM"::Z,]T4lP8qˢk׮:t[k׮ 6lX˛=X͟:u(KKK_@ ???1sL!RRR%]MHH`EDi\z^^^˫T-NzaÆo/{Y޺Juu<(pVVVͭȜ%J {<)K}P[Zh!to vڥXVhnncƌVVV_~:::bȑA888WI^]t666bݺub֭Ez"99Yi%yԕJkەx_>)::KEy033Zqٹ/kkDR#(xM(Qu}ѦMann.n޼)fffBGGG̛7O|޴srrѣEzDff" ZrrY8q6.]$444D``bYVVFʷϬd8u fϞ5k<&\.Zn-씖y(U5f3gđ#G%{~2[dgg+-Um۶? 6,9b߿r\W4lP߿?vbnC!{=S7 b޽eab赪"O* ӧEHH֭)ILLIUX#X#*zA4NHD۷ѵkW 22RGgΝpss.^ ZjE'oJG)NזdJ9rO>'|M6ɓ'tttдiSܹׯGFFmU y}s-[zdFbbru=7't^?FI@ylڴ迍A`` zJܸqЩS'|y?ׯ_הر5j۷8uq$CBBd26mZҥK5jLV<w}:u(߸ 2 7FRRR2iF)K7oBKK &&&E޿f͚077^|Y DDT6ٳ'4iR.MT^addx. {±*M9qd2455Ѹqc,^'Oƺu+ku#GT!GaU'X ZEQ}RZZ222n:,XnnnE|}}qIFe7kDX#*'-իW₆ b߾}o&  ̞={ˋ}qqaҤIvy֭ غu+ݷVZJkgϞEll,v܉uaΝ@xx844J}dR(ΝCn`ccUVyØ1co}u<ƒ #ˡU=zSN:u*֬Yf͚شi&M]]B{L89s&*^ZZ85kHAA.CSSTUۼ{*ԟVZテ6lsss3F_w x}9{Iu ֪c)>f͚puu|g8sbҥذazQ}FkDA4"4bccѽ{wj Yԑ$0vX}F 6য়~B-7f͚|ިe2틝uhݺ5`ԩXz5Ξ= GG?dhԨwf a IDATJF[f ?p+m U>y Ǐ#22͚5S)Pڿr9ܹRǚ5k`ggoooܿfff??ǯ '''?^-)""*_;vL&$/KTeyÇK}HaoS46T\?uօk-kmUS^:ZU4**uIoʛ)999m6lĉrJ F5rtDT)ܸq~!Zh{VcӦMؿ?accEk``#(((߷?4m+V@nnmO<ɷ[n).א˛BuyS <||%ɐw*44Ti} ~q<}2Li1;;2%#[F=RZcǎb_9sɓ'9sbyI@Ƿ{n#H4}PQJޛ-۷`XZZbر{=$''cժU044&6m:(M=3g +WTaEk޽{شi-Z$u}.*4:t(ߵ|T>0y]r%RRRгgO$&&bѢEh޼9Z|V^sUV믿0o<ҼO{6]mTUJ87.\@ڵacco /uu *֪ԪԫWpa\\ &MѣG1a8::իWǮ]tReaܸq߿?W^2kkDy*!*damm-ڶm+RSS%@d[S}HKKSZ޶m[@ ><6N<)z-EرcG6m$#4i"U&,--Ō3Ç wM) xľ}JbQFqbĉ֭[ضm[9_X[[ ===aoo/V^-^x!\]]<_%:w,E۶mW_}%RSS=1)6m*#;V趋 b֭G aÆRB֭[ ---Ev\9sFiݤ<}B.M}Ϟ=Tx{{{˫vZ!Ŀ_cp =}]%$$D""u[nWWu4^wQ@lذu^W,Xx411C-ӧb̘1P {{{q|* 6m R{UuoӢE ѥKbݿgϞ))3mI^WVZxݼN]u VxfwR˗/ŲeD=Dzpvv{ >︸X#X# RkE QA(55ݻwǫWp14h@<2 !!!4h9?cǎL&͛7T鄆bemzjժ{ʚ8p Ln޼ +++^&M:U@mN"Ub*;u"kjq&D# )==zӧOH>F6mZ9kժgggt <(ӵH=222p!xyyIBaoCDk5B&U8/_D>}ǏK6.ѻś.:u… _1m4hjjJpArK(D {" X( U(6lp IgASS+~<}Tq?:"#ի'u UUDTip*3f`߾}?ТE 4440c ̘1C(DDTرcQ!XZZކ**"* k48FDŋPtE8DDDDŋq[(DDDDDDUшBزe Ν+WbR!"""t~wN(DDDDDDU9#G/ԩSCT޽.tPtjBѻwoDҥKLDDѻwohhcUn=abb}}}8::"<<H^ A #!p3gѽ{wԪU _~}-ZN:^zG!zgΜ|||@&!((H폯$ X:066F˖-[}6oތGaҤI*LDD:~8ajj*u*!@;>>Gxx8&MlL>]MGGG̞=-ȑ#ѽ{w. իWm~yamm]mWlmm˴""8"##ѵkWcPTp]e˖^Һ֨U֭[777ԨQqFԩSz*HRyH5XgT /B&J2IKKĒ%KB_矅B8qBхnC.֭[ ;;|eeemۊ-[l1|pahh(xQ<2 HLLwp <7nD@@@΄ՅП;zۥ%̙3ѨQ#L2%?#ׯ[ٳgHMMEnnnyG%""y&Z%)ƌ'"-- III~&Mжm[cϞ=Xv-R?6"R~EZ3*Q}6 WW|ƥK0j(X[[COO2 ᅮokkiӦaǎիT'ODpp>d޽;:$ z'Uպ&~"NU3*>QDѠACCܹsprrB\\VZK.!99keffb޽_ YYYd>}L/@,]k֬A=XZZСC8t3n""?Dǎ!ɤB*@ 4(-C&MXQzuhhhFڵ[HD%RyH:Uΰ@Dӧƍ?dŚ5ksXZjӧDFFzPW[[7n,vKKRm46l؀'bʕ^$#Gm„ 7ԚTܹs㏥A*@7o;2-- :tP~uԫWuQZO__666)U"*\raSqn֭ DXXCUӧO!ɔeX|GUVZf*U x{{1ڵk駟" G.t-[`˖-J˶o///\p!߷KOOGBB줎B*@>7n [<** )))֭bY۶mqQ)}x1Ο?[[[#UQEP Z]~ƍ1`P !Əh?DJJ +B`ؾ}{?,Iܹs_>5j$u*'U4ijԨ͛7c̘10447.\MMMz&&&8<,Xܾ} 6DNk׮|!T[DCU3N&R qL&!$$ : UR?޽{նԶ"]XH}<Nj*"ׄq:G"R@lܸ7nhDDDDŋy""""""5 իW1c ̙3G邘DDDD:qZj%u"""""*hDR^ZlKʺu233ѢE UIyDT%̝;W\s砭-u"""*ڵkkkkUMD#"Ċ+͛KJv5jZjIJtDiii>|81zhP%`jj Lqww:R*[^""8cWʖJ+[^"Ge{VT8FD*駟"++ RGJb˖-8x < J*[^ EΝQV-ԯ_{FLLLILL-.]TN)8cWʖ`CT5^n055}e.=zpttDxxx9$Rڭly?9Q }P~}P%ѥKרQC$Sbɒ%ׯ-ZGNpix?šQFJ!!!RǠ"TeCT25^1uTl۶ Ec899!##'OF-pdffSZ"lʖO IRRN)SgϞR!f͚aԨQe%,X;wOff&֮]aÆnݺJիWHNNFӦMB$;DTM6000@DDv܉ 6eːSNm۶kDDEaS8#ɤIPN|RGwڵkksAvvv릦bܸqhݺ5j֬ ,]YYYJ-Z:uBzBxHjcooP1Zlyf>>Gxx8&MlL>]K.#,,]\\P^=/99^NNNUH=hU `CI%Vx^޽lիW/վ*+`F߿/֭+L"ur@HJnnnޞ 㕖]V۷oWZޯ_?+㕖?Yrhݺ+uޒ V~"&&FK,w[dd 6m$ҥKP\tIe'!BBB/"R +r%ԾHG}X_ƌ# -88X۷WVZb͚5*?Ksxp5*"ׄr:G"*I&VZ XӮ]+|'Jˇ  ٳGعs' KKKsXLkkk7MWWBB:v쨒!0sL4jSLw?+RSSjݾ}M4Qf.Unwʎ&ם;wcƌĉ$xxx`񈊊R{ш6oތ;v 008RHHH4mTiyadd7o*]v кubmҥK5jLp%8|}}qIk.qAGGK)bQ1%%%QFR bSvwT5Ir9`;v, `bb~ ::: 8!Q`);?A4"*L6 &L~(uzG\o455ߺs qqqXj.]bc YYYd>} ҥKf#Ɂ%:C!::?s[DDσРAc );DŚ^y666JФIũ=QEZS6ʇr9s&jԨKJ!ߺÇѧOŲ͛@>tYϟ?Gxx8˫Uo7zSԆ 0qD\>>>suȑ#6aXYYƍeADD888HTN!R=$ܼySIKKCԞ"`)=?凃hDTlǎæMcԬYS8TŋާO|ؼyҜ[l\.bY ХKlڴ _~%7nŋJ?}2LiZl$&&97򖄆U7]~)0zB۲e l٢l… };t@*>;w*$ԤzѸqcwފQQQHIIAn$LG:5ϗ:U|pww.\(uI|8p`^⋋CXX7cǎ"%%~~~xD7(mXn2337抿}66l(L2IIIHNNFaffVAPPƌOOO"66VԴo`]z0a SXl۶ l͛N  4H驻>(_ IDATkz={W^ŭ[~ǣK.u^|ຮ.-[Dȑ#8q"Zj???hh*<9<^T8cEADT _QHHH:dcTIO>cƌB___ۋ/[oժUCB___kN̝;Wdgg۷o oooѬY3/w}'233BOOOۋիW/^WWW12BRRRmq…rJn /"*/^b?///U.驻>kztҥК4||/EԮ][XYY3fgϞIjS/*kQ"ׄʄxmb5kq$#oAQbox2QQ޽ SSSDEEI8p !"ʣCD9<^DTq&[M6 VVV>}Qyiii:uHjӒ:Ul[nž} mmm㔻{o&u""""@zz:@$DDDDDDUшPϞ=QFޓ:NATTb˨Y&z)u4""""A4"""""" jx9-Z$uz>={ ""O<\\\h"dRG%"""Bzz:QF UiD#%$$`ŊXd r AcǎA.SN={6ѪU+#(==gQ>scĉRGQ/_ɓسgv܉D{Xv-<<|;vmmmk.ڵ GEvv61j(ޞS4QKTVMDDDDtDEEԩSRG!"5 )^zInݺpرNBѫW/O>066:"QdeeAWWWDDDD[nɓ‰' \f͚IԌhD䧟~B\\m&ub˻YXXPvm`߿?HDDD2YYY<HMrssq5s|055,GDUшH!-- Ǵiмys)66aaa իWahh޽{c裏<V˶#33D۶mQԺ}"R$>}NBtt4bbb ԫWNNN?~<:w:E&/"8FD /L&ܹs\.GTT"""}vǣiӦ?t?JGL@@6lo߾JG*PfΜɺuHHH&--GPPGGGuW>p˖-c֭3e'Eʊ/+W*GDrZ3>CZl PbExjk2ydTªUܹұ ,JE58q"SNU:x7~RDB0tP[[[lll055oܹs9 .0adNꫯJGFhB[ٰa"oT*<ȯΝ;y!m۶gϞtޝڵk{&rv/aÆQ^=#wh3gse֬Y)I!rrr?G(ޞ,G}t]vTR???7Ipp0AAA?~ҥKӾ}{ڵ+˗W:btQfϞMhh(666L:yC=z46mӘ(XRTDGGsΜ9ɓ' ;;5j`mmskѢKV:rf͚666^ZZ;@w΍7Qqۓ"~:ΝC[[;6m̠͛A_~MV3uT,Y+K,Eď?:t(W1BQa``ڵkR9s{{{4h@`` UVU:RqF΍7j(ٻw/AAA۷J5CZMHH̙30g:tt4OHMMԩSR׮]9E0=zDRe˖X[[Ӻukԩt"#((QF;_Rzz:UTaٲe >\8IMȑ#|ݻ7_f#EFFǖ-[|2&&&ݛ/BVPߟ?oooiWQڵ~ѩS'n*/k׮fffZJ(Çqtt???өY&g.0sŻIIIaȃd+dee믿pBbccg֬YXYY)M7o[lQ:N"""r昝9s۷o?זJѱ$EU\\Gf߾} 0e˖QrecZ=z ##'E4!3FC6mgyvxvŦM8wtggglmmݸq'''?V:#Nёz$+ƆK*ڳg={SNʛwoa8q\ɓ'ߟubgg;wFOOO.##___͛G||<}e k׮ 6L81 #::FC5ren}}}#iYYY,[ 777Xz5mڴQ:VqFFARR.|&DqOEz߉ǩP9 C*8y$ݺujժbhht$ҥ *ݻwP!(ZhAǎYpQ@CϞ=ٰaB~K'NM6={KKK}:|Zu*ȱ#E4!+9}6͛7Ғb}'55Z2ĠV)Wfff1355-v?HÄ (Y$+Vyc֭\rE(HMHVӼys>#|}}>ذa>>>ܾ}v1dwN2er9*1cưvZ~gt$ J+^^^\ѣG+I!iڴ)̟?_(ņ7#F`̘1C8۷100`ƍ|J x G%((;vHzpttёO>w2v˗/ۛeeR1vСv'NT:Ns?>g˗h4TV-0+.^ȑ#9s Fb-[VXEڱcǰ%&&Fg.RD8ڶm… 4i䍿ÁU믿AyXLH@@[lgϞJGʕ+?~<,_\v& !D`nnS:Ji& 3gT:NDzz:S:PZ9T8::ҹs˗Xnkfܸq >\vU .\̙3 m۶Jy/j8.\-115kbaa\v %--ŋN&MXf -ZP:VRZ*3gdܸqJoNhB7*&Mмys6oǫj?[lARGGG'RczɱcرґDðaڵ+>>>|JGBbI&у9s(o߾|7,]T8Eʮ]ٳ'W\+9W\!((`Otuusݛ5j(+<̞=[(E־}ڵ+ ---#I&Mߟ8Y /Szz:'88@nݺEqppٙ6m꿣GƁaԩ888x#.\M6L2EE/)))DDD//߿@ڵ133Yf9Fq}իWӓ>LXZBBukN8HMd֭ 4h5j0_ȠSN 4ݻKk"*!!O>e˲w^Z'11?۷o"B&&&|{'>|;;;ڵk <o^8QT8q`rtGGGUՄ0o<Μ9 s̡Cy(DQ鉫+!!!t9cTT9^F\r4l0cƍ177HEܱcpqqҥKL:ӧSdIc ^z C8::Dj^YYY,\X5kVVVDQ5h BBB /111H`JBOOFѸq fՓ!={6VO>cccc:t(qqq:tH(HM" ݻ]g :M*Q۷oӾ}{j5 ,33C닧'_ґ022b|JG)R޽K6mX"tJG*~gƏObb\ݻٳ???ёnݺ=7<33m۶1o<۷/ӧOϗYkxx1-[lٲ>|%J<,22ϓ@5rd4n333iT1fYh R:x  FJJ\oRD8>-ZΝ;bii߿;ӝ;wh߾=Q7FÜ9s;w.f͒VBO5jĐ!C1cQ4>SݻǥU>z5kdٲe1B8IKK155J?wޥO>̜9BXꫯX|s_uQQQ,믿HIIVZ9033ԔRJ)PDu%FEhh(gŊTXQXUv ;{6lƍddd䄛͚5S:gwޥCdeeIM---000`ȑ$$$?t4!(4eE*~'4+W=z~z)\WtiqttDR~QT*U GGGSqEeddĂ ;v,ɔ(Q(se+V333틙M4B Yzz:-b…p1%C͚5iذ!"Z!!E4!T6mڄXYYQ^=jԨ]'LǎIOO?f͚JGEW_}E*U/q-[VXBQdI-w;}qم!CСC"""hҤqDt<==YbI&ѵkW9BPPNNN*UO?gggv*E[&!!+Wӆ1<W8(l< 7n`Μ9L4 c7Ծ}{VH;G!<<<شi׏QF5o^"=y>ׯsaY])ԙ3gpppz޽Zj)I!3bNtBoٲeL2;wҵkW[Fѽ{w~"$>>e˖Mٲeqqqa+W b߾}T9j=zxNVVׯ_PETTәe df}GtޝLN<);uSL<==100P:xK6lŅD&DaV eʕP^=ΰaèTݻw֭[8qB"i4Hpp0GU"_\r{{{233ٽ{7JGB"АѣG3ydjSN3i${sÃ7nHkhXx1ׯf͚?ÇOPP?"QՊy IDATj֮]ɓTV^Xbll̩ShٲqIMlذ+VO>|8=zxnK066Ϗ=z(X(aҤIO޽[z,|QQQڵm*I!u*puZh xp ٱcݺuS:(Yt)[nА)S0dȐw.fggsIعs'7nܠnݺ (RRR^hŵkPa``ߤ0ڵ+k׮eСyDQsFɹspqqa)SFX=h4T¬Yo#^OhB&\-[PdI ¨Q^ZŅ{r%\ϸqFtQ =yƍӧґȨScǎeĉJG)h߾=III:u?PHtԉRJtQ=zE9&L:822??? B t^h) FC|||β؜e+W>SSSLLL066ԔzS?rq,,,rEc͛ǒ%KhӦ WqJޞ˳uVד"FaϞ=XG1vX+/99:uh"FBBBprrbL6M8ST;OOO.\Ȕ)S$EǏg„ JG)ζm8u&&&J믿2h QqD!pQ̙aԩ888k׮o>ػw/VZL^d>BIHHMlYxx8=B 9=^z39;;:ٳg+^ѣygmmmc\4w\6l+W"^OhBTxb"##ܹsYr% -Ӵoߞfʕ+0acƌar/VZLB?cǎ,YDsYYYxzzRV-<==e}*VΝ;qrrR:x5) QPdoNŊ5j#G|&޸rUiRLj:wիW(?N׮]iԨAAAT\YHBQ(ըQӧ򷐑A-T+;{,VVV?~֭[+Gl۶GϞ=qssThIVsyrUX:@)[1 'OMdd$DFFr54 K33 ,`Μ9bkkt#G•+W2e 3f̠DJgggΝtjRDBi,^}ann۷/%K|h4amm/iEA5c /_αch޼qx(tB%سg 6T:B:իWoU(ƤIŋԭ[W8?4k֌-[tG/?p]̙3iԨٕ+Wr j'35kT:bߙeJBOOF=׆2 FC ܹsҺINNfx{{cooϪU044T:G}%==W"Jxɓ:Y?݋/U HQxеkWꫯ#y&$&&,3"-UT 0rH G]v]/R8 X3grMʔ)t>|իYx1 :)SPV-媤$BBBfϞ=aaa{.;^GR\'OK:u+5n33w^T\`iiIٻwo-4 >>>L8%JΠA%ŵkה"^MhB䧌 |}}qwwޞokk\;#?&444S\ 4oޜΝ;yf=zD޽9|0|JGBBC__)bnnNfصkqJJJVZxyyɛȝ;wdŊh4\\\8::Ү]ݲ011BIKK!~V(saYΜ9C۶m>}:gV:#5Ç3j(͛LJ~t,IJJB J/'E4!CJJ <|3iҤ\ocO ؾ};=zOFFdddpIJ.t$!Jvv6FbݺuZJvT!eW(ޗ_~ݻZjJoW^$%%q%&&ԩSL&r[||<˖-ۛe¸q(_9jPret那#]tQl{ŋDDDAtt4?SSShܸU;v,wjYHxb133c͚5XYY)K(ƍp!>c㈗"yݻxxxrJj5_~e4i۷oʕ+z| 7n̙3)Gwh3gsaԩw[[!(233)Y$ݺuS:N@nk׮JoHf߾}jNŋY~=5kd >\ .5???N8Aɒ%Сtڕի9ŋ9E/@ժU177I&|,[lg)ݻ9wss<:t(>P0x[r}3f9Tٳ3fQIMp ~0a...yzANڵ4iӧOϳa߾}tܙ-[_(GaN~FOO﹯'&&2zheX{!ʕcϞ=tY8֣G011ClذA8ӽ{wݻǣG!;;TV]x%KuV quueȑrۺ{.{Ϗ?lqtt cc㷾ϔ"## ix9VσG'yZ?СC(Q 60rH222vDbb"ӦMV^Mڵ% O? trRD"7]v˗Er?~|H֭LJ(T!sUi׮q SMll,UTQ:xC<חkrzḤ$*V@B>;… I&L8nwƁf׮]ܹs'g3666uxP\|.^ȭ[VZ &M`nn DUV 6T֭[:::׏7*X۲e ׳`̙TZ`ܸq8qSN)Eф P[ڵk3n8F6m- @hh(bȶ(^.^=+VdTV ;;;=Jvv6ժUʕ+GQl]~:up S ]t&Ml2Ftñ&33FުYf>ѣھ-"88NE*'NoM 066bŊ$''GĄf͚annNӦMiڴ,-/_΢EHJJى PL(Q銯K2i$KN^s1bL0777i)^k͚5L6+EC=\xggg8z(ׯҥK;6_A=z/2z|;Pݻٲe ^^^R@E9G!33[[[zÇBp=-[tL!P̓'O͘o022bJGoiӦL4 4$^ӓO>Wh UVmۖ9z(R@E8q(өR %K$%%'O™3g[.~- \xM61i$>3)@̚5 hLb?`ʔ)hii_\ӮAcǎe˖… MFݻt RDDFFһwo,,,|2۷o'""(2z͚5h++|??iiiҷo_#DWǎ\rRr͂ y B9ެ/c˪U.gܹ8;;g8))Ã1c߿uZfff899QjUN>ի?,ZK.S3grȑ\~tޙ6mݺu#---mرc銷k׮ <](;~~~˺u8x &&&JHÆ 9Y=i([g֭3e r褤$j׮O?İa!ĉ_5_^ǠAXvm>B9s-[r5֭t%--ƍ'HB,==?y p6mɄ#G)MLL 5"33m۶1|ٳ'nnn*waaaqYHHHnݺXXX`aaAfͰ?DŽG`` <GGG^2*ʕ+C̙Ì3}O(Ύ<|ܻwOf GaeeE\\stuuԩS >wwwʗ/`RQjbJ/yBWpB֭[GFXn]qFtuuӧQDx"?#R@Eޞ={^ۂ+++u1j(7oɄByIIIaѢEJGTRѼys޽B3hJqqqyզ ,‚~wҧO122R0m"""rfaaaDGGhQ 2KKKZjqtt1g[laѢEԩSΝ;Kgn;v,gnٳٿ?+5jxxZ`={Zc߿Çi߾}>&,4 ~Ebb"ǎYikkS~}._t M׈gݺu0m4Q<{q|Ǭ^Z("uԉdN>-DF۶mykҶm7Z)Eɞ={'552e(}6 6d̙L2E8"DFFҪU+_x#СC| %+~֬YèQ^zmEҥ9r$&L( xٳ[˖-VZeHܹs/_;ʕ`„ xxx <./S [nK.h4x{{3n8_qZJŋ{eQSKKM61`|N&'''ʔ)Ö-["^'E4!^ƍ̙37bhh̙3ׯ_*ڵ#,,Lvca8;;salmm#DZt) ,hkk?7eBBBϧtB-[0tPى?ƍ۹|2KV:%{7.g}PeŊL0ᕋ:t(k֬dOV͛9zhÇ Ӝ={gr5ԩ\Ғ*Uyו+W 00#GKqrrbܸq㵴h4dɒ`oի 2#GSR%n߾]ޫ*J޽kxjiiQ\9.]Dʕ1(J&MÇ9}Qċ&Ŀ%%%իWg 0%߿?/_ԩSJGy$##SSSZn͛#D$ OOO:^JА<]-,O?Ă uQ ׯӨQ#-[ƨQ#r˙8qso``aŊ??+Y$ׯ_/0wq FXXZZZzSYYY\p3gpiΜ9CLL jZjaeeS,kѢEy%''sx+ѡI&S~|LYt=ە6a233_ؕvYlG"##iٲ%Os233_[@֦Ddgg~z1(Jj׮Mrr2iiiJG/!;Dqss#55ѣG3}^q˗Fbb"Jy4hЀd'OĪU8|0zzz90˗/ϵk sBs)?tΝ;xbƌtGѣdggCUd-[')S7n뱠 Fݻw|%9sfn޼ٳgsfZm۶9I0ZhA\\-x !Zߟ3f_~ܹsm`onظq#}ZrJ?~̔)S">qvvܹsY͛7߿ϢE)yNv=oʕ.]!C(E!===vE˖-hyhɒ%L?6}VVVaeeEf͊Dgnʅ (/ ZZZ 2;;;kvͭ[8}4ZR:^1uTۇVMi߾=X[[ӠA"FERD+xd'(v~wLBTT ͍:u(<[p1ڴitRSS[.cƌaܹo=۷oO>yL!(Zzꅟ1XNSk׮U:=z'N{B?Ϟ糲dڴiTfK[ɒ%Xb 3KKKڴiCJ^8VWhϲIvv6|۶mW^v^Fd'(>""">>ܽ{oF(B!(@233INN"Bll,GV:Pě8s TQJWW'OGL!x:MhDEFaǎL2w2f;ʔ)t\~z:uꄁQDP,[߱B!s-4 իWW:V\INhҤQ:uBVN||s˗/sUܹZ@GG===2228r@!DqRzun߾t RDEʡC0a 69sPZ5c۷oo>l٢tG_(B!(`SIŁV:##~=++k׮111JN>'BQ+WK.)CDpu&O/;w&<<ƍ++mٲ2eеkW_n[r%Çm۶_ɓ'c``@ڵ_8FT^ yW ŪLnܸ3M$%%1bjԨA2ehٲ%;w|8JO?9K^z?{Cx>[n%J ++cOY&UTcǎ|cǎ.jcccVX>[mF߿?Ӆ :t ((?Ill,jzРAvO:;ۗ?OOO/Ǝ˽{5kVqz]QYA " +X"X=Fcn5hTlD"kPQl"J×.ewY9s`g;Ν7 m۶E||<|}}#::?#n߾ 6OQ盯//^޽{}ϣEJ{&t5k0rHwW5֮]+Ν;ǰa*]ޱcC(ԫ ;ץq.Z 6[^vҢL{E[fKc heWJ+%%sE@@7o(^EF:+ڑ#GNҥt &įӧO$&EիWHJJױo>A[[6m½{K?h"߿-ӧcΝԩ,,,p1J*صk,Yv4&d0zh >ӧO!믿"!!Ν-K.]jj*֭[ W*jB׮] MW_mڴ.0`6m ۉ@ <}Y~U<ئM-[ݻLǏƿf\~G3///TV M+++-|򂥥%F!]֯_?cv޽ѣG `qƕwWRecǎ6l,2aO̼x"L ʴ?N:_~9r$Km*f͚xrss)t8#<#+vkkkl۶ ˖-Cttt@~:bbbx( l׮]puuСΝubŊr:777q?~<͇fh֭0h^и㩩2e 7n mmm 'OFll,ݻ!4i"y [f___D"<==C|z#)==].f͛ѴiShiiRm/_bԨQA͚5%K 33Sx?uMkÇG`mm SO<ܺu uAllP÷~_~8vXPp%۷oǫW y~;s=߿&M+22qqq>|n:!33;v쐦F:Dpu2h`ll&M<1EϷC͛J}qݺu޽{/U2!/mdd$V^ '''|gڵke2T>s-#(/}?3PF W^)IAgEׯ!p1A\\:t]]]̚5KӒ(.>>>ʒIh{M}~~~pqqb17oaiҶST)u "t;wFttw{M4?KKK"'' UٺfE+sLAY#ADHOO:)b uuؑ444hر"tH!SSSH$BJ@VV듿 &ee˖ӇݻwV^M"J۷oׯOvvv-MG‚i!ѣGҮZJϜ9C(44T,++ĄmFѴdW9sF#A7o,t?<|Μ9C;%KЙ3g̙3CMdaaA'UZ[rrr"'''-/}z͛G'M$hҤIsN}:e""wwwrqqL)t_|ԨQ#2dHյ<?~(88Xs|ӦMW&2gg}Fϟ?;*{}_|AqFڹs'Y[[ɗgϞ$iƌsNZf 2LMM͛7r1(ROQTiʖ+y:>(SSS խ[~w={6FիiԩA/^ =O|r@W^-239oNg&mmmj׮̽Ek3"iӦȑ#) 6o,=owXi)۶STݚÇ# OOO|w߻w FdnnN7o\tQWJ\]^1Y^EQ:>}PBBB7+Tw2ݻw4g֦͛ӹsI0hƌBJȡCH$(uwM:UH5k֤cʤqihh*G_~%eeeI?|}}*R %r7ІJՓۆsR*U7OJMM-p?~<3w\Z*իWۛU6{mmmYxU=x 5jԈVJ3gΤ˥yYff&UREHݻ ƽD"K(#HgϞT^=:z}1 0JIIPեmJ>{l֭[7{9?VTܹs@<߰aCg FM.ۼy3cǎQͥ O]]]ڰa\QQQdjjJjբ'7\Vv?(S}DUHmܸ1]p}ry vڑ}7B### m۶> t |dѡSʭ9sLkٲeB,S=i'NH"d8q-FDt%@/YVs m֬Ydee%t6mPNNNQg'ZE+)NO-,,ЯwuUyo[y@c֭rue$MMM1bDAzj'"g~ yByk׮Zv-\ky,E;߿OZZZ4zh偁 ߇j)#HƆZhzeڗUHL"={-D"ꫯ޺u @"4iB!!!E>PUfy+Jc<!T'ZDDPVBx8GV:u -Ze0o<\x...B%hs\~ ,w333D"E.]عsgVV- UV ʱy>a%<9wCz{{cرHMMEBBzѣGٳ2y8p$tE˒^:aZZ}ݻwݥK 0``` fʕ\R|ri9ׯ_cr뒓| $==˖-ٳFFFx4Q#Gɓ'HLL1h )FD:u*ׯ &ȬS|+9T\v ͛7: Adeea9rd"ԕJҧ*U]?ly <bX :2V b޽믿dZjQR)z|/iFFLzuj޼̐"H޷oCCC7_EkD"XYY!>>^U픢Ciлwo\v ۷oǍ7`gg'wΝ;q5XYYrO|U| Y?($ .;X3|7Joݺ3fTcΝ7M*cǎGB!'oo}}}Ν;wj8D"4h *ԬY3XܻwUTAݺu>ceee˖صk5kYfaҥ*oV{~Tu%7RÆ TV 4Çq-ԭ[zzzhܸ1ܹ{A[[ޚ5k UVպIz~߿HT: W_lF ===ԭ[k֬AժU$Mgggʕ+!СC8qBVo߾;v,Zn XVZa…x19o^x 6n܈S=!!aaa5jf_AǷ2As>;;+WD޽ѨQ#y7rJtM6ǝ666زe N:ԭ[+WĢEv/yfBeηv۵k`gg'tػw/ZE+---Ԯ]Ϟ=S_OA]سg0bO>ؽ{Zj+=>PTU;+q@\^:j׮]Oj70bXYYAGG"T+uPeH$>j֬ D"7]X }}}hjjڑN5>Y?(.0&Hzj̜9kÇѭ[7*$ BCC1zhCa%$..O$$O>ɓ]uJIIkL8{FLL ѰaCoDηX~=,YM6SNr99N xl6oތ.]~B"ՕJD"QDbagg/"&&{ƍw^ aaa\WBqSUuQ:\ ;N^|2ڵk{{{X7(Jq3*31sLDEE}8}4ڶm+ɓ'Xh6n###.0vuR|*AYo]܉u-x{{… 1c~'M'QC9V`QZl)t(2 KKKyHGGGH$x1)$X,Ɔ ТE 2Ϟ=CF0{lL6MШQ#?^Uȶ yqDŽ:D"9={?ÇcӦMXf eҚ#<<nݒ67n0deeI}ŋ6mlWWW\rED\yc=?ӧMu IDAT?'?%55Uܺs btt:Caeɧ7422*2m D;w}z7Ϙ1c`aa!Sf?zACNC___Kprss=?U&=֟>}_~;wM7elڴ cǎ V!uD*U*e'ZRR;&kYPJ)ΛCHJJʷC]_M666O?'bժUx"T)s|➧% ȋ{%Æ 0Kk(NET;h̚5 Ǐ+?7774ϟ?ŋfaҥ3fL_eꚕᚯR?(M#L999Xd ;DGGc܁;wѱY֭['>>>HIIԩS;v숆 _n|zGJJ <<< xð/5jz{ݻwy&|}}em۶Dq|+yCW|:wٶ:u/muLضm=xc2CcXXX >>1112 w"..N,""_8p֮](o yyyaÆ8~ܺCAKT9ցoh m,/hO>k߾=4h wٳxڵk']fkk$d\reEҷ>sNhhhs}ܼyS.޽{)s3oǎ "PիWepSf?rGDDm۶9bbbdވ 6\gcbbУG" t ȑ#n:L4tʜo=ʳHhBfbΝVz%t(R ec^`H$t5E]GUٿ\)>=E"aa*#Eϒtؾ};v@"O>%CZZD"̵8++ Γ (^S$E̼uzWWWСC8{LZzz:|}}c˖-7o>SN'/_.4fQbcc{n_wwwx)m???t5Щ;iKݺu >UuC}ժUѴiS\v 3gΔ6,z;;;̜9999}F} ?5jԀ1|lRfm___s+W۷_iU%'OD.]йsg 8r6mڄӧ,YD缿? }C]$?uHYgϖ-ŋe]9"##ann>>t؍5 }X,FppnݺAOOr[qΡ.22R,lقJn>VJR6X|9^x.] >>~~~hܸ1 o^(R)|CBB0h 8ڵC͚5 M6-Zv|%?kkk3+Wᡡ۷o;wc\2m۶a?~5hЀƎK<(xȣ}wnZJfyttԟITV-)S۷o%$$И1cښWN4dK~P,9<}WdffFzzzB[lK{_SNd``@F.t %tb"wQ6mh Ÿgر#鑳3X"t , ԩ_UdEo_u|woC*Zjj*ijjZTT'{@ uڴiSۥh y6|խ[ DIIIuT[_QLj۶mԽ{w255jժ9M2ExPR(-W IؾNMM%HDۗyիW ˬ/TQ˗//N+VP˖-I,ruEk3""rttUVѻwÃΝoʴMH9l -3%IߡH>Uٺfy+Cc>5u # X cRt9 ?>&LE:t(>|HCa%ŋhٲ%ܹKKbk,͛7pF H{ cO}BUn۷G-T c1ʠwŋ߿С7o"11;c1Xo]܉TFDXj5k \t 3fi߾}ݻw{2Άa0ʩĤrpww>\]]yfԫW:t:Rw)QP+\\vvvQ0e|Rcv N4$SL?sΡiӦBU=zׯ_+tf)ٳgׯzB޽}X,F@@:uI&aJ+YD۷cȐ!p8;;Wmgτ Aa޽1cBBBеkWdddcyNaAFFѨQ#Ca"t9y$*Uɓh۶!U탮.ڷo/t(J.c{55k===|܉VF+L!|8~G888`???L8kÆ X,FXXƍ,Ltj:VJk`044-| "VVV8}ܺU"((0`كݻwHXN)m>t,[ 'NիW*e"B@@ Vix ԩV|Á899!)) %򝌱Qn֭[hҤah,_׮]N:#G`ٲe܁V233qQKPX)222υ ƍ1bH;88`ҤIسgu놾}b%#|RRV\:iӦ><(p*ʤeÇ#66СӧXQQpvv\8aÆ!22=o[\|W . 77͚5S)-cdѣػwСիWυ|ݾs /\,.]R)_XE.G* .@$EB h p]<]W_ RC9VNHNN۷oc3Ɗ/-- "HB||]4440dAڶm sss $$D<((}aшFHH:w/^ %%Ҵo޼1cУG̚5 7oh+ vE8q'NӧO!HHӉD"A$aaa2e ֬Y3fTЕIS޽S ";;Gèd*uܹ3/_" mڴ۷o믿 "c(SXep%8:: +DnUr999A>}УG9sNѣGQF iFPX)377MoX}WŋKy&<<<!M;i$<{ ׯH$._z57 ''G(Д)Swww>|DŋeÈ#pYX͓W1;w.6m>}ಲuʕ+uп׮]7cjQQ)bH$8{,\]]BDD$t%&& ׮]1h C{:tHP_VEHH .c1k.bBCC 3ڷo_boD"%?cl)rXJիW+Wy%}L%MJ… prrBRRΟ?hũSХKCa't1ٳaoo޽{ J```W^ c1c%."":VDdoߎhڴ!Uj/y>J.\: c 22{eTj׮/_ c1c%.<<m۶"uߩ$rss!C`ȑ8x ;z(4hkkkCaqqq/Ca1X)H$2e )t8e!w1c /33'N@n݄̙3 7|#tH;v J ZZZ8<̄1cdv^*t(eJ:uS`1cD#-- ݻw:V~~:Zl۷o#22;ʐT\pCW;;;?^Pc1VJRRR0k,L8666BSHMM:c1JѼys4lPPX E6m`jj˗/IGN8"С0j BX`bbH$ѣ!+̙ ̚5KP;;;?Ca)o׻/cr(oeSy򄈰~ֽN q1Ԯ][';-[>:&VZʕ+:c?~ǏGzH-^ A6m ###|嗸tRnܸQJQ2Vq]rk֬EPV-)sLLLׯ  Hyޕx\vvvQ0e# M-<;ꊀCbLΙ3g SωV`„ ذa?^X :T0XL\r...BØ 5j0Ŕx}}}xb[觅 IDAT~~~hѢE @ll,ׯ_2Vdgg1bYxa]y&L͛7#߿ ӈBjՄ1VLl*oݻ1p@xxx &M/_bBǘԎ;`kkf͚  SwU iii߿?Μ9{eXll,uرc2+ GEE)ժU&NHdooOwUKUi\0UpYt̙hٲe4`""ԣG*v=Р{HDpҥKѿ|w8ttuu) &&/^@!NNNӧrc߾}7|#|˒w^ 8Pn$E&DB||z/6u LD:u*ׯ &ȭ_r%x-^|\|?cAxx8~,]TaBgݹsחY.aooK./cTy>&M.\\4kL|SL=z4jժ%t8LI܉VN$''G?Cb*}6?|Zj8}4w1@GG޽+p}?b2رDk:u_`۶m5k4h ];  ˲_\`Eū 2D-y}*00=֭[t;v;d޽^^^z*KX!ñ`ptt:r =zŋ ϶Gll,˓q888;cTei۶mannHA[[Vw2k"%%SL:͹s:VիWVݻ˗pLN*Up1}={T111 묢Ů]DlݺZ6/^ի,};v… ekѢ6oތ7"##qqqغu+ 333Dppp@LL̏IO޺u !!!3ftʳ]vﯶ<&LCaeѣGѭ[7<{L}:$0c1S&M:V ܉V=z@hh(W.tHL """PF \JWW666:c)诿o@~`ƍ :c1Ɗti`ժUQbN2ݻ7zm۶AKKK萘DDDu֨ZС2M6܉cOƘ1c0sL :Tp*&M`ūW1ceffb̘1ٳ'<==w!}Eqo`vDH46c71jL b `AP (HS)l)oas]\33̜WWW̘1 H Еɤ^͑2Q!iii 6l:[~=~zQ!By>( P ĉ1sLٳRR$wYG!bUUUHIIaB! ''' 6 cI)**⧟~ݻ:!B!oؾ};ѧOqHJM+pqL6 sݻ&|>deeahh: C޽iHGB!z!ӧO]v#f͂-<<gFPP̙4tСMHHhd֤!ǠvB$Gc,_?!aq:D[www,Z+8|XXX 77 `*t;wĜ9sL<B!eҤI |:^رchhkkP:>EEEǘ1cpz='O;1]!T߲INN,--allӧOCJ0TDka?~<͛~u~Wx uIXYYaȐ!ػw/(iO>|||0|qZThh(CAVVu$BZ\qq1PPPp볎D… prr.\:aoũS: !BڐBXZZBYY.]BǎYG"M/ʢ-ŋ0aN qH ILLH ))u Bڼ]vAAAӧOgŹܹs +YG"E?F\\Z!Xd Ν;:a$>>cB! y%!##0*I0*> &:ېDAĐ 233kQi*++{n| oG5 .]BRR+֑i5Cr+(( 66n֬Y> z*8UUUի077gB!mDyy9ƎgϞ!22=z`4#䴀$899G4HTD#bbb@Qi;_~%(L"::yyy=z4>}:!~ FxDރaϞ=Ƙ1c:iAׯ_GYY!"^x1c ;;`43*57nO>;֑H JHH YG!bHCC4#! pwwb5 EEE#JJJ0yd,Yk׮E@@X" ++ #nݺaB!D=z666ŋ:iTDkF!//:iaՅ"(DLSFŋjÇYG"\rBxx8;V! &M YG"-ܜ>BiVw܁^zX :u$BL={ggghhh ((ڵc0@󡑏ebbBCȎ;`aacccQZUUU@NNsH|jlܸk`kk:i$iii>45!B\uu5VZYfa޼y8s ,FQ q#&&!!!d0hiiBXN0d*҂JJJ燅 BFFuVK.B+m6BGGu,LOxܻwr H |@AAuB!Hv‰'YYYֱ#TDkBWƑ#G~>|8YG!bĄh gfE,tիF֑Saa!0n8֭[IaÆʕ+ȑ#o>֑H4#!BTxx8/_ʕ+pssc0FGM?֭[w^8::CZTA$ Q]]: !O(ٳѵkWqFΝq(++#"RZZHOOŋ=zFZ2Ο?%K/3UbHOO"!Bׯ_ HJJ*5'Ob…شif͚:i1|pQ011AYY233YG!D>}ꫯXG;;w!// j*qH+24 (**Ґ;6kݻҥKx3f ֑H ,Z...~ yyyH+`ffׯ_~%SֱH|{fB!b͛7Xj0h ddd`ƌcVh~)F;vCZTC[[u"a``@E4BYzz::Xٳ'"""+ۣu$҆$$$BVVك={FZcǕ+WÇ8H=|hB:t(???9scVhT^^ &@QQǏ4Hv!++: &&&HLLd}vPoM@]]x1Ər֑KLLرcannv!998p hÍ7ꊙ3gbȑHKKc|P(Dbb"!`wĉ===Ãu,ҊQ8|sά#V&%%r$M7o˗/YG!D"=y'N_ :D8p """)S@ D$ŋakk 333<.\ FDII !```˗/aaa: !BDee%||| ={'Oh"Z#]NB`` ]J" F'oH255P(Drr2(H$___t ӦMcEhkkܹsx">s&4H χ=hD >طoΟ?mmmQ |t B!D >}C w}prrb *5P`` 6mڄ666V())"iR***PWWyiسgKoߞucll'NرcXn8D BPVVFBB:RRR={6n߾)S`޼yG`` h_|>LMMiZB!W\\,-- ###ܼy+Wsv IDAThDPRRR0k,|7?>8JMM,]I|>H#G_|(~~~sNq)))HKKChh(LMMY#skЧOW^e!NYYYpww ::ǏG޽'#∊hTXX [lab׮]ڵk: 0P(dsNL2***H3f`ƍXd A%//K,VZ;;;ܼyGxCXX `ll {{{f=~4!Br=xzzB__@\\FHcP***0~xt4dyׯcذac daa/^ ++uB$ƥK &|X`<<<:i# K,AAA0cjj(>}EEE000' XGks!%%cccQ!Jb޼y4h._#GƍpsscH*Ճnݺ'OBQQuʥѤY 6 :uB||<(H X[[Ȉu68q"Y!DEE+++(((ѭ[7 7iiiؿ?.\AaΜ9aӃ(Ba,==ӦMk.dffbx AE8z( ???hiiCZgϞTD#BZZ&&&TD#",, _5(m=}bرxHt,^***􄑑]dxxx@FFuDB"--3gؿ?|>T$^||< H!q׮] tX,]uRRR UUU,\۷j*tޝuDBEJJJT8QXX38uDqUTTTPBiCJJJcȐ!pqq4.^7m$͎hu8gFqq1NDAFF|>uBھ}D ;o]vCAqq1~7 :ƍQXXA/RRRpuuErr2Μ9@GGGax b/>>zB~XG!BH3sVZ}bٲe0117ѣGG*a Ç:#4#iV;v>͋FGصkfϞ %%%qڼO?6l.]:iVX#""B׮]Y$x<899!22iiiŋUVÇ#->KKK1!LB!"""ꊁرcXr% O] TDY?3X!b8deeQcN%{.-Z: ?}&NSpb,!!_}z1cœ'O3fcϞ=χ'8bܹ~:xb8$$$PB*,,ď??}c|'HIIAVV֯_O4Bޡ}DVVEwK->>۷YG!BGڵk<xC4P8s йsgqJOO:f}Ґ470rHQH455o>ܹ8_={{X|93g똄#Fòep9b̘1 Buu5눭 χݩL!gϞfffāၜDFFbʔ)g:Q ]bԨQ1(((q̤Hyygť礨Xt)qފ ,@ff&8mZqq1UUUxyy}8x =z#G 222"zwŅ 777hhh 7n`U4!"F*** www˗/:BBBp=lڴ  `jsUTT222e;v`ɒ%塨5558;;CKK &&%jX|9%!u8tf͚WXiiiؿ? hޙV FBqq1\;fx!!! ą  %%;;;Օ$aaaٳgͭM~`C!wFTT?'Oװ:HHC"ڲe˰{nBSSu"&`ccr)))ʢ B#FtD"ӡ>:l߾k֬Aee%! Ѯ];ĉ֭s \\\ctޝuB!Kuu5._@ٳg1bOSW^#1p/H :VP Bٙ hYK.eVǡPQQg},)a߾}8pYǑ8o֭[affuuu,[ ?aԩT@#Aq}/˃=7"??uf1x`*B!@ @\\㫯͛7%KPH6{'Zqq1 cccp5 :Sd„ `Ii}<==qATWW,1w\ݻӑ?>p #2330k׮pvv `F~:O?~O<Owww(++lllп8puB!*//Gdd$N>`?000h~3"h8NF344T!YYY̘1 hY"**EBڲO! //LE]v̙3FjJDDD`ѢEЀ.:+++DEEɓ'3Æ Î;PXXbժUիR1Duu5ann: !<}~~~puu2Ət,_HIIUF$ZСC={6.]#GCyfTTT nݺ3HFڊlhkk#11&&&Ҫ!11j毼t)))033۱h"qZGٳ8s """PRR}}}L0&LYG$4Rȑ#&N)S222#6իWalllhiiC!H;w $$aaa4,--1vXL4 {f(#|7XhG6lX4YYY|T@#nȐ!PSSŋF<}<RRR8}4Ȉ#bŊ5jXGjU233P$$$@NNظq#&L>}Hi:t1}t<~'NѣGnݺaĉpww Yǭ7>.]`B!͛7sp9ܺuK4{M_F mYmҤIHIIAzz:}G{.Ν;ٳ'TÇqEQiUvⷖKIIĉ4iTcTWWHJJ;*ׯ3gٳ(** [[[(((Ii%ݻ  RpssSoO'O2B!He#BTɓpww:sAII %%%e222Xf 6l0iK:OOO}*pYYYZy<ۇ9s0JF>֭[0tPXl8-*?? Cll,0|paرc! ~:zO?&MUCM]] ,իYG!BVii)bbbD(**ptt:똄FmshkkcHKKKNj~VRR½{0iK>|555\p*x]tykm۰tRHSeKϞ=Cdd$"pttĘ1cн{w1 !bۢZzz:1~x#}Ett4Y!BFuu5^HDFF"11ׇ#`aaYYYQ iN̬!dڵ ֭[7߰E899a۶m*avZ_](d*++1|phhhܹs4@ׯrq &RRRB$Н;w"}=z40af ;v 3gċ/hZB!E(..FϞ=amm ;;;899f4\(;w| N:WWWq?1gpݻݻ4iq+V@HHn޼: !BRRLMM]v1NERll,FǏݝuF}6.^H\t /^@>}0f8::֖n' 88AAA 0aG-eѢEr Z= !qqm ::/_ÇZZZc"$VRR]]]رc su >~g0QF4h80wY8;;CJJ 'OÇ 4w\9sYYYb3ǏEE/޽{ر#F {{{3r !?!""011qf}5 k!"-*C077ȑ#akk ##V9)!bLh ,@@@u\@@@ "PVV7"ѣ֭[//yϖﳎALLL |}}1|p,_f1T˗2dƍݻw@+++ )))vvv먄A刌DXXBCCQTT}cǎŘ1c=+--EΝq1L4ϣ#!jjjc„@ @FFbcc<~;v%F kkkѼf4/.%%%>}z9!huʔ)x9.\bpI1!-ѣGgʕ+1bD37o !!QQQtP]] ===p*ر#먄QB!]pqq=?=.^;;;@ @DD&N:!L@ZЎ;`ddA}5Fuu5D3ǣ***ѯ_fA!̀d,YHHH@hh(OlذݺuѣaggggzYaaaC!00˗/dž  //[D!4!%%Eiii <XhXVH"hϟ?ڵk===qi uSSSSҦ`XbƎv5k!)) GYY1j(bԨQtK!ҡCѼ>>>Gdd$BCCxbxzzB[[...fȟ~ k׮P(GB!WQQׯիr ^[n83f̀1%j4!B$DVZYYY[uBi1&Mºu͛&=L!P(1uT:u >>>Xre_ٳgE\\RRRPUU X[[6660`@n!Hc?>lٲ:t \\\ dff˗ѣn༼<a*B!--??_tP(**BOOXz5eF"ZFFPPP@^^deeQUU4ضmN>Gb,7BHRXXXXsbРA1b 22#DBH\miXG!ջwo!00hLaa!TUUY D"bx<|>>`ݺuػwoϭ9Y8ܹs222:t(,,,rJ=ݺuk"w#::ϟʰuV' ###,[ 7n,- cB>@ @NNpu!55?4allM6 &Q8a֭T'%//<*ðaСCsXL(//g#GwbMn~ASS;v@ @UU8Cll,O?y&CYY ҥKQTTDDDիWHNNܨF!oرcwEB!m6 ;;CG]]<O:ةo߭)=\JJJ]v&&&PTTĐ!C0c [nXbbbbKܸq'N4B$DU֮] kkk3u:%%%())AWWk׮Y#M`ov0p@ҥKI̟?VVVˡ55F˗/7aҖR"6 "ɡpvvn}^zJ@gӧO =z$ΫƳgϐ (++cԨQԩ yfc刍 IDAT۷ōBC߾}͛/Cb˖-8)/:C5kcǎdq&߻/))Vy1bCnMyFHs(,,Ddd$|||tXz52331l0lڴ x_o:tz3!-Lbs|888عs'BCC;2HJگ 5553B3񐒒ӧO!''>ĪUеkW7%ݻw~}6~W̜9fff666͚R}K0`233EAuuu5wkc7BJUUn߾dee!33W\'OɈ#ₕ+WbĈcI̝h6m3LLLXG:t}5|駘6m6mUnspp-Z˗/c7h%dffرc2e ***EEE߿?֭[W+BÇѫW/1ZiӦ!88/_dUy_ܵkW8;;cܸq/SSS^Z&-'.gjj*7ߠLt.?~<===DEEU@,jx<~IIIzj[!{fdZ544oAoxN@|w_E˃&"N*++@lذ'O:t]]]|tTTT#66/_Daa!BCCyfiT@#Dbccj*Qkkk̚5 F3|p͛7EJKKEC+ YYY 4klڴ ֭:vaÆa׮]ٳg?>tttDmٲEtΜ9[[[tXp!JJJZo1x`(((`?~<.\}> ٶq2롢ݺu#?*/dhjjB^^={!kܺu yyy%%%|矡}:yd 0ٳgѷo_̚5A./_Å 0n8(++cժUŨQk׊_z5x<cǎ}fΜ$ZҴi'N`uhL,-- 777\vM!ԩSk 3dȐ:߫}CPQY?\x1ιpwޅ2`ƍ8wB|>FFFHMMEѻwo >>}:.\ua Bll,7022,idY766044D׮]1f̘Zw}5VCow PѫW/ܺu <xjأFcTUUaŊD=0i$<|^~:?kbҤI2d:t===L:GAUU\\\Lʕ+ػw/+XZZBQQB '9KK&y-܉'Rn՜,7`ov]k9\TTh@ sr}:z1rss9^ky\\;yhٶm8\FF[rrrDk2 8366|}};wrʜWPPШ!9rKLLl©rqqqu ʊM:+**>y&M6k~jmEEEW.]DxprK.夤'Opqw8oooe...޽'NpӧsL34I&q&Mjs.siii?ח>}Z!Qzz:EFFrV!-x\ח W^^.zѣGs @!7opܡC7o45l ?f#Fڶ]r]v?~\{ 8;v;3r\Cp܇!!!q&7w\ |||j=On5{q ߿Wߛ+8///_)((S_=?G[EEp֭8mmmNZZZ߿?7vXnʕܡCdutB "ZVV'%%Ņ65tXX׷o_NNNj"ڜ9sǏsfNII[fMY~='''ǩrqܹs9;;f g``c&LZѣG\N ZIIIq{-थٳg~PڶT'.]Zސ[ɉ-pWnTޓ'OrZ~g;P(Dv{7nʅӧ={r'O~kp 9+++|>_^I͛7sqiii 53U#22PvE4jl2ǽ6m4w޽w>}m9::@7~駟8ܶm۸ŋscǎ崵9EEZ'dee9NFFx%$$ps5:!M]D~HsmNpKbLC/կߟ% NFFFTXr w=k|VZre"ӳgϸ܊+8ggg_~8wwwn\@@Q! ~37ѣG8pg+++X[[sJ̚5K4ߛJKK<FLLLϟ?Gpp0fΜz^|%%%\WWD||<͛C>} KKK|駢ۣޛ޵m-1j0ΐlkk eeE} _1tZˇ  wE߾}zìYD~W>6mTx<>}9ƍ'}:w,NNN={\a:tP;@߾`?k4]! QTT???=z'O;W{XiOj?~aKׯQPPܻwCAArrrPPPgϞ!11SSS8;;cƍpttlBGϞ=ѯ_?QD$mcǎ |~^!~ հ"nݺݻhN[}5>s̩CJVQQ\dee!??_"MawwwhkkCGGg?b]D+--ʼn'?0f>}N0}ݻ PBx{{>>>XxqE߻xzz: T3sIݻAAA={6fϞ 3fĉ?Smk Mf,.]h ǃ IWWWEMMM>>T1r\\\6BAA .͛kא>7Fر#tttهXv-LMMQF@JB!ҒuZ$Ҝdeeѽ{w jo]vPSSݻw ())ASSoƫW //>}5$u{>$ ߯U )ݽ{W4 tttп١ֆo!XL6Ywwwz +WDhh(mۆ3gRRRPSSM6VaKjj*1tP@SS:tܹsXkj?]?.2;>`8p?~ʣG)/^}aРAđ#GW_re,XXt)Fe ?dž )Sb.kkkc֭*'FCۣz_{~~EVVJJJ`ԨQشi!8 Xr%(HZ?%FomH(((͛7Ew <nBii)$ݚj5G|6Ee(%Bߛ T:@ͿݻpppQ^lٲo&&LtXZZbѢE7o^d7ov GGG?'!a133ȑ#jժFUDIܪUWN \H,l?_g`̙3ptt;U͛d۷QTR޽{xk|[[[#22׮]:vpU8*oELMM!RSS9MI䔙r۷oG`lVVV˜1c/vȟH17n ::bGb8|0o`ѢEUZ3ѣG8{,uV'=zDRfDQQO222vލ2=Ѷm[^yrrr}v?K$XYY4 UϦ Obeeeپ*_"''/~СC)uzyyaȑu?~Wbe/7o """? ::7nnnh۶-;Vn߯ 333L8JR)@ݻ'j4w\;wgΜ;JM\GbcYOj3+V^BŎ@u!Q[HR());S~{xQMߪ۷odŕ)ݼy eCS[M_oEӰ2ZTPPk׮~Æ gaԨQ֭tuuѪU+888>@`` `ii &`֭믿l;waaaX|9&O {{{ЈH)HpªuiҥK|r^cΞ=-ZٳgHKKۑ 1%%%s_x-ZoQnʇQFaݘ1cfϞdlܸ2 999Y~= {{{̞=&&&Î;i&xxU0~x[ 8q"""p5|aaa?~<ƍ^CLL mۆΝ;W:T)*C6mЩS'Ϟ=CHHH:u {.vލӧOcʔ)SƍwyIIIXd ,[ݼȀ?mۢGe%''>{gkז*۶msAAAHMMݻl2St>ŋzzz8{,jGl}E^f͚F;*rUTtrss<8uިWWT^5zcȑr t"v""@TTz%_֮] L#99˖-Cǎ1~ 8p 6l؀ sθtKy&oS\|t wGMW1]vŗ_~bo]'^iiiHJJBbb"n߾]QBvvlaaQP""$()GGGã @ ssLL@jڴ`aa!L8Q믿W+V@@ЩS'A[[[6l <~Xxw??r'%% &L:t w}';6::Z:t`hh( _ݻw %X[[ sݻWVMQ.]$Wt",^X@طo^w.mVpuuN:U鹫&$$~I2e`ll,| jWȑ#QZv턿̱iii5aB۶m#G! &L977 A~/ ru0j(A___#U%*4uϞ=(vjsss&. NNNUzuڣT؎<==Ul?+ϒK.ĉDDT7:u$,XIQ!"##+\UPٖ,Y"l_|) :::p}/~ QBB̟?XdqRN?^]um]?#899I{.q]!55U^ KKKӧO<755,--ѵkWym۶-5)R"##mmm)5sssBSS{FӦM/bΝፗ·~cb!Rzl?&f̘M6/ؑEGG_G-Ď!zʕ+uuu<}2ޮ_(t$''#==)))ijg1441ХKʋcm۶1%"j`S"DTu~)ձsNlݺ=/&m6L4ITGZl |DS9y{{cŊسg<<<ĎCDD/**AiƬ+V^H4i%%%())A&MYk!33dHMMEZZZYjjj)555abbSSSo߾077/GDDKhHHH@^ĎB0w\̝;W(T>S_ׯ… ŎC~*'c͚56m$ؑ'O9sĎ4B?ںMCaa!BCCtR$$$GkN<eE -- 20 IDAT )))dHKKCff&ӑLܿZl)/011LLL```NDDS"ZLL gϞ"'!"R.zzzU0{llRHDD8u  v"F/&&EEEFDwn݊S$*))ALL .]*AVZ 'Ojhh&&&022%i7+]r; On:[/;QG^eш(B(Dlٲ&M eFU/_Ɲ;wE=z044 8+U۰;„o߾ՅϿ=޽;\RO)IYO>ڵk+v"Q?ƍ˽'++ 3f̀1tttгgO?_tt4/v U^u| رc(**R A9_??֮] ___L2C A.]`hhUܹKKKc)1vX`ٲeXhлwo\p >>4447߈H8p@:ptt۷c(((#>ՁǣiӦسgQgϞ!&&}; Qs򺡥k>#L8OؤIekՄMxmGD :Cm̘1ƒ%Kpr)((@PP>jժ>㒒Յ>SxyyCbG"W-Z#m߾};ڷo!CپrJ̙3ҥ 5)/;AAA3gq+W\k\1 www@bb"C!33H$(..'++KDDn$Zzz:ĎAuH$8r}}}"!! ..\X=˖-C޽Ѻuk[n/0[LRf{`` $ vءWe h`hh[[[U={ ++ T̙3ѩS'|bGz¶nݺCǧ.?7N^@#+W௿; Q===ىc_k\񬬬0ydl޼z*֭[ÇED<TҍD{17o.v RS/w%K 44e˖aΜ9hӦ '']vŔ)Spx{{H>w69-[iӦad?>FQ_U%%%ػwoѺu"ԩS6lGGGt޽ήGO]]k֬3= #Q=a[[k^ԩSl1zh@JJ d2lmmѬY:6W^ҥ ЫW/5J۷/ŎB}ν6럭-lmm1k,X8q'ODnn| H""ZHQQ@8p®@ U\|rAҥK~ABLLL())7|ܾBK.PTT$L8Q233k@PeYeIR{nAʕ+pʕ:>U_hh,ͫobG&v rقg,9"tM~m]]]a˖-ur}n?W^-hBxI]\o? eU^爈S3?.000͓ٳgur}R}VիWm6P=a[[̞͛=ܾwx>߬Y413fӧ$cܸqx~75:wݻwZþsչ}9jS"ӧOJ7 %ձ+W:u6$ +={1d\ rӘ!<<^^^hڴ)3gė_~lPڧOb9r$,--/))|ccclڴIc)/ccc[ ; QMA(TCWν6ˉTRJ|9 … pttD||>H\Ǐ/Q;ѣGH$PTT ?y$qFt֭Ø1cj*?Q555L0ƟeG!00ӧOrOpssŋˍ z777 >}._̵'IZ8u.\)z-aǎ:t|ӧ!пѢE :!!!,Փ\z|QTX{mޗ>Zj,cFA0c ,, ...dAdd؇bԩptt̙3GDD ^֡CNjH%a˖-b ՛#FGAAQTZaa!Ν;'p!S"Zǎ{H%oxb={V8DDbȑlj'ĎBDΞ=899蕔֣G!&&F(DD*kܹ߿?L'OHLMMaooCHEEEVVVbG!""""z%+l666; RSSCpp0222+v"z?CDD*-::;Q(] "bX~=~w) p $ApiFDDDDJCihΝJD6lؑ^J)ڄ 0vXxzz"++K8Tek-/5.{=*v""lVlyH)emРA(,,ŎBcǎرc011;+)[:thҤKŒ3`ll Q@@0c PR}BWW:t(Ο?$''{rJ=FD QQQA׮]Ŏh([KDDDIC5affx^Dd; ձ Y]v/*<򂝝^{5888`Ŋ(,,,sܲeлwon:::֭ B}|$Î;Q?7NY'/ ¸q>c;[ue h`hh[[[U={ ++ XaffT*v"";wp=4%b񞜈H)HΝ;!!!|JXbŊ1{l\~+V3g D"?֭[߿? 1goѡC=Z~lNNv)S@GGFQQ>ݻhݺu{U鸘ܿ_@&͚5ѵqQSSݻobʔ)($)u###:up߆ ݻ~QQQb "R)RիQ%W>ޓ6-)S|r\pm^3f? naaOOO߿L'|޼yB\\ۿrs\̟'N.]`Νa)ƍW:q"w4iݻwŋXj<==z}R ZO?z V¼yĎDvn . %%g./** v qIur}R<''',Z%%%PSSډhtkFxW=9Կ L:8xQɓll8pƍ+YD"AN\7_ASS T1K;잞5krss#F`ƌ8}3jݻ7k̟?#5 }LMMۿ~z׶ٳ:>'''"66V(DD*#::S96p+*ɉR 1dlݺU(Tm۶-YfhӦ nݺ%vu]}xxxSNֆD"Axxx%W%%%~!`llM6iӦرc I̛7?~<233ŎCUvϟh(-- BӦMO<#&R׮]Çqe.X''""RNJ]D?~Lgӫ~qb+{8::">>r 222#b2~gŋ:~->|]]])5T}EޓQ^u[͛7B_}ƌSyw^"##vB^ ׇL&ƍ-Rm8p 2{7HHH]0ekNw(>}1ҐҲysڵk}6>899x GDD ^'nnniӦ믿FQQq߿w̙3ŎCDTk]tA\\1T*E~ĎADDDDT#*SD/رcQ#$$!!!oĎCDT+q={&v""39sE4""""RZ*UDh4"";شi.\)ЈHڢw; Rt!$¯Ν;ÇHOOqT*(}?R A-ĉ~z̚5 Uͭ)**BLL u]]]P?~C&&&Шf̌mIFUVO?!&M8::<@JJ RRR#iӆEƍ(**믿.v .@KK 氰@˖-v~^"Q5HRL:ޯ[G8d2 8P!dff"66 sSssscA"4ϟG^$JTC||{.كm۶!>>pwwǤI`mm-vz bG!"R )))077lj'0p@P=:y$\\\i&| FXXƎ˩>U5k͛{TNN({f͚aذa Cff&ӧO)&O<}T䤍۲e0m4L0QQQuv޸8:T$ɉ.//:::bǠ?Ç~@ǎܿ_ ծ];$%%Hi:u M4A^ĎBH&a„ =z hDDDDMh7@KK ; ))єO-Et|wHLLĈ#`ddɓ'رc|zQ?\]]{ҥK5>׿ g7n==dy1(|m|rð‚ `ffaÆa޽(**;f377Grr1T*7o.v'%%%8q"7o-[4__{5^[nɓ'ŎCJ(;;M4%///DEEΝ;Xh (z5uuuڵ o\]]qΝ*711Q^{pv!#88,))DSpvvFpp0ҰyfϧT,,,XD#"T~ѷ~Hڵ zzzb!"""3Fwww 2gFAAqHp(-?>}:~wVVVE||1---:tmڴ;#J}pfmmVVVe gÆ F=~RDS>IIIXx1bccKKK͛bǬD#"ڵk,5"111ꫯwߡwb!"""SHIIQHɰ7n 66|_bb1U> uuue i/άtR<є)|||pbFǎd2cVUrJ"D>}ANNƍ5"%6l؀u_;)jJ@@*СLc$CCCDFFBCCNNNXdI |N"ݮ/ \\\ǏJ,,,PRRT5xR]tS# MgϞaǎH$bG""""s&M၌ 㐒HCMMM^4KOOÇaee NNNؼy3>|(vTq@[[oƒ%K`llY#őh] L&CHH0}t(i)?T?~C___8DDDD hi7oiӦ5lfRWW31 1l0#??_JΝ;۵kKSNعs':u˗/ޞFš5k1c ""ꫯm>>>t1122&޽+v"-///^d8<|}}W_ab!"""RFYDî]pQsf͚aذa Cff&ӧOOJJJ*ْ%Kʭq6qD;v jjj4hŎM ͑h1|||8xxx ""ݺuVX`f H$055H4"W8}4$vR5YIIQEb]vLؤI{q @~(V|gyyyhPΝ燄HR899aٲe033VC,FD R۷QHfΜٳb!"""RF[DŋùP<hT֭[ QQQs-ZTnj(c֫jhӦ wǑhileF{6333uT*EŎA e`Ν066;5"v3bGkQUXXXȋf>}:~wڵ/_.vLxpfiiڅkժ=.]`V'#EZZZit322vZ_KKK… v=""eSTTgr=4O>9,v"""zѨhвeK:tW^Ō3ĎC Paa!;(XD&M`߾}ر#d#Ȳ---P#PoDD222faĈ022﫫X/,,,lF'Oprrb["򠭭-v R!X >6mBfeeU\mڴGU@*e˖; ՑObhժ!?"""ƈE 4o\]]-v$QNNr$Qn^^^BRR-ZD >m۶ BΛZfŋ}гgO;wݻ7UFdgg˗#-- ,XSSS 6 {EQQQWSS>hDDJprr*dΜ9tá+v""""Ѱ[֭[x! &ںD$NH377?|}}_: XXX/@˖-m6 gjÀ~#Q-s$)܋kR8add$_? YD#"@tt4CS!wF`` mۆΝ;HT,9>7obСxؑHF M>ӓ;;w-[n gÆ 1>#hiia۶mw"""0yd4o\T+zzz0qDXhJJJĎE5ǑhT޹s/~]v͛7rM4"Gzz:h*Ht5Bׯ_ǠA`bb#GL4 } Ç!HФI[ٹs'ochѢؑ&N|($aΝرc222`ooI&!**  ((>>>iӁ8r444ĎT0;^D8qpqqi GQC]vaŊضm>}޽{O>hڴ)N8͛7cxqjҤI8qqqqbGjH4j(JOKMMT*=K߿Rx1Hy @~~>v` hDDDDE*ԩN 22pssŋ3c+p$5d͚5àA$!11...h۶-|}}/vL"zd 0@(T Ǐǒ%Kf899Aaq)m۶8 S͜9gΜArr2v튣G*kӦ m;w Je˖...*=}.ѩSЧOhjjjɓ'xall 5H,P֭q/"v$R|rM4R gFFF1c ((uR8y?Я_?kvZ_>uϟǀ0tPDX/Α"ڽ{{qL߿-['LLL};6\Myyy֭[GŎCDDD V 8p&M#G~;ձH4R^qk[񈍍ԩSΝ;~~~uVK E@@[p#Ѩ300PqEQ!##+WDbb"FKKK… HanݺДw}ڵ ۷;Q"Z-cӦM/1k,̟?ThTrrr\2E)ʜi&oGFF1 'nݺ{{{8qBHO}իW_v6))I#22͚5zhJȑ#/ػۊ6Q M QТ(ٗ&`dZZcf ;cR$ cUHJmï;{vϽ~>=9>{>|>C!"ӨMB.];wbڵpwwGnn.בP';d"eC爎Fǎxb}>Gō7УG8;;c޼y(**:V=FdNOU}_'''lذm۶E޽e{N‰ !n|hѣGŨQ0sLB!<cq>y&<==HXZZr(##h֬`ҥx!tuu3,''QQQٳg'''\G?"::8|0JKK  :Tn(ڽ{7M۷VVV\Gj֬Yuuu4n5c ؾ};ФI4nZZZ\G%DHn0`\R"+,,ٳgg=z pwwرc1p@(++K8"i[?/^u"<<

ȦMbܸqFjj*ΝXۣcǎXt)RRR؄"X4jԈC#ùs)hB!FhuDUU۷oGpp0~G 4IXvSv|!e˖xEǣGpu8;;cƍ077E]9!"e5jԈ(Dwƚ5kuVtޝ8B!r:dXYYٳu1L09LJťʧ"1c ;Ԝg40###A3gmڴA@@n޼uL!}ݻw1d 6 Ç/wzHHH9sMP9׶m[V^AA4(9D5s9sm۶_U>E_nGiзo_c\|'OƼy0fB!Dܸq8p ,XRl޼Y4c tG1LLL`bbRz>I&I/D]AA-[999Xnu@tR<|Ä nݺK.ERRXÔ)SPTT$Ѭؼy3N:+WGdffb޼yxDP :O.4F___IQQNNNؽ{7?͛7ǏL"֓1#&&B'O -- \G!HMMŰa0x}"B!ԉ&58p֭[kעO>={`R~cǎ!77 /*++LJƏ3 V\)RGKAAsE9r$rrrj*xBp''uf.]6msss 88X98-[o߾u2G ݻ!CÇ? |Xχ>| w^6iCr"BײeK0*[梌Fzz:~߂'},XP W^իWBBDV6 (ٳ tB!&,JʦM+Wӧ)..ƙ3gzjE.# D&0c 5 x+,eǙRRR$8 @-*?>tdֽ~ܶؿ?ܹ3$QKK 7oÇq98qB <|R={V!4i҄TTM__>cU ޻w# IDATF]nL*޽{>ٳgc!2qqqdXii)Fl;v jjj\G"B[ԉƁϞ=p > ҥK$#eeee B=8JEđ{{{4\2e>8ե3޽{cx%"##Ѷm[,\P0ݻ{@tt4޾} SGVvv6\'\\\ШQ -))}q:9v}77rmnQQ9JEH:WIʕ+3DGGcǎXhhAAw^C1۷ݻwӧO9~YKɸ3f󈌌!q!BuI1{*lxً3lmmϝ;DDqqqܹ3n޼Rb>|v킗7oaÆ!++ ֭ëWL4jHp7##6mBqq11c?|)--EII f͚1cH|x@zJ0 cqEz2[AAvvv0!kҤ 444dIT6ĉadd___ mSRRW^(9!D|h2.887nΝ;iMB! N4)o*˔"//#Gb-[PRR_KK >>>&"a!88GnnnOzYYYO:'Yf&„ pi<+WDJJ .\Pakk4dv6lPnފxyy!##C"mEEEeӦM( !ח4440~xi ,Gu#''?"C: 3g+W/q!BD3gٳ())JeqE,_\J  8ӧӘ2,//CٳQZZ*甕#xLWWWIImbڴi5kVhe)ҥ Ο?_c`ҤI"PZZwaСέHD^djhh`ذa"jx1j'OFRRRhII 1qDLh˗/Ǚ3g8H۰uUdǔ)S8ND*smt'NQQQ> Jd_hhhT\\<8;;cժU5>Vii)~',\{*Oe(..?3f  ???0GdĞtBXXX7|iǎpqqANNN"M6={  6muB!z:ѤLCCNNNXr%n߾W^СCoѮ];x<(((=6bY^x< 80l0pTd޽ųgϪ|*s>|@ttt'#u)//N>>@1bOUUXb.]wի_)xQAAAVZZ͛7cb!rww͇F1ɓ'ʕ+k0hB!V=Yivkc"Uzΐݽ{ڵ I;e 55ӧzAm)! ,7nȿ.]={ȑ#ڵ+q!B-*P!Huahheeeœ'Ou`ǎX|9֭[wwwB!kԉF!BahhTBT\p:Jw|wXx1;B!{ԉF!BLLLu B8уC؍7 ___,[8B! hϟ?:!BL$:=z9Am^PKJJakkm۶ImcB!NP @522⤡Çk}rr2x<6l dHe…[K.PSS PZuu$>}FFFظqD \Yp.<})))۷/Q,PQQ:!BHi'>Zl SSSwALL Zj%} }'++KǓoرcxDr]vӧOe~W(((ÇRJEd<[^qovQF033;Ο?u4xf͚:@MM իW+,/o${ɓ';wܹsaddCCre}_} FFF4#2򖷦֬YCCCdff"""'áTě7oгgOdggHKKɓ'aggp 6LhykvU"ҶsN֭l]'quUߴoEEEHIIAv#jK?RqI{,-}@QQKCOOHB! N>k.ԛ[ѣG!N!##Cpyxx8-Z5kINN\~:W1JKKѷo__ujRz ؿ?FAi7`Dž 0w\DFF:Z'~ ~-~gܼy8=jnxm۶??J|!##-[:ۃÇ\G 6h>4|=7N8#B!4Hu҉v}&Lڷo/~ȑBCvС>޾} g:::X`MMM,^XPl.K.aÆ Af"tٶmzMMMt ,@QQP|{տݻѱcG(++Gh޼9ѥK1&T.00<NNN777A׸l'NFv0}t{FCE*xhٲ%=z$699EEE0ggg$$$;VAA:tٳg۶mPO055ɓ011 Oqq1͛vAWWȨ(E6n܈nݺy8p ޽+TyR*"77fBv ===`̙x}y=.\ 4i2dΞ=+TN+N}ׯ_cɰlllj*Ě'K.>˭\6,ח  T7)v[KK 'N? ++rՑT[,QΓsСCDDDxm;~ASS  Ԗ6䶴8u]T➇ˢO"99C̙3q DEEK.\!Bi<*X,|Ȕ);}t޽bbbXLL a۷/W&77`gK,a ۰a3gSPP`^b10kkkֽ{waf͚f͚/_V̌Vdڵ uVeϟI&}%K0UUU֧OJKKYBBgffflǎLEEM80Ƙc?a&Mb!!!lӓ`PTve3lժU,55eclÆ 㱱cDz}N:b߇UW'; .0k׮gXnn.+**b,88XhSN1kkk7n;vrr2?~|iVىrJcofئMc~MHH`uРAHN̦L"|۶m @'K.e***UVϜcgҒu֭111 ;~xOԲLCCM:Uhݻw۲eKm{ʈR'߿?2%%%E=~Cȑ#ʊx<6lذ /J=x2:t„:I+󙇇kժ;sLGezzz׷\r`Zur_,*}fX```\P:tPufرUVU֪2EEEWẊSE^Xd,))Ihy~~~VG:̙#t!PҥKCdǎ,77Ϻn|>۹s~HHH77H-\d_Un[yc~n={d޽,߻w/N`aatq^n]p޽+>|W033z>W jlӦ x<cO>"l>/yzzطo޽N:ŋBeڷo۰Wx<=z0`@āOdd$oZ^p65,}߿?tttʽ*Bs>5گ ZnHܹyyyB$AMQy1bLMM5IEU6'J*u~{0gZŠ+ [P?nx7MEUIRmqӦMѿ***ήZF###jQ6sĈh֬Y2 +++ܻw|>(Fmi-5>Rqeم Я_?c4;wD@@V\YB!n(Ib'ecnZa#+ѢE xBѦMeaJII=\ƍѢE*}8q>>>y]dzgPPPR?~ ѣG *ݶA:٨Q#"55<>Ю];C^^TUU}Nxֆ|~9ʨC[[R=%%<ejYx044 +ACCCd())A__F#G~~~:u*1c`Сx5$Ce35eshkkߺlaaիWcժU8r~W,^ʘ?~r|N4m i***D_,h3Ν;#??)))033:NP[*jKU_RIs.=zgϞ $u+""'OƢE0o<B!'NÇ#//GTTV^]$󡨨(c:::ks [u ;wFpp0ڵk555#!!A"ǨNٝⓦfV>S'MMM &OG333?E!>>}ŋaoo/Tٳg駟}v?%*|PRYү_?3f֭[annbڴiPUU~SN~:۷#22C ADDHQu$YIݒW_ޔ uYQQ2d:t;vH¯Oi.\ԩS9s`ɒ%}@\h3냯 }wQ[*jK?Om9UΝR={Fw}˗sB!|F"g<&Mƒ0`L0;,Gdeeؘ(BʞR{_Rn݊|DDD`h۶-$6(ʾx >OĤ:QQRWW֭[#=FFFXl,Y_Eaii "99ӧOѣGqu[eC' cE*\t veeee ~u+76n޼+W͔u79n߾uZT-/u}DpYuyWhV"yϟǐ!C0bsB!|A!44gΜAzz::w%KHm:߻h...WSS a(M"'| ޽G(--paaݿ(eѺukY1srr=FuDfffHOOGbbǏ#))Ih.vvv'_ q@m{e̜9SJooon111֝8q3fL( ,,Lhӧ˕g;v 999puu,+{7;;[#G*ɓd /x>^0L\u$#q̆sr݉Fm) -Gs.|>h>:vUxzz۷o! !BdDsҀp=Xr%w777۷o#77;_ ׯpkQ]^€ k#G#֯_ cǎ}6-ZTa;Dվ}{|Xn>DPP.(qMnBӦMѹs^^^ Ŕ)S0}tcÆ x> /} IDATrrr m:v~@QQp5x{{G@***0rH~~~`󈊊ƒ}NwʦBJ;:q)̟?f͂ȿ=zÇ4m۶!## I!CSNXhJJJ`ffG.WVϢ̆  a055ŤIeZh l޼(--]h+LCGGhݺ509#F@>}ܹ;vp Q8uRaݺuppp5OVZ!11vƍ1|pqqqHIIӧO Ǐɓe.k׮ō7kkkm>kזwCj1gaڵӫxT[,:Ou5i3.]`ƍ\ P[Jm)PT=RpYt-~CCwPB!}5QnP᏿?c\m۶1XRR:c֭[ Z|r6~xȑ#YVVV޽{SWWgΝ;5~k׮eح[D*̾k:u.\ȊʼB}c,$$YXX0555fmm֯_ +[tiaZZZgϞӵ* .߿?;rHއ|Y'oܸ!WRRcS([/TW{ QTСCY֭ٳg˭bFFFl͛7l̘1S'OՍωYխ~ZjF^zUnofzbʊf8{,dJJJBs6m]Lhh(~X<(vߙիW _EEEֺuk6x`W6RKJJ؆ +kӦ kܸ1k߾=oXJJJ!ڶϟ?8_d[\FGGy{{WYF$ׯ>!!APk_Do3PIJCtt4PzVS֥/Q[ZԖN➇WE㯿tttXiiԎِ>>pSP[J*i 8 5$?#LLLpqI!BVDD#@@@2MMM}˗/+YBuCKK ڵ! p/_@RR8q:!B@̉F!5add @UU=z􀊊 p-ٳPTTDvv6ttt_LL @8!j]&ԉV-%W^M ]]]:u ZZZ\G"B!"N4B̘={6gر޽C-`aa;wbرuuul۶uС#R!Q2!~޽;CAzjK ƹs`ll 333iiippp6ѬY3#B!՛N4SSS9Yfa֬YUSUU7|#TOԺLlmm[<|;v:NAm)!8< AwP!B[e !BΝ;CCC.]: !JAA^ GGG pttDӦM 4B!9EhB!Ԃz쉋/rBjŋ(**'$ ==BLL 7ou$B!RԉF agg#BHUZZu֡SNhҤ ڴiYf\G#rO>:! WGEEBeЫW/hjjBWW ?Qr" ѰDV":455B!DQ'!2:t(~! ˗/:!K=1rHDFFb >~u<"g郌 $''sZk߾=N>- eeeA@BWWAAAXd УGܸq6u >}@KK Ν4B!9uB' ,_ÇCQQصk0}t?B$(777oƊ+(Xnll WWWPVD,ݻwG&MpErZiڴ)\\\, sss ˗/Gddd]${aժU\G[߇3ZjӧOS!BH=@Oμ}<gϞttt`$%% Xx6AAAѣ7ouuut!!!` £ -߼y3x<vUO.]BRR&LEEEꢰ8!!>v_?/ahhx)SQQ-bccBDmcX[[ u:tDR({ޞ(r͛ppp@۶miGB!zD#0a͛]b8x Ν aƌhѢ '':u㡮L6 EEE9s`ݻw'^B`` RB|>+]T/Kw>|UVСCٳ`=!H455}vI&{B[[Q^Ұ9;;?c <8DL6 +**BRRall,vձ 7ѫW/sE\~; #B!Ia_8x `1!+W2}66mc KHHt|>YZZnݺ[WXXȬXXQQ3f a/_q?2T*-bc^2SWWg5L wόYӦMٌ3?ܹ3{qIjGۇ`nݪcy{{3oo:?NC@m0'''ִiSּysڵkǮ]Vee%n|>gAAAuv*..ijj2WWWVPPuA!"WI49cx|5ΪZnIx O9??WƹsΖ1 !  III“'O`cc,1IֵkWܹ3qmCff&tN:!11GСCC_baΜ9000_'HILLċ/uri :ػw/D!B$D#rݻ󃅅Qi]" G1tP)]fYYYXnбcG@^^OL}mwѽ{w͛HJJ qx<8;;̙3\G!u0tPL:={:lmmb <}Ot@cݕv mmmtڕ(r򂗗BCCB!N4"7nܸ;;;<~[lUuVO& ggg(**rE.۷^^^Þ={DB!WtG֭[ 7jԨmW ǏǥKPceeeݻ5oم@899Ck׮_}UK!Qh޼9sj_B b?\!u>)z!(OL:k׮%z\"EEEx"֮]un:̜9sUD!Bu;x</|(---W… Ɔ бcGos(F]P{{{"11aaav킪*F%cBHuskeeX͉FD}nwggg]ǏGHHz_:"c8z(JJJB@}nϟ?cРAشi<'"$$sAewڅɓ'cȐ!PWW~޾}+!∎9ڴiuUZZo+V-[B!N4"7 \~NNN_0|pܿ /e56leee7N.l >BRR_!88˖-:!>n޼ WWWl޼Çǚ5k`mm۷oӓhV l_\G!u>_555/2e >|={_~*>#F)**Uq͇F*VXX___ݻG?ב!Bcaaa !4<dwR>tĚ5kd>>>:?!DU+ȑ#4mrrrDDEEW^\G"_ By$!B!#G>BL;}4Я_?Ȝϟ)))4B!:!B0OOO\G!J:u }:Qdʃ`gg$$$ʊHB!#ԉF!B}066 !24hQdʥKлwo022:!BuB!"a<#F@hh( HI "3v ggg888ٳ:!BuB!RF4\r(RΩSЦMXXXps1,]&L)SƍsB!D#gdd'qww:R-/!|N0yKHmtؿ?Q$yko-/'Obر駟aCA.B!O̐HjՊ8Ւemmm!$$H[&oyOsƬ[:uB&MЦM̚5 _:#FQRRuGy aaaի455ArttwޕRJRLܺuχk8;;ɓ8s ;#B!uR[&MpD4>#F!!!wk,Yx)6Lݻwc…FJJ ~G\xhԨ 9r$,YsŅ8 7700+W'!!!ѣ^nݺU]HH=zi%_8y$TUUu$&& GD!Bdu",X" k.ah֬ !^͛b  ߿? 355EJh^9|||`jj˗#226?~Ķm0j(:_ةS u!::>>>ر#"##u$B!"h8G"m& SNX`*,kL<Ѐ VZBrAAAѣ7ouuut!!!`I%ՙK.!)) &L"`Eaa!ͻB{1|>LMM>}E,"gƍC!77(>Z СC$&&V;}iӦI#"DII bbbP6l .P!B=F8`Z &MCZ W\All,x|UVСCٳ`=!T\XXX@SS۷o{B[[qfp=sŁrTḠE֯_;;;tUb#|2rss\'Ǐ1uTڵ ˖-… ~ !BuN=yk֬)Si&rcccȑ#B! ;;BOO"_?fXYYaϞ=R`Q\\#FT>!!F,233:::>+aoo;;;ŋ5 LĩS0rH`ܸq(((ke˖M---x{{cԉ&}L'ڍ73L>ܺ˗/͛ ܻwp+&22;v9Q&##Æ q!xyyqB! Αpѣ(..Ƹqㄖ5 ,{ "##1bĈrj2?ǃ%`Uckk[y|^K,";;[bRP[3ưm6(++#++Kw| _7orTcaΜ9000_nu렫 oookJDtǎ'1ҥKANN^JhB!D,ԉF8hݺƍEHNN,{!R}߽{~~~x<"""$;egeeaݺuppp@ǎyyyRP+ttjjjy&pqx<888ܹs\G$rO>h߾=vuRjk/00ؽ{wϟ{Y{AQ hl!FFYB1)*]PQP"EKcAĎ"ԝPy92{w8wggggJrRk׮ѣGaÆO>x" ŎDDDDD h$n9sssŮի߿?[n˗(_RʂD"yυ ʴzj(4Ldjժ¶j?իWؼy3֭ %%%ɓhܸ1+xK&sttD@@222ĎB|||| ___ :[lAnn.q 8qϟtΝ+w*n&&&bGTprr kDDDQFb""""jD#QoSnZ<33_ȑ# Ů |y:u**zTUUk׮>J[nߦtxO "z/w]4k M4)\CC=z+Wʴ^w!$$&M; Dz۱cKxyy{.`ɅsqqAp޽r堒ѣ k?cƌA||#7oDlذo޼SC O?T`}{?mۆLܿv¤Iо}{)36 IDATA@pp0sa֬Yx9^|CWWyD---!33999pߔ&*%Kֶz)-¸qʵƦ|+ĉILLCCCzqC%:uĎC P[JBhή؛kQ$HTiwާ2e =zT`*Ұ#҈f+ommma̘1N<2e 455$4l|%&vvvh֬|||ĎBDD`` ׯ?\("33=z4q%ЈRFDDDD@pvv͛)v"acczѿر{쁿?ׯ/v,""""XD#""""R0`Ϟ=bG!.66W^T*;J***z*Ə/v$""""XD#""""R0mmm_~L&;`;wDv0p@Yrr2'''@___XDDDDT FDDDD$777ܻwbG!J&!00RJJ044ę3gpx{{CMMMXDDDDTKTϳh""""jsΰ²eĎBD5ԱcSۋ޾}ӧcԨQ4h_O?TXDDDDT˰FDDDD$www\t N; @7n):u$vR9z(vHDDD 88M6;B,o߾CϞ=鉠?/44})i͚5_q̙*O$%%۶mѣi&lRXDDDDD(}ǥ;<qgN_;6m sss :bG2H$XhlmmpBt޽|װU@:*k׮aXlĎCݻw˗!郁GPVV;`͘;w.VXSSS㼗L&/,Xub޽;vر  JXf meee8:: bG2zBǎ"v*tt Þ={ĎC(55'ФIA•N%K`ѢE(΅ 0sLܸqg `فHBxO4"""jHWWx~@OORbǣRH$o>\~]8TFx-֬Y#vd 6T*űcǐoNѥKxxx !!AV^SbUv}١kװrJЈbkԨ\]]#ŋ022%Ο?/v<*QFK,; ŋqF^b!jӦ p]\|Çdž SSS ==^_ow}^0|t7n@PPN<.]8#Q "pAϸp 777XXX@"kkk?};Pvv6ѢE 8q5BVV=DDDnݺT*Űa*IOO#c7B[QRRRUVaÆɓRD%H8#QM$vY4iѣ#vDKKK 0; CFuuuXZZ"88/_Ċ+C|h۶-ϟw5ݻԩS8|p*%''GvZ޽{6m hDDDDTFDDDTC"** ׯ_GϞ=1uTkx=V\gbGDze˰dMѸ}6L t &&&ׯK࣏> .]WNRz <<<///L>>Ă P^=s$"""%i&l޼ nnn;ǘ1cpmܺu60LO>iiitTUUŎDՄL&ùs={ ''~)R)V󲳳h"\SLuPN/ƦMfB ĎF5s$"""tDDDDEv|rv)v͛7ؾ}; hT*JJJ055Ŗ-[㡥ӧ#::sbccGaؾ};|}}E/aʔ)h߾=pB<~ ,`jD#"""K޽{9r$,X̘1aaaGÆ ŎCst...Xlqx)ۇ۷͛044-?k׮صk:v(j+W{쁮.f͚gggԭ[W\T;p$)GVJ}6"""?X~/5^׏?\,YD(T3fEXxQс+nܸ˗/O>Xl.\͛chժ(ٲk.&&&xBCCq]FDDDD5hDDDD,--ٳhҤ ѣG ++K숵RӦMO?aڵyq_7p@"''GEHH zj|GpuuE˖-1vXDEE!//ҳXp!ڶm '''mшQ DDDDTsq:G""""*֭[Xb݋f͚aꫯиqc*2 :Μ9D"vZ۷022ȑ#+v.^gggܽ{/ܹsKNNFHHq9hkkc̘1prrB=*4Gtt4֮]p4k /Ѷm }tDDD@Α ֭ӧ ڵ+={&vZCII 6l@LL ŎCkd2/bG&)) ߿?6m7ob4iΈ۷1uTDEEgϞҥ <==X iiiA1p@<|6lǏ|rЈH4""""*Vjj*/ )) vvvX`:w,vZaʔ)8rܹ ZOСC P !ɰk.˗#Oe2Ν;"##C lmmK4ݻw}vݻw_={w*Gq$aÆpuuÇK.K.{Q_~I(ֻw0m4940O1M'''ܽ{RTS*))[lW:u`ʔ)] O?;wFhh(SFDDDDE4""""*1uuuHR""">c"** $͚5֬Y;wV@bb"6o,v`ee!C@KK 7oބgG֭[Ǐ။/b044N8aԨQ ͚5M$""""8#Ktt4<==qAt3fQN(yyy011Aq1*7n@>}vZ$ظq# JK.Ǒ555 >˗/Gn**s$""" a*ĭ[~zܹM4W_qbG1bbb0p@V8B^^>#!::JJ̃J/55WƪUиqc㏐J~<]r>>> Dvv6޽{ѣG L&%CEERUшHXD#"""Kl޼ސdpttķ~֭[F6mi`ŊpU _ d2͛ u֭|m6ܺu FFFJprrB-RRR~ĉhժlll^zUZ>bE4""""Ê+kbGRSSѵkW 4v;NctǢEĎCHNNd}3g΄4iR)#G رc ccb{<Y)ʊE4"""R шreggc޽Xl޽#GѪCaaa=zqj,333xW\q֭[rJ~^`XE}6燤$ :AzJ>Ls! {Ezz: RuU4шHXD#""""Őd8x -[ +ƌLT*[j3???L:gϞe[lܸ^^^1uJٳg EHHbbbЦML0...խDTTqaԯ_VVVJ6l$IQiFDDD ")^tt4<==qAc̙>}:ԩ#vj#%%]vo.v%)) q KJJvZbɘ?>ZjUP޽gϞEƍacc'Դ ZYիWѶm[?ӦM~6FDDD "޽{X~=|||ШQ#૯BƍŎV-DEE +F!vcܸqAbǡ*իWXf ֭[zaƌpuuQ8v %%% >R֢M1ܹ/_10qD4kLLTFDDD "˗ؼy3ɓ'c޼y;Zggg,>V߿bǡ*ѣG̙YfUؽ{OT*Ÿqаa ySNaaa˓FUUU#R ")hDDDDTu+VׯaggHhUVRRt/6m;N.]`РAصkq |2VZ 6,t?^x###HRL4 ZZZ۷D@@N8---B*XxTðFDDD ;QѰaCÇ˗ѭ[7XZZ"&&FxUR-[ɓbǩݑիWyyyXd ~'|'8uҥ Ѹqclڴ  fΜ~IT ݻm۶ax ggg| bccp\r͚5ѣakkCBEEv{)vލ۷ݻ044رc]]]deee˖XD.''&MŽ;ĈIDDD5hDDDDTs]rسgtuu1k,8;;nݺbGSkkk\v Ǐ Μ9Leee|sh˖-1c򠤤Au"66u;"UlDFF'N6&N3fM6%^ x"wBKK /X8+W@_߿?R)&L Q%:u*vGRQ-"|ǺuFi#o߾SVׯ_Ȑ_TVV͛1uT*ĉ\2UUU(++c5kDLH?bix9 ggg=Ů< AXX>} ]]]XYYf ѣGH(++9rdq\[eee 2ǎ+BikkŋE>ر NF=zuBKKDxx8p j SLiӠSu;v @dd$^z###XZZ (0eU- ?bbb{{{899TvШQJJKe%ɠׯ_Ok*8,Q퓖۷cʕHLLХKBmG|___L2EqK--- NM*8bh۶(++CII SP2w)37o^/^;v 007>sL2%cǎ!** HOOG^`aaǣSNY`qqq Ν;c&Lq' IDAT@-}q!tQ4̙ 6wJsΡ NEDDDhDDDDT{eggc޽D\\ ٳg$''$ ֬YWWW1cЍ7`mmO}_JJJؾ};&Mtg888@&}#6m)غu0gd2to.ݛ7oM6ܹ3Tlp!ѣC~`kk [[[hkkWHd2Ν;ٳ033T*5 =hժA@Çŋ裏|L[[O>Q""" !j-555HRXYYضmRSSAW_}իWXٳ'>x/D`&Gtt{ UUUxyyϏ4ʂfϞ<8ܺuK&//Ǐرcy[n8vn߾ >|oooBSS...QhV())[lAbb"v?~<0}tDGGggST}]vё4"""4FDDDD/111Xb"DOXEEEaʔ)HIIyWxuPNpBUUUѴiSDDD_~"$?kkk\vhIUUU̞=o߾EJJ Ξ=ׯ_JG߻\={Cl֭M*ٳg nܸ!8i$5 n*K$L<6m*r)ŋ|Bnݺ]j8NHDDDDT5k`ܹァD"~˗+8Y%''o֭[ThJC%%%^+99͚5+*++_~طo455EJW;]zHJJ*+H 6lsbҤIE>ɗ'N@bb"`aaKKK |QQQ8vrssѯ_?XZZʪEvM6šCлwo'ڵ+n߾ A%K]XDDDTshDDDDDuqܼy 2 Wd FxL4 D~<>| +Gtt|6ԩS!!!Xv- h 999~ 117nܐ OOOBWWs]Ϟ=Ctt4X@RQQQALL{ h$ 0 LGEJ\bܸq"'"""#шc8uT cJJJ}#G6ZhÇԩSE ƀаaCl9\RY+LMFDT^'H?3ڵkWhyu˗ammM6V222P^=>bcc]rF׮]amm oiu-| :/^;Fǎ`jjuU:ʾX655|K~~zj˗ ða0j(@Ev]z~2wݻDze>>>6l`nn.o˗cԨQXt)`ƍׯ.\޽{XwU:߈ʋ#шF277_kΝՕKCxrss!!!XhV^-΃```x#33S@ڳgϠ%v "oMf©S0o]Ӱa022*:vik$ = +++4o1x`4l}]u/]Cf͠={bƍ@;wwwH$ >`aa!4iҤ2wA 6 M4fΜ2퇢ܾ} #Ν;g$L&ӧOqqqB||<@N5ɓl2t G}kE^ >ttuuXzƏ_`PCC"ٳ 5! ~ l666x{s}+M;7Tϕ_}ccnÆ 011AӦMgyZ7.\N:^zׇuE/^B-[ Hs2m۱SR?xxxUV.XI| `ӦMݻ75k#F֭[ʛ9sԄ +n_E(XUU (N:իWi T`& U|$ ,^^^ŶussӦM#,^XPWW $d2y<ԩS:۷oԄɓ' &&& "Zj%YFXx@6l ̝;WPRRoiӦ 7nQF 5kc!::ZXf@hen+aA"ž={5k[w.z?[zz. Ç <~p)A@:u@HIIeeeۻaa‹/>V w &z<>[n Ǐ?.;w.УGa۶m޽{Ν; u^|YѣD" {+^z!44@ےoPc$lll2=֟O֯__`yttt}}iqߟ&Mە_}cۖ} 6l֮]+4mThڴի"tA`^+++ACCCpss+lڴIڶm+yb|A,XP{ФIں\m+k~;_u;vJ4ʕ+Qh=Gݓ/+mݡCyBF ɛ-mڴv-?^2ﳒ*,J9w }Æ +11g߷ DD"UI]D{𡠪*L>[ }z&hkk ϟAN* >\Ȱ|rAƍa͂ E& ]tz]Ǐ˗/ _~enB)n?;p+ -޽{Wh]o;vyaŋ½{E^ĕdƍnL&mmmȑ#455;;(#F( iiiv/[uZ48+Nu/%''/6lP8psە_}cۖ?s!;;[0bŊ"_BHVV,899؇խ[7P&N(4o޼bPiVֶ.JU?vJhs-P-Kp1^E7vqJO BWq|.\A =z(Qʕ+>E|*}{шHDDDDDTkٳвh<ɁT*-f„ pvvFTTBΆ#>#%==@͛ׯ_w /$ :u~DY^NBZZZеkWt1116mZ"""0zh :O_۷D"ABBݻ\ 4 6D||vĐ>,ðaмy2#OܣG ǏCWW-M?Y_ o߾&.\f͚} ;w.ZnYfX3UQU"Hk֬A ,w1ÇvhSn]h|DmWC**Zh/^yprrK1'N_|DRi+N^UVaĈEҶCE;?&M~["@GGϟ?/S]ʳȗ?x***hժU[U,RRR~ 8p>޾1118|0իW Qm"HC )M buU 4=z􀷷7 P~}L:ϟ(NӦM{E =?ʣ(cǎEjj*+WfHHH;w#:uxCPRR*N—_~cܹXx{3U%E_mo@Td2ݻˈEDDmۆX[[#,,DEġC~ /s[ڶ4{(Eo]:t(ٳg;vDZ{n̘1eZoiThРͱ`|׸x|tܿ;vСC]ou=!" %R{i噙x5+=/#F@OOSNv yƅ~:vXJ$L6 qqq033# ;wj_D 9Xbb"R)&MڵkX|y]Un G/|HeoMqsbm۶-t邅 9s&"##q HY3\p5¤I ݶV899A[nųgϰ{n,]#nܸ83gZ*P+)kkkt-Bnn.:tH\xPҼoeQ:jѢ:u-[K.C``` ֊P}]Տ|^^^HLL}뜦Pwƌ8}4\\\зo_ ,, ,p;w_|444ڻ<h(AKvK@!Y󲭮Dr+/mYGkYec4GHҶPJ9TXk5#Ɩ5%"i*`g; ^<\ޯ3#{=O> UR#رõMrrǬZʜ={֌1<СCf& zf޻4AAA&00 :l޼FJ{sss]}c9},;;ۭ?pMŒ3tl۶J֭[MǎͤIWDD%υ$3}tWUVmc$7ɓ'ۛ:'x|7tGIDATn_۵pѵ.gرfر׼]]駟AfBCCs=g=Pjѵ^cXobbbLpp 6&L0ǎ6ӧOӧcL>}ڼfSNE&$$]_O3c:ui޼ܶ/((p篹m][u*_ixkqׇKt >|}-7CvĉMQQQ5Ƙm۶4kw֥Kk׮.wUWw|9d3doСCͻ[ɓ'Wۧ*y2UGIOO8-cTtt4k \TT$)##Akw!~Zj|IO΍w)޽[>,ҡC<PeYJOO׸q< '5FnΜ9UСCu}ѣծ @SŚh@#ױcG͛7O>>> M7ݤ|۷Oׯל9s0WHܟgy{{k?ӧOM6z{:Dhք%jNR\\< kD*!Iںu"##ժU++99!өAOzgϞKpV\=ze˖ҥT\\|u$&'33ScƌfSrr 9s(>>ӡckԩK*++ӆ <!XקO${s+ꫯTQQ:H 9DSgU޽t=3۷4{liFt C111lғO>r=S>ﯹs*!!AӦMSDDp8CUqz***Qm}ddK%IΝSbb6mڤa^aav[n^}U9R-[$UV6lXxe*EN|5?n /sjٳV^3f(;;[VNN p>1 VW^^}֧~X{S```b.++S-\`-ZH?/jڸqn׿UQUTT$]fw}h #./77W}q_O4a:uJSLٳg{nmڴI~{h,RzzƍP4=<4!$___m6$m-˒nG}TnRjjhm޼Y59*I>>>uD-))19sFK./I k}0FTeO?k֬YU;uPk֭oԷo_;v$k(66Vv],KYYYնݻ̙͛7kذa3f nZt1\R;%INjcDeCZnkƋ Կj޽o-˲hH"77W:x𠒒tjȑnSZZwyG6M}+2YUkΝ5m۶?\˖-… %Ή'|]q@cטLj_{W5khȐ!U/^Gjպ#F>m&q1b:GUYFgΜQVV\[pΝ;}w7ojCBBjohh+Hu]{n]pAw}w1z'|r\͗_~)jʭfgϞUֆ I4U>}Zer… UVRR^z%yZb/gQ ^^^4iR:{BBB'*OMM&NXƤ1vZ=JII#={\ rJC-[T.]5Z<}믿w}GV-pbcce+˲UK{N֭%Iǎʕ+;StԩZ <19eggkݺuU߿|}}w^ߖeYDce?,Ib P#GAYY,˪s{qz?\˖-s=VQQ'N(88֎<1+JLLԚ5k4dȐ*/ѣGzj|҈#n@cM4ҥ$j߾Tǎ]ewq$Ww͚5:s挲*oѢEm~E͛7WZZZZ~y 22Rݻw$޽[.\w]~a_]O7|m 6BGvmVo CgϺMSuiY%*+//WAA.\Px&MT+}ڽޫtSSS㣉'>.0F!Ik׮?=#ն Վ;tAvWyIIݫ޽{I|x?{:>>饗^RIIá_-r{ѺuꫯTJKKSLLtZ #өoVڵ'|Yf~Paa""",R۶mR?^VZ=KȐ$7A4/55UӧOѣջwo幽:vzW^JKKӛo)˲T\\۷kƌ***Rzz:S^j-sIp:-~IqqqڲeJKKek$hŊJKK5j{95o޼J_]vȑ#֭ƍٳg|嗕Ç[n6mbcc~瞫q-[b nkҤI={j$? ƈ_vȢ"i#G;vaaaZpkLUYt02H^"4$ 2<PߐD*!TB $P I4h@%$Jy:rNCp>:>Vm$PEGG{:iرuNN 2O#TB $PI3IGreۢ7MIENDB`ufmt-0.2.0/src/helpers.rs000064400000000000000000000242010072674642500134240ustar 00000000000000use crate::{uDebug, uWrite, Formatter}; impl<'w, W> Formatter<'w, W> where W: uWrite + ?Sized, { /// Creates a `DebugList` builder designed to assist with creation of `uDebug` implementations /// for list-like structures. pub fn debug_list(&mut self) -> Result, W::Error> { self.write_str("[")?; if self.pretty { self.indentation += 1; } Ok(DebugList { first: true, formatter: self, }) } /// Creates a `DebugMap` builder designed to assist with creation of `uDebug` implementations /// for map-like structures. pub fn debug_map(&mut self) -> Result, W::Error> { self.write_str("{")?; if self.pretty { self.indentation += 1; } Ok(DebugMap { first: true, formatter: self, }) } /// Creates a `DebugSet` builder designed to assist with creation of `uDebug` implementations /// for set-like structures. pub fn debug_set(&mut self) -> Result, W::Error> { self.write_str("{")?; if self.pretty { self.indentation += 1; } Ok(DebugSet { first: true, formatter: self, }) } /// Creates a `DebugStruct` builder designed to assist with creation of `uDebug` implementations /// for structs. pub fn debug_struct(&mut self, name: &str) -> Result, W::Error> { self.write_str(name)?; if self.pretty { self.indentation += 1; } Ok(DebugStruct { first: true, formatter: self, }) } /// Creates a `DebugTuple` builder designed to assist with creation of `uDebug` implementations /// for tuple structs. pub fn debug_tuple(&mut self, name: &str) -> Result, W::Error> { self.write_str(name)?; if self.pretty { self.indentation += 1; } Ok(DebugTuple { fields: 0, first: true, formatter: self, unnamed: name.is_empty(), }) } } /// A struct to help with [`uDebug`] implementations. /// /// This is useful when you wish to output a formatted list of items as a part of your /// [`uDebug::fmt`] implementation. /// /// This can be constructed by the [`Formatter::debug_list`] method. pub struct DebugList<'f, 'w, W> where W: uWrite + ?Sized, { first: bool, formatter: &'f mut Formatter<'w, W>, } impl DebugList<'_, '_, W> where W: uWrite + ?Sized, { /// Adds a new entry to the list output. pub fn entry(&mut self, entry: &impl uDebug) -> Result<&mut Self, W::Error> { if self.first { self.first = false; if self.formatter.pretty { self.formatter.write_str("\n")?; } } else if !self.formatter.pretty { self.formatter.write_str(", ")?; } if self.formatter.pretty { self.formatter.indent()?; } entry.fmt(self.formatter)?; if self.formatter.pretty { self.formatter.write_str(",\n")?; } Ok(self) } /// Adds the contents of an iterator of entries to the list output. pub fn entries( &mut self, entries: impl IntoIterator, ) -> Result<&mut Self, W::Error> { for entry in entries { self.entry(&entry)?; } Ok(self) } /// Finishes output pub fn finish(&mut self) -> Result<(), W::Error> { if self.formatter.pretty { self.formatter.indentation -= 1; self.formatter.indent()?; } self.formatter.write_str("]") } } /// A struct to help with [`uDebug`] implementations. /// /// This is useful when you wish to output a formatted map as a part of your [`uDebug::fmt`] /// implementation. /// /// This can be constructed by the [`Formatter::debug_map`] method. pub struct DebugMap<'f, 'w, W> where W: uWrite + ?Sized, { first: bool, formatter: &'f mut Formatter<'w, W>, } impl DebugMap<'_, '_, W> where W: uWrite + ?Sized, { /// Adds a new entry to the map output. pub fn entry(&mut self, key: &impl uDebug, value: &impl uDebug) -> Result<&mut Self, W::Error> { if self.first { self.first = false; if self.formatter.pretty { self.formatter.write_str("\n")?; } } else if !self.formatter.pretty { self.formatter.write_str(", ")?; } if self.formatter.pretty { self.formatter.indent()?; } key.fmt(self.formatter)?; self.formatter.write_str(": ")?; value.fmt(self.formatter)?; if self.formatter.pretty { self.formatter.write_str(",\n")?; } Ok(self) } /// Adds the contents of an iterator of entries to the map output. pub fn entries( &mut self, entries: impl IntoIterator, ) -> Result<&mut Self, W::Error> { for (k, v) in entries.into_iter() { self.entry(&k, &v)?; } Ok(self) } /// Finishes output pub fn finish(&mut self) -> Result<(), W::Error> { self.formatter.write_str("}") } } /// A struct to help with [`uDebug`] implementations. /// /// This is useful when you wish to output a formatted set of items as a part of your /// [`uDebug::fmt`] implementation. /// /// This can be constructed by the [`Formatter::debug_set`] method. pub struct DebugSet<'f, 'w, W> where W: uWrite + ?Sized, { first: bool, formatter: &'f mut Formatter<'w, W>, } impl DebugSet<'_, '_, W> where W: uWrite + ?Sized, { /// Adds a new entry to the set output. pub fn entry(&mut self, entry: &impl uDebug) -> Result<&mut Self, W::Error> { if self.first { self.first = false; if self.formatter.pretty { self.formatter.write_str("\n")?; } } else if !self.formatter.pretty { self.formatter.write_str(", ")?; } if self.formatter.pretty { self.formatter.indent()?; } entry.fmt(self.formatter)?; if self.formatter.pretty { self.formatter.write_str(",\n")?; } Ok(self) } /// Adds the contents of an iterator of entries to the set output. pub fn entries( &mut self, entries: impl IntoIterator, ) -> Result<&mut Self, W::Error> { for entry in entries { self.entry(&entry)?; } Ok(self) } /// Finishes output pub fn finish(&mut self) -> Result<(), W::Error> { self.formatter.write_str("}") } } /// A struct to help with [`uDebug`] implementations. /// /// This is useful when you wish to output a formatted struct as a part of your [`uDebug::fmt`] /// implementation. /// /// This can be constructed by the [`Formatter::debug_struct`] method. pub struct DebugStruct<'f, 'w, W> where W: uWrite + ?Sized, { first: bool, formatter: &'f mut Formatter<'w, W>, } impl DebugStruct<'_, '_, W> where W: uWrite + ?Sized, { /// Adds a new field to the generated struct output. pub fn field(&mut self, name: &str, value: &impl uDebug) -> Result<&mut Self, W::Error> { if self.first { self.first = false; self.formatter.write_str(" {")?; if self.formatter.pretty { self.formatter.write_str("\n")?; } else { self.formatter.write_str(" ")?; } } else if !self.formatter.pretty { self.formatter.write_str(", ")?; } if self.formatter.pretty { self.formatter.indent()?; } self.formatter.write_str(name)?; self.formatter.write_str(": ")?; value.fmt(self.formatter)?; if self.formatter.pretty { self.formatter.write_str(",\n")?; } Ok(self) } /// Finishes output pub fn finish(&mut self) -> Result<(), W::Error> { if self.formatter.pretty { self.formatter.indentation -= 1; } if !self.first { if self.formatter.pretty { self.formatter.indent()?; } else { self.formatter.write_str(" ")?; } self.formatter.write_str("}")?; } Ok(()) } } /// A struct to help with [`uDebug`] implementations. /// /// This is useful when you wish to output a formatted tuple as a part of your [`uDebug::fmt`] /// implementation. /// /// This can be constructed by the [`Formatter::debug_tuple`] method. pub struct DebugTuple<'f, 'w, W> where W: uWrite + ?Sized, { fields: u8, first: bool, formatter: &'f mut Formatter<'w, W>, unnamed: bool, } impl DebugTuple<'_, '_, W> where W: uWrite + ?Sized, { /// Adds a new field to the generated tuple struct output. pub fn field(&mut self, value: &impl uDebug) -> Result<&mut Self, W::Error> { self.fields += 1; if self.first { self.first = false; self.formatter.write_str("(")?; if self.formatter.pretty { self.formatter.write_str("\n")?; } } else if !self.formatter.pretty { self.formatter.write_str(", ")?; } if self.formatter.pretty { self.formatter.indent()?; } value.fmt(self.formatter)?; if self.formatter.pretty { self.formatter.write_str(",\n")?; } Ok(self) } /// Finishes output pub fn finish(&mut self) -> Result<(), W::Error> { if self.formatter.pretty { self.formatter.indentation -= 1; } if !self.first { if self.formatter.pretty { self.formatter.indent()?; } else if self.unnamed && self.fields == 1 { // this is a one-element tuple so we need a trailing comma self.formatter.write_str(",")?; } self.formatter.write_str(")")?; } Ok(()) } } ufmt-0.2.0/src/impls/array.rs000064400000000000000000000011340072674642500142240ustar 00000000000000use crate::{uDebug, uWrite, Formatter}; macro_rules! array { ($($N:expr),+) => { $( impl uDebug for [T; $N] where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { <[T] as uDebug>::fmt(self, f) } } )+ } } array!( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ); ufmt-0.2.0/src/impls/core.rs000064400000000000000000000101050072674642500140340ustar 00000000000000use crate::{uDebug, uDisplay, uWrite, Formatter}; impl uDebug for bool { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { if *self { f.write_str("true") } else { f.write_str("false") } } } impl uDisplay for bool { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } // FIXME this (`escape_debug`) contains a panicking branch // impl uDebug for char { // fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> // where // W: uWrite + ?Sized, // { // f.write_str("'")?; // for c in self.escape_debug() { // f.write_char(c)? // } // f.write_str("'") // } // } impl uDisplay for char { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.write_char(*self) } } impl uDebug for [T] where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_list()?.entries(self)?.finish() } } // FIXME this (`escape_debug`) contains a panicking branch // impl uDebug for str { // fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> // where // W: uWrite + ?Sized, // { // f.write_str("\"")?; // let mut from = 0; // for (i, c) in self.char_indices() { // let esc = c.escape_debug(); // // If char needs escaping, flush backlog so far and write, else skip // if esc.len() != 1 { // f.write_str( // self.get(from..i) // .unwrap_or_else(|| unsafe { assume_unreachable!() }), // )?; // for c in esc { // f.write_char(c)?; // } // from = i + c.len_utf8(); // } // } // f.write_str( // self.get(from..) // .unwrap_or_else(|| unsafe { assume_unreachable!() }), // )?; // f.write_str("\"") // } // } impl uDisplay for str { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.write_str(self) } } impl uDebug for &'_ T where T: uDebug + ?Sized, { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDisplay for &'_ T where T: uDisplay + ?Sized, { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for &'_ mut T where T: uDebug + ?Sized, { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDisplay for &'_ mut T where T: uDisplay + ?Sized, { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for Option where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { match self { None => f.write_str("None"), Some(x) => f.debug_tuple("Some")?.field(x)?.finish(), } } } impl uDebug for Result where T: uDebug, E: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { match self { Err(e) => f.debug_tuple("Err")?.field(e)?.finish(), Ok(x) => f.debug_tuple("Ok")?.field(x)?.finish(), } } } ufmt-0.2.0/src/impls/hex.rs000064400000000000000000000042200072674642500136710ustar 00000000000000use crate::{uDisplayHex, uWrite, Formatter, HexOptions}; macro_rules! hex_format { ($buf:expr, $val:expr, $options:expr) => {{ let mut cursor = $buf.len(); let mut val = $val; if val <= 0 { cursor -= 1; $buf[cursor] = b'0'; } else { while val != 0 && cursor > 0 { let rem = val & 0xf; cursor -= 1; $buf[cursor] = hex_digit(rem as u8, $options.upper_case); val >>= 4; } } unsafe { core::str::from_utf8_unchecked(&$buf[cursor..]) } }}; } macro_rules! hex_pattern { ($itype: ty, $utype:ty) => { impl uDisplayHex for $itype { fn fmt_hex( &self, fmt: &mut Formatter<'_, W>, options: HexOptions, ) -> Result<(), W::Error> where W: uWrite + ?Sized, { let positive = if false && // the standard rust library doesn't format negative numbers with a minus sign *self < 0 { fmt.write_char('-')?; ((!*self) as $utype).wrapping_add(1) } else { *self as $utype }; <$utype as uDisplayHex>::fmt_hex(&positive, fmt, options) } } impl uDisplayHex for $utype { fn fmt_hex( &self, fmt: &mut Formatter<'_, W>, options: HexOptions, ) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buffer = [b'0'; 2 * core::mem::size_of::<$utype>()]; let hex_string = hex_format!(buffer, *self, options); options.with_stuff(fmt, hex_string) } } }; } hex_pattern! {i8, u8} hex_pattern! {i16, u16} hex_pattern! {i32, u32} hex_pattern! {i64, u64} hex_pattern! {i128, u128} hex_pattern! {isize, usize} fn hex_digit(val: u8, upper_case: bool) -> u8 { if val < 10 { b'0' + val } else { (if upper_case { b'A' } else { b'a' }) + (val - 10) } } ufmt-0.2.0/src/impls/ixx.rs000064400000000000000000000126000072674642500137160ustar 00000000000000use core::{mem::MaybeUninit, slice, str}; use crate::{uDebug, uDisplay, uWrite, Formatter}; macro_rules! ixx { ($uxx:ty, $n:expr, $buf:expr) => {{ let ptr = $buf.as_mut_ptr().cast::(); let len = $buf.len(); let n = $n; let negative = n.is_negative(); let mut n = if negative { match n.checked_abs() { Some(n) => n as $uxx, None => <$uxx>::max_value() / 2 + 1, } } else { n as $uxx }; let mut i = len - 1; loop { unsafe { ptr.add(i).write((n % 10) as u8 + b'0') } n /= 10; if n == 0 { break; } else { i -= 1; } } if negative { i -= 1; unsafe { ptr.add(i).write(b'-') } } unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr.add(i), len - i)) } }}; } fn isize(n: isize, buf: &mut [MaybeUninit]) -> &str { ixx!(usize, n, buf) } impl uDebug for i8 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 4]; f.write_str(isize(isize::from(*self), &mut buf)) } } impl uDisplay for i8 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for i16 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 6]; f.write_str(isize(isize::from(*self), &mut buf)) } } impl uDisplay for i16 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for i32 { #[cfg(not(target_pointer_width = "16"))] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 11]; f.write_str(isize(*self as isize, &mut buf)) } #[cfg(target_pointer_width = "16")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 11]; let s = ixx!(u32, *self, buf); f.write_str(s) } } impl uDisplay for i32 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for i64 { #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 20]; let s = ixx!(u64, *self, buf); f.write_str(s) } #[cfg(target_pointer_width = "64")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 20]; f.write_str(isize(*self as isize, &mut buf)) } } impl uDisplay for i64 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for i128 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 40]; let s = ixx!(u128, *self, buf); f.write_str(s) } } impl uDisplay for i128 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for isize { #[cfg(target_pointer_width = "16")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i16), f) } #[cfg(target_pointer_width = "32")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i32), f) } #[cfg(target_pointer_width = "64")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i64), f) } } impl uDisplay for isize { #[cfg(target_pointer_width = "16")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i16), f) } #[cfg(target_pointer_width = "32")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i32), f) } #[cfg(target_pointer_width = "64")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as i64), f) } } ufmt-0.2.0/src/impls/nz.rs000064400000000000000000000022150072674642500135360ustar 00000000000000use core::num::{ NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }; use crate::{uDebug, uDisplay, uWrite, Formatter}; macro_rules! nz { ($($NZ:ident : $inner:ident,)*) => { $( impl uDebug for $NZ { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { <$inner as uDebug>::fmt(&self.get(), f) } } impl uDisplay for $NZ { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { <$inner as uDisplay>::fmt(&self.get(), f) } } )* } } nz!( NonZeroI16: i16, NonZeroI32: i32, NonZeroI64: i64, NonZeroI8: i8, NonZeroIsize: isize, NonZeroU16: u16, NonZeroU32: u32, NonZeroU64: u64, NonZeroU8: u8, NonZeroUsize: usize, ); ufmt-0.2.0/src/impls/ptr.rs000064400000000000000000000033450072674642500137210ustar 00000000000000use core::{mem::MaybeUninit, slice, str}; use crate::{uDebug, uWrite, Formatter}; macro_rules! hex { ($self:expr, $f:expr, $N:expr) => {{ let mut buf = [MaybeUninit::::uninit(); $N]; let i = hex(*$self as usize, &mut buf); unsafe { $f.write_str(str::from_utf8_unchecked(slice::from_raw_parts( buf.as_mut_ptr().add(i).cast(), $N - i, ))) } }}; } fn hex(mut n: usize, buf: &mut [MaybeUninit]) -> usize { let ptr = buf.as_mut_ptr().cast::(); let len = buf.len(); let mut i = len - 1; loop { let d = (n % 16) as u8; unsafe { ptr.add(i) .write(if d < 10 { d + b'0' } else { (d - 10) + b'a' }); } n /= 16; i -= 1; if n == 0 { break; } } unsafe { ptr.add(i).write(b'x') } i -= 1; unsafe { ptr.add(i).write(b'0') } i } impl uDebug for *const T { #[cfg(target_pointer_width = "16")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { hex!(self, f, 6) } #[cfg(target_pointer_width = "32")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { hex!(self, f, 10) } #[cfg(target_pointer_width = "64")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { hex!(self, f, 18) } } impl uDebug for *mut T { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { (*self as *const T).fmt(f) } } ufmt-0.2.0/src/impls/std.rs000064400000000000000000000040620072674642500137030ustar 00000000000000use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use crate::{uDebug, uDisplay, uWrite, Formatter}; impl uDebug for Box where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDisplay for Box where T: uDisplay, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for BTreeMap where K: uDebug, V: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_map()?.entries(self)?.finish() } } impl uDebug for BTreeSet where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_set()?.entries(self)?.finish() } } impl uDebug for HashMap where K: uDebug, V: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_map()?.entries(self)?.finish() } } impl uDebug for HashSet where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_set()?.entries(self)?.finish() } } // TODO // impl uDebug for String { // fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> // where // W: uWrite + ?Sized, // { // ::fmt(self, f) // } // } impl uDisplay for String { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for Vec where T: uDebug, { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { <[T] as uDebug>::fmt(self, f) } } ufmt-0.2.0/src/impls/tuple.rs000064400000000000000000000022320072674642500142370ustar 00000000000000use crate::{uDebug, uWrite, Formatter}; macro_rules! tuple { ($($T:ident),*; $($i:tt),*) => { impl<$($T,)*> uDebug for ($($T,)*) where $($T: uDebug,)* { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.debug_tuple("")?$(.field(&self.$i)?)*.finish() } } } } impl uDebug for () { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { f.write_str("()") } } tuple!(A; 0); tuple!(A, B; 0, 1); tuple!(A, B, C; 0, 1, 2); tuple!(A, B, C, D; 0, 1, 2, 3); tuple!(A, B, C, D, E; 0, 1, 2, 3, 4); tuple!(A, B, C, D, E, F; 0, 1, 2, 3, 4, 5); tuple!(A, B, C, D, E, F, G; 0, 1, 2, 3, 4, 5, 6); tuple!(A, B, C, D, E, F, G, H; 0, 1, 2, 3, 4, 5, 6, 7); tuple!(A, B, C, D, E, F, G, H, I; 0, 1, 2, 3, 4, 5, 6, 7, 8); tuple!(A, B, C, D, E, F, G, H, I, J; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); tuple!(A, B, C, D, E, F, G, H, I, J, K; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); tuple!(A, B, C, D, E, F, G, H, I, J, K, L; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); ufmt-0.2.0/src/impls/uxx.rs000064400000000000000000000117700072674642500137410ustar 00000000000000use core::{mem::MaybeUninit, slice, str}; use crate::{uDebug, uDisplay, uWrite, Formatter}; macro_rules! uxx { ($n:expr, $buf:expr) => {{ let ptr = $buf.as_mut_ptr().cast::(); let len = $buf.len(); let mut n = $n; let mut i = len - 1; loop { unsafe { ptr.add(i).write((n % 10) as u8 + b'0') } n /= 10; if n == 0 { break; } else { i -= 1; } } unsafe { str::from_utf8_unchecked(slice::from_raw_parts(ptr.add(i), len - i)) } }}; } fn usize(n: usize, buf: &mut [MaybeUninit]) -> &str { uxx!(n, buf) } impl uDebug for u8 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 3]; f.write_str(usize(usize::from(*self), &mut buf)) } } impl uDisplay for u8 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for u16 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 5]; f.write_str(usize(usize::from(*self), &mut buf)) } } impl uDisplay for u16 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for u32 { #[cfg(not(target_pointer_width = "16"))] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 10]; f.write_str(usize(*self as usize, &mut buf)) } #[cfg(target_pointer_width = "16")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 10]; let s = uxx!(*self, buf); f.write_str(s) } } impl uDisplay for u32 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for u64 { #[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 20]; let s = uxx!(*self, buf); f.write_str(s) } #[cfg(target_pointer_width = "64")] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::uninit(); 20]; f.write_str(usize(*self as usize, &mut buf)) } } impl uDisplay for u64 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for u128 { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { let mut buf = [MaybeUninit::::uninit(); 39]; let s = uxx!(*self, buf); f.write_str(s) } } impl uDisplay for u128 { #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(self, f) } } impl uDebug for usize { #[cfg(target_pointer_width = "16")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u16), f) } #[cfg(target_pointer_width = "32")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u32), f) } #[cfg(target_pointer_width = "64")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u64), f) } } impl uDisplay for usize { #[cfg(target_pointer_width = "16")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u16), f) } #[cfg(target_pointer_width = "32")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u32), f) } #[cfg(target_pointer_width = "64")] #[inline(always)] fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { ::fmt(&(*self as u64), f) } } ufmt-0.2.0/src/impls.rs000064400000000000000000000001550072674642500131100ustar 00000000000000mod array; mod core; mod hex; mod ixx; mod nz; mod ptr; #[cfg(feature = "std")] mod std; mod tuple; mod uxx; ufmt-0.2.0/src/lib.rs000064400000000000000000000324670072674642500125450ustar 00000000000000//! `μfmt`, a (6-40x) smaller, (2-9x) faster and panic-free alternative to `core::fmt` //! //! # Design goals //! //! From highest priority to lowest priority //! //! - Optimized for binary size and speed (rather than for compilation time) //! - No dynamic dispatch in generated code //! - No panicking branches in generated code, when optimized //! - No recursion where possible //! //! # Features //! //! - [`Debug`] and [`Display`]-like traits //! - [`core::write!`][uwrite]-like macro //! - A generic [`Formatter<'_, impl uWrite>`][formatter] instead of a single `core::Formatter`; the //! [`uWrite`] trait has an associated error type so each writer can choose its error type. For //! example, the implementation for `std::String` uses [`Infallible`] as its error type. //! - [`core::fmt::Formatter::debug_struct`][debug_struct]-like API //! - [`#[derive(uDebug)]`][derive] //! - Pretty formatting (`{:#?}`) for `uDebug` //! - Hexadecimal formatting (`{:x}`) of integer primitives (e.g. `i32`) -- currently cannot be extended to other types //! //! [`Debug`]: trait.uDebug.html //! [`Display`]: trait.uDisplay.html //! [uwrite]: index.html#reexports //! [formatter]: struct.Formatter.html //! [`uWrite`]: trait.uWrite.html //! [`Infallible`]: https://doc.rust-lang.org/core/convert/enum.Infallible.html //! [debug_struct]: struct.Formatter.html#method.debug_struct //! [derive]: derive/index.html //! //! # Non-features //! //! These are out of scope //! //! - Padding, alignment and other formatting options //! - Formatting floating point numbers //! //! # Examples //! //! - `uwrite!` / `uwriteln!` //! //! ``` //! use ufmt::{derive::uDebug, uwrite}; //! //! #[derive(uDebug)] //! struct Pair { x: u32, y: u32 } //! //! let mut s = String::new(); //! let pair = Pair { x: 1, y: 2 }; //! uwrite!(s, "{:?}", pair).unwrap(); //! assert_eq!(s, "Pair { x: 1, y: 2 }"); //! ``` //! //! - Hexadecimal formatting //! //! Lowercase (`{:x}`), uppercase (`{:X}`), `0x`-prefix (`{:#x}`) and padding (`{:02x}`) are //! supported on primitive integer types. //! //! ``` //! use ufmt::uwrite; //! //! let mut s = String::new(); //! uwrite!(s, "{:#06x}", 0x42); //! assert_eq!(s, "0x0042"); //! ``` //! //! - implementing `uWrite` //! //! When implementing the `uWrite` trait you should prefer the `ufmt_write::uWrite` crate over the //! `ufmt::uWrite` crate for better forward compatibility. //! //! ``` //! use core::convert::Infallible; //! //! use ufmt_write::uWrite; //! //! struct MyWriter; //! //! impl uWrite for MyWriter { //! type Error = Infallible; //! //! fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { //! // .. //! Ok(()) //! } //! } //! ``` //! //! - writing a `macro_rules!` macro that uses `uwrite!` (or `uwriteln!`). //! //! ``` //! // like `std::format!` it returns a `std::String` but uses `uwrite!` instead of `write!` //! macro_rules! uformat { //! // IMPORTANT use `tt` fragments instead of `expr` fragments (i.e. `$($exprs:expr),*`) //! ($($tt:tt)*) => {{ //! let mut s = String::new(); //! match ufmt::uwrite!(&mut s, $($tt)*) { //! Ok(_) => Ok(s), //! Err(e) => Err(e), //! } //! }} //! } //! ``` //! //! # Benchmarks //! //! The benchmarks ran on a ARM Cortex-M3 chip (`thumbv7m-none-eabi`). //! //! The benchmarks were compiled with `nightly-2019-05-01`, `-C opt-level=3`, `lto = true`, //! `codegen-units = 1`. //! //! In all benchmarks `x = i32::MIN` and `y = i32::MIN` plus some obfuscation was applied to //! prevent const-propagation of the `*write!` arguments. //! //! The unit of time is one core clock cycle: 125 ns (8 MHz) //! //! The `.text` and `.rodata` columns indicate the delta (in bytes) when commenting out the //! `*write!` statement. //! //! |Code |Time|% |`.text`|% |`.rodata`|% | //! |------------------------------------------|----|---------|-------|---------|---------|--------| //! |`write!("Hello, world!")` |154 |~ |1906 |~ |248 |~ | //! |`uwrite!("Hello, world!")` |20 |**13.0%**|34 |**1.8%** |16 |**6.5%**| //! |`write!(w, "{}", 0i32)` |256 |~ |1958 |~ |232 |~ | //! |`uwrite!(w, "{}", 0i32)` |37 |**14.5%**|288 |**14.7%**|0 |**0%** | //! |`write!(w, "{}", x)` |381 |~ | //! |`uwrite!(w, "{}", x)` |295 |77.4% | //! |`write!(w, "{:?}", Pair { x: 0, y: 0 })` |996 |~ |4704 |~ |312 |~ | //! |`uwrite!(w, "{:?}", Pair { x: 0, y: 0 })` |254 |**25.5%**|752 |**16.0%**|24 |**7.7%**| //! |`write!(w, "{:?}", Pair { x, y })` |1264|~ | //! |`uwrite!(w, "{:?}", Pair { x, y })` |776 |61.4% | //! |`write!(w, "{:#?}", Pair { x: 0, y: 0 })` |2853|~ |4710 |~ |348 |~ | //! |`uwrite!(w, "{:#?}", Pair { x: 0, y: 0 })`|301 |**10.6%**|754 |**16.0%**|24 |**6.9%**| //! |`write!(w, "{:#?}", Pair { x, y })` |3693|~ | //! |`uwrite!(w, "{:#?}", Pair { x, y })` |823 |**22.3%**| //! //! //! Benchmark program: //! //! ``` ignore //! static X: AtomicI32 = AtomicI32::new(i32::MIN); // or `0` //! static Y: AtomicI32 = AtomicI32::new(i32::MIN); // or `0` //! //! #[exception] //! fn PendSV() { //! // read DWT.CYCCNT here //! //! let x = X.load(Ordering::Relaxed); //! let y = Y.load(Ordering::Relaxed); //! //! let p = Pair { x, y }; //! //! uwrite!(&mut W, "{:#?}", p).ok(); //! //! // write!(&mut W, "{:#?}", p).ok(); //! //! asm::bkpt(); // read DWT.CYCCNT here //! } //! ``` //! //! Writer used in the benchmarks: //! //! ``` //! use core::{convert::Infallible, fmt, ptr}; //! //! use ufmt::uWrite; //! //! struct W; //! //! impl uWrite for W { //! type Error = Infallible; //! //! fn write_str(&mut self, s: &str) -> Result<(), Infallible> { //! s.as_bytes() //! .iter() //! .for_each(|b| unsafe { drop(ptr::read_volatile(b)) }); //! //! Ok(()) //! } //! } //! //! impl fmt::Write for W { //! fn write_str(&mut self, s: &str) -> fmt::Result { //! s.as_bytes() //! .iter() //! .for_each(|b| unsafe { drop(ptr::read_volatile(b)) }); //! //! Ok(()) //! } //! } //! ``` //! //! # Minimum Supported Rust Version (MSRV) //! //! This crate does *not* have a Minimum Supported Rust Version (MSRV) and may make use of language //! features and API in the standard library available in the latest stable Rust version. //! //! In other words, changes in the Rust version requirement of this crate are not considered semver //! breaking change and may occur in patch version release. #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] #![deny(warnings)] // this lets us use `uwrite!` in the test suite #[allow(unused_extern_crates)] #[cfg(test)] extern crate self as ufmt; use core::str; pub use ufmt_write::uWrite; /// Write formatted data into a buffer /// /// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be /// formatted according to the specified format string and the result will be passed to the writer. /// The writer must have type `[&mut] impl uWrite` or `[&mut] ufmt::Formatter<'_, impl uWrite>`. The /// macro returns the associated `Error` type of the `uWrite`-r. /// /// The syntax is similar to [`core::write!`] but only a handful of argument types are accepted: /// /// [`core::write!`]: https://doc.rust-lang.org/core/macro.write.html /// /// - `{}` - `uDisplay` /// - `{:?}` - `uDebug` /// - `{:#?}` - "pretty" `uDebug` /// /// Named parameters and "specified" positional parameters (`{0}`) are not supported. /// /// `{{` and `}}` can be used to escape braces. pub use ufmt_macros::uwrite; /// Write formatted data into a buffer, with a newline appended /// /// See [`uwrite!`](macro.uwrite.html) for more details pub use ufmt_macros::uwriteln; pub use crate::helpers::{DebugList, DebugMap, DebugStruct, DebugTuple}; mod helpers; mod impls; /// Derive macros pub mod derive { pub use ufmt_macros::uDebug; } /// Just like `core::fmt::Debug` #[allow(non_camel_case_types)] pub trait uDebug { /// Formats the value using the given formatter fn fmt(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized; } /// Just like `core::fmt::Display` #[allow(non_camel_case_types)] pub trait uDisplay { /// Formats the value using the given formatter fn fmt(&self, _: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized; } /// HEADS UP this is currently an implementation detail and not subject to semver guarantees. /// do NOT use this outside the `ufmt` crate // options for formatting hexadecimal numbers #[doc(hidden)] pub struct HexOptions { /// when we need to use digits a-f, should they be upper case instead? pub upper_case: bool, /// when we are padding to a target length, what character should we pad using? pub pad_char: u8, /// when we are padding to a target length, how long should our string be? pub pad_length: usize, /// should we include a 0x prefix? (also controlled by upper_case) pub ox_prefix: bool, } impl HexOptions { /// applies the various padding/prefix options while writing the `payload` string pub fn with_stuff( &self, fmt: &mut Formatter<'_, W>, payload: &str, ) -> Result<(), ::Error> { let pad_before = self.ox_prefix && self.pad_char == b' '; let pad = self.pad_length as isize - (if self.ox_prefix { 2 } else { 0 } + payload.len()) as isize; let do_pad = |fmt: &mut Formatter<'_, W>, pad: isize| -> Result<(), ::Error> { if pad > 0 { for _ in 0..pad { // miri considers the `write_char` defined in `ufmt-write` v0.1.0 unsound // to workaround the issue we use `write_str` instead of `write_char` fmt.write_str(unsafe { str::from_utf8_unchecked(&[self.pad_char]) })?; } } Ok(()) }; let do_prefix = |fmt: &mut Formatter<'_, W>, go: bool, upper_case: bool| -> Result<(), ::Error> { if go { fmt.write_str(if upper_case { "0X" } else { "0x" }) } else { Ok(()) } }; if pad_before { do_pad(fmt, pad)?; do_prefix(fmt, self.ox_prefix, self.upper_case)?; } else { do_prefix(fmt, self.ox_prefix, self.upper_case)?; do_pad(fmt, pad)?; } fmt.write_str(payload) } } /// HEADS UP this is currently an implementation detail and not subject to semver guarantees. /// do NOT use this outside the `ufmt` crate // just like std::fmt::LowerHex #[doc(hidden)] #[allow(non_camel_case_types)] pub trait uDisplayHex { /// Formats the value using the given formatter fn fmt_hex(&self, _: &mut Formatter<'_, W>, options: HexOptions) -> Result<(), W::Error> where W: uWrite + ?Sized; } /// Configuration for formatting #[allow(non_camel_case_types)] pub struct Formatter<'w, W> where W: uWrite + ?Sized, { indentation: u8, pretty: bool, writer: &'w mut W, } impl<'w, W> Formatter<'w, W> where W: uWrite + ?Sized, { /// Creates a formatter from the given writer pub fn new(writer: &'w mut W) -> Self { Self { indentation: 0, pretty: false, writer, } } /// Execute the closure with pretty-printing enabled pub fn pretty( &mut self, f: impl FnOnce(&mut Self) -> Result<(), W::Error>, ) -> Result<(), W::Error> { let pretty = self.pretty; self.pretty = true; f(self)?; self.pretty = pretty; Ok(()) } /// Writes a character to the underlying buffer contained within this formatter. pub fn write_char(&mut self, c: char) -> Result<(), W::Error> { self.writer.write_char(c) } /// Writes a string slice to the underlying buffer contained within this formatter. pub fn write_str(&mut self, s: &str) -> Result<(), W::Error> { self.writer.write_str(s) } /// Write whitespace according to the current `self.indentation` fn indent(&mut self) -> Result<(), W::Error> { for _ in 0..self.indentation { self.write_str(" ")?; } Ok(()) } } // Implementation detail of the `uwrite*!` macros #[doc(hidden)] pub trait UnstableDoAsFormatter { type Writer: uWrite + ?Sized; fn do_as_formatter( &mut self, f: impl FnOnce(&mut Formatter<'_, Self::Writer>) -> Result<(), ::Error>, ) -> Result<(), ::Error>; } impl UnstableDoAsFormatter for W where W: uWrite + ?Sized, { type Writer = W; fn do_as_formatter( &mut self, f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>, ) -> Result<(), W::Error> { f(&mut Formatter::new(self)) } } impl UnstableDoAsFormatter for Formatter<'_, W> where W: uWrite + ?Sized, { type Writer = W; fn do_as_formatter( &mut self, f: impl FnOnce(&mut Formatter<'_, W>) -> Result<(), W::Error>, ) -> Result<(), W::Error> { f(self) } } ufmt-0.2.0/tests/gh36.rs000064400000000000000000000013070072674642500131060ustar 00000000000000//! `uwrite!` works in presence of a third-party `Ok` constructor mod third_party { #[allow(dead_code)] pub enum Result { Ok(A), Err(B), } } use ufmt::{derive::uDebug, uwrite, uwriteln}; #[allow(unused_imports)] use crate::third_party::Result::{self, Err, Ok}; #[derive(uDebug)] struct Pair { x: u32, y: u32, } #[test] fn uwrite() { let mut s = String::new(); let pair = Pair { x: 1, y: 2 }; uwrite!(s, "{:?}", pair).unwrap(); assert_eq!(s, "Pair { x: 1, y: 2 }"); } #[test] fn uwriteln() { let mut s = String::new(); let pair = Pair { x: 1, y: 2 }; uwriteln!(s, "{:?}", pair).unwrap(); assert_eq!(s, "Pair { x: 1, y: 2 }\n"); } ufmt-0.2.0/tests/vs-std-write.rs000064400000000000000000000171110072674642500147070ustar 00000000000000use core::convert::Infallible; use std::collections::{BTreeMap, BTreeSet}; use ufmt::{derive::uDebug, uDebug, uWrite, uwrite, uwriteln, Formatter}; macro_rules! uformat { ($($tt:tt)*) => {{ let mut s = String::new(); #[allow(unreachable_code)] match ufmt::uwrite!(&mut s, $($tt)*) { Ok(_) => Ok(s), Err(e) => Err(e), } }}; } macro_rules! cmp { ($($tt:tt)*) => { assert_eq!( uformat!($($tt)*), Ok(format!($($tt)*)), ) } } #[test] fn core() { cmp!("{:?}", None::); cmp!("{:#?}", None::); cmp!("{:?}", Some(0)); cmp!("{:#?}", Some(0)); cmp!("{:?}", Ok::<_, ()>(1)); cmp!("{:#?}", Ok::<_, ()>(1)); cmp!("{:?}", Err::<(), _>(2)); cmp!("{:#?}", Err::<(), _>(2)); } #[test] fn recursion() { #[derive(uDebug, Debug)] struct Node { value: i32, next: Option>, } fn x() -> Node { let tail = Node { value: 0, next: None, }; Node { value: 1, next: Some(Box::new(tail)), } } cmp!("{:?}", x()); cmp!("{:#?}", x()); } #[test] fn uxx() { cmp!("{}", 0u8); cmp!("{}", 10u8); cmp!("{}", 100u8); // extreme values cmp!("{}", u8::max_value()); cmp!("{}", u16::max_value()); cmp!("{}", u32::max_value()); cmp!("{}", u64::max_value()); cmp!("{}", u128::max_value()); cmp!("{}", usize::max_value()); } #[test] fn ixx() { // sanity check cmp!("{}", 0i8); cmp!("{}", 10i8); cmp!("{}", 100i8); // extreme values cmp!("{}", i8::min_value()); cmp!("{}", i8::max_value()); cmp!("{}", i16::min_value()); cmp!("{}", i16::max_value()); cmp!("{}", i32::min_value()); cmp!("{}", i32::max_value()); cmp!("{}", i64::min_value()); cmp!("{}", i64::max_value()); cmp!("{}", i128::min_value()); cmp!("{}", i128::max_value()); cmp!("{}", isize::min_value()); cmp!("{}", isize::max_value()); } #[test] fn fmt() { cmp!("Hello, world!"); cmp!("The answer is {}", 42); } #[test] fn map() { fn x() -> BTreeMap { let mut m = BTreeMap::new(); m.insert(1, 2); m.insert(3, 4); m } cmp!("{:?}", BTreeMap::<(), ()>::new()); cmp!("{:?}", x()); cmp!("{:#?}", BTreeMap::<(), ()>::new()); cmp!("{:#?}", x()); } #[test] fn set() { fn x() -> BTreeSet { let mut m = BTreeSet::new(); m.insert(1); m.insert(3); m } cmp!("{:?}", BTreeSet::<()>::new()); cmp!("{:?}", x()); cmp!("{:#?}", BTreeSet::<()>::new()); cmp!("{:#?}", x()); } #[test] fn struct_() { #[derive(Debug, uDebug)] struct Braces {} #[derive(Debug, uDebug)] struct Parens(); #[derive(Debug, Default, uDebug)] struct I32(i32); #[derive(Debug, Default, uDebug)] struct Tuple(i32, i32); #[derive(Debug, Default, uDebug)] struct Pair { x: i32, y: i32, } #[derive(Debug, Default, uDebug)] struct Nested { first: Pair, second: Pair, } cmp!("{:?}", Braces {}); cmp!("{:?}", Parens()); cmp!("{:?}", I32::default()); cmp!("{:?}", Tuple::default()); cmp!("{:?}", Pair::default()); cmp!("{:?}", Nested::default()); cmp!("{:#?}", Braces {}); cmp!("{:#?}", Parens()); cmp!("{:#?}", I32::default()); cmp!("{:#?}", Tuple::default()); cmp!("{:#?}", Pair::default()); cmp!("{:#?}", Nested::default()); } #[test] fn enum_() { #[derive(Debug, uDebug)] enum X { A, B(u8, u16), C { x: u8, y: u16 }, } cmp!("{:?}", X::A); cmp!("{:?}", X::B(0, 1)); cmp!("{:?}", X::C { x: 0, y: 1 }); cmp!("{:#?}", X::A); cmp!("{:#?}", X::B(0, 1)); cmp!("{:#?}", X::C { x: 0, y: 1 }); } #[test] fn ptr() { cmp!("{:?}", 1 as *const u8); cmp!("{:?}", 0xf as *const u8); cmp!("{:?}", 0xff as *const u8); cmp!("{:?}", 0xfff as *const u8); cmp!("{:?}", 0xffff as *const u8); cmp!("{:?}", 0xfffff as *const u8); cmp!("{:?}", 0xffffff as *const u8); cmp!("{:?}", 0xfffffff as *const u8); cmp!("{:?}", 0xffffffff as *const u8); #[cfg(target_pointer_width = "64")] cmp!("{:?}", 0xfffffffff as *const u8); } #[test] fn tuples() { cmp!("{:?}", ()); cmp!("{:?}", (1,)); cmp!("{:?}", (1, 2)); cmp!("{:?}", (1, 2, 3)); cmp!("{:?}", (1, 2, 3, 4)); cmp!("{:?}", (1, 2, 3, 4, 5)); cmp!("{:?}", (1, 2, 3, 4, 5, 6)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7, 8)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7, 8, 9)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); cmp!("{:?}", (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)); cmp!("{:#?}", ()); cmp!("{:#?}", (1,)); cmp!("{:#?}", (1, 2)); } #[test] fn slice() { cmp!("{:?}", [0; 0]); cmp!("{:?}", [0]); cmp!("{:?}", [0, 1]); cmp!("{:#?}", [0; 0]); cmp!("{:#?}", [0]); cmp!("{:#?}", [0, 1]); } #[test] fn uwriteln() { let mut s = String::new(); uwriteln!(&mut s, "Hello").unwrap(); uwriteln!(&mut s, "World",).unwrap(); assert_eq!(s, "Hello\nWorld\n"); } #[test] fn formatter_uwrite() { #[derive(uDebug)] struct X; struct Y; impl uDebug for Y { fn fmt(&self, f: &mut Formatter<'_, W>) -> Result<(), W::Error> where W: uWrite + ?Sized, { uwrite!(f, "{:?}", X) } } assert_eq!(uformat!("{:?}", Y).unwrap(), "X") } #[test] fn generic() { #[derive(uDebug, Debug)] struct X(T); cmp!("{:?}", X(0)); #[derive(uDebug, Debug)] enum Y { Z(T), } cmp!("{:?}", Y::Z(0)); } // compile-pass test #[allow(dead_code)] fn static_lifetime(x: &'static mut u32) { fn foo(x: &'static mut u32) -> *mut u32 { x as *mut u32 } uwrite!(&mut String::new(), "{:?}", foo(x)).ok(); } // test dynamically sized writer #[test] fn dst() { struct Cursor where B: ?Sized, { pos: usize, buffer: B, } impl Cursor { fn new(buffer: B) -> Self { Cursor { pos: 0, buffer } } } impl uWrite for Cursor<[u8]> { type Error = Infallible; fn write_str(&mut self, s: &str) -> Result<(), Infallible> { let bytes = s.as_bytes(); let len = bytes.len(); let start = self.pos; if let Some(buffer) = self.buffer.get_mut(start..start + len) { buffer.copy_from_slice(bytes); self.pos += len; } Ok(()) } } let mut cursor = Cursor::new([0; 256]); let cursor: &mut Cursor<[u8]> = &mut cursor; uwrite!(cursor, "The answer is {}", 42).ok(); let msg = b"The answer is 42"; assert_eq!(&cursor.buffer[..msg.len()], msg); } #[test] fn hex() { cmp!("{:x}", 771u32); cmp!("{:x}", -10000); cmp!("{:4x}", 33); cmp!("{:4x}", 89001); cmp!("{:04x}", 33); cmp!("{:#03x}", 33); cmp!("{:#09x}", 33); cmp!("{:#x}", 71); // extreme values cmp!("{:x}", i8::min_value()); cmp!("{:x}", i8::max_value()); cmp!("{:x}", i16::min_value()); cmp!("{:x}", i16::max_value()); cmp!("{:x}", i32::min_value()); cmp!("{:x}", i32::max_value()); cmp!("{:x}", i64::min_value()); cmp!("{:x}", i64::max_value()); cmp!("{:x}", i128::min_value()); cmp!("{:x}", i128::max_value()); cmp!("{:x}", isize::min_value()); cmp!("{:x}", isize::max_value()); // ::fmt(-128) }